diff --git a/.agents/skills/impeccable/SKILL.md b/.agents/skills/impeccable/SKILL.md index 0d50553c2..1870c8541 100644 --- a/.agents/skills/impeccable/SKILL.md +++ b/.agents/skills/impeccable/SKILL.md @@ -1,65 +1,64 @@ --- name: impeccable description: Use when the user wants to design, redesign, shape, critique, audit, polish, clarify, distill, harden, optimize, adapt, animate, colorize, extract, or otherwise improve a frontend interface. Covers websites, landing pages, dashboards, product UI, app shells, components, forms, settings, onboarding, and empty states. Handles UX review, visual hierarchy, information architecture, cognitive load, accessibility, performance, responsive behavior, theming, anti-patterns, typography, fonts, spacing, layout, alignment, color, motion, micro-interactions, UX copy, error states, edge cases, i18n, and reusable design systems or tokens. Also use for bland designs that need to become bolder or more delightful, loud designs that should become quieter, live browser iteration on UI elements, or ambitious visual effects that should feel technically extraordinary. Not for backend-only or non-UI tasks. -version: 4.0.0-alpha.9 +version: 4.0.0-alpha.10 --- -Designs and iterates production-grade frontend interfaces. Real working code, committed design choices, exceptional craft. +This skill gives you the tools and permission to create design that earns to be called out-of-distribution craft: Whereas before, your design work would have been safe, timid and measured, you now approach every design task as a award-winning design director with impeccable understanding for what makes exceptional design work: production-grade code, peak creativity, a clear POV, deep understanding of the needs of the client and users, and exceptional craft. -Approach every design task as the design lead at a small studio known for giving every client a visual identity that could not be mistaken for anyone else's. The client has already rejected work that felt templated; they are paying for a point of view. GPT is capable of extraordinary work. Don't hold back. +Core principles: +- Go all out. No hedging, no shortcuts. The deliverable must be complete (except assets the user must provide). +- Dream big and bold. Distinct, beautiful, outstanding and highly inspiring work. +- Iterate with tools available to you (e.g. visual understanding, browser screenshots) until you think this meets the bar. ## Setup -1. Run `node .agents/skills/impeccable/scripts/context.mjs` once per session (if the runtime shows this skill's loaded base directory, run `node /scripts/context.mjs`; keep cwd at the user's project). It prints the project's context and its directives; follow what it prints. Once its output is in the conversation, never rerun it on a later turn. -2. `craft` and `shape` are build-path exceptions: the new-work gate below owns their flow, and on unattended runs its checkpoints resolve without pausing. For any other invoked sub-command (`audit`, `polish`, `live`, ...), immediately read **`reference/.md`** after `context.mjs` (the `.native` variant from the Commands table when the platform is `ios`/`android`/`adaptive`) and follow it. This read is a hard gate: do not inspect the target, run command-specific scripts, or edit files until the reference is loaded. -3. Read at least one project file (CSS / tokens / theme / a representative component) to learn what world you're in. If PRODUCT.md's `## Platform` is `ios` or `android`, also read `reference/.md` (`adaptive` reads both). +1. Run `node .agents/skills/impeccable/scripts/context.mjs` once per session (if the runtime shows this skill's loaded base directory, run `node /scripts/context.mjs`; keep cwd at the user's project). Pass a named source file or route as `--target `. It loads PRODUCT.md, DESIGN.md, the matching surface brief, and native-platform guidance when applicable; follow its directives and do not rerun it. +2. Before acting, load the one playbook that owns the request: the Commands table's reference for an explicit or clearly implied sub-command, or [reference/new-work.md](reference/new-work.md) for a new surface or replacement visual world. Then inspect the target and at least one representative source of incumbent visual truth (tokens, theme, CSS, component, or asset) before editing. ## How to design -**The brief wins.** Where the brief pins down a direction (a named aesthetic, an era, a place, a material, a specific font or palette), follow it exactly, including when it asks for a look this skill warns is saturated. Redirecting a pinned direction toward your own taste is a failure, not a save. - -**Existing worlds are sacred.** Most of impeccable's work happens inside a site or app that already exists. When the surface has a committed design system (real tokens, deliberately chosen faces, a palette the brand owns), work inside that world: extend it, sharpen it, leave it unmistakably the same brand, and never degrade a working page's performance. Inventing parallel colors, fonts, or styles on an existing surface is a defect, not creativity. A scoped refinement keeps the named scope's content and media footprint unless the brief explicitly changes them; build the added emphasis by recomposing what is already there through hierarchy, proportion, rhythm, and the system's own motifs. - -**New identity work reads the playbook first.** When nothing committed exists (greenfield, or a codebase with no real tokens or chosen faces), or the user explicitly asks for a redesign that discards the current look, you MUST read [reference/new-work.md](reference/new-work.md) before making any design decision. Missing DESIGN.md alone does not make work greenfield: code, tokens, chosen type, components, and assets are incumbent design authority. Preserve and extend them unless the user asked to replace them. Producing a genuinely new identity without the playbook yields the generic default this skill exists to prevent. Scoped fixes inside an existing world don't need it; the craft floor below governs them. `context.mjs` prints the appropriate authority directive. +- **The brief wins.** Honor pinned aesthetics, eras, materials, fonts, and palettes even when they conflict with a saturated-pattern warning. Redirecting a clear brief toward your taste is failure. +- **Refinement preserves; redesign replaces.** Refinement keeps the incumbent identity, behavior, copy, and everything outside scope. Ask before replacing factual copy or adding claims. Redesign keeps product truth, content, function, native affordances, and constraints, but treats the old look as evidence and anti-reference; choose a replacement world in new-work and replace DESIGN.md. Never split the difference into polish on the discarded look. +- **Visual authority is evidence, not a filename.** Missing DESIGN.md alone does not make a project greenfield; new-work decides whether to preserve, expand, or replace the incumbent world. ## Modes -Name the visitor's mode before designing; the page's grammar follows from it, and most ruined pages are one mode wearing another mode's grammar. **The mode belongs to the surface, not the subject**: a landing page for a dense technical tool is still Persuade, with Persuade's full permission to be striking; a docs page for a fashion house is still Read. Deciding a page can be plain because its subject is workmanlike is the same category error in reverse. The brief and the surface decide the mode; PRODUCT.md's `register` field survives only as a family hint (`brand` covers Persuade and Experience, `product` covers Operate and Read). Depth beyond the paragraphs below: [reference/new-work.md](reference/new-work.md) when inventing identity, [reference/operate.md](reference/operate.md) for substantial Operate and Read work. +Choose the mode from the requested surface, not the product, and persist it only in that surface brief. A tool's landing page is still Persuade; a fashion house's documentation is still Read. See [new-work.md](reference/new-work.md) for new surfaces and [operate.md](reference/operate.md) for deeper Operate/Read guidance. -**Persuade** (the surface exists to win someone over; design IS the product). The deliverable is an impression that stops the scroll, earns the click, converts. Spans every genre; don't collapse them into one look. On new surfaces, briefs that imply imagery must ship real, verified imagery; a colored rectangle where a photo belongs reads as incomplete. New Persuade surfaces take their typeface procedure and reject list from [reference/new-work.md](reference/new-work.md). - -**Operate** (the surface is a tool someone works in; design SERVES the task). A person getting something done: scanability and consistency outrank expressiveness. These surfaces earn trust by feeling native to their platform: system font stacks and workhorse UI faces are legitimate and often correct here (the Persuade reject list does not apply). The brand lives in the details: focus states, empty states, microcopy, one owned accent. The usage scene is part of the spec: an interface read outdoors, in motion, or at a glance must survive its real ambient light, and the theme follows the scene, not the category's habit. - -**Read** (the surface exists to be understood; long-form, reference, guidance). The deliverable is comprehension, and comprehension is earned twice: a structure the reader can hold in their head with nothing standing between them and the answer, and a reading experience good enough to stay in, through typographic quality and whatever visual or interactive support genuinely helps the reader follow. The brand lives in type, spacing, and small accents. - -**Experience** (the surface presents a body of work; the page IS the work). The artifact leads, the interface recedes, and the visitor meets the work itself in the first viewport at every screen size. Boldness here means trusting the work. +- **Persuade:** win someone over; design is the product. Earn attention and action. Ship real imagery when the brief needs it; follow the committed world, not category habit. +- **Operate:** help someone do work. Scanability, consistency, native expectations, and the real usage scene outrank expression. Brand lives in precise details. +- **Read:** make something understood. Structure for comprehension, then make the reading experience worth staying in. +- **Experience:** present a body of work. Let the artifact lead from the first viewport; the interface recedes. ## Craft floor -Build to this floor without announcing it. The design detector (the project hook, `node .agents/skills/impeccable/scripts/detect.mjs --json `, or `audit`) verifies most of it mechanically; resolve every finding before finalizing. Fix real defects, but use context judgment rather than distorting intentional design to appease a false positive. Classify any intentional exception explicitly and use the hook system's narrowest appropriate waiver when it must persist. +Build to this floor without announcing it. -- Contrast: body text ≥4.5:1 against its background (placeholders too); large text ≥3:1. Gray text on a colored background looks washed out: use a darker shade of the background's own hue, or a transparency of the text color. -- Shadows describe real light: an offset and a soft blur. A zero-offset colored halo is decoration announcing itself. -- Spacing has rhythm: generous separations, tight groupings; cramped padding reads as broken; the space above a heading exceeds the space below it. Verify computed spacing, not intended spacing. -- Type: body line length 65-75ch; display clamp() max ≤6rem; letter-spacing ≥-0.04em; `text-wrap: balance` on headings; modular scale ≥1.25 between steps; light-on-dark adds 0.05-0.1 line-height. Pair faces on a contrast axis, never two similar-but-not-identical ones; one family with committed weight contrast beats a timid pair. Test headings at every breakpoint; overflow means reduce the clamp or rewrite the copy. -- Motion is part of the build: one orchestrated moment beats scattered effects; ease-out exponential curves; reveals enhance an already-visible default (content gated on a class-triggered transition ships blank in hidden tabs and headless renderers). Responsive down to mobile and visible keyboard focus are part of the floor. -- Ship real content (no placeholders, dead links, or fake controls) and cover the interaction states people will actually hit (hover, focus, disabled, loading, error, empty). -- Copy is design material: name things the way the page's own people speak, make every control say what it does, and make every error say what happened and what to do next. -- Before finishing, re-read the brief: every requirement it names must exist on the page, findable in seconds. A beautiful page missing an asked-for feature is unfinished. +- **Contrast:** body and placeholder text ≥4.5:1; large text ≥3:1. On colored surfaces, tint secondary text from that hue or the foreground instead of using gray. +- **Depth:** shadows describe light with offset and soft blur; zero-offset colored halos are decoration. +- **Spacing:** tight groups, generous separation, no cramped containers; space above a heading exceeds space below. Verify computed values. +- **Type:** body measure 65–75ch; display max 6rem and tracking floor -0.04em; balance headings; use clear scale/weight contrast; test overflow at every breakpoint. +- **Motion:** author one coherent moment instead of scattered effects. Use exponential ease-out and an already-visible default. Premium motion is not transform/opacity alone: it may add focus, depth, masks, light, or material change through blur/filter, backdrop-filter, clip-path/masks, or shadow when smooth. Always provide reduced motion. +- **Shipping:** real content, working controls, responsive composition, keyboard focus, and the states users hit: hover, disabled, loading, error, and empty. +- **Copy:** use the product's language; controls name their action, errors name the problem and recovery. +- **Coverage:** every brief requirement must exist and be findable within seconds. + +Before finishing changed UI, follow the quality guidance supplied by `context.mjs` and hooks. Context requests a manual scan only when no automatic detector is active; never add a second detector pass. Calibration for this provider: -- Display letter-spacing floor is -0.04em; -0.02 to -0.03em is plenty for tight grotesque display. Your default runs tighter and the letters touch. -- An element declares its elevation once: a border or a shadow, chosen deliberately, never both as decoration. Corner radius is a brand decision made once; containers keep it modest, and full rounding belongs to small controls. -- Illustration is real or absent; a sketched stand-in reads as filler. Backgrounds are surfaces, not decoration; texture appears only when the subject's world supplies it. Copy makes the specific claim instead of staging a concept to react to. +- Display tracking stops at -0.04em; -0.02 to -0.03em is usually enough. +- Declare elevation once: border or shadow, not both as decoration. Keep container radii modest; reserve pills for small controls. +- Use real illustration or none. Treat backgrounds as surfaces, add texture only from the subject's world, and make specific claims without meta-commentary. ## Commands | Command | Category | Description | Reference | |---|---|---|---| -| `craft [feature]` | Build | The standard build flow with attended checkpoints | [reference/new-work.md](reference/new-work.md) | +| `craft [feature]` | Build | Deprecated alias for an ordinary new-work request | [reference/craft.md](reference/craft.md) | | `shape [feature]` | Build | Plan UX/UI before writing code | [reference/shape.md](reference/shape.md) | -| `init` | Build | Set up project context: PRODUCT.md, DESIGN.md, live config, next steps | [reference/init.md](reference/init.md) | +| `init` | Build | Capture durable product context in PRODUCT.md | [reference/init.md](reference/init.md) | | `document` | Build | Generate DESIGN.md from existing project code | [reference/document.md](reference/document.md) | | `extract [target]` | Build | Pull reusable tokens and components into design system | [reference/extract.md](reference/extract.md) | | `critique [target]` | Evaluate | UX design review with heuristic scoring | [reference/critique.md](reference/critique.md) | @@ -81,7 +80,14 @@ Calibration for this provider: | `optimize [target]` | Fix | Diagnose and fix UI performance | [reference/optimize.md](reference/optimize.md) | | `live` | Iterate | Visual variant mode: pick elements in the browser, generate alternatives | [reference/live.md](reference/live.md) | -Routing: **no argument** → read [reference/routing.md](reference/routing.md) and present the context-aware menu (never auto-run a command). **First word matches a command** (or `pin` / `unpin` / `hooks`) → load its reference (native variant on native platforms) and follow it; everything after the command name is the target. **Intent clearly maps to one command** ("fix the spacing" → `layout`, "rewrite this error" → `clarify`) → same; if two fit, ask once. **Otherwise** → general design invocation: apply Setup and this file's guidance; builds flow through the new-work gate above, whose playbook carries the direction checkpoint and the finishing pass. `teach` routes to `init`, and `craft` routes to the standard build flow with attended checkpoints. If setup diverted into `init` for a build request, finish init, refresh context, then resume. +Routing: + +- **No argument:** read [routing.md](reference/routing.md) and present its context-aware menu; never auto-run a command. +- **Explicit or clearly implied command:** load its reference (native variant on native platforms) and follow it. Ask once if two commands fit. +- **Otherwise:** treat the request as general design work. Missing PRODUCT.md routes through init; new surfaces and replacement worlds use new-work. +- `teach` aliases `init`. `craft` is a deprecated alias for ordinary new-work and adds nothing. `shape` owns task discovery, then enters new-work only for visual-world and surface-concept decisions. + +After init writes PRODUCT.md, resume without rerunning `context.mjs`. **Pin / Unpin:** `node .agents/skills/impeccable/scripts/pin.mjs ` creates or removes a standalone `$` shortcut. Report the script's result concisely; relay stderr verbatim on error. diff --git a/.agents/skills/impeccable/reference/android.md b/.agents/skills/impeccable/reference/android.md index 3575c31fb..6337b9018 100644 --- a/.agents/skills/impeccable/reference/android.md +++ b/.agents/skills/impeccable/reference/android.md @@ -2,7 +2,7 @@ For native Android apps: Jetpack Compose, Android Views, React Native, Expo, Flutter shipping to Android hardware. -On native, register narrows. Material Design 3 governs structure, navigation, and interaction whatever the register; brand expresses through Material's theming (color roles, type scale, shape, motion). A Material-everywhere cross-platform app that also ships to iPhone still owes iOS its OS guarantees on that hardware: safe-area insets, Reduce Motion, edge-swipe back. +On native, the visitor mode narrows what expression may override. Material Design 3 governs structure, navigation, and interaction in every mode; brand expresses through Material's theming (color roles, type scale, shape, motion). A Material-everywhere cross-platform app that also ships to iPhone still owes iOS its OS guarantees on that hardware: safe-area insets, Reduce Motion, edge-swipe back. ## The Android slop test diff --git a/.agents/skills/impeccable/reference/animate.md b/.agents/skills/impeccable/reference/animate.md index f3d504c90..4226b4d4f 100644 --- a/.agents/skills/impeccable/reference/animate.md +++ b/.agents/skills/impeccable/reference/animate.md @@ -1,203 +1,88 @@ > **Additional context needed**: performance constraints. -Add motion that conveys state, gives feedback, and clarifies hierarchy. Cut motion that exists only for decoration. Animation fatigue is a real cost; spend the budget on the moments that need it. +Use motion to explain state, relationship, and hierarchy, or to create one authored moment the surface has earned. Decoration without purpose is animation debt. --- -## Register +## Visitor mode -Persuade + Experience: 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. +- **Persuade + Experience:** motion may carry the voice. Prefer one rehearsed focal sequence to repeated section reveals. +- **Operate + Read:** motion serves feedback, state, and continuity. Keep routine transitions fast and do not make users wait through page-load choreography. +- **Native (`ios` / `android` / `adaptive`):** follow the Motion section of [ios.md](ios.md) or [android.md](android.md), including the platform's Reduce Motion behavior. Do not apply the web tooling below. -Operate + Read: 150–250 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. +## Find the job -Native (`ios` / `android` / `adaptive`): implementation follows the Motion section of [ios.md](ios.md) / [android.md](android.md) (read it first if Setup hasn't already): system transitions and OS Reduce Motion, never the web tooling below. +Inspect the existing motion language, interaction states, target devices, and performance budget. Find only the places where motion would: ---- +- acknowledge an action; +- make a state change or spatial relationship legible; +- preserve continuity through navigation or layout change; +- direct attention at a meaningful moment; +- embody the selected visual world. -## Assess Animation Opportunities +Ask only when a material constraint cannot be inferred. Do not animate a static area merely because it exists. -Analyze where motion would improve the experience: +## Set the motion thesis -1. **Identify static areas**: - - **Missing feedback**: Actions without visual acknowledgment (button clicks, form submission, etc.) - - **Jarring transitions**: Instant state changes that feel abrupt (show/hide, page loads, route changes) - - **Unclear relationships**: Spatial or hierarchical relationships that aren't obvious - - **Lack of delight**: Functional but joyless interactions - - **Missed guidance**: Opportunities to direct attention or explain behavior +Write a short plan before implementation: -2. **Understand the context**: - - What's the personality? (Playful vs serious, energetic vs calm) - - What's the performance budget? (Mobile-first? Complex page?) - - Who's the audience? (Motion-sensitive users? Power users who want speed?) - - What matters most? (One hero animation vs many micro-interactions?) +- **Focal moment:** the one sequence or interaction that deserves authorship, if any. +- **Continuity:** the state, layout, or navigation changes that need explanation. +- **Feedback:** the controls and outcomes that need acknowledgment. +- **Budget:** which effects may be expensive, how often they run, and the reduced-motion equivalent. -If any of these are unclear from the codebase, STOP and use Codex's structured user-input/question tool when available; if unavailable, ask directly in chat to clarify what you cannot infer. +The focal moment must come from this product and surface concept. A generic fade-and-rise, hover lift, parallax layer, or scroll reveal is not a thesis. -**CRITICAL**: Respect `prefers-reduced-motion`. Always provide non-animated alternatives for users who need them. +## Choose material by meaning -## Plan Animation Strategy +Transform and opacity are reliable foundations, not the entire palette. Choose properties for what the transition communicates: -Create a purposeful animation plan: +- **Continuity and relationship:** shared-element motion, FLIP-style transforms, view transitions, or deliberate spatial movement. +- **Focus and depth:** bounded blur, filter, backdrop, light, or shadow changes. +- **Reveal and composition:** masks, clip paths, cropping, or controlled occlusion. +- **Material and energy:** color, gradient position, texture, distortion, or shader effects when the world and runtime support them. +- **State and feedback:** the smallest change that makes cause and result unmistakable. -- **Feedback layer**: Which interactions need acknowledgment? -- **Transition layer**: Which state changes need smoothing? -- **Entrance moment**: The ONE entrance worth rehearsing, where the mode invites it. Not every surface wants one. -- **Delight layer**: Where can we surprise and delight? +Do not stack techniques for spectacle. One strong material idea, carried through the focal sequence and quiet supporting states, is usually enough. -**IMPORTANT**: One well-orchestrated experience beats scattered animations everywhere. Focus on high-impact moments. +Sibling stagger is appropriate when a list appears as a list. Cap the total delay, and never reinterpret every scrolled section as a staggered list. -## Implement Animations +## Timing and easing -Add motion systematically across these categories: +Timing should express distance and consequence: -### Micro-interactions -- **Button feedback**: - - Hover: Subtle scale (1.02-1.05), color shift, shadow increase - - Click: Quick scale down then up (0.95 → 1), ripple effect - - Loading: Spinner or pulse state -- **Form interactions**: - - Input focus: Border color transition, slight scale or glow - - Validation: Shake on error, check mark on success, smooth color transitions -- **Toggle switches**: Smooth slide + color transition (200-300ms) -- **Checkboxes/radio**: Check mark animation, ripple effect -- **Like/favorite**: Scale + rotation, particle effects, color transition +| Duration | Typical use | +|---|---| +| 100–150 ms | immediate feedback | +| 150–300 ms | routine state change | +| 300–500 ms | layout, overlay, or view transition | +| 500–800 ms | a deliberately authored focal entrance | -### State Transitions -- **Show/hide**: Fade + slide (not instant), appropriate timing (200-300ms) -- **Expand/collapse**: Height transition with overflow handling, icon rotation -- **Loading states**: Skeleton screen fades, spinner animations, progress bars -- **Success/error**: Color transitions, icon animations, gentle scale pulse -- **Enable/disable**: Opacity transitions, cursor changes +Exit faster than entrance. Use natural deceleration such as `cubic-bezier(0.16, 1, 0.3, 1)` for confident arrivals; do not use bounce or elastic curves by reflex. Long feedback feels like latency. -### Entrance Animations -- **The one entrance moment** (where the mode invites it): a committed entrance for primary content (scale, parallax, or a creative effect) -- **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. +## Implement to the runtime - 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. +- Use CSS transitions and keyframes for declarative state and bounded sequences. +- Use Web Animations API or the project's existing motion library for interruption, sequencing, and dynamic values. +- Use View Transitions or shared-element techniques when continuity across states is the point. +- Use scroll-driven motion only when the scroll relationship itself carries meaning, with a robust fallback. +- Do not add a dependency for an effect the existing stack can express cleanly. -### Navigation & Flow -- **Page transitions**: Crossfade between routes, shared element transitions -- **Tab switching**: Slide indicator, content fade/slide -- **Carousel/slider**: Smooth transforms, snap points, momentum -- **Scroll effects**: Parallax layers, sticky headers with state changes, scroll progress indicators +Keep content visible in the default state so failed scripts do not hide the page. Avoid casually animating layout-driving properties such as `width`, `height`, `top`, `left`, and margins; use FLIP, transforms, or grid techniques when appropriate. Bound blur, filter, shadow, canvas, and shader work to isolated regions. Apply `will-change` only during known animation. Measure on target viewports and devices rather than assuming transform means fast. -### Feedback & Guidance -- **Hover hints**: Tooltip fade-ins, cursor changes, element highlights -- **Drag & drop**: Lift effect (shadow + scale), drop zone highlights, smooth repositioning -- **Copy/paste**: Brief highlight flash on paste, "copied" confirmation -- **Focus flow**: Highlight path through form or workflow +## Accessibility and control -### Delight Moments -- **Empty states**: Subtle floating animations on illustrations -- **Completed actions**: Confetti, check mark flourish, success celebrations -- **Easter eggs**: Hidden interactions for discovery -- **Contextual animation**: Weather effects, time-of-day themes, seasonal touches +Honor `prefers-reduced-motion` with an intentional alternative. Preserve state change and hierarchy while removing travel, parallax, flashing, or prolonged sequences. Do not replace every animation globally with `0.01ms` if that destroys useful feedback. Motion must never block focus, interaction, reading, or task completion. -## Technical Implementation +Respect autoplay and sound preferences. Any nonessential loop must stop when offscreen or hidden. -Use appropriate techniques for each animation: +## Verify -### Timing & Easing +- The focal motion is specific to the selected world and surface. +- Every supporting animation explains feedback, state, or relationship. +- Interruption and repeated use behave correctly. +- Desktop, mobile, keyboard, and reduced-motion paths remain usable. +- Expensive effects stay smooth on the target device. +- Removing an animation would lose meaning or authored character, not merely decoration. -**Duration: the 100/300/500 rule.** Timing matters more than easing for "feels right": - -| Duration | Use Case | Examples | -|----------|----------|----------| -| **100–150ms** | Instant feedback | Button press, toggle, color change | -| **200–300ms** | State changes | Menu open, tooltip, hover state | -| **300–500ms** | Layout changes | Accordion, modal, drawer | -| **500–800ms** | Entrance animations | Page load, hero reveal | - -**Easing curves (use these, not CSS defaults):** -```css -/* Recommended: natural deceleration */ ---ease-out-quart: cubic-bezier(0.25, 1, 0.5, 1); /* Smooth */ ---ease-out-quint: cubic-bezier(0.22, 1, 0.36, 1); /* Slightly snappier */ ---ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1); /* Confident, decisive */ - -/* AVOID: feel dated and tacky */ -/* bounce: cubic-bezier(0.34, 1.56, 0.64, 1); */ -/* elastic: cubic-bezier(0.68, -0.6, 0.32, 1.6); */ -``` - -**Exit animations are faster than entrances.** Use ~75% of enter duration. - -### CSS Animations -```css -/* Prefer for simple, declarative animations */ -- transitions for state changes -- @keyframes for complex sequences -- transform and opacity for reliable movement -- blur, filters, masks, clip paths, shadows, and color shifts for premium atmospheric effects when verified smooth -``` - -### JavaScript Animation -```javascript -/* Use for complex, interactive animations */ -- Web Animations API for programmatic control -- Framer Motion for React -- 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 -- **Layout safety**: Avoid casual animation of layout-driving properties (`width`, `height`, `top`, `left`, margins) -- **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) { - * { - animation-duration: 0.01ms !important; - animation-iteration-count: 1 !important; - transition-duration: 0.01ms !important; - } -} -``` - -**NEVER**: -- Use bounce or elastic easing curves; they feel dated and draw attention to the animation itself -- Animate layout properties casually (`width`, `height`, `top`, `left`, margins) when transform, FLIP, or grid-based techniques would work -- Use durations over 500ms for feedback (it feels laggy) -- Animate without purpose (every animation needs a reason) -- Ignore `prefers-reduced-motion` (this is an accessibility violation) -- Animate everything (animation fatigue makes interfaces feel exhausting) -- Block interaction during animations unless intentional - -## Verify Quality - -Test animations thoroughly: - -- **Smooth at 60fps**: No jank on target devices -- **Feels natural**: Easing curves feel organic, not robotic -- **Appropriate timing**: Not too fast (jarring) or too slow (laggy) -- **Reduced motion works**: Animations disabled or simplified appropriately -- **Doesn't block**: Users can interact during/after animations -- **Adds value**: Makes interface clearer or more delightful - -When the motion clarifies state instead of decorating it, hand off to `$impeccable polish` for the final pass. +When motion earns its place, hand off to `$impeccable polish` for the final pass. diff --git a/.agents/skills/impeccable/reference/audit.md b/.agents/skills/impeccable/reference/audit.md index 7769a4398..24553d95b 100644 --- a/.agents/skills/impeccable/reference/audit.md +++ b/.agents/skills/impeccable/reference/audit.md @@ -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 - diff --git a/.agents/skills/impeccable/reference/audit.native.md b/.agents/skills/impeccable/reference/audit.native.md index cc3f01ffd..21bc5586f 100644 --- a/.agents/skills/impeccable/reference/audit.native.md +++ b/.agents/skills/impeccable/reference/audit.native.md @@ -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) diff --git a/.agents/skills/impeccable/reference/bolder.md b/.agents/skills/impeccable/reference/bolder.md index b158c694e..9fe39ca59 100644 --- a/.agents/skills/impeccable/reference/bolder.md +++ b/.agents/skills/impeccable/reference/bolder.md @@ -8,12 +8,12 @@ ## Why it reads flat -A section usually reads flat for reasons its neighbors have already solved. Look at what the rest of the page does that this section does not: the display type at full strength, the structural devices that carry meaning, the signature motif, the density and pacing, the specificity of the copy. A flat section is typically one that quietly opts out of the system's own strongest moves. The most reliable bolder pass brings the target up to the expressive level its neighbors already reach, in the system's own vocabulary rather than a new one. +A section usually reads flat for reasons its neighbors have already solved. Look at what the rest of the page does that this section does not: the display type at full strength, the structural devices that carry meaning, the signature motif, the density and pacing. A flat section is typically one that quietly opts out of the system's own strongest moves. The most reliable bolder pass brings the target up to the expressive level its neighbors already reach, in the system's own vocabulary rather than a new one. ## The amplification - **Amplify what the system already owns.** Reuse its motif and its type scale at full strength, turned up for this section rather than invented for it. The bolder version should look more like the same brand, not less. -- **Let the content carry the weight.** Flat copy makes a flat design, and no amount of size fixes a generic claim. Make every word specific enough to earn its place, and let the section's real evidence do the work that a decorative addition would otherwise fake. +- **Keep content true.** Existing claims are part of the scope: preserve them unless the user supplies replacements. If real evidence is essential to the direction but absent, ask for it. - **Commit, then clarify.** Half-measures read as noise. Make the one decisive move completely, then quiet everything around it so the move is legible. If every element got louder, the section got flatter. - **Give it its own rhythm.** The target should read as a peak in the scroll, a shift in density or pace from what surrounds it, not simply more of the same. diff --git a/.agents/skills/impeccable/reference/clarify.md b/.agents/skills/impeccable/reference/clarify.md index 123f294d1..4506a31c9 100644 --- a/.agents/skills/impeccable/reference/clarify.md +++ b/.agents/skills/impeccable/reference/clarify.md @@ -1,288 +1,94 @@ -> **Additional context needed**: audience technical level and users' mental state in context. +> **Additional context needed**: audience knowledge and emotional state. -Find the unclear, confusing, or poorly written interface text and rewrite it. Vague copy creates support tickets and abandonment; specific copy gets users through the task. +Rewrite unclear interface text so users understand what happened, what matters, and what to do next. Preserve factual meaning, product terminology, and brand voice. +## Audit the language ---- +Read the entire interaction path, not isolated strings. Identify: -## Assess Current Copy +- ambiguous nouns, verbs, and actions; +- internal jargon or assumed knowledge; +- vague labels, outcomes, and system states; +- missing consequences, recovery, or timing; +- inconsistent terminology and capitalization; +- redundant headings, intros, helper text, and confirmations; +- text that breaks at realistic widths or in translation; +- tone that ignores stress, risk, success, or urgency. -Identify what makes the text unclear or ineffective: +Infer audience and task from product context and surrounding UI. Ask before changing factual claims, legal meaning, or a term that may be domain-specific. -1. **Find clarity problems**: - - **Jargon**: Technical terms users won't understand - - **Ambiguity**: Multiple interpretations possible - - **Passive voice**: "Your file has been uploaded" vs "We uploaded your file" - - **Length**: Too wordy or too terse - - **Assumptions**: Assuming user knowledge they don't have - - **Missing context**: Users don't know what to do or why - - **Tone mismatch**: Too formal, too casual, or inappropriate for situation +## Set the message hierarchy -2. **Understand the context**: - - Who's the audience? (Technical? General? First-time users?) - - What's the user's mental state? (Stressed during error? Confident during success?) - - What's the action? (What do we want users to do?) - - What's the constraint? (Character limits? Space limitations?) +For each state, decide: -**CRITICAL**: Clear copy helps users succeed. Unclear copy creates frustration, errors, and support tickets. +1. the one fact the user needs now; +2. the action available next; +3. supporting context that changes the decision; +4. the appropriate tone for this moment. -## Plan Copy Improvements +Say each idea once. If the heading already explains the state, the introduction should add new information or disappear. -Create a strategy for clearer communication: +## Rewrite by function -- **Primary message**: What's the ONE thing users need to know? -- **Action needed**: What should users do next (if anything)? -- **Tone**: How should this feel? (Helpful? Apologetic? Encouraging?) -- **Constraints**: Length limits, brand voice, localization considerations +### Actions and navigation -**IMPORTANT**: Good UX writing is invisible. Users should understand immediately without noticing the words. +Use a specific verb and object when the outcome is not already obvious. Labels should describe what will happen, not the gesture used to trigger it. Keep the same noun and verb for the same concept throughout the product. -## Improve Copy Systematically +For destructive actions, name the object and consequence. Prefer undo over confirmation when recovery is safe. When confirmation is necessary, name the action on both the message and button instead of using `Yes`, `No`, `OK`, or `Submit`. -Refine text across these common areas: +### Forms -### Error Messages -**Bad**: "Error 403: Forbidden" -**Good**: "You don't have permission to view this page. Contact your admin for access." +Use persistent labels; placeholders are examples, not labels. Put format and eligibility requirements before submission. Explain why information is requested only when it is not obvious. Required and optional treatment should be consistent. -**Bad**: "Invalid input" -**Good**: "Email addresses need an @ symbol. Try: name@example.com" +Validation says what needs attention and how to correct it without blaming the user. Keep related instructions near the field and announce errors accessibly. -**Principles**: -- Explain what went wrong in plain language -- Suggest how to fix it -- Don't blame the user -- Include examples when helpful -- Link to help/support if applicable - -### Form Labels & Instructions -**Bad**: "DOB (MM/DD/YYYY)" -**Good**: "Date of birth" (with placeholder showing format) - -**Bad**: "Enter value here" -**Good**: "Your email address" or "Company name" - -**Principles**: -- Use clear, specific labels (not generic placeholders) -- Show format expectations with examples -- Explain why you're asking (when not obvious) -- Put instructions before the field, not after -- Keep required field indicators clear - -### Button & CTA Text -**Bad**: "Click here" | "Submit" | "OK" -**Good**: "Create account" | "Save changes" | "Got it, thanks" +### Errors and permissions -**Principles**: -- Describe the action specifically -- Use active voice (verb + noun) -- Match user's mental model -- Be specific ("Save" is better than "OK") +An actionable error answers: -### Help Text & Tooltips -**Bad**: "This is the username field" -**Good**: "Choose a username. You can change this later in Settings." +1. what failed; +2. why, when known and useful; +3. how to recover or what alternative remains. -**Principles**: -- Add value (don't just repeat the label) -- Answer the implicit question ("What is this?" or "Why do you need this?") -- Keep it brief but complete -- Link to detailed docs if needed +Do not expose internal codes as the primary message. Do not promise a cause or resolution the system cannot know. Treat privacy, payment, deletion, access loss, and blocked work seriously; warmth is welcome, jokes are not. -### Empty States -**Bad**: "No items" -**Good**: "No projects yet. Create your first project to get started." +### Loading, empty, and success states -**Principles**: -- Explain why it's empty (if not obvious) -- Show next action clearly -- Make it welcoming, not dead-end +Loading text names the real operation and sets an honest expectation when the wait is meaningful. Show determinate progress when available; never invent progress. -### Success Messages -**Bad**: "Success" -**Good**: "Settings saved! Your changes will take effect immediately." +An empty state distinguishes first use, no results, filters, permissions, and failure. Explain the state and provide the next useful action. -**Principles**: -- Confirm what happened -- Explain what happens next (if relevant) -- Be brief but complete -- Match the user's emotional moment (celebrate big wins) - -### Loading States -**Bad**: "Loading..." (for 30+ seconds) -**Good**: "Analyzing your data... this usually takes 30-60 seconds" - -**Principles**: -- Set expectations (how long?) -- Explain what's happening (when it's not obvious) -- Show progress when possible -- Offer escape hatch if appropriate ("Cancel") +Success confirms the completed outcome and mentions the next consequence only when it changes what the user should do. Routine success should be brief. -### Confirmation Dialogs -**Bad**: "Are you sure?" -**Good**: "Delete 'Project Alpha'? This can't be undone." +### Help and instructional text -**Principles**: -- State the specific action -- Explain consequences (especially for destructive actions) -- Use clear button labels ("Delete project" not "Yes") -- Don't overuse confirmations (only for risky actions) +Helper text answers an implicit question instead of restating the control. Use progressive disclosure for uncommon detail. Link text must make sense out of context; icon-only controls need accessible names. -### Navigation & Wayfinding -**Bad**: Generic labels like "Items" | "Things" | "Stuff" -**Good**: Specific labels like "Your projects" | "Team members" | "Settings" +## Voice, accessibility, and localization -**Principles**: -- Be specific and descriptive -- Use language users understand (not internal jargon) -- Make hierarchy clear -- Consider information scent (breadcrumbs, current location) +Voice stays consistent; tone adapts to the moment. Use plain language without flattening terminology the audience genuinely knows. -## Apply Clarity Principles +- Write complete translatable messages rather than concatenated fragments. +- Keep variables and numbers structured so translators can reorder them. +- Allow expansion instead of abbreviating prematurely. +- Make alt text convey the image's information; use empty alt for decoration. +- Keep screen-reader names aligned with visible labels and outcomes. +- Do not rely on punctuation, color, or iconography to carry the message alone. -Every piece of copy should follow these rules: +Maintain a short terminology glossary when inconsistency spans the product. Do not vary words for literary effect in an interface. -1. **Be specific**: "Enter email" not "Enter value" -2. **Be concise**: Cut unnecessary words (but don't sacrifice clarity) -3. **Be active**: "Save changes" not "Changes will be saved" -4. **Be human**: "Oops, something went wrong" not "System error encountered" -5. **Tell users what to do**, not just what happened -6. **Be consistent**: Use same terms throughout (don't vary for variety) +## Verify -**NEVER**: -- Use jargon without explanation -- Blame users ("You made an error" → "This field is required") -- Be vague ("Something went wrong" without explanation) -- Use passive voice unnecessarily -- Write overly long explanations (be concise) -- Use humor for errors (be empathetic instead) -- Assume technical knowledge -- Vary terminology (pick one term and stick with it) -- Repeat information (headers restating intros, redundant explanations) -- Use placeholders as the only labels (they disappear when users type) +Read the flow in context and test: -## Verify Improvements +- comprehension without hidden product knowledge; +- actionability at errors, empty states, and decision points; +- factual accuracy and consistent terminology; +- scanability at target widths and 200% zoom; +- long names, localization expansion, pluralization, and dynamic values; +- accessible names and announced state changes; +- tone appropriate to consequence and emotional context. -Test that copy improvements work: +The final copy is as short as it can be without removing meaning or recovery. -- **Comprehension**: Can users understand without context? -- **Actionability**: Do users know what to do next? -- **Brevity**: Is it as short as possible while remaining clear? -- **Consistency**: Does it match terminology elsewhere? -- **Tone**: Is it appropriate for the situation? - -When the copy reads cleanly, hand off to `$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. +When the language reads cleanly, hand off to `$impeccable polish` for the final pass. diff --git a/.agents/skills/impeccable/reference/codex.md b/.agents/skills/impeccable/reference/codex.md index 08f3f80ea..6c550f1dc 100644 --- a/.agents/skills/impeccable/reference/codex.md +++ b/.agents/skills/impeccable/reference/codex.md @@ -1,105 +1,38 @@ -# Codex: Visual Direction & Asset Production +# Codex: Surface Probes & Asset Production -This file is loaded by `$impeccable craft` when the harness has native image generation (currently Codex via `image_gen`). Other harnesses skip it. It covers the two craft steps that depend on real image generation: landing the visual direction, and producing the raster assets the implementation will compose. +Load this from [new-work.md](new-work.md) only when the harness has native image generation and a substantial, high-fidelity surface would benefit from seeing the shortlisted concept before code. PRODUCT.md and DESIGN.md are preconditions. New-work has already resolved the visual world; this file must not reopen it. -Read this *before* generating any images. The order matters, and the per-step user pauses are what keep generated imagery from drifting away from the brief. +The purpose of a probe is to test composition, narrative, hierarchy, density, focal moment, signature use, and image requirements. It is not a second identity workshop. Keep DESIGN.md's palette, typography direction, material language, component character, imagery stance, and motion grammar fixed. -### Four stop points before code +## Generate the smallest useful probe set -Steps A through D each end with the user. Do not advance past any of them on your own read of the situation. +Generate one to three high-fidelity north-star comps using the native image-generation capability. Base them on the real content and the surface concepts already developed with the user. -1. **STOP after Step A questions.** Wait for answers. -2. **STOP after Step B palette generation.** Wait for "confirm palette." -3. **STOP after Step C mocks.** Wait for direction approval or delegation. -4. **Only after Step D approves a direction** do you return to the build (new-work.md's plan, then code). +- When the user shortlisted multiple concepts, show one clear expression of each. +- When one concept is already selected, vary only the structural uncertainty that the image can resolve: topology, sequence, density, hierarchy, focal composition, or interaction framing. +- Show enough beyond the opening moment to prove the concept can govern the whole requested surface. +- Do not generate a palette artifact, ask new atmosphere questions, introduce a different type voice, or invent a new motif. If the committed world cannot support the concept, return to the concept shortlist rather than changing the world. -Prior shape approval does **not** satisfy any of these. Shape's "confirm or override" advances you into Step A; it is not a substitute for it. +Treat each comp as a direction test, not a screenshot specification. Core UI text, responsive behavior, accessibility, semantics, and interaction states remain implementation responsibilities. -## Step A: Explore Directions with the User +## One approval point -Before generating anything, run a brief direction conversation grounded in the shape brief. +Show the probes together and ask what should carry forward, what feels false to the world, and whether the selected surface concept should be approved, combined, revised, or rejected. Then stop and wait. A structured simulated user counts as attended and receives the same question. -**Step A is required even when shape just produced a confirmed brief.** The shape questions and Step A questions cover different ground: shape pins purpose, content, scope; Step A pins palette, atmosphere, and named visual references for the comps you're about to generate. The only time you can skip Step A is when the user has already answered these exact palette/atmosphere/reference questions in the same session. +Do not begin code until the user approves a direction or explicitly delegates the choice. If they delegate, choose using the task brief, PRODUCT.md, and DESIGN.md, and state the evidence. Approval refines the task concept; it does not modify DESIGN.md. -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: +After approval, summarize the composition and the parts of the comp that must not be literalized. Return to new-work.md and write the direction contract from the approved surface concept before code. -- "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.)" +## Inventory implementation fidelity -**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. +Before building, inventory the approved comp's major visible ingredients and choose an implementation medium for each: semantic HTML/CSS/SVG, existing project asset, generated raster, sourced raster, icon library, canvas/WebGL, or accepted omission. -## Step B: Generate the Brand Palette First +Pay special attention to the dominant composition, signature use, image-native content, second-fold system, and any interaction the still image only implies. If the concept depends on a photograph, architectural scene, product object, portrait, or other raster-native material, do not silently replace it with generic CSS scenery. -Generate **one** palette artifact before any mocks. This is a small, focused image: typography pairing on the chosen background, primary + accent color swatches, one signature ornament or motif. Single image, single pass. +Treat the comp as a north star, not something to trace. Do not rasterize core UI text or controls. Do not substitute a different visual driver after approval without asking. -Why palette first: mocks generated against a vague color sense produce noise that drowns out the structural decisions. A confirmed palette is the first concrete contract for everything downstream. +## Produce only the assets the build needs -Show the palette to the user. Ask one question: "This is the palette I'm locking in for the mocks. Confirm, or call out what to shift?" +When clean raster ingredients are required and a scoped subagent is available and authorized, use `impeccable_asset_producer`. Give it the approved comp, output paths, required dimensions and formats, transparency needs, crop notes, and what must remain semantic code. Otherwise produce the minimum required assets with the native image-generation capability in the current thread. -**STOP and wait for confirmation.** Do not generate mocks against an unconfirmed palette. "Probably good enough" is the wrong call here; the palette is the contract for everything downstream. - -## Step C: Generate 1-3 Visual Mocks Against the Palette - -Once the palette is confirmed, generate **1 to 3** high-fidelity north-star comps. Each mock must use the confirmed palette and typography. Mocks differ in *structural* direction (hierarchy, topology, density, composition), not in color or motif. - -- Brand work: push visual identity, composition, mood, and signature motifs. -- Product work: push hierarchy, topology, density, tone, grounded in realistic product structure. -- Landing pages and long-form brand surfaces: show enough of the second fold to establish the system beyond the hero. - -Use the `image_gen` tool directly (or via the imagegen skill when available). Don't ask the user to install anything. - -## Step D: Approval Loop - -Show the comps. Ask what carries forward. Iterate until **one direction is approved** or the user explicitly delegates. - -**STOP and wait for the approval or the delegation.** Do not begin Step E or return to the build until a single direction is named. If the user delegates, pick the strongest direction and explain it from the brief, not personal taste. - -Before moving to assets, summarize what to carry into code and what *not* to literalize from the mock. This is the handoff between visual exploration and semantic implementation. - -## Step E: Mock Fidelity Inventory - -Inventory the approved mock's major visible ingredients. For each, decide implementation: semantic HTML/CSS/SVG, generated raster, sourced raster, icon library, canvas/WebGL, or accepted omission. - -Common ingredients to inventory: - -- Hero silhouette and dominant composition -- Signature motifs (planets, devices, portraits, charts, route lines, insets, badges, etc.) -- Nav and primary-action treatment (when the surface has one) -- Section sequence, especially the second fold -- Image-native content the concept depends on -- Typography, density, color/material treatment, motion cues - -Treat the mock as a north star, not a screenshot to trace. Don't rasterize core UI text. But if the live result lacks the mock's major ingredients, the implementation is wrong. - -If a photographic, architectural, product, or place-led mock becomes generic CSS scenery, decorative diagrams, bullets, or copy, stop and fix it. That's a broken implementation, not a harmless interpretation. - -Don't substitute a different hero composition or visual driver post-approval without user sign-off. - -## Step F: Asset Slicing via the Asset Producer - -Raster ingredients identified in Step E need clean production assets. Use the bundled `impeccable_asset_producer` subagent rather than producing inline. - -Spawn it as a scoped subagent. If you do not have explicit permission to use agents, stop and ask: - -```text -Asset production will work better as a scoped subagent job. Should I spawn the Impeccable asset producer subagent for this step? -``` - -Pass to the agent: - -- Approved mock path or screenshot reference -- Crop paths or a contact sheet with crop ids -- Output directory -- Required dimensions, format, transparency needs -- Avoid list -- Notes on what should remain semantic HTML/CSS/SVG instead of raster - -Attach image generation capability to the spawned agent when the harness supports it. Do **not** load image-generation reference material into the parent thread. - -Inline asset production is allowed only if the user declines subagents, the harness cannot spawn the authorized agent, or the user explicitly asks for single-thread mode. - -Prefer HTML/CSS/SVG/canvas when they can credibly reproduce an ingredient; reach for real, generated, or stock imagery when the mock or subject matter calls for actual visual content. - -## After This File - -Once Steps A through F are complete, return to the build: implement per new-work.md and SKILL.md's craft floor. The implementation builds against the confirmed palette, approved mock, and the assets the producer wrote. +Return to [new-work.md](new-work.md) for the direction contract, implementation, and finishing pass. diff --git a/.agents/skills/impeccable/reference/colorize.md b/.agents/skills/impeccable/reference/colorize.md index eebf613c3..02fa661ef 100644 --- a/.agents/skills/impeccable/reference/colorize.md +++ b/.agents/skills/impeccable/reference/colorize.md @@ -1,257 +1,86 @@ > **Additional context needed**: existing brand colors. -Replace timid grayscale or single-accent designs with a strategic palette: pick a color strategy, choose a hue family that fits the brand, then apply color with intent. More color ≠ better. Strategic color beats rainbow vomit. +Introduce color as hierarchy, meaning, and atmosphere. Preserve confirmed brand and semantic conventions; do not replace a visual world under the guise of colorizing it. --- -## Register +## Visitor mode -Persuade + Experience: palette IS voice. Pick a color strategy first per SKILL.md (Restrained / Committed / Full palette / Drenched) and follow its dosage. Committed, Full palette, and Drenched deliberately exceed the ≤10% rule; that rule is Restrained only. Unexpected combinations are allowed; a dominant color can own the page when the chosen strategy calls for it. +- **Persuade + Experience:** color may carry the voice and own large regions when the selected world calls for it. +- **Operate + Read:** color primarily encodes action, selection, status, wayfinding, and reading hierarchy. Rarity gives an accent force. -Operate + Read: semantic-first and almost always Restrained. Accent color is reserved for primary action, current selection, and state indicators. Not decoration. Every color has a consistent meaning across every screen. +## Audit before choosing ---- +Read DESIGN.md, tokens, assets, current themes, and representative states. Identify: -## Assess Color Opportunity +- which colors are confirmed brand commitments; +- current surface, text, action, and semantic roles; +- places where grayscale obscures hierarchy or state; +- contrast failures and color-only communication; +- light/dark or data-visualization requirements; +- whether the task asks for more color or a new identity. -Analyze the current state and identify opportunities: +If a new identity is required, use [new-work.md](new-work.md). Ask only when a binding brand decision cannot be inferred. -1. **Understand current state**: - - **Color absence**: Pure grayscale? Limited neutrals? One timid accent? - - **Missed opportunities**: Where could color add meaning, hierarchy, or delight? - - **Context**: What's appropriate for this domain and audience? - - **Brand**: Are there existing brand colors we should use? +## Choose a strategy -2. **Identify where color adds value**: - - **Semantic meaning**: Success (green), error (red), warning (yellow/orange), info (blue) - - **Hierarchy**: Drawing attention to important elements - - **Categorization**: Different sections, types, or states - - **Emotional tone**: Warmth, energy, trust, creativity - - **Wayfinding**: Helping users navigate and understand structure - - **Delight**: Moments of visual interest and personality +Name the intended emotional temperature, dominant relationship, contrast range, and color dosage before editing. The strategy may be restrained or immersive; it must follow the brief and selected world rather than a fixed percentage rule. -If any of these are unclear from the codebase, STOP and use Codex's structured user-input/question tool when available; if unavailable, ask directly in chat to clarify what you cannot infer. +Build roles, not a bag of swatches: -**CRITICAL**: More color ≠ better. Strategic color beats rainbow vomit every time. Every color should have a purpose. +- canvas and elevated surfaces; +- primary and secondary text; +- action, focus, and selection; +- borders and separators; +- success, warning, error, and information; +- data categories or scales when needed. -## Plan Color Strategy +Use the project's existing color space. For a new web palette, prefer OKLCH because lightness and chroma can be adjusted predictably. Choose hue from product meaning and visual direction, never from a default category association. -Create a purposeful color introduction plan: +## Apply at system scale -- **Color palette**: What colors match the brand/context? (Choose 2-4 colors max beyond neutrals) -- **Dominant color**: Which color owns 60% of colored elements? -- **Accent colors**: Which colors provide contrast and highlights? (30% and 10%) -- **Application strategy**: Where does each color appear and why? +- Let the strongest color own a deliberate region or role instead of scattering tiny accents. +- Keep the primary action easy to find; do not spend its color on decoration. +- Tint neutrals only when the brand hue genuinely creates cohesion. Neutral gray is valid when it serves the world. +- On colored surfaces, derive secondary text from the foreground or surface hue rather than using washed-out generic gray. +- Keep semantic meanings consistent, but respect platform and domain conventions instead of assuming fixed hues. +- For data, use distinct lightness, chroma, shape, label, or pattern so color is not the only code. +- 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. -**IMPORTANT**: Color should enhance hierarchy and meaning, not create chaos. Less is more when it matters more. +Decoration without a relationship to hierarchy, state, content, or the visual world is not a color strategy. -## Introduce Color Strategically +## Contrast and perception -Add color systematically across these dimensions: +Verify computed foreground/background pairs: -### Semantic Color -- **State indicators**: - - Success: Green tones (emerald, forest, mint) - - Error: Red/pink tones (rose, crimson, coral) - - Warning: Orange/amber tones - - Info: Blue tones (sky, ocean, indigo) - - Neutral: Gray/slate for inactive states +| Content | WCAG AA minimum | +|---|---| +| body text | 4.5:1 | +| large text | 3:1 | +| controls, icons, focus indicators | 3:1 | -- **Status badges**: Colored backgrounds or borders for states (active, pending, completed, etc.) -- **Progress indicators**: Colored bars, rings, or charts showing completion or health +Do not rely on eyesight alone. Check interactive states, overlays, text on images, disabled content, and both themes. Simulate common vision deficiencies. Information conveyed by color also needs text, shape, iconography, or position. -### Accent Color Application -- **Primary actions**: Color the most important buttons/CTAs -- **Links**: Add color to clickable text (maintain accessibility) -- **Icons**: Colorize key icons for recognition and personality -- **Headers/titles**: Add color to section headers or key labels -- **Hover states**: Introduce color on interaction +When deriving OKLCH ramps, vary lightness and reduce chroma near white and black. Do not keep high chroma at extreme lightness merely to make the math uniform. Prefer explicit colors over chains of translucent overlays when alpha would make contrast context-dependent. -### Background & Surfaces -- **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 toward the brand, not "for warmth" by reflex +## Verify -**Use OKLCH for color**: It's perceptually uniform, meaning equal steps in lightness *look* equal. Great for generating harmonious scales. - -### Data Visualization -- **Charts & graphs**: Use color to encode categories or values -- **Heatmaps**: Color intensity shows density or importance -- **Comparison**: Color coding for different datasets or timeframes - -### Borders & Accents -- **Hairline borders**: 1px colored borders on full perimeter (not side-stripes; see the absolute ban on `border-left/right > 1px`) -- **Underlines**: Color underlines for emphasis or active states -- **Dividers**: Subtle colored dividers instead of gray lines -- **Focus rings**: Colored focus indicators matching brand -- **Surface tints**: A 4-8% background wash of the accent color instead of a stripe - -**NEVER**: `border-left` or `border-right` greater than 1px as a colored accent stripe. This is one of the three absolute bans in the parent skill. If you want to mark a card as "active" or "warning", use a full hairline border, a background tint, a leading glyph, or a numbered prefix. Not a side stripe. - -### Typography Color -- **Colored headings**: Use brand colors for section headings (maintain contrast) -- **Highlight text**: Color for emphasis or categories -- **Labels & tags**: Small colored labels for metadata or categories - -### Decorative Elements -- **Illustrations**: Add colored illustrations or icons -- **Shapes**: Geometric shapes in brand colors as background elements -- **Gradients**: Colorful gradient overlays or mesh backgrounds -- **Blobs/organic shapes**: Soft colored shapes for visual interest - -## Balance & Refinement - -Ensure color addition improves rather than overwhelms: - -### Maintain Hierarchy -- **Dominant color** (60%): Primary brand color or most used accent -- **Secondary color** (30%): Supporting color for variety -- **Accent color** (10%): High contrast for key moments -- **Neutrals** (remaining): Gray/black/white for structure - -### Accessibility -- **Contrast ratios**: Ensure WCAG compliance (4.5:1 for text, 3:1 for UI components) -- **Don't rely on color alone**: Use icons, labels, or patterns alongside color -- **Test for color blindness**: Verify red/green combinations work for all users - -### Cohesion -- **Consistent palette**: Use colors from defined palette, not arbitrary choices -- **Systematic application**: Same color meanings throughout (green always = success) -- **Temperature consistency**: Warm palette stays warm, cool stays cool - -**NEVER**: -- 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 -- Violate WCAG contrast requirements -- Use color as the only indicator (accessibility issue) -- Make everything colorful (defeats the purpose) -- Default to purple-blue gradients (AI slop aesthetic) - -## Verify Color Addition - -Test that colorization improves the experience: - -- **Better hierarchy**: Does color guide attention appropriately? -- **Clearer meaning**: Does color help users understand states/categories? -- **More engaging**: Does the interface feel warmer and more inviting? -- **Still accessible**: Do all color combinations meet WCAG standards? -- **Not overwhelming**: Is color balanced and purposeful? +- Every color has a stable role or a world-specific atmospheric purpose. +- Attention lands on the intended action, content, or state. +- The palette works across quiet, dense, interactive, error, and empty states. +- Light and dark themes are each composed, not mechanically inverted. +- Contrast and non-color cues pass in all relevant states. +- The result is recognizably this product, not a generic “colorful” treatment. When the palette earns its place, hand off to `$impeccable polish` for the final pass. ## Live-mode signature params -When invoked from live mode, each variant MUST declare a `color-amount` param so the user can dial between a restrained accent and a drenched surface without regeneration. Author the variant's CSS against `var(--p-color-amount, 0.5)`, typically as the alpha multiplier on backgrounds, or as a scaling factor on the chroma axis in an OKLCH expression. 0 = neutral/monochrome, 1 = full saturation / dominant coverage. +When invoked from live mode, every variant declares a `color-amount` parameter. Author CSS against `var(--p-color-amount, 0.5)` so the user can move from neutral to the variant's full color strategy without regeneration. ```json {"id":"color-amount","kind":"range","min":0,"max":1,"step":0.05,"default":0.5,"label":"Color amount"} ``` -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). +Add at most two variant-specific parameters, such as palette, temperature, or tint behavior. Follow [live.md](live.md)'s parameter contract. diff --git a/.agents/skills/impeccable/reference/craft.md b/.agents/skills/impeccable/reference/craft.md index cc7f98d37..dbbc9402c 100644 --- a/.agents/skills/impeccable/reference/craft.md +++ b/.agents/skills/impeccable/reference/craft.md @@ -1,3 +1,5 @@ # Craft (deprecated alias) -`craft` is a deprecated alias for the standard build flow. Builds route through SKILL.md's new-work gate; [new-work.md](new-work.md) carries the direction checkpoint ("Decide, then build"), the identity playbook, and the finishing pass. Invoking `craft` explicitly forces the attended checkpoints: pause at the stated direction for confirmation, and follow [codex.md](codex.md)'s mock flow when the harness generates images. Nothing else differs from a plain build request. +`craft` is a deprecated alias for an ordinary request to make new visual work. It adds no setup, interview, checkpoint, tool, or quality behavior. Apply SKILL.md's normal routing: create missing PRODUCT.md through [init.md](init.md), then follow [new-work.md](new-work.md) for visual authority, world and surface decisions, implementation, and finish. + +Do not tell users they need to invoke `craft`. Natural requests such as “build this feature,” “make a landing page,” or “redesign this screen” use the same flow. diff --git a/.agents/skills/impeccable/reference/critique.md b/.agents/skills/impeccable/reference/critique.md index 454bebbfb..46f66099a 100644 --- a/.agents/skills/impeccable/reference/critique.md +++ b/.agents/skills/impeccable/reference/critique.md @@ -52,13 +52,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 @@ -130,11 +130,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. @@ -196,7 +196,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: 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. diff --git a/.agents/skills/impeccable/reference/delight.md b/.agents/skills/impeccable/reference/delight.md index 4909f450f..dd21dff32 100644 --- a/.agents/skills/impeccable/reference/delight.md +++ b/.agents/skills/impeccable/reference/delight.md @@ -1,302 +1,70 @@ -> **Additional context needed**: what's appropriate for the domain (playful vs professional vs quirky vs elegant). +> **Additional context needed**: the brand's emotional range. -Find the moments where personality and unexpected polish would turn a functional interface into one users remember and tell other people about. Add only where the moment earns it; delight everywhere reads as noise. +Make the experience memorable at moments that earn it. Delight is not a layer of generic whimsy; it is product character revealed through a useful interaction, a humane response, or an unexpectedly considered detail. --- -## Register +## Visitor mode -Persuade + Experience: delight can be distributed across copy voice, section transitions, discovery rewards, seasonal touches, personality across the whole surface. +- **Persuade + Experience:** personality may run through voice, composition, motion, and discovery, provided the artifact remains the focus. +- **Operate + Read:** concentrate delight at meaningful moments such as first use, completion, recovery, or mastery. Reliability carries everything else. -Operate + Read: delight at specific moments, not pages. Completion, first-time actions, error recovery, milestone crossings. Reliability and consistency carry the rest of the experience; delight pushed everywhere reads as noise. +## Find the opportunity ---- +Inspect the target, DESIGN.md, product voice, repeated-use frequency, and emotional context. Look for: -## Assess Delight Opportunities +- effort worth acknowledging; +- waiting that can become informative; +- an empty or first-use state that can orient; +- an error or recovery moment that needs empathy; +- an interaction whose physical or verbal response could express the brand; +- a useful capability people might enjoy discovering. -Identify where delight would enhance (not distract from) the experience: +Do not manufacture a celebration for an ordinary click. Ask only when the brand's emotional range or the stakes cannot be inferred. -1. **Find natural delight moments**: - - **Success states**: Completed actions (save, send, publish) - - **Empty states**: First-time experiences, onboarding - - **Loading states**: Waiting periods that could be entertaining - - **Achievements**: Milestones, streaks, completions - - **Interactions**: Hover states, clicks, drags - - **Errors**: Softening frustrating moments - - **Easter eggs**: Hidden discoveries for curious users +## Define one delight thesis -2. **Understand the context**: - - What's the brand personality? (Playful? Professional? Quirky? Elegant?) - - Who's the audience? (Tech-savvy? Creative? Corporate?) - - What's the emotional context? (Accomplishment? Exploration? Frustration?) - - What's appropriate? (Banking app ≠ gaming app) +State in one sentence what the user should feel and why that feeling belongs to this product. Then choose the smallest system that can deliver it: -3. **Define delight strategy**: - - **Subtle sophistication**: Refined micro-interactions (luxury brands) - - **Playful personality**: Whimsical illustrations and copy (consumer apps) - - **Helpful surprises**: Anticipating needs before users ask (productivity tools) - - **Sensory richness**: Satisfying sounds, smooth animations (creative tools) +- a distinctive response to a meaningful action; +- product-specific language that clarifies while carrying voice; +- an interaction or transition with a recognizable material behavior; +- an illustration, sound, haptic, or environmental detail grounded in the product world; +- a discovery reward that reveals real utility. -If any of these are unclear from the codebase, STOP and use Codex's structured user-input/question tool when available; if unavailable, ask directly in chat to clarify what you cannot infer. +Derive the treatment from product mechanism and visual world, not a stock catalog. -**CRITICAL**: Delight should enhance usability, never obscure it. If users notice the delight more than accomplishing their goal, you've gone too far. +## Build for the emotional moment -## Delight Principles +- **Success:** match the response to the effort and consequence. Major milestones can expand; routine saves should simply feel certain. +- **Waiting:** show truthful progress, useful context, or product-specific activity. Never fake work or delay completion to stage a flourish. +- **Empty and first use:** make the next action clear before adding personality. +- **Error and recovery:** lead with the problem and recovery. Warmth may reduce stress; jokes must not trivialize loss, money, privacy, or blocked work. +- **Repeated interaction:** keep the response satisfying after the hundredth use. Variation is useful only when it remains coherent and predictable enough to trust. +- **Discovery:** reward curiosity without hiding required functionality. -Follow these guidelines: +Copy must use the product's language. Generic whimsy is worse than neutral clarity. -### Delight Amplifies, Never Blocks -- Delight moments should be quick (< 1 second) -- Never delay core functionality for delight -- Make delight skippable or subtle -- Respect user's time and task focus +## Protect the experience -### Surprise and Discovery -- Hide delightful details for users to discover -- Reward exploration and curiosity -- Don't announce every delight moment -- Let users share discoveries with others +Delight must not: -### Appropriate to Context -- Match delight to emotional moment (celebrate success, empathize with errors) -- Respect the user's state (don't be playful during critical errors) -- Match brand personality and audience expectations -- Cultural sensitivity (what's delightful varies by culture) +- delay, block, or obscure the primary task; +- override platform conventions or accessibility; +- add unrequested factual claims; +- play sound without consent or ignore mute settings; +- become mandatory, unskippable, or exhausting on repeat; +- add a dependency or asset cost disproportionate to the moment. -### Compound Over Time -- Delight should remain fresh with repeated use -- Vary responses (not same animation every time) -- Reveal deeper layers with continued use -- Build anticipation through patterns +For authored motion, load [animate.md](animate.md). Respect reduced motion, screen readers, keyboard use, touch, localization, and cultural context. Nonessential loops stop when hidden. Make celebration intensity proportional to frequency and consequence. -## Delight Techniques +## Verify -Add personality and joy through these methods: +- The moment is specific enough that a neighboring product could not use it unchanged. +- It improves comprehension, confidence, motivation, or emotional recovery. +- The interface remains fast and obvious without the flourish. +- Repetition does not turn charm into friction. +- Reduced-motion, muted, keyboard, touch, and localized paths work. +- The result feels like the selected world, not a generic “delight” treatment. -### Micro-interactions & Animation - -**Button delight**: -```css -/* Satisfying button press */ -.button { - transition: transform 0.1s, box-shadow 0.1s; -} -.button:active { - transform: translateY(2px); - box-shadow: 0 2px 4px rgba(0,0,0,0.2); -} - -/* Ripple effect on click */ -/* Smooth lift on hover */ -.button:hover { - transform: translateY(-2px); - transition: transform 0.2s cubic-bezier(0.25, 1, 0.5, 1); /* ease-out-quart */ -} -``` - -**Loading delight**: -- Playful loading animations (not just spinners) -- Personality in loading messages (write product-specific ones, not generic AI filler) -- Progress indication with encouraging messages -- Skeleton screens with subtle animations - -**Success animations**: -- Checkmark draw animation -- Confetti burst for major achievements -- Gentle scale + fade for confirmation -- Satisfying sound effects (subtle) - -**Hover surprises**: -- Icons that animate on hover -- Color shifts or glow effects -- Tooltip reveals with personality -- Cursor changes (custom cursors for branded experiences) - -### Personality in Copy - -**Playful error messages**: -``` -"Error 404" -"This page is playing hide and seek. (And winning)" - -"Connection failed" -"Looks like the internet took a coffee break. Want to retry?" -``` - -**Encouraging empty states**: -``` -"No projects" -"Your canvas awaits. Create something amazing." - -"No saved articles" -"Nothing on the nightstand yet. Save a story for later." -``` - -**Playful labels & tooltips**: -``` -"Delete" -"Send to void" (for playful brand) - -"Help" -"Rescue me" (tooltip) -``` - -**IMPORTANT**: Match copy personality to brand. Banks shouldn't be wacky, but they can be warm. - -### Illustrations & Visual Personality - -**Custom illustrations**: -- Empty state illustrations (not stock icons) -- Error state illustrations (friendly monsters, quirky characters) -- Loading state illustrations (animated characters) -- Success state illustrations (celebrations) - -**Icon personality**: -- Custom icon set matching brand personality -- Animated icons (subtle motion on hover/click) -- Illustrative icons (more detailed than generic) -- Consistent style across all icons - -**Background effects**: -- Subtle particle effects -- Gradient mesh backgrounds -- Geometric patterns -- Parallax depth -- Time-of-day themes (morning vs night) - -### Satisfying Interactions - -**Drag and drop delight**: -- Lift effect on drag (shadow, scale) -- Snap animation when dropped -- Satisfying placement sound -- Undo toast ("Dropped in wrong place? [Undo]") - -**Toggle switches**: -- Smooth slide with spring physics -- Color transition -- Haptic feedback on mobile -- Optional sound effect - -**Progress & achievements** (Operate surfaces with recurring tasks only; gamification on editorial, portfolio, or one-visit surfaces reads as noise): -- Streak counters with celebratory milestones -- Progress bars that "celebrate" at 100% -- Badge unlocks with animation -- Playful stats ("You're on fire! 5 days in a row") - -**Form interactions**: -- Input fields that animate on focus -- Checkboxes with a satisfying scale pulse when checked -- Success state that celebrates valid input -- Auto-grow textareas - -### Sound Design - -**Subtle audio cues** (when appropriate): -- Notification sounds (distinctive but not annoying) -- Success sounds (satisfying "ding") -- Error sounds (empathetic, not harsh) -- Typing sounds for chat/messaging -- Ambient background audio (very subtle) - -**IMPORTANT**: -- Respect system sound settings -- Provide mute option -- Keep volumes quiet (subtle cues, not alarms) -- Don't play on every interaction (sound fatigue is real) - -### Easter Eggs & Hidden Delights - -**Discovery rewards**: -- Konami code unlocks special theme -- Hidden keyboard shortcuts (Cmd+K for special features) -- Hover reveals on logos or illustrations -- Alt text jokes on images (for screen reader users too!) -- A view-source or console colophon (a note on the typefaces, a thank-you, a hiring line) - -**Seasonal touches**: -- Holiday themes (subtle, tasteful) -- Seasonal color shifts -- Weather-based variations -- Time-based changes (dark at night, light during day) - -**Contextual personality**: -- Different messages based on time of day -- Responses to specific user actions -- Randomized variations (not same every time) -- Progressive reveals with continued use - -### Loading & Waiting States - -**Make waiting engaging**: -- Interesting loading messages that rotate -- Progress bars with personality -- Mini-games during long loads -- Fun facts or tips while waiting -- Countdown with encouraging messages - -``` -Loading messages: write ones specific to your product, not generic AI filler: -- "Syncing with your team's changes..." -- "Fetching this week's issue..." -- "Developing your photos..." -- "Checking tomorrow's tide tables..." -``` - -**WARNING**: Avoid cliched loading messages like "Herding pixels", "Teaching robots to dance", "Consulting the magic 8-ball", "Counting backwards from infinity". These are AI-slop copy, instantly recognizable as machine-generated. Write messages that are specific to what your product actually does. - -### Celebration Moments - -**Success celebrations**: -- Confetti for major milestones -- Animated checkmarks for completions -- Progress bar celebrations at 100% -- "Achievement unlocked" style notifications -- Personalized messages ("You published your 10th article!") - -**Milestone recognition** (same scoping as Progress & achievements: Operate surfaces with recurring tasks): -- First-time actions get special treatment -- Streak tracking and celebration -- Progress toward goals -- Anniversary celebrations - -## Implementation Patterns - -**Animation libraries**: -- Framer Motion (React) -- GSAP (universal) -- Lottie (After Effects animations) -- Canvas confetti (party effects) - -**Sound libraries**: -- Howler.js (audio management) -- Use-sound (React hook) - -**Physics libraries**: -- React Spring (spring physics) -- Popmotion (animation primitives) - -**IMPORTANT**: File size matters. Compress images, optimize animations, lazy load delight features. - -**NEVER**: -- Delay core functionality for delight -- Force users through delightful moments (make skippable) -- Use delight to hide poor UX -- Overdo it (less is more) -- Ignore accessibility (animate responsibly, provide alternatives) -- Make every interaction delightful (special moments should be special) -- Sacrifice performance for delight -- Be inappropriate for context (read the room) - -## Verify Delight Quality - -Test that delight actually delights: - -- **User reactions**: Do users smile? Share screenshots? -- **Doesn't annoy**: Still pleasant after 100th time? -- **Doesn't block**: Can users opt out or skip? -- **Performant**: No jank, no slowdown -- **Appropriate**: Matches brand and context -- **Accessible**: Works with reduced motion, screen readers - -When the moments feel earned, hand off to `$impeccable polish` for the final pass. +When the personality feels earned, hand off to `$impeccable polish` for the final pass. diff --git a/.agents/skills/impeccable/reference/document.md b/.agents/skills/impeccable/reference/document.md index 1210ff10d..eddaa44f1 100644 --- a/.agents/skills/impeccable/reference/document.md +++ b/.agents/skills/impeccable/reference/document.md @@ -1,6 +1,6 @@ Generate a `DESIGN.md` file at the project root that captures the current visual design system, so AI agents generating new screens stay on-brand. -DESIGN.md follows the [official DESIGN.md format spec](https://raw.githubusercontent.com/google-labs-code/design.md/main/docs/spec.md): YAML frontmatter carrying machine-readable design tokens, followed by a markdown body with exactly six sections in a fixed order. **Tokens are normative; prose provides context for how to apply them.** Sections may be omitted when not relevant, but **do not reorder them and do not rename them**. Section headers must match the spec character-for-character so the file stays parseable by other DESIGN.md-aware tools (Stitch itself, awesome-design-md, skill-rest, etc.). +DESIGN.md follows the [official DESIGN.md format spec](https://raw.githubusercontent.com/google-labs-code/design.md/main/docs/spec.md): optional YAML frontmatter carrying machine-readable design tokens, followed by up to eight markdown sections in a fixed order. **Tokens are normative; prose provides context for how to apply them.** Sections may be omitted when not relevant, but those present stay in the specified order. Use the canonical headings below so the file remains portable across DESIGN.md-aware tools. ## The frontmatter: token schema @@ -43,26 +43,28 @@ components: 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. +- **Colors accept any valid CSS color string.** Hex is the recommended default for portability, but preserve an incumbent `rgb()`, `hsl()`, `oklch()`, wide-gamut, or mixed-color value when it is the project's normative source. 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 (`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) +## The markdown body: eight sections (canonical order) 1. `## Overview` 2. `## Colors` 3. `## Typography` -4. `## Elevation` -5. `## Components` -6. `## Do's and Don'ts` +4. `## Layout` +5. `## Elevation & Depth` +6. `## Shapes` +7. `## Components` +8. `## Do's and Don'ts` -Optional evocative subtitles are allowed in the form `## 2. Colors: The [Name] Palette` (Stitch's own outputs do this), but the literal word in each header (Overview, Colors, Typography, Elevation, Components, Do's and Don'ts) must be present. Do NOT add extra top-level sections (Layout Principles, Responsive Behavior, Motion, Agent Prompt Guide). Fold that content into the six spec sections where it naturally belongs. +Omit irrelevant sections rather than filling them with invented rules. Put responsive layout in Layout, depth in Elevation & Depth, radius and form language in Shapes, and per-component behavior in Components. Unknown sections are preserved by the format, but new visual guidance should use the canonical structure whenever it fits. ## When to run -- 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. +- New-work found a coherent incumbent visual system but no `DESIGN.md`. +- The first implementation of a new world is complete and its provisional decisions need to be carbonized. - An existing `DESIGN.md` is stale (the design has drifted). - Before a large redesign, to capture the current state as a reference. @@ -71,9 +73,9 @@ 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 init, nothing built yet). Interview for five high-level answers, write a minimal DESIGN.md marked ``. Re-run in scan mode once there's code. +- **Seed mode**: the project is pre-implementation. Ensure PRODUCT.md exists, then reuse new-work's visual-world workshop and write its directional DESIGN.md 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. +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` requests new-work's world workshop, but it does not authorize replacing coherent code: when an incumbent system exists, offer scan mode or route an explicit identity-replacement request through new-work. ## Scan mode (approach C: auto-extract, then confirm descriptive language) @@ -97,7 +99,8 @@ Build a structured draft from the discovered tokens. For each token class: - **Typography**: Map observed sizes and weights to the Material hierarchy (display / headline / title / body / label). Note font-family stacks and the scale ratio. - **Elevation**: Catalogue the shadow vocabulary. If the project is flat and uses tonal layering instead, that's a valid answer; state it explicitly. - **Components**: For each common component (button, card, input, chip, list item, tooltip, nav), extract shape (radius), color assignment, hover/focus treatment, internal padding. -- **Spacing + layout**: Fold into Overview or relevant Components. The spec does NOT have a Layout section. +- **Layout + spacing**: Extract grid, container, breakpoint, rhythm, and density behavior into Layout. +- **Shapes**: Extract radius, corner, border, clipping, and recurring form behavior into Shapes. ### Step 2b: Stage the frontmatter @@ -112,19 +115,19 @@ Skip anything the project doesn't have. Empty scale keys or fabricated tokens po ### Step 3: Ask the user for qualitative language -The following require creative input that cannot be auto-extracted. Group them into one `AskUserQuestion` interaction: +The following require creative input that cannot be auto-extracted. Ask them in two structured rounds of no more than three questions each (or the harness's lower limit), waiting between rounds: - **Creative North Star**: a single named metaphor for the whole system ("The Editorial Sanctuary", "The Golden State Curator", "The Lab Notebook"). Offer 2-3 options that honor PRODUCT.md's brand personality. -- **Overview voice**: mood adjectives, aesthetic philosophy in 2-3 sentences, anti-references (what the system should not feel like). +- **Overview voice**: mood adjectives, aesthetic philosophy in 2-3 sentences, and any confirmed visual anti-reference. - **Color character** (for auto-extracted colors): descriptive names ("Deep Muted Teal-Navy", not "blue-800"). Suggest 2-3 options per key color based on hue/saturation. - **Elevation philosophy**: flat/layered/lifted. If shadows exist, is their role ambient or structural? - **Component philosophy**: the feel of buttons, cards, inputs in one phrase ("tactile and confident" vs. "refined and restrained"). -Quote a line from PRODUCT.md when possible so the user sees their own strategic language carry forward. +Carry a line from PRODUCT.md only when it is a durable brand commitment that actually constrains the visual system. Page strategy and surface concepts do not belong here. ### Step 4: Write DESIGN.md -The file opens with the YAML frontmatter staged in Step 2b (schema documented at the top of this reference), then the markdown body using the structure below. Headers must match character-for-character. Optional evocative subtitles (e.g. `## 2. Colors: The Coastal Palette`) are allowed. +The file opens with the YAML frontmatter staged in Step 2b (schema documented at the top of this reference), then the markdown body using the canonical structure below. ```markdown --- @@ -136,13 +139,13 @@ colors: # Design System: [Project Title] -## 1. Overview +## Overview **Creative North Star: "[Named metaphor in quotes]"** -[2-3 paragraph holistic description: personality, density, aesthetic philosophy. Start from the North Star and work outward. State what this system explicitly rejects (pulled from PRODUCT.md's anti-references). End with a short **Key Characteristics:** bullet list.] +[2-3 paragraph holistic description: personality, density, and aesthetic philosophy. Start from the North Star and work outward. State only confirmed visual rejections. End with a short **Key Characteristics:** bullet list.] -## 2. Colors +## Colors [Describe the palette character in one sentence.] @@ -162,7 +165,7 @@ colors: ### Named Rules (optional, powerful) **The [Rule Name] Rule.** [Short, forceful prohibition or doctrine, e.g. "The One Voice Rule. The primary accent is used on ≤10% of any given screen. Its rarity is the point."] -## 3. Typography +## Typography **Display Font:** [Family] (with [fallback]) **Body Font:** [Family] (with [fallback]) @@ -180,7 +183,11 @@ colors: ### Named Rules (optional) **The [Rule Name] Rule.** [Short doctrine about type use.] -## 4. Elevation +## Layout + +[Describe the grid or spatial model, container behavior, density, responsive changes, and the spacing rhythm. Include exact values only when observed.] + +## Elevation & Depth [One paragraph: does this system use shadows, tonal layering, or a hybrid? If "no shadows", say so explicitly and describe how depth is conveyed instead.] @@ -191,7 +198,11 @@ colors: ### Named Rules (optional) **The [Rule Name] Rule.** [e.g. "The Flat-By-Default Rule. Surfaces are flat at rest. Shadows appear only as a response to state (hover, elevation, focus)."] -## 5. Components +## Shapes + +[Describe the form language: corner/radius strategy, borders, clipping, and any recurring silhouette or geometry.] + +## Components For each component, lead with a short character line, then specify shape, color assignment, states, and any distinctive behavior. @@ -223,16 +234,16 @@ For each component, lead with a short character line, then specify shape, color ### [Signature Component] (optional; if the project has a distinctive custom component worth documenting) [Description.] -## 6. Do's and Don'ts +## Do's and Don'ts -Concrete, forceful guardrails. Lead each with "Do" or "Don't". Be specific: include exact colors, pixel values, and named anti-patterns the user mentioned in PRODUCT.md. **Every anti-reference in PRODUCT.md should show up here as a "Don't" with the same language**, so the visual spec carries the strategic line through. Quote PRODUCT.md directly where possible: if PRODUCT.md says *"avoid dark mode with purple gradients, neon accents, glassmorphism"*, the Don'ts here should repeat that by name. +Concrete visual guardrails grounded in the incumbent implementation or the user's chosen world. Lead each with "Do" or "Don't" and include exact values only when established. Do not turn a task-specific concept or surface strategy into a system-wide prohibition. ### Do: - **Do** [specific prescription with exact values / named rule]. - **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** [...] ``` @@ -307,7 +318,7 @@ The `html` and `css` fields must be **self-contained, drop-in snippets** that re Aim for a tight set of **5-10 components** that best represent the visual system: - **Canonical primitives (always include if the project has them):** button (each variant as a separate component entry), input/text field, navigation, chip/tag, card. -- **Signature components (include if distinctive):** hero CTA, featured card, filter pill, a distinctive table or list-row treatment, any custom pattern the user mentioned as important in PRODUCT.md. +- **Signature components (include if distinctive):** the recurring custom patterns that actually define the implemented system. - **Skip the rest.** Utility components, form building blocks, wrapper layouts: not worth documenting unless visually distinctive. If the project has **no component library yet** (bare landing page, new project), synthesize canonical primitives from the tokens using best-practice defaults consistent with the DESIGN.md's rules. Every `.impeccable/design.json` has *something* to render, even on day zero. @@ -338,64 +349,40 @@ Your own write is the freshest source; subsequent commands in this session don't ## Seed mode -For projects with no visual system to extract yet. Produces a minimal scaffold, not a full spec. +For projects with no visual system to extract yet. Produces a user-chosen visual-world scaffold, not a fabricated token spec. -### Step 1: Confirm seed mode +### Step 1: Route through new-work's workshop -Before interviewing: "There's no existing visual system to scan. I'll ask five quick questions to seed a starter DESIGN.md. You can re-run `$impeccable document` once there's code, to capture the real tokens and components. OK?" +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 the user prefers to skip, stop. No file. +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. -### Step 2: Five questions +If new-work already completed the workshop in this session, use its chosen direction directly. Do not ask again. -Group into one `AskUserQuestion` interaction. Options must be concrete. +### Step 2: Write seed DESIGN.md -1. **Color strategy.** Pick one: - - Restrained: tinted neutrals + one accent ≤10% - - Committed: one saturated color carries 30–60% of the surface - - Full palette: 3–4 named color roles, each deliberate - - Drenched: the surface IS the color - - Then: one hue family or anchor reference ("deep teal", "mustard", "Klim #ff4500 orange"). - -2. **Typography direction.** Pick one (specific fonts come later): - - Serif display + sans body - - Single sans (warm / technical / geometric / humanist; pick a feel) - - Display + mono - - Mono-forward - - Editorial script + sans - -3. **Motion energy.** Pick one: - - Restrained: state changes only - - Responsive: feedback + transitions, no choreography - - Choreographed: orchestrated entrances, scroll-driven sequences - -4. **Three named references.** Brands, products, printed objects. Not adjectives. - -5. **One anti-reference.** What it should NOT feel like. Also named. - -### Step 3: Write seed DESIGN.md - -Use the six-section spec from Scan mode. Populate what the interview answers; leave the rest as honest placeholders. The seed is a scaffold, not a fabricated spec. +Use the canonical section order from Scan mode. Populate the selected workshop direction and leave unresolved implementation facts as honest placeholders. The seed commits a world and its invariants; it does not pretend implementation tokens already exist. Lead the file with: ```markdown - + ``` Per-section guidance in seed mode: -- **Overview**: Creative North Star and philosophy phrased from the answers (color strategy + motion energy + references). Reference the user's anti-reference directly. -- **Colors**: Color strategy as a Named Rule (e.g. *"The Drenched Rule. The surface IS the color."*). Hue family or anchor reference. No hex values; mark as `[to be resolved during implementation]`. -- **Typography**: the direction the user picked (e.g. "Serif display + sans body"). No font names yet: `[font pairing to be chosen at implementation]`. -- **Elevation**: inferred from motion energy. Restrained/Responsive → flat by default; Choreographed → layered. One sentence. +- **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. +- **Elevation & Depth**: the selected material and depth behavior, stated as an invariant rather than inferred from a generic preset. +- **Shapes**: the selected form and corner language. - **Components**: omit entirely; no components exist yet. -- **Do's and Don'ts**: carry PRODUCT.md's anti-references directly plus the anti-reference named in Q5. +- **Do's and Don'ts**: record the durable guardrails confirmed during the world choice, not task-local refusals. 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 +### Step 3: 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." @@ -405,15 +392,15 @@ Your own write is the freshest source; no reload needed. ## Style guidelines - **Frontmatter first, prose second.** Tokens go in the YAML frontmatter; prose contextualizes them. Don't redefine a token value in two places; the frontmatter is normative. -- **Cite PRODUCT.md anti-references by name** in the Do's and Don'ts section. If PRODUCT.md lists "SaaS landing-page clichés" or "generic AI tool marketing" as anti-references, the DESIGN.md Don'ts should repeat those phrases verbatim so the visual spec enforces the strategic line. -- **Match the spec, don't invent new sections.** The six section names are fixed. If you have Layout/Motion/Responsive content to document, fold it into Overview (philosophy-level rules) or Components (per-component behavior). +- **Carry only durable product constraints.** A binding logo, identity asset, accessibility need, or brand commitment from PRODUCT.md may constrain DESIGN.md. Surface strategy stays in its surface brief. +- **Match the spec.** Use its eight canonical sections in order and omit any that are irrelevant. Put motion guidance with the world or component it affects rather than creating a token group the schema does not support. - **Descriptive > technical**: "Gently curved edges (8px radius)" > "rounded-lg". Include the technical value in parens, lead with the description. - **Functional > decorative**: for each token, explain WHERE and WHY it's used, not just WHAT it is. - **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 forceful**. The voice of a design director. "Prohibited", "forbidden", "never", "always", not "consider", "might", "prefer". Match PRODUCT.md's tone. -- **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. -- **Reference PRODUCT.md**. The anti-references section of PRODUCT.md should directly inform the Do's and Don'ts section here. Quote or paraphrase. +- **Be decisive where evidence is decisive.** Use hard language for actual invariants and softer language for provisional guidance. +- **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. ## Pitfalls @@ -423,7 +410,7 @@ Your own write is the freshest source; no reload needed. - Don't invent components that don't exist. If the project only has buttons and cards, only document those. - Don't overwrite an existing DESIGN.md without asking. - Don't duplicate content from PRODUCT.md. DESIGN.md is strictly visual. -- Don't add a "Layout Principles" or "Motion" or "Responsive Behavior" top-level section. The spec has six, not nine. Fold that content where it belongs. +- Don't replace canonical sections with near-synonyms. Put layout and responsive behavior in `Layout`; put motion with the affected world or component. - Don't rename sections even slightly. "Colors" not "Color Palette & Roles". "Typography" not "Typography Rules". Tooling parsing depends on exact headers. - Don't duplicate token values between frontmatter and prose. If a color is in `colors.primary` as hex, the prose can name it and describe its role but should not reassert a different hex. The frontmatter is normative. - Don't invent frontmatter token groups outside Stitch's schema (no `motion:`, `breakpoints:`, `shadows:` at the top level). Stitch's Zod schema only accepts `colors`, `typography`, `rounded`, `spacing`, `components`. Anything else belongs in the sidecar's `extensions`. diff --git a/.agents/skills/impeccable/reference/hooks.md b/.agents/skills/impeccable/reference/hooks.md index ce2032e7e..420a4d51f 100644 --- a/.agents/skills/impeccable/reference/hooks.md +++ b/.agents/skills/impeccable/reference/hooks.md @@ -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. diff --git a/.agents/skills/impeccable/reference/init.md b/.agents/skills/impeccable/reference/init.md index de7d12a8a..f21937f68 100644 --- a/.agents/skills/impeccable/reference/init.md +++ b/.agents/skills/impeccable/reference/init.md @@ -1,224 +1,114 @@ -# Init Flow +# Init flow -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 [DESIGN.md format spec](https://raw.githubusercontent.com/google-labs-code/design.md/main/docs/spec.md). Answers "how it looks". -- **`.impeccable/live/config.json`** (live mode): pre-configured so `$impeccable live` boots straight into variant mode with no first-time detour. - -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. +`init` captures durable product truth in PRODUCT.md. It does not invent a visual world and does not write DESIGN.md; [new-work.md](new-work.md) creates or expands one, and [document.md](document.md) records an incumbent one. Existing runnable web projects may also receive `.impeccable/live/config.json`. ## Step 1: Load current state -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). +Use the PRODUCT.md path resolved by context.mjs. Update it instead of creating a competing authority. In a child app inheriting root context, confirm shared versus app-specific scope before writing. -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. -- **PRODUCT.md exists, DESIGN.md missing**: skip to Step 5 and offer to run `$impeccable document` for DESIGN.md. -- **PRODUCT.md exists but has no `## Register` section (legacy)**: add it. Infer a hypothesis from the codebase (see Step 2), confirm with the user, write the field. -- **PRODUCT.md exists but has no `## Platform` section (legacy)**: add it the same way, but only when the project is native (`ios` / `android` / `adaptive`) or the user wants it explicit; a missing field already means `web`. -- **Both exist**: STOP and use Codex's structured user-input/question tool when available; if unavailable, ask directly in chat to clarify what you cannot infer. Ask which file to refresh. Skip the one the user doesn't want changed. -- **Just DESIGN.md exists (unusual)**: do Steps 2-4 to produce PRODUCT.md. +- **No PRODUCT.md:** explore, interview, and write it. +- **PRODUCT.md exists:** ask what product knowledge is stale or missing; do not reopen confirmed fields without a reason. +- **Legacy PRODUCT.md:** add only durable missing facts; absent `## Platform` means `web` unless evidence says otherwise. +- **Only DESIGN.md exists:** leave it untouched and create PRODUCT.md. +- **Redesign/rebrand request:** preserve confirmed product truth unless the user changes it. Visual replacement happens later in new-work, not here. -Never silently overwrite an existing file. Always confirm first. +Never silently overwrite an existing file or offer DESIGN.md during init. If another request invoked init, finish PRODUCT.md and resume it. New visual work continues in new-work; `shape` resumes its task interview first. -If init was invoked as a setup blocker by another command, such as `$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 project -## Step 2: Explore the codebase +Before asking, scan enough to avoid making the user repeat known facts: product docs and copy; package/config and app boundaries; features, workflows, routes, and roles; names, logos, legal/proof assets, and brand commitments; platform/accessibility signals; and the dev command/entry when live mode applies. -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: +Treat repository evidence as a hypothesis, not user approval. Note visual maturity without documenting, extending, or replacing the world. -- **README and docs**: Project purpose, target audience, any stated goals -- **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 -- **Any style guides or brand documentation** +Form a platform hypothesis: `web`, `ios`, `android`, or `adaptive` (one product that genuinely adapts its design language per OS). Mobile web remains `web`; a native wrapper around a website does not make its design language native. -Also form a **register hypothesis** from what you find: +## Step 3: Interview for product truth -- Brand signals: `/`, `/about`, `/pricing`, `/blog/*`, `/docs/*`, hero sections, big typography, scroll-driven sections, landing-page-shaped content. -- Product signals: `/app/*`, `/dashboard`, `/settings`, `/(auth)`, forms, data tables, side/top nav, app-shell components. +STOP and use Codex's structured user-input/question tool when available; if unavailable, ask directly in chat to clarify what you cannot infer. Ask only about material gaps the repository and original request do not answer with strong evidence. -Register is a hypothesis at this point, not a decision; Step 3 confirms it. +Use the structured question tool when available; otherwise ask and wait. Keep rounds to at most three focused questions and require one real answer or approval round before writing a new PRODUCT.md. Confirm inferences. -Also form a **platform hypothesis**: +Start with the unknowns that most change future product decisions: -- Native signals: React Native / Expo (`react-native`, `expo`), Flutter (`pubspec.yaml`, `flutter`), SwiftUI / UIKit (`.swift`, `.xcodeproj`, an `ios/` app target), Jetpack Compose / Android (`build.gradle`, an `android/` app module, `AndroidManifest.xml`). An `ios/` and/or `android/` directory that is a real app target, not just a Capacitor/Cordova wrapper around a website. -- Web signals (the default): a web framework (Vite, Next, Nuxt, SvelteKit, Astro), an HTML entry, a CSS/Tailwind setup, no native app target. +1. Who is the primary user, in what situation, and what job are they doing? +2. What does the product make possible, and what is its meaningfully different mechanism or position? +3. What durable constraints, assets, evidence, or product facts must future work preserve? -Values: `web` / `ios` / `android` / `adaptive` (one codebase, ships both, adapts per OS). Mobile web is still `web`. Like register, this is a hypothesis; Step 3 confirms it. +Confirm ambiguous platform separately. Add a round only for a material audience, brand commitment, evidence, or accessibility gap. Record undecided facts instead of inventing them. -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. +Do not ask for an aesthetic direction, emotional feel, visual references, colors, typography, or style during init. If the user volunteers a binding visual constraint, record it without expanding it. -## Step 3: Ask strategic questions (for PRODUCT.md) +### What belongs here -STOP and use Codex's structured user-input/question tool when available; if unavailable, ask directly in chat to clarify what you cannot infer. Ask about anything the codebase doesn't answer with strong, explicit evidence. +- users, jobs, workflows, purpose, success, positioning, and operating context; +- capabilities, constraints, terminology, evidence, platform, and accessibility; +- confirmed voice, assets, and brand commitments. -### Interview mode, not confirmation mode +### What does not belong here -If the repo is empty or the user's brief is sparse, run a short interview before proposing PRODUCT.md. Do **not** turn a one-sentence request into a complete inferred PRODUCT.md and ask for blanket confirmation. - -- Use the harness's structured question tool when one exists. Otherwise, ask directly in chat and stop: one question at a time, with lettered options where the crawl suggests likely answers, waiting for each answer before the next. -- Keep skill vocabulary (register, belief ladder, anti-references) out of question text; ask for the thing in words the user would use. For the brand register, ask like a magazine editor profiling the brand: curious and narrative, drawing out the story, the feel, and what a visitor should come to believe. -- Ask in focused rounds and wait for answers between them. Keep **one topic per question**; add rounds rather than fold several topics into one either-or choice. Options obey the same rule: an option answers only the question asked; never write a compound option that bundles a feeling with a business outcome or names an additional audience. -- Use inferred answers as hypotheses or options, not as finished facts. -- Complete at least one real user-answer round before drafting PRODUCT.md, unless every required answer is directly discoverable from repo docs. -- Round 1 should establish register, platform, users, purpose, positioning, and desired outcome. -- Round 2 should establish brand personality or references, anti-references, and accessibility needs, plus conversion & proof for Persuade surfaces. - -### Minimum viable interview - -Ask enough to complete PRODUCT.md. At minimum, cover register confirmation, **platform confirmation** (`web` / `ios` / `android` / `adaptive`), users, purpose, positioning, brand personality, anti-references, and accessibility needs (plus conversion & proof for Persuade surfaces) unless each answer is directly discoverable from repo context. Never let the template's default `web` stand unconfirmed for a native or cross-platform repo. After at least one interview round, you may propose inferred answers, but the user must confirm them before you write PRODUCT.md. Never synthesize PRODUCT.md from the original task prompt alone. - -### Register (ask first; it shapes everything below) - -Every design task serves one of four visitor modes (see SKILL.md's Registers): **Persuade** (marketing, landing, campaigns), **Experience** (portfolios, albums, bodies of work), **Operate** (app UI, admin, dashboards, tools), and **Read** (documentation, editorial, long-form). PRODUCT.md stores the two-value family that covers them: `brand` covers Persuade and Experience, `product` covers Operate and Read. The brief decides the exact mode per task; the stored register only sets the default family. - -If Step 2 produced a clear hypothesis, lead with it: *"From the codebase, this looks like a [brand / product] surface. Does that match your intent, or should we treat it differently?"* - -If the signal is genuinely split (e.g. a product with a big marketing landing), STOP and use Codex's structured user-input/question tool when available; if unavailable, ask directly in chat to clarify what you cannot infer. Ask which register describes the **primary** surface. The register can be overridden per task later, but PRODUCT.md carries one default. Settle the default before drafting any register-dependent questions; never batch Persuade-only questions (Conversion & proof) into the same round as the question that decides the register. - -### Platform (ask right after register) - -Every project targets **web** (includes responsive mobile web), **ios**, **android**, or **adaptive** (one codebase, ships both, adapts per OS: Flutter, React Native, KMP). Platform picks the native rulebook: HIG for `ios`, Material 3 for `android`, both for `adaptive`, none for `web`. - -If Step 2 produced a clear hypothesis, lead with it: *"From the codebase, this looks like a [web / ios / android / adaptive] project. Does that match?"* For cross-platform apps, decide by the **design language the app renders**, not the toolchain: one look on both platforms (Flutter's Material-everywhere default) takes that platform's value; genuine per-OS adaptation (Cupertino on iOS, Material on Android) is `adaptive`. When in doubt, `web`. - -A monorepo shipping both a website and a native app gets a PRODUCT.md per app, each with its own `## Platform`; the root PRODUCT.md carries the primary surface's platform. - -### Users & Purpose -- Who uses this? What's their context when using it? -- What job are they trying to get done? -- What is this for? A purpose stated in README or docs is a hypothesis, not strong evidence; confirm it, don't transcribe it. -- What does success look like? -- If more than one kind of user is plausible, confirm a primary and secondary audience; don't manufacture a split that isn't there. An audience implied by another answer (a success metric, a CTA) is still unconfirmed; ask before writing it as secondary. -- If the surface speaks to a different audience than the people who use the product, ask the user to name both. -- For brand: what emotions should the interface evoke? (confidence, delight, calm, urgency) Ask this standalone; don't fold emotions into the success question. -- For product: what workflow are they in? What's the primary task on any given screen? - -### Positioning -- In one line, what does this do that nothing else does? The single strategic claim every screen reinforces. - -### 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? - - 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? - -### Conversion & proof (Persuade surfaces only) - -Ask these only when the primary surface is a Persuade one (marketing, landing, campaigns). Experience and Read surfaces (portfolios, albums, long-form writing, documentation) get no CTA, belief-ladder, or proof questions even when the stored register is `brand`; skip this section and its PRODUCT.md counterpart. - -- What's the primary CTA? -- What's the secondary fallback, for visitors not ready for the primary? -- The one line a visitor should remember after 10 seconds. -- What must the visitor believe, in order, before taking the primary CTA? (The template's belief ladder.) -- What proof is on hand? Ask the user to hand over any testimonials, case studies, press, or client/partner logos they already have. If you can receive files directly, collect them; otherwise create `.impeccable/assets/proof/` and ask the user to add files there. Reference supplied files by path; record text proof inline. - -### Accessibility & Inclusion -- Specific accessibility requirements? (WCAG level, known user needs) -- Considerations for reduced motion, color blindness, or other accommodations? - -Skip questions where the answer is already clear. **Do NOT ask about colors, fonts, radii, or visual styling here.** Those belong in DESIGN.md, not PRODUCT.md. +- visual worlds, palettes, typography, components, or page concepts; +- visitor mode, narrative, CTA/proof sequence, or other surface strategy; +- invented testimonials, customers, benchmarks, pricing, licensing, or deployment claims; +- a requirement to decide every optional field. ## Step 4: Write PRODUCT.md -Write PRODUCT.md only after the user has confirmed the strategic answers from Step 3. If an inferred answer is uncertain or unconfirmed, ask before writing. Confirmed means what the user actually said yes to; do not pad a confirmed answer with extras they never picked (additional anti-references, audiences, roadmap claims, a WCAG level), whether drawn from the crawl, another answer, or your own option text. If an extra belongs in the doc, ask about it first. - -Synthesize into a strategic document: +Write only confirmed facts and explicitly marked open decisions. Omit irrelevant sections rather than filling them with generic prose. ```markdown # Product -## Register - -product - ## Platform web ## Users -[Who they are, their context, the job to be done. Primary audience; a secondary audience or a surface-vs-user split only when they apply.] +[Primary users, their situation, and job. Add other audiences only when confirmed.] ## Product Purpose -[What this product does, why it exists, what success looks like] +[What the product does, why it exists, and what success means.] ## Positioning -[The single strategic claim every screen reinforces. Not a visual rule, not an anti-reference.] +[The product mechanism or claim a neighboring product could not truthfully copy.] -## Conversion & proof -[Persuade surfaces only (marketing, landing, campaigns). Omit this section entirely, heading included, for the product register and for Experience or Read surfaces such as portfolios, albums, or long-form writing.] -- Primary and secondary CTA: [...] -- The line a visitor remembers after 10 seconds: [...] -- Belief ladder: [...] -- Proof on hand: [testimonials, case studies, press, or logos, referenced by path] +## Operating Context +[Workflows, environments, tools, documents, materials, and rituals that are factual parts of using or evaluating the product.] -## Brand Personality -[Voice, tone, 3-word personality, emotional goals] +## Capabilities and Constraints +[Confirmed functionality, technical constraints, terminology, and explicitly undecided product facts.] -## Anti-references -[What this should NOT look like. Specific bad-example sites or patterns to avoid.] +## Brand Commitments +[Existing name, voice, assets, personality, identity constraints, and references the user explicitly made binding. Omit when none exist.] -## Design Principles -[3-5 strategic principles derived from the conversation. Principles like "practice what you preach", "show, don't tell", "expert confidence". NOT visual rules like "use OKLCH" or "magenta accent".] +## Evidence on Hand +[Real content, data, demonstrations, testimonials, case studies, press, or assets, with paths where applicable. State absences that future work must not fabricate.] + +## Product Principles +[Three to five durable strategic principles derived from confirmed answers; no visual recipes.] ## Accessibility & Inclusion -[WCAG level, known user needs, considerations] +[Known user needs or required standard. Omit when no product-specific requirement was established.] ``` -Register is either `brand` or `product` as a bare value. No prose, no commentary. Platform is `web`, `ios`, `android`, or `adaptive`, also a bare value; omit the section only on legacy files you're leaving untouched, otherwise write `web` explicitly. +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. -Write fields as prose, and use bold sparingly: only where a word carries a decision, never as a label lead-in on every line. +### Completion gate -Write to `PROJECT_ROOT/PRODUCT.md`. If `.impeccable.md` existed, the loader already renamed it; merge into that content rather than starting from scratch. +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: Decide on DESIGN.md +## Step 5: Configure live mode when useful -Offer `$impeccable document` either way. Two paths: +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. -- **Code exists** (CSS tokens, components, a running site): "I can generate a DESIGN.md that captures your visual system (colors, typography, components) so variants stay on-brand. Want to do that now?" -- **Pre-implementation** (empty project): "I can seed a starter DESIGN.md from five quick questions about color strategy, type direction, motion energy, and references. You can re-run once there's code, to capture the real tokens. Want to do that now?" +## Step 6: Wrap up or resume -If the user agrees, delegate to `$impeccable document` (it auto-detects scan vs seed). Load its reference and follow that flow. +Summarize captured and deliberately undecided facts. Do not offer DESIGN.md merely because it is missing. -If the user prefers to skip, mention they can run `$impeccable document` any time later. +Recommend the next action from the actual project state: -## Step 6: Configure live mode (when code exists) +- Empty or early project: ask naturally for the surface to be built, or use `$impeccable shape ` when the user wants a confirmed brief without implementation. New-work will establish a visual world only when the requested work needs one. +- Existing coherent interface without DESIGN.md: `$impeccable document` if the user wants the incumbent system recorded independently of a new build. +- Existing surface needing work: name the most relevant scoped command. +- Web project ready for visual iteration: `$impeccable live` when configured. -**Skip this step when the platform is native** (`ios` / `android` / `adaptive`): live mode drives a browser overlay. A hybrid wrapper or Expo web target serving HTML doesn't change that. - -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 .agents/skills/impeccable/scripts/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) and platform captured (web / ios / android / adaptive) -- 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 or live config is pending, one line on how to set it up later - -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 platform**, offer the 2-4 most relevant (not a menu dump), and give the exact command to type. Group by intent: - -- **Build something new**: `$impeccable craft ` (shape, then build end-to-end) or `$impeccable shape ` (plan first). Lead with this for empty or early-stage projects. -- **Improve what's there**: name the specific surface. `$impeccable critique ` for a scored UX review; `$impeccable audit ` for a11y / perf / responsive checks; `$impeccable polish ` 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** (web only): `$impeccable live` (configured in Step 6) to pick elements in the browser and generate variants in place. **Skip this group for native platforms.** - -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 STOP and use Codex's structured user-input/question tool when available; if unavailable, ask directly in chat to clarify what you cannot infer. Ask whether they'd like a brief summary of PRODUCT.md appended to AGENTS.md for easier agent reference. If yes, append a short **Design Context** pointer section there. +If init was invoked by another request, resume without rerunning context.mjs; new-work owns later visual decisions. diff --git a/.agents/skills/impeccable/reference/ios.md b/.agents/skills/impeccable/reference/ios.md index ef10ee0d2..ccef5d2c4 100644 --- a/.agents/skills/impeccable/reference/ios.md +++ b/.agents/skills/impeccable/reference/ios.md @@ -2,7 +2,7 @@ For native iOS / iPadOS apps: SwiftUI, UIKit, React Native, Expo, Flutter shipping to Apple hardware. -On native, register narrows. HIG conformance governs structure, navigation, and interaction whatever the register; brand expresses through the expressive layer the platform provides (tint, type, motion, content). Calm, Duolingo, and Spotify carry strong identity entirely inside HIG conventions. +On native, the visitor mode narrows what expression may override. HIG conformance governs structure, navigation, and interaction in every mode; brand expresses through the layer the platform leaves open (tint, type, motion, content). ## The iOS slop test diff --git a/.agents/skills/impeccable/reference/layout.md b/.agents/skills/impeccable/reference/layout.md index 9e79e7eb6..7875fe45d 100644 --- a/.agents/skills/impeccable/reference/layout.md +++ b/.agents/skills/impeccable/reference/layout.md @@ -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. --- -## Register +## 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, register, 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 .agents/skills/impeccable/scripts/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 `$impeccable polish` for the final pass. +When the structure holds, hand off to `$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) * )`: 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. diff --git a/.agents/skills/impeccable/reference/live.md b/.agents/skills/impeccable/reference/live.md index bc1e3e9ea..199847bcd 100644 --- a/.agents/skills/impeccable/reference/live.md +++ b/.agents/skills/impeccable/reference/live.md @@ -35,7 +35,7 @@ Chat is overhead. No recap, no tutorial output, no pasting PRODUCT / DESIGN bodi node .agents/skills/impeccable/scripts/live.mjs ``` -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. +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, DESIGN.md, and any surface brief already loaded by Setup in mind for variant generation: **DESIGN.md wins on visual decisions; PRODUCT.md wins on durable product and voice decisions; the surface brief wins on this surface's strategy.** 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 requires the user's explicit redesign/replacement intent. `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). @@ -189,7 +189,7 @@ All three carry `fallback: "agent-driven"`. Follow **Handle fallback** below. ### 3. Load the action's reference -If `event.action` is `impeccable` (the default freeform action), use SKILL.md's shared laws plus the loaded register reference (`brand.md` or `product.md`). Do not load a sub-command reference. **Freeform is not a pass to skip parameters:** you still follow the composition budget and the freeform bias in **§7 Parameters** below. Sub-command files list MUST-have signature knobs; freeform has no such file, so sizing knobs from surface weight and primary axes is entirely on you. +If `event.action` is `impeccable` (the default freeform action), use SKILL.md's shared laws and decide the visitor mode from the selected surface. Do not load a sub-command reference. **Freeform is not a pass to skip parameters:** you still follow the composition budget and the freeform bias in **§7 Parameters** below. Sub-command files list MUST-have signature knobs; freeform has no such file, so sizing knobs from surface weight and primary axes is entirely on you. Any other `event.action` (`bolder`, `quieter`, `distill`, `polish`, `typeset`, `colorize`, `layout`, `adapt`, `animate`, `delight`, `overdrive`): Read `reference/.md` before planning. Each sub-command encodes a specific discipline; skipping its reference produces generic output. Those files may require specific params; layer them on top of the §7 budget, not instead of it. @@ -218,7 +218,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. @@ -226,10 +226,7 @@ This sentence is the **identity lock**. Every variant must be readable as the sa **Default mode**: the existing identity is preserved. Variants vary expression axes within it. *This is the right mode for ~90% of live sessions.* The user picked an element on a real product they're shipping; they expect variants of *their* hero, not three different brands' heroes. -**Departure mode**: the existing identity is rejected. Variants propose alternatives consistent with PRODUCT.md voice. Trigger only when at least one is true: - -- PRODUCT.md anti-references explicitly call out the current surface ("the current `index.html` is itself an example"; "diffuse away from this"; "the page on screen is the failure"). Generic anti-references that describe what to avoid in general do **not** trigger departure mode; only ones that point at *this* surface specifically. -- The user's freeform prompt explicitly asks for departure ("rebuild this from scratch", "what if it weren't editorial at all", "show me something completely different"). +**Departure mode**: the existing identity is rejected. Variants propose alternatives consistent with durable product and brand truth. Trigger only when the user explicitly asks for departure in the current request or freeform prompt ("redesign this", "rebuild this from scratch", "what if it weren't editorial at all", "show me something completely different"). A stale page critique or an old task note is not replacement authorization. If you're unsure, you're in default mode. The cost of being wrong about default is "three on-brand variants with similar feel": recoverable, the user picks none. The cost of being wrong about departure is "three off-brand variants": unrecoverable, the user is annoyed. @@ -248,13 +245,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 the brand's stated voice and register in PRODUCT.md. Do NOT pick from a fixed catalog of lane categories. The right three directions for this brand are not the same as the right three for another brand, 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. @@ -264,7 +261,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. @@ -291,7 +288,7 @@ In **default mode**, the prompt narrows the axes you choose, not the identity. * In **departure mode**, the prompt narrows the lanes you draw from, not the families. *"Make it feel like a newspaper front page"* would itself be a departure-mode prompt; honor it but pick three meaningfully different newspaper-adjacent lanes (broadsheet vs. tabloid vs. trade journal), and run the family pass to confirm they don't collapse into one. -When the prompt and PRODUCT.md anti-references conflict (the prompt asks for X, the anti-references ban X), the anti-references win; they describe the brand's standing position, the prompt is one moment. +When the prompt conflicts with a confirmed binding brand commitment or DESIGN.md invariant, preserve the invariant unless the user explicitly revokes or replaces it. Task-local strategy from the matching surface brief may change when the user changes that surface's goal. ### 6. Write all variants in a single edit diff --git a/.agents/skills/impeccable/reference/new-work.md b/.agents/skills/impeccable/reference/new-work.md index 3ac8b6b6e..92ad50f39 100644 --- a/.agents/skills/impeccable/reference/new-work.md +++ b/.agents/skills/impeccable/reference/new-work.md @@ -1,47 +1,178 @@ -# New identity work +# New visual work -Read this only when the project has no committed visual identity, or the user explicitly asked to replace the current one. A missing DESIGN.md is not enough: code, tokens, chosen type, components, and assets are incumbent design authority. If they exist, preserve and extend them unless the brief says to discard them. +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. -This playbook is task-scoped. PRODUCT.md owns durable product facts and DESIGN.md owns durable visual invariants; the direction for one page or feature belongs in the current task, not in either document. +## 1. Name the intent -## Choose the authority +- **Greenfield:** no coherent visual implementation. +- **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. -Classify the work before inventing anything: +A plain “redesign this page/site” authorizes replacement. “Redesign this within the current brand/system” means extension or refinement. Ask once only when the wording is genuinely ambiguous. -- **Extend**: an incumbent system exists and the task adds or improves a surface. Preserve its lineage, semantics, and affordances. -- **Surface redesign**: the current identity remains, but this surface may change its topology, hierarchy, and expression. -- **New identity**: nothing committed exists, or the user explicitly authorized a rebrand. +## 2. Resolve visual authority -When the evidence is mixed, preserve. Novelty is not permission to erase authored decisions. +Read DESIGN.md and representative code, tokens, components, and assets. Choose one path: -## Find the concept +### A. Explicit redesign -Name the subject, audience, page job, and visitor mode from SKILL.md. Then identify the product's unique mechanism: what it does, proves, or enables that a neighboring product cannot truthfully claim. +The old DESIGN.md and implementation are not authority. Keep only unrevoked product facts, content, function, native expectations, constraints, and brand commitments. Establish a replacement world. -Derive the concept from that mechanism through one primary transformation: information topology, evidence, temporal behavior, or spatial mapping. The transformation must change how the surface is organized or behaves, not merely how it is decorated. This produces stranger and more defensible work than imitating a culturally specific document or object. Borrowing a literal cultural form is allowed only when it is genuinely native to the product and audience, and never as costume. +### B. DESIGN.md covers this kind of surface -For **Operate** and **Read**, start with the task and information structure. Native controls remain native controls; expression comes from hierarchy, density, rhythm, state, and the system around them. For **Persuade** and **Experience** creating a new identity, explore more freely, but keep the product legible and the primary action obvious. +Use its invariants and normative tokens. Skip world-building and discover the surface. -Run `node .agents/skills/impeccable/scripts/concept-seed.mjs` only for Persuade or Experience new-identity work when two or more directions remain equally credible. It is an entropy source, not an authority: never let the draw overrule the strongest fit. When color is genuinely unconstrained, `node .agents/skills/impeccable/scripts/palette.mjs` may break a reflex palette; subject evidence and incumbent color still win. +### C. A coherent implementation exists but DESIGN.md does not -## State the direction +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. -Before code, write a task-scoped direction contract of at most 120 words in your reasoning: +### D. The brand exists, but a whole-surface family is unresolved -- the thesis this surface owns; -- the structural or behavioral transformation; -- what authority is preserved; -- the first viewport's composition and primary action; -- the signature element the surface will be remembered by. +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. -Do not embed this contract as a comment in the production artifact or turn it into a second design system. In an attended substantial build, confirm it once. In an unattended run, record it and continue. +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. -## Build once, commit fully +### E. No confirmed visual authority exists -Plan one compact token system and one layout source: the concept or the content's real structure. Build the strongest coherent direction once. Commitment means the transformation governs the whole surface; it does not mean every control must be rebuilt as a metaphor. +Establish a world. Scaffolds, framework defaults, and stray utilities are not identity. -Make the first viewport demonstrate the product's mechanism. Pace long surfaces through contrast in density and scale, and cut sections that only repeat claims. Briefs that depend on imagery ship real, verified imagery. Preserve semantic HTML, familiar interaction behavior, accessibility, and the project's technical conventions. +## 3. Discover the requested surface -## Finish proportionally +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. -Inspect desktop and mobile, write one honest critique against the brief and direction, patch material defects, then run the detector once. Repeat only when a real defect remains. A separate reviewer is optional when the harness already provides one and the extra cost is earned by the risk; it is not a default step and its presence is not evidence of quality. +Ask one attended round of at most three material questions without repeating durable facts. CTA hierarchy, proof sequence, content gaps, and interaction outcomes belong here, not PRODUCT.md. For a fully specified narrow request, state the interpretation and invite correction. + +When `shape` has already completed its discovery interview, reuse those confirmed answers and do not ask this round again. + +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. Each candidate joins a durable visual system (identity thesis, information/layout grammar, material and type behavior, color strategy, imagery, motion, reusable signature) to a concrete expression for this surface (topology, sequence, focal moment, and implementation consequence). Do not rank yet. +3. **Break the ranking rut once.** Run `node .agents/skills/impeccable/scripts/concept-seed.mjs --scope direction`. Promote the assigned grounded pair and weigh its challengers only when they can become both a coherent system and a strong solution to this task. +4. **Test coupling and breadth.** Reject a pair if its surface could swap into another candidate unchanged, or if its world is merely the surface motif repeated. Test each survivor on the requested surface plus navigation, quiet and dense content, interaction/state, and one unlike future surface. Compare it with both the category habit and predictable contrarian response. +5. **Offer coupled choices.** Present two or three materially different pairs without recommendation cues. For each, show the world rules, this surface's 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. 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 .agents/skills/impeccable/scripts/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 promoted candidate when a roll ran; otherwise use the strongest grounded structure. It must survive both tests. + +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). + +### Write or update DESIGN.md + +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: + +`` + +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 .agents/skills/impeccable/scripts/surface-brief.mjs read ` + +Exit 0: preserve still-valid decisions and change only what the user changed. For redesign, retain valid product strategy, content, function, and open decisions; replace visual direction and contract. Exit 2: no brief. After the contract, write with `node .agents/skills/impeccable/scripts/surface-brief.mjs write [related-target ...]`. + +The body is concise and contains: + +```markdown +# Surface brief: [name] + +## Scope +[Primary/related route or artifact, visitor mode, and what this surface owns.] + +## Product strategy +[Surface-specific audience and job, desired outcome, primary/secondary action or task, content and proof sequence, factual constraints, and explicitly open decisions.] + +## Selected direction +[Reference to the applicable DESIGN.md world or expression range, selected surface concept, focal moment, narrative/interaction sequence, and implementation consequence.] + +## Direction contract +[The six contract blocks below.] + +## Open decisions +[Only unresolved items that later work must not silently invent. Omit when empty.] +``` + +Commit `.impeccable/surfaces/.md` as stable later-work context. Exclude global truth, exact tokens, transient notes, and work logs. + +## 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. + +Before code, write a direction contract of at most 150 words in an opening HTML or framework comment. The first 200 characters must name `DIRECTION CONTRACT`. + +- `UNIQUE`: the task thesis tied to the product mechanism; +- `NOT-TEMPLATE`: the category-default arrangement this structure refuses; +- `OWN-WORLD`: the confirmed DESIGN.md invariants, tokens, and materials it uses; +- `STORY`: what the visitor understands, believes, and does; +- `FIRST VIEWPORT`: exact composition, hierarchy, action, and where the concept exceeds competent convention; +- `FORM`: chosen structure or behavior, signature, implementation consequence, and seed key. + +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. + +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. + +## 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. + +Build the strongest coherent direction once. Its grammar governs navigation, actions, controls, content, and transitions without disguising affordances. Give the focal form the scale that gives it force; do not trap it inside a standard hero panel. + +**Make the opening a thesis.** Demonstrate the mechanism immediately; leave an idea, interaction, or evidence, not merely mood. + +**Commit before correcting.** Land the hard move at full strength before refining it. In unattended work, safety is the known risk. + +**Commit at page scale.** Let color, material, image, or type own a region when the world calls for it. Scattered signature decoration is not commitment. + +**Pace the whole surface.** Vary density, scale, image, motion, and quiet inside one grammar. Cut repeated claims; prove the mechanism with real artifact, interaction, data, transformation, or content. + +**Author motion as material.** Motion expresses the world and task. Premium moments go beyond repeated transform/opacity through earned focus, depth, masks, continuity, light, or material change. Bound expensive effects, test in-browser, keep content visible by default, and design reduced motion. + +Preserve semantics, affordances, accessibility, performance, responsiveness, and project conventions. Operate/Read express through topology, hierarchy, density, rhythm, and state; Persuade/Experience may earn drama. + +## 8. Solidify the visual record + +After first implementation of a new/replacement world or approved expansion, refresh DESIGN.md from the build: + +- replace provisional direction with the exact type, color roles, tokens, spacing/radii, components, states, and motion that survived; +- add normative YAML tokens only for values the implementation actually uses; +- remove the directional-seed status once the record and implementation agree; +- preserve broader world invariants and expression ranges; +- do not promote the task's story, hero composition, or one-off motif into a global rule unless it is intentionally reusable. + +Ordinary extension does not rewrite DESIGN.md; only approved durable changes do. + +## 9. Finish like a 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. diff --git a/.agents/skills/impeccable/reference/operate.md b/.agents/skills/impeccable/reference/operate.md index 04ee9d7d2..f420cbaa2 100644 --- a/.agents/skills/impeccable/reference/operate.md +++ b/.agents/skills/impeccable/reference/operate.md @@ -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. - No orchestrated page-load sequences. Product loads into a task; users don't want to watch it load. -## Product bans (on top of the shared absolute bans) +## Product constraints - Decorative motion that doesn't convey state. - Inconsistent component vocabulary across screens. If the "save" button looks different in two places, one is wrong. @@ -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. diff --git a/.agents/skills/impeccable/reference/polish.md b/.agents/skills/impeccable/reference/polish.md index 8a0039319..02df022e4 100644 --- a/.agents/skills/impeccable/reference/polish.md +++ b/.agents/skills/impeccable/reference/polish.md @@ -1,241 +1,98 @@ -> **Additional context needed**: quality bar (MVP vs flagship). +> **Additional context needed**: quality bar and shipping constraints. -Perform a meticulous final pass to catch all the small details that separate good work from great work. The difference between shipped and polished. +Polish is refinement, never concealed redesign. Preserve the incumbent visual world, content, behavior, and everything outside scope. If the concept itself is wrong, say so and recommend redesign or `bolder` instead of smuggling in a replacement. -Detector and automated QA output are defect evidence only. A clean script result is never proof that the design is strong; gather browser evidence and inspect the real interaction path. +A detector result is defect evidence, not proof of quality. Inspect the rendered experience and real interaction path. -## Design System Discovery +## 1. Establish the system -Aligning the feature to the design system is **not optional**. Polish without alignment is decoration on top of drift, and it makes the next person's job harder. Discovery comes before any other polish work. +Read DESIGN.md and representative tokens, shared components, patterns, and neighboring flows. If no formal system exists, use coherent project conventions. -1. **Find the design system**: Search for design system documentation, component libraries, style guides, or token definitions. Study the core patterns: design principles, target audience, color tokens, spacing scale, typography styles, component API, motion conventions. -2. **Note the conventions**: How are shared components imported? What spacing scale is used? Which colors come from tokens vs hard-coded values? What motion and interaction patterns are established? What flow shapes are used for comparable actions (modal vs full-page, inline vs route, save-on-blur vs explicit submit)? -3. **Identify drift, then name the root cause**: For every deviation, classify it as a **missing token** (the value should exist in the system but doesn't), a **one-off implementation** (a shared component already exists but wasn't used), or a **conceptual misalignment** (the feature's flow, IA, or hierarchy doesn't match neighboring features). The fix differs by category: patch the value, swap to the shared component, or rework the flow. Fixing the symptom without naming the cause is how drift compounds. +Classify each drift before fixing it: -If a design system exists, polish **must** align the feature with it. If none exists, polish against the conventions visible in the codebase. **If anything about the system is ambiguous, ask. Never guess at design system principles.** +- **missing token:** the system needs a reusable value; +- **one-off implementation:** an existing shared component or pattern should replace it; +- **conceptual mismatch:** the flow, information architecture, or hierarchy differs from comparable product areas; +- **local defect:** the implementation is simply incomplete or inconsistent. -## Pre-Polish Assessment +Fix the cause at the narrowest correct level. Ask when a binding system principle cannot be inferred. -Understand the current state and goals before touching anything: +## 2. Gather the evidence -1. **Review completeness**: - - Is it functionally complete? - - Are there known issues to preserve (mark with TODOs)? - - What's the quality bar? (MVP vs flagship feature?) - - When does it ship? (How much time for polish?) +Use the feature yourself at representative desktop and mobile sizes. Determine: -2. **Think experience-first**: Who actually uses this, and what's the best possible experience for them? Effective design beats decorative polish; a feature that looks beautiful but fights the user's flow is not polished. Walk the path from their perspective before opening DevTools. +- whether the path is functionally complete; +- the intended quality bar and time available; +- known constraints or deliberately unfinished work; +- the states, content lengths, roles, and input methods users will actually encounter. -3. **Identify polish areas**: - - Visual inconsistencies - - Spacing and alignment issues - - Interaction state gaps - - Copy inconsistencies - - Edge cases and error states - - Loading and transition smoothness - - Information architecture and flow drift (does this feature reveal complexity the way neighboring features do?) +If a prior critique exists, use it as one input: -4. **Pull in any prior critique** (optional signal): If `$impeccable critique` has been run on the same target, its priority issues are a useful prior for what to address first. Resolve the target to a file path or URL, then: - ```bash - slug=$(node .agents/skills/impeccable/scripts/critique-storage.mjs slug "") - node .agents/skills/impeccable/scripts/critique-storage.mjs latest "$slug" - ``` - Exit 0 with body = found; fold the P0/P1 items into your polish list and mention the snapshot path so the user sees what you read. Exit 2 = no snapshot, continue without it. The critique is one input among many. Do your own pass either way. +```bash +slug=$(node .agents/skills/impeccable/scripts/critique-storage.mjs slug "") +node .agents/skills/impeccable/scripts/critique-storage.mjs latest "$slug" +``` -5. **Triage cosmetic vs functional**: Classify each issue as **cosmetic** (looks off, doesn't impede the user) or **functional** (breaks, blocks, or confuses the experience). When polish time is tight, functional issues ship first; cosmetic ones can land in a follow-up. Quality should be consistent; never perfect one corner while leaving another rough. +Exit 0 returns the latest snapshot; incorporate relevant P0/P1 findings and name the snapshot read. Exit 2 means none exists. Perform an independent pass either way. -**CRITICAL**: Polish is the last step, not the first. Don't polish work that's not functionally complete. +## 3. Triage -## Polish Systematically +Separate functional defects from cosmetic ones and fix in this order: -Work through these dimensions methodically: +1. broken or blocked tasks, data loss, misleading state, and inaccessible paths; +2. missing loading, empty, error, success, disabled, and permission states; +3. flow, hierarchy, responsive, and design-system drift; +4. visual and motion inconsistencies; +5. code and asset cleanup. -### Visual Alignment & Spacing +Do not perfect one corner while leaving the rest below the same quality bar. -- **Pixel-perfect alignment**: Everything lines up to grid -- **Consistent spacing**: All gaps use spacing scale (no random 13px gaps) -- **Optical alignment**: Adjust for visual weight (icons may need offset for optical centering) -- **Responsive consistency**: Spacing and alignment work at all breakpoints -- **Grid adherence**: Elements snap to baseline grid +## 4. Polish the whole path -**Check**: -- Enable grid overlay and verify alignment -- Check spacing with browser inspector -- Test at multiple viewport sizes -- Look for elements that "feel" off +### Flow and hierarchy -### Information Architecture & Flow +- Match neighboring mental models, terminology, disclosure, routing, save behavior, and optimistic or pessimistic patterns. +- Make the primary task and current state obvious without flattening every element to equal weight. +- Ensure arrival, transition, empty, and recovery paths connect instead of behaving as isolated screens. -Visual polish on a misshapen flow is wasted work. Match the *shape* of the experience to the system, not just the surface. +### Layout and type -- **Progressive disclosure**: Match how much is revealed when, compared to neighboring features. A settings page exposing 40 fields when the rest of the app reveals 5 at a time is drift, even if every field is perfectly styled. -- **Established user flows**: Multi-step actions follow the same shape as comparable flows elsewhere: modal vs full-page, inline edit vs separate route, save-on-blur vs explicit submit, optimistic vs pessimistic updates. -- **Hierarchy & complexity**: The same conceptual weight gets the same visual weight throughout. Primary actions don't become tertiary in one corner of the product, and tertiary actions don't shout. -- **Empty, loading, and arrival transitions**: How content arrives, updates, and leaves matches how it does in adjacent features. -- **Naming and mental model**: The feature uses the same nouns and verbs as the rest of the system. A "Workspace" here shouldn't be a "Project" three screens away. +- Align to the project's grid and spacing scale; fix optical as well as mathematical alignment. +- Group related content tightly and separate distinct groups generously. +- Keep same-role typography consistent; test measure, wrapping, localization expansion, zoom, and font loading. +- Verify every supported viewport rather than correcting only the current screenshot. -### Typography Refinement +### Color, imagery, and icons -- **Hierarchy consistency**: Same elements use same sizes/weights throughout -- **Line length**: 45-75 characters for body text -- **Line height**: Appropriate for font size and context -- **Widows & orphans**: No single words on last line -- **Hyphenation**: Appropriate for language and column width -- **Kerning**: Adjust letter spacing where needed (especially headlines) -- **Font loading**: No FOUT/FOIT flashes +- Use semantic tokens and stable color meanings across themes. +- Verify text, control, and focus contrast in every state. +- Keep icon families, stroke/weight, sizing, and optical alignment coherent. +- Prevent image layout shift; use correct aspect ratios, responsive sources, and useful alt text. -### Color & Contrast +### Interaction and state -- **Contrast ratios**: All text meets WCAG standards -- **Consistent token usage**: No hard-coded colors, all use design tokens -- **Theme consistency**: Works in all theme variants -- **Color meaning**: Same colors mean same things throughout -- **Accessible focus**: Focus indicators visible with sufficient contrast -- **Gray on color**: Never put gray text on colored backgrounds; use a shade of that color or transparency +- Every control needs appropriate default, hover, focus, active, disabled, loading, error, and success behavior. +- Preserve visible keyboard focus, logical tab order, labels, and platform-appropriate touch targets. +- Keep motion coherent, interruptible, performant, and reduced-motion aware. Do not add animation merely to make polish visible. +- Validate long, missing, localized, offline, slow, and permission-limited content where the product can encounter it. -### Interaction States +### Content and code -Every interactive element needs all states: +- Keep terminology, capitalization, punctuation, and factual copy consistent. Ask before changing claims. +- Remove debug output, dead code, unused imports, obsolete styles, and polish-created duplication. +- Replace custom implementations with shared components where the system owns the pattern. +- Promote genuinely reusable values to tokens; do not create a system abstraction for one local exception. -- **Default**: Resting state -- **Hover**: Subtle feedback (color, scale, shadow) -- **Focus**: Keyboard focus indicator (never remove without replacement) -- **Active**: Click/tap feedback -- **Disabled**: Clearly non-interactive -- **Loading**: Async action feedback -- **Error**: Validation or error state -- **Success**: Successful completion +## 5. Verify and finish -**Missing states create confusion and broken experiences**. +Walk the complete path again with mouse, keyboard, and touch where applicable. Check: -### Micro-interactions & Transitions +- mobile, intermediate, and wide layouts; +- loading, empty, error, success, disabled, long-content, and missing-content states; +- zoom, contrast, focus, semantics, screen-reader names, and reduced motion; +- console errors, layout shift, interaction latency, image loading, and supported browsers; +- agreement with DESIGN.md, neighboring features, and the user's scope. -- **Smooth transitions**: All state changes animated appropriately (150-300ms) -- **Consistent easing**: Use ease-out-quart/quint/expo for natural deceleration. Never bounce or elastic; they feel dated. -- **No jank**: Smooth animations; use atmospheric blur/filter/mask/shadow effects when they add polish, but bound expensive paint areas and avoid casual layout-property animation -- **Appropriate motion**: Motion serves purpose, not decoration -- **Reduced motion**: Respects `prefers-reduced-motion` +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. -### Content & Copy - -- **Consistent terminology**: Same things called same names throughout -- **Consistent capitalization**: Title Case vs Sentence case applied consistently -- **Grammar & spelling**: No typos -- **Appropriate length**: Not too wordy, not too terse -- **Punctuation consistency**: Periods on sentences, not on labels (unless all labels have them) - -### Icons & Images - -- **Consistent style**: All icons from same family or matching style -- **Appropriate sizing**: Icons sized consistently for context -- **Proper alignment**: Icons align with adjacent text optically -- **Alt text**: All images have descriptive alt text -- **Loading states**: Images don't cause layout shift, proper aspect ratios -- **Retina support**: 2x assets for high-DPI screens - -### Forms & Inputs - -- **Label consistency**: All inputs properly labeled -- **Required indicators**: Clear and consistent -- **Error messages**: Helpful and consistent -- **Tab order**: Logical keyboard navigation -- **Auto-focus**: Appropriate (don't overuse) -- **Validation timing**: Consistent (on blur vs on submit) - -### Edge Cases & Error States - -- **Loading states**: All async actions have loading feedback -- **Empty states**: Helpful empty states, not just blank space -- **Error states**: Clear error messages with recovery paths -- **Success states**: Confirmation of successful actions -- **Long content**: Handles very long names, descriptions, etc. -- **No content**: Handles missing data gracefully -- **Offline**: Appropriate offline handling (if applicable) - -### Responsiveness - -- **All breakpoints**: Test mobile, tablet, desktop -- **Touch targets**: 44x44px minimum on touch devices -- **Readable text**: No text smaller than 14px on mobile -- **No horizontal scroll**: Content fits viewport -- **Appropriate reflow**: Content adapts logically - -### Performance - -- **Fast initial load**: Optimize critical path -- **No layout shift**: Elements don't jump after load (CLS) -- **Smooth interactions**: No lag or jank -- **Optimized images**: Appropriate formats and sizes -- **Lazy loading**: Off-screen content loads lazily - -### Code Quality - -- **Remove console logs**: No debug logging in production -- **Remove commented code**: Clean up dead code -- **Remove unused imports**: Clean up unused dependencies -- **Consistent naming**: Variables and functions follow conventions -- **Type safety**: No TypeScript `any` or ignored errors -- **Accessibility**: Proper ARIA labels and semantic HTML - -## Polish Checklist - -Go through systematically: - -- [ ] Aligned to the design system (drift named and resolved by root cause) -- [ ] Information architecture and flow shape match neighboring features -- [ ] Visual alignment perfect at all breakpoints -- [ ] Spacing uses design tokens consistently -- [ ] Typography hierarchy consistent -- [ ] All interactive states implemented -- [ ] All transitions smooth (60fps) -- [ ] Copy is consistent and polished -- [ ] Icons are consistent and properly sized -- [ ] All forms properly labeled and validated -- [ ] Error states are helpful -- [ ] Loading states are clear -- [ ] Empty states are welcoming -- [ ] Touch targets are 44x44px minimum -- [ ] Contrast ratios meet WCAG AA -- [ ] Keyboard navigation works -- [ ] Focus indicators visible -- [ ] No console errors or warnings -- [ ] No layout shift on load -- [ ] Works in all supported browsers -- [ ] Respects reduced motion preference -- [ ] Code is clean (no TODOs, console.logs, commented code) - -**IMPORTANT**: Polish is about details. Zoom in. Squint at it. Use it yourself. The little things add up. - -Sweat the details. Zoom in until the alignment is right and the spacing reads as deliberate. Then ship. - -**NEVER**: -- Polish before it's functionally complete -- Polish without aligning to the design system; that's decoration on drift -- Guess at design system principles instead of asking when something is ambiguous -- Spend hours on polish if it ships in 30 minutes (triage) -- Introduce bugs while polishing (test thoroughly) -- Ignore systematic issues (if spacing is off everywhere, fix the system, not just one screen) -- Perfect one thing while leaving others rough (consistent quality level) -- Create new one-off components when design system equivalents exist -- Hard-code values that should use design tokens -- Introduce new patterns or flows that diverge from established ones - -## Final Verification - -Before marking as done: - -- **Use it yourself**: Actually interact with the feature. -- **Test on real devices**: Not just browser DevTools. -- **Ask someone else to review**: Fresh eyes catch things. -- **Compare to design**: Match intended design. -- **Check all states**: Don't just test happy path. -- **Treat automation carefully**: Run detector or QA commands when they are available and relevant, fix their defects, but never cite a clean result as proof that the work is polished. - -## Clean Up - -After polishing, ensure code quality: - -- **Replace custom implementations**: If the design system provides a component you reimplemented, switch to the shared version. -- **Remove orphaned code**: Delete unused styles, components, or files made obsolete by polish. -- **Consolidate tokens**: If you introduced new values, check whether they should be tokens. -- **Verify DRYness**: Look for duplication introduced during polishing and consolidate. +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. diff --git a/.agents/skills/impeccable/reference/quieter.md b/.agents/skills/impeccable/reference/quieter.md index c059bd5c9..50d110229 100644 --- a/.agents/skills/impeccable/reference/quieter.md +++ b/.agents/skills/impeccable/reference/quieter.md @@ -2,7 +2,7 @@ Quiet design is harder than bold design. Subtlety needs precision. Reduce visual --- -## Register +## Visitor mode Persuade + Experience: "quieter" means more restrained palette, more whitespace, more typographic air. Drama is reduced, not eliminated; the POV stays intact. diff --git a/.agents/skills/impeccable/reference/routing.md b/.agents/skills/impeccable/reference/routing.md index a1f83505a..405bfc629 100644 --- a/.agents/skills/impeccable/reference/routing.md +++ b/.agents/skills/impeccable/reference/routing.md @@ -11,7 +11,7 @@ Reason over the signals; there is no score to obey: - `critique.latest` with a low `score` or non-zero `p0` / `p1` → `polish` (it reads that snapshot as its backlog), or re-run `critique` if the snapshot looks stale. - `git.changedFiles` pointing at one surface → scope `audit` or `polish` to those files specifically, naming them. - `devServer.running` true → `live` is available for in-browser iteration; if false, don't lead with `live`. **`live` and the bundled `detect.mjs` are web-only.** If `setup.platform` is `ios`, `android`, or `adaptive`, don't lead with either; the browser overlay and the HTML rule engine don't apply to native app code. -- Otherwise group by intent exactly as init's "Recommend starting points" step does (build new / improve what's there / iterate visually), tailored to `setup.register`. +- Otherwise group by intent (build new / improve what's there / iterate visually), tailored to the current surface and `setup.platform`. **If `scan.targets` is non-empty and `setup.platform` is not `ios`/`android`/`adaptive`, run `node .agents/skills/impeccable/scripts/detect.mjs --json ` once** (the bundled detector over local files: no network, no npx; it reads HTML/CSS, so skip it for native projects). `scan.via` tells you what they are: `git-changes` (the markup/style files in your dirty tree, the most relevant set), `source-dir` (e.g. `src`, `app`), `html`, or `root`. Fold the hits into your picks: many quality / contrast hits → `audit` or `polish`; a specific slop family → the matching command (gradient text or eyebrows → `quieter` / `typeset`, flat or gray palette → `colorize`, and so on). It's a real, current signal that beats guessing. If detect errors or the tree is large and slow, skip it and recommend the user run `audit` themselves; never block the suggestion on it. diff --git a/.agents/skills/impeccable/reference/shape.md b/.agents/skills/impeccable/reference/shape.md index be8838b80..acbb26e5b 100644 --- a/.agents/skills/impeccable/reference/shape.md +++ b/.agents/skills/impeccable/reference/shape.md @@ -1,167 +1,61 @@ -Shape the UX and UI for a feature before any code is written. This command produces a **design brief**: a structured artifact that guides implementation through discovery, not guesswork. +# Shape -**Scope**: Design planning only. This command does NOT write code. It produces the thinking that makes code good. +Discover what should be made and how it should work, then return a confirmed design brief without code. -**Output**: A design brief that can be handed off to $impeccable craft, or directly to $impeccable for freeform implementation. When visual direction probes are used, the images are supporting artifacts, not the primary output. +**Product gate:** when context reports that PRODUCT.md is missing, load and complete [init.md](init.md) before Phase 1. Do not substitute shape questions for the product interview. Once PRODUCT.md exists, return here; product context does not replace task discovery. -## Philosophy +## Phase 1: Discovery interview -Most AI-generated UIs fail not because of bad code, but because of skipped thinking. They jump to "here's a card grid" without asking "what is the user trying to accomplish?" This command inverts that: understand deeply first, so implementation is precise. +Do not write code or choose visual direction yet. -## Phase 1: Discovery Interview +### Cadence -**Do NOT write any code or make any design decisions during this phase.** Your only job is to understand the feature deeply enough to make excellent design decisions later. +- Use the structured question tool when available; otherwise ask and stop. +- Ask two or three related questions per round, then wait. One round is the default; add a second only when the answers expose a material gap. +- Do not dump a questionnaire, repeat settled facts, or turn obvious facts into menus. Assert the likely reading and invite correction. +- A sparse prompt requires at least one answer round. A precise prompt may need only a compact confirmation. -**Unattended runs:** when no user can respond (a one-shot task, an automated run, or an explicit instruction not to ask questions), answer the interview questions yourself from the brief, record the answers as the design brief, and continue without pausing at any confirmation gate in this file. The thinking is still required; only the waiting is waived. +### Round 1: purpose, people, and outcome -This is a required interaction, not optional guidance. Ask these questions in conversation, adapting based on answers. Don't dump them all at once; have a natural dialogue. STOP and use Codex's structured user-input/question tool when available; if unavailable, ask directly in chat to clarify what you cannot infer. +Choose the two or three questions that most change the result: -### Interview cadence +- What is this surface or feature for, and what problem must it solve? +- Who specifically reaches it, in what situation and state of mind? +- What is the primary thing they must understand or do? What would success look like? +- What is uniquely true here that a neighboring product or generic template could not claim? -Discovery includes at least one user-answer round unless PRODUCT.md, DESIGN.md, or an already-confirmed brief directly answers the needed inputs. With a sparse prompt, do **not** synthesize a complete brief for confirmation on the first response. +### Round 2: material, behavior, and boundaries -- Use the harness's structured question tool when one exists. Otherwise, ask directly in chat and stop. -- Ask **2-3 questions per round**, then wait for answers. -- Treat PRODUCT.md and DESIGN.md as anchors; they reduce repeated questions but do **not** replace shape for craft. Shape is task-specific. -- One round is the default. Add a second only if the first answers leave material gaps. Don't run a second round just to feel thorough. -- Round 1 should clarify purpose, audience/context, content/scope, and (for brand) visual direction. -- Round 2, when needed, fills in whatever's still genuinely missing. +Run only for material unresolved decisions: -**Assert-then-confirm, not menu-with-escape.** When PRODUCT.md and the user's prompt make one option obvious, name it and ask the user to confirm or override. Don't enumerate "Restrained / Committed / Or something else?" as a real choice; "This reads as Restrained, confirm?" beats a four-option menu when the answer is already clear. +- What real content, evidence, data, and assets must the experience carry? What are realistic minimum, typical, and maximum ranges? +- Which states and transitions matter: first-run, empty, loading, error, success, permissions, overflow, or expert use? +- What is the intended fidelity, breadth, and interactivity: exploration, production-ready screen, full flow, or broader surface? +- What must remain untouched? What would make the result feel wrong even if it looked polished? +- Which platform, framework, performance, accessibility, localization, or delivery constraints are binding? -### Purpose & Context -- What is this feature for? What problem does it solve? -- Who specifically will use it? (Not "users"; be specific: role, context, frequency) -- What does success look like? How will you know this feature is working? -- What's the user's state of mind when they reach this feature? (Rushed? Exploring? Anxious? Focused?) +Never ask for CSS values or canned aesthetic lanes. New-work owns visual-world and concept choices. -### Content & Data -- What content or data does this feature display or collect? -- What are the realistic ranges? (Minimum, typical, maximum, e.g., 0 items, 5 items, 500 items) -- What are the edge cases? (Empty state, error state, first-time use, power user) -- Is any content dynamic? What changes and how often? -- What visual assets are real content here? Note required images, product shots, illustrations, maps, textures, diagrams, generated objects, or existing project assets. +## Phase 2: Resolve the design direction -### Design Direction +For new surfaces, brand expansion, or replacement, follow [new-work.md](new-work.md) through visual authority, any world workshop, and concept choice. Reuse discovery, then return before its contract, persistence, or implementation. Inside an established world, use its concept process only when composition or interaction remains materially open. -Force a visual decision on three fronts. Skip anything PRODUCT.md or DESIGN.md already answers; ask only what's missing. +## Phase 3: Write the brief -- **Color strategy for this surface.** Pick one: Restrained / Committed / Full palette / Drenched. Can override the project default if the surface earns it (e.g. a drenched hero inside an otherwise Restrained product). -- **Theme via scene sentence.** Write one sentence of physical context for this surface: who uses it, where, under what ambient light, in what mood. The sentence forces dark vs light. If it doesn't, add detail until it does. -- **Two or three named anchor references.** Specific products, brands, objects. Not adjectives like "modern" or "clean." +Write the smallest useful brief: -### Scope +1. **Job and audience:** who arrives, their context, need, and visitor mode. +2. **Outcome and proof:** primary task/action, success, real evidence, and product-specific truth. +3. **Selected direction:** visual authority, structural/interaction thesis, sequence, focal moment, and implementation consequence. +4. **Scope and boundaries:** fidelity, breadth, interactivity, named target, what remains untouched, and explicit anti-goals. +5. **States and ranges:** realistic content/data ranges and material states. +6. **Interaction and layout:** hierarchy, topology, responsiveness, affordances, feedback, and transitions; intent, not CSS. +7. **Constraints and open decisions:** platform, delivery, accessibility, localization, reusable components, and choices a builder must not invent. -Always ask. Sketch quality and shipped quality are different outputs; don't guess between them. +Use three to five bullets when the task is settled; use the full structure only for ambiguous, multi-screen, or standalone planning. Do not restate the conversation. -- **Fidelity.** Sketch / mid-fi / high-fi / production-ready? -- **Breadth.** One screen / a flow / a whole surface? -- **Interactivity.** Static visual / interactive prototype / shipped-quality component? -- **Time intent.** Quick exploration, or polish until it ships? +## Confirm and stop -Scope answers are task-scoped. Don't write them to PRODUCT.md or DESIGN.md; carry them through the design brief only. +Present the brief for explicit confirmation or one correction round, then stop: shape never writes code or a direction contract. -### Constraints -- Are there technical constraints? (Framework, performance budget, browser support) -- Are there content constraints? (Localization, dynamic text length, user-generated content) -- Mobile/responsive requirements? -- Accessibility requirements beyond WCAG AA? - -### Anti-Goals -- What should this NOT be? What would be a wrong direction? -- What's the biggest risk of getting this wrong? - -## Phase 1.5: Visual Direction Probe (Capability-Gated) - -After the discovery interview, generate a small set of visual direction probes **before** writing the final brief when all of these are true: - -- The work is **net-new** or directionally ambiguous enough that visual exploration will clarify the brief. -- The requested fidelity is **mid-fi, high-fi, or production-ready**. Skip for sketch-only planning. -- The current harness gives you native image generation (Codex's `image_gen`, an equivalent MCP tool, or similar). Don't ask the user to install APIs or tooling. - -When those conditions are met, this step is mandatory. If image generation isn't natively available, do not ask the user to install APIs or tooling. State in one line that the image step is skipped because the harness lacks native image generation, then proceed. The one-line announcement is required, not optional; it forces a conscious decision instead of letting the step quietly evaporate. - -Use probes to explore visual lanes, not to replace the brief. - -Do not skip probes because the final UI will be semantic, editable, code-native, responsive, or accessible. Those are implementation requirements, not reasons to avoid visual exploration. - -### What to generate - -Generate **2 to 4** distinct direction probes based on the discovery answers, especially: - -- Color strategy -- Theme scene sentence -- Named anchor references -- Scope and fidelity - -The probes should differ in primary visual direction (hierarchy, topology, density, typographic voice, or color strategy), not just palette tweaks. - -### How to use the probes - -- Treat them as **direction tests**, not final designs. -- Use them to pressure-test whether the brief is pointing at the right lane. -- Ask the user which direction feels closest, what feels off, and what should carry forward. -- If the probes reveal a mismatch, revise the brief inputs before finalizing the brief. - -### Important limits - -- Do **not** skip discovery because image generation is available. -- Do **not** treat generated imagery as final UX specification, final copy, or final accessibility behavior. -- Do **not** use this step for minor refinements of existing work. It's for shaping a new surface or clarifying a big directional choice. - -If image generation isn't natively available, announce the skip in one line and proceed to the design brief. - -## Phase 2: Design Brief - -After the interview and any required probes, present a brief and **end your response**. The user must confirm before any implementation runs. Do not present a brief and then continue to code in the same response, even if the brief feels obvious to you. The user's confirmation is the gate. - -**Choose the brief shape based on how clear the answers are:** - -- **Compact form (3-5 bullets)** when discovery was crisp and the original prompt + PRODUCT.md already pinned scope, content, and direction. State what you're building, the visual lane, and end with one or two specific questions or a clear "confirm or override?" prompt. This is the default for typical craft requests with a clear prompt. -- **Full structured form (sections below)** when the task is genuinely ambiguous, multi-screen, or when the user asked for shape as a standalone step. Use this when the discipline of structure earns its weight. - -Don't pad a clear brief into a long one to look thorough. A 70-line brief restating answers the user just gave is noise, not rigor. Equally, don't skip the confirmation pause to look efficient: the pause is the point. - -Present the brief, then **stop and wait for explicit confirmation**. You are not the judge of whether the user already approved. Even when the brief feels obviously right, ask once and wait. The pause is what separates shape from premature implementation. - -### Brief Structure - -**1. Feature Summary** (2-3 sentences) -What this is, who it's for, what it needs to accomplish. - -**2. Primary User Action** -The single most important thing a user should do or understand here. - -**3. Design Direction** -Color strategy (Restrained / Committed / Full palette / Drenched) + the theme scene sentence + 2–3 named anchor references. Reference PRODUCT.md and DESIGN.md where they already answer, and note any per-surface overrides. - -If you ran the Visual Direction Probe step, name which probe direction won and what changed in the brief because of it. - -**4. Scope** -Fidelity, breadth, interactivity, and time intent from the Scope section of the interview. Task-scoped; these don't persist beyond the brief. - -**5. Layout Strategy** -High-level spatial approach: what gets emphasis, what's secondary, how information flows. Describe the visual hierarchy and rhythm, not specific CSS. - -**6. Key States** -List every state the feature needs: default, empty, loading, error, success, edge cases. For each, note what the user needs to see and feel. - -**7. Interaction Model** -How users interact with this feature. What happens on click, hover, scroll? What feedback do they get? What's the flow from entry to completion? - -**8. Content Requirements** -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., 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. - ---- - -STOP and use Codex's structured user-input/question tool when available; if unavailable, ask directly in chat to clarify what you cannot infer. Ask for explicit confirmation of the brief before finishing. - -If the user disagrees with any part, revisit the relevant discovery questions. A shape run is incomplete until the user confirms direction. - -Once confirmed, the brief is complete. The user can now hand it to $impeccable, or use it to guide any other implementation approach. (If the user wants the full discovery-then-build flow in one step, they should use $impeccable craft instead, which runs this command internally.) +When no human or structured answer mechanism exists, mark assumptions plainly, return the brief, and stop. diff --git a/.agents/skills/impeccable/reference/typeset.md b/.agents/skills/impeccable/reference/typeset.md index 80529edf5..47dfd452e 100644 --- a/.agents/skills/impeccable/reference/typeset.md +++ b/.agents/skills/impeccable/reference/typeset.md @@ -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. --- -## Register +## Visitor mode -New identity work: run the font selection procedure in [new-work.md](new-work.md). Fluid `clamp()` scale, ≥1.25 ratio between steps. +- **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, register, **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 .agents/skills/impeccable/scripts/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 `$impeccable polish` for the final pass. +When the hierarchy holds, hand off to `$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) * )` 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 - -The tactical selection procedure and the reflex-reject list live in [new-work.md](new-work.md) (loaded for new identity work). 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 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. diff --git a/.agents/skills/impeccable/scripts/command-metadata.json b/.agents/skills/impeccable/scripts/command-metadata.json index 83796d54b..dad8ef2e0 100644 --- a/.agents/skills/impeccable/scripts/command-metadata.json +++ b/.agents/skills/impeccable/scripts/command-metadata.json @@ -1,6 +1,6 @@ { "craft": { - "description": "Full confirmed-brief-then-build flow. Runs multi-round shape discovery first, resolves visual probe and north-star mock gates when available, then builds and visually iterates. Use when building a new feature end-to-end.", + "description": "Deprecated compatibility alias for an ordinary Impeccable new-work request. It adds no behavior; natural build and redesign requests use the same flow.", "argumentHint": "[feature description]" }, "init": { diff --git a/.agents/skills/impeccable/scripts/concept-seed.mjs b/.agents/skills/impeccable/scripts/concept-seed.mjs index 1f141a80f..25f20d9ab 100644 --- a/.agents/skills/impeccable/scripts/concept-seed.mjs +++ b/.agents/skills/impeccable/scripts/concept-seed.mjs @@ -1,6 +1,7 @@ #!/usr/bin/env node /** - * Concept-seed picker: the dice half of the new-work concept procedure. + * External concept seed: the dice half of new-work's coupled-direction and + * established-world surface procedures. * * The model derives a grounded shortlist of candidate FORMS from the * audience's world and the subject's cultural home (see @@ -12,10 +13,9 @@ * * This script rolls them from outside, the same trick that made the * palette seed work: - * - BUILD INDEX (2-5): which entry of the model's own resonance-ordered - * shortlist to build. The dice never choose an ungrounded ingredient; - * they only refuse the argmax rut. (Index 1 is excluded: that's the - * concept every run would ship anyway.) + * - PROMOTED INDEX: which entry of the model's own resonance-ordered + * shortlist must be taken seriously beside its favorites. The dice never + * choose an ungrounded ingredient; they only refuse the argmax rut. * - CHALLENGERS (3): outside forms from concept-ingredients.json, weighed * against the derived candidates on exactly two axes — audience * identification and product clarity. They win only when they beat the @@ -23,8 +23,8 @@ * material and win over thin categories, which is the intended shape. * * Usage: - * node scripts/concept-seed.mjs # roll at random - * node scripts/concept-seed.mjs --from # deterministic (hash key) + * node scripts/concept-seed.mjs --scope direction + * node scripts/concept-seed.mjs --scope surface --from * * Env vars: * IMPECCABLE_CONCEPT_SEED — same as --from; for reproducible eval runs. @@ -40,6 +40,12 @@ const pool = JSON.parse(readFileSync(join(here, 'concept-ingredients.json'), 'ut const args = process.argv.slice(2); const fromIdx = args.indexOf('--from'); +const scopeIdx = args.indexOf('--scope'); +const scope = scopeIdx !== -1 ? args[scopeIdx + 1] : 'surface'; +if (scope !== 'surface' && scope !== 'direction') { + process.stderr.write('concept-seed: --scope must be direction or surface\n'); + process.exit(1); +} // When no key is supplied, generate one and print it: a user reporting a // bad outcome can hand us the key and we replay the exact roll. const key = fromIdx !== -1 @@ -47,7 +53,7 @@ const key = fromIdx !== -1 : (process.env.IMPECCABLE_CONCEPT_SEED || crypto.randomBytes(4).toString('hex')); function hashUnit(k, salt) { - const h = crypto.createHash('sha256').update(`${salt}:${k}`).digest(); + const h = crypto.createHash('sha256').update(`${scope}:${salt}:${k}`).digest(); return h.readUInt32BE(0) / 0xffffffff; } const unit = (salt) => hashUnit(key, salt); @@ -67,22 +73,48 @@ for (let i = 0; picks.length < 3 && i < 60; i++) { } } -process.stdout.write(`CONCEPT SEED (key: ${key}; rerun with --from ${key} to reproduce this roll) -BUILD INDEX: ${buildIndex} - After ordering your derived candidates by resonance, build the page whose - form comes from candidate number ${buildIndex}, exactly as if it had ranked - first: full commitment. Your top-ranked candidate is what every run in this - category would ship; the assignment exists to refuse that rut, not to - punish it. -CHALLENGERS (weigh against your derived candidates on the same two axes, -audience identification and product clarity; a challenger wins only when -it beats the grounded list on both): +const promotedInstruction = scope === 'direction' + ? `After ordering the grounded coupled directions by product fit, promote + candidate ${buildIndex} into the serious shortlist. Each candidate must join + a durable visual system to a concrete expression for the requested first + surface; select or revise that pair as one decision. It must survive the + current task plus navigation, quiet and dense content, interaction and state, + and a substantially different future surface.` + : `After ordering the task's grounded structural candidates by resonance, + promote candidate ${buildIndex} into the serious shortlist. In an attended + run, present it beside the strongest materially different candidates and + let the user select or revise the surface concept. In a truly unattended + run, use it when it survives audience identification and product clarity.`; + +const challengerInstruction = scope === 'direction' + ? `A challenger enters only when it can supply both reusable identity grammar + and a strong first-surface structure, not a one-page costume or a style pasted + onto an unrelated layout. Weigh audience identification, product clarity, + current-surface force, and cross-surface system breadth.` + : `A challenger wins only when it beats the grounded list on both audience + identification and product clarity. It may change task topology or + interaction, but never the committed visual identity.`; + +const authorityInstruction = scope === 'direction' + ? `PRODUCT.md and explicit incumbent brand commitments constrain every coupled +direction. The seed never chooses exact colors, fonts, tokens, or a user +preference, and it never permits the world and first surface to be selected +independently.` + : `PRODUCT.md and DESIGN.md constrain every surface candidate's identity +vocabulary; they do not cancel task-level composition. The seed never +authorizes a new palette, type system, material world, or unfamiliar control +behavior.`; + +process.stdout.write(`${scope.toUpperCase()} CONCEPT SEED (key: ${key}; rerun with --scope ${scope} --from ${key} to reproduce this roll) +PROMOTED INDEX: ${buildIndex} + ${promotedInstruction} + The promotion exists to refuse the model's ranking rut, not to outrank the + user or the brief. +CHALLENGERS: 1. ${picks[0]} 2. ${picks[1]} 3. ${picks[2]} -If a challenger wins, it replaces the assigned candidate. If the surface is -an existing world whose incumbent carries a deliberate, ownable idea, the -incumbent IS the chosen candidate: intensify its lineage and ignore the -roll entirely. The same override applies when the user, PRODUCT.md, or -DESIGN.md pins a direction: pinned direction beats the roll, always. +${challengerInstruction} +${authorityInstruction} +A user- or brief-pinned decision beats the roll, always. `); diff --git a/.agents/skills/impeccable/scripts/context-signals.mjs b/.agents/skills/impeccable/scripts/context-signals.mjs index 2fc27bea7..d296320d7 100644 --- a/.agents/skills/impeccable/scripts/context-signals.mjs +++ b/.agents/skills/impeccable/scripts/context-signals.mjs @@ -11,7 +11,7 @@ * output is always valid JSON. * * Signals: - * - setup: PRODUCT.md / DESIGN.md presence, register, whether code exists + * - setup: PRODUCT.md / DESIGN.md presence and whether code exists * - critique: the latest cached critique score (.impeccable/critique) * - git: branch + files changed vs the default branch (a scope hint) * - devServer: whether a local dev server answers on a common port (gates live) @@ -21,7 +21,7 @@ import net from 'node:net'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { execFileSync } from 'node:child_process'; -import { loadContext, extractRegister, extractPlatform } from './context.mjs'; +import { loadContext, extractPlatform } from './context.mjs'; import { getCritiqueDir } from './lib/impeccable-paths.mjs'; /** Is there code here at all, or just context files / an empty repo? */ @@ -196,7 +196,6 @@ export async function gatherSignals(cwd = process.cwd()) { hasDesign: ctx.hasDesign, designPath: ctx.designPath, hasCode: hasCode(cwd), - register: extractRegister(ctx.product), platform: extractPlatform(ctx.product), }, critique: { latest: latestCritique(cwd) }, diff --git a/.agents/skills/impeccable/scripts/context.mjs b/.agents/skills/impeccable/scripts/context.mjs index 2ef2d4ce8..080e7ee3a 100644 --- a/.agents/skills/impeccable/scripts/context.mjs +++ b/.agents/skills/impeccable/scripts/context.mjs @@ -1,8 +1,10 @@ /** - * Context loader: prints PRODUCT.md (and DESIGN.md if present) as one - * markdown block on stdout, or prints a `NO_PRODUCT_MD:` message when no + * Context loader: prints PRODUCT.md, DESIGN.md when present, the matching + * persisted surface brief when one can be resolved, and native-platform + * guidance selected from PRODUCT.md. It prints a + * `NO_PRODUCT_MD:` message when no * PRODUCT.md is found anywhere. The skill keys off that message to branch: - * from-scratch build commands (init / teach / craft / shape) and clear + * from-scratch build requests (plus init / teach / shape) and clear * build/shape intent divert into the init flow, while scoped commands proceed * using the existing code as context. * @@ -23,10 +25,13 @@ import os from 'node:os'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { parseTargetOptions } from './lib/target-args.mjs'; -import { IMPECCABLE_COMMAND } from './lib/provider.mjs'; +import { IMPECCABLE_COMMAND, IMPECCABLE_PROVIDER_ID } from './lib/provider.mjs'; +import { renderLlmOnlySlopReview } from './lib/slop-review.mjs'; +import { resolveSurfaceBrief } from './lib/surface-briefs.mjs'; const PRODUCT_NAMES = ['PRODUCT.md', 'Product.md', 'product.md']; const DESIGN_NAMES = ['DESIGN.md', 'Design.md', 'design.md']; +const SKILL_REFERENCE_DIR = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', 'reference'); const FALLBACK_DIRS = ['.agents/context', 'docs']; const MONOREPO_MARKER_FILES = ['pnpm-workspace.yaml', 'turbo.json', 'nx.json', 'lerna.json']; const MONOREPO_FALLBACK_PROJECT_DIRS = ['apps', 'packages']; @@ -41,6 +46,8 @@ const WORKSPACE_DISCOVERY_IGNORED_DIRS = new Set([ '.turbo', '.cache', 'coverage', + 'vendor', + 'vendors', ]); const VISUAL_SOURCE_DIRS = ['src', 'app', 'pages', 'components', 'site', 'public', 'styles']; const STYLE_EXTENSIONS = new Set(['.css', '.scss', '.sass', '.less', '.styl']); @@ -73,6 +80,12 @@ export function loadContext(cwd = process.cwd(), options = {}) { const designPath = resolved.designPath; const product = productPath ? safeRead(productPath) : null; const design = designPath ? safeRead(designPath) : null; + const platform = extractPlatform(product); + const surfaceResolution = resolveSurfaceBrief( + resolved.projectRoot, + hasTargetOption(options) ? options.targetPath : null, + ); + const surfaceBrief = surfaceResolution.brief; return { hasProduct: !!product, product, @@ -83,7 +96,18 @@ export function loadContext(cwd = process.cwd(), options = {}) { contextDir: resolved.contextDir, productContextDir: productPath ? path.dirname(productPath) : null, designContextDir: designPath ? path.dirname(designPath) : null, + hasSurfaceBrief: !!surfaceBrief, + surfaceBrief: surfaceBrief?.text ?? null, + surfaceBriefPath: surfaceBrief?.path ? path.relative(absCwd, surfaceBrief.path) : null, + surfaceBriefReason: surfaceResolution.reason, + surfaceBriefCandidates: surfaceResolution.candidates.map((brief) => ({ + slug: brief.slug, + path: path.relative(absCwd, brief.path), + primaryTarget: brief.primaryTarget, + relatedTargets: brief.relatedTargets, + })), hasVisualImplementation: hasVisualImplementation(resolved.projectRoot), + platform, projectRoot: resolved.projectRoot, repoRoot: resolved.repoRoot, isMonorepo: resolved.isMonorepo, @@ -696,6 +720,19 @@ function safeRead(p) { } } +function loadNativePlatformReferences(platform) { + const names = platform === 'adaptive' + ? ['ios', 'android'] + : platform === 'ios' || platform === 'android' + ? [platform] + : []; + return names.flatMap((name) => { + const filePath = path.join(SKILL_REFERENCE_DIR, `${name}.md`); + const content = safeRead(filePath); + return content ? [{ name, filePath, content }] : []; + }); +} + /** * Best-effort evidence that the project already has an incumbent visual * implementation. DESIGN.md is documentation, not the only source of design @@ -719,9 +756,11 @@ export function hasVisualImplementation(projectRoot) { let styledComponents = 0; const inspectFile = (filePath) => { - if (scannedFiles++ >= VISUAL_SCAN_FILE_LIMIT) return false; const ext = path.extname(filePath).toLowerCase(); if (!STYLE_EXTENSIONS.has(ext) && !UI_EXTENSIONS.has(ext)) return false; + const base = path.basename(filePath).toLowerCase(); + if (/\.min\.[a-z]+$/.test(base)) return false; + if (scannedFiles++ >= VISUAL_SCAN_FILE_LIMIT) return false; let body; try { body = fs.readFileSync(filePath, 'utf-8').slice(0, 64 * 1024); @@ -729,18 +768,28 @@ export function hasVisualImplementation(projectRoot) { return false; } - const base = path.basename(filePath).toLowerCase(); + const evidence = body + .replace(/\/\*[\s\S]*?\*\//g, '') + .replace(//g, '') + .replace(/^\s*\/\/.*$/gm, ''); if (STYLE_EXTENSIONS.has(ext)) { - const customProperties = body.match(/--[a-z0-9_-]+\s*:/gi)?.length ?? 0; - const visualDeclarations = body.match(/\b(?:color|background(?:-color)?|border(?:-color)?|font-family)\s*:/gi)?.length ?? 0; - if (/\b(?:tokens?|theme|design-system)\b/.test(base) && body.trim().length > 80) return true; + const customProperties = evidence.match(/--[a-z0-9_-]+\s*:/gi)?.length ?? 0; + const visualDeclarations = evidence.match(/\b(?:color|background(?:-color)?|border(?:-color)?|font-family)\s*:/gi)?.length ?? 0; + if (/\b(?:tokens?|theme|design-system)\b/.test(base) && evidence.trim().length > 80) return true; if (customProperties >= 3 || visualDeclarations >= 5) return true; } - if ((ext === '.html' || ext === '.htm') && body.length > 600 && /]+stylesheet/i.test(body)) { + if ((ext === '.html' || ext === '.htm') && evidence.length > 600 && /]+stylesheet/i.test(evidence)) { return true; } - if (!['.html', '.htm'].includes(ext) && body.length > 300 && /class(?:Name)?\s*=|style\s*=|styled\(|css`/i.test(body)) { + if (!['.html', '.htm'].includes(ext) && evidence.length > 300) { + const embeddedCustomProperties = evidence.match(/--[a-z0-9_-]+\s*:/gi)?.length ?? 0; + const embeddedVisualDeclarations = evidence.match(/\b(?:color|background(?:-color)?|border(?:-color)?|font-family)\s*:/gi)?.length ?? 0; + const classTokens = [...evidence.matchAll(/class(?:Name)?\s*=\s*["'`]([^"'`]+)["'`]/gi)] + .reduce((count, match) => count + match[1].trim().split(/\s+/).length, 0); + if ((embeddedCustomProperties >= 3 && embeddedVisualDeclarations >= 3) || embeddedVisualDeclarations >= 5 || classTokens >= 12) return true; + } + if (!['.html', '.htm'].includes(ext) && evidence.length > 300 && /class(?:Name)?\s*=|style\s*=|styled\(|css`/i.test(evidence)) { styledComponents += 1; if (styledComponents >= 3) return true; } @@ -781,9 +830,9 @@ function escapeRegExp(value) { /** * Read the first non-empty line under a bare `## ` section of - * PRODUCT.md (e.g. `## Register`, `## Platform`). Returns null when the + * PRODUCT.md (for example `## Platform`). Returns null when the * section is absent. The heading match is exact (`\s*$`) so near-miss - * headings like `## Register guidelines` don't shadow the real field. + * near-miss headings don't shadow the real field. */ export function extractSectionValue(product, heading) { if (!product) return null; @@ -802,16 +851,6 @@ export function extractSectionValue(product, heading) { return null; } -/** - * Pull the register (`brand` or `product`) out of PRODUCT.md by looking - * for a `## Register` section and reading the first non-empty line that - * follows it. Returns null when the file is legacy / register-less. - */ -export function extractRegister(product) { - const word = (extractSectionValue(product, 'Register') || '').toLowerCase(); - return word === 'brand' || word === 'product' ? word : null; -} - /** * Pull the platform (`web`, `ios`, `android`, or `adaptive`) out of PRODUCT.md * by looking for a `## Platform` section and reading the first non-empty line @@ -985,30 +1024,35 @@ async function cli() { const parts = ctx.hasVisualImplementation ? [ 'NO_PRODUCT_MD: This project has no PRODUCT.md yet, but it does have an incumbent visual implementation. ' + - 'For `init` or `teach`, load reference/init.md and create PRODUCT.md. For `craft`, `shape`, or other work ' + - 'against the existing product, read its CSS, tokens, components, and assets first and proceed without blocking; ' + - `offer \`${IMPECCABLE_COMMAND} init\` as a follow-up. Only treat the work as greenfield when the user explicitly ` + - 'asks to discard or replace the current identity.', - 'EXISTING_VISUAL_SYSTEM: Code and assets are the incumbent design authority. Missing DESIGN.md is a documentation ' + - 'gap, not permission to invent a replacement identity. Preserve and extend the implementation unless the user ' + - 'explicitly requests a rebrand, or inspection proves the detected files contain no coherent visual decisions.', + 'For `init`, `teach`, `shape`, or any request to create a new surface or replacement visual world, load reference/init.md and create PRODUCT.md with the user first. ' + + 'After init writes PRODUCT.md, reference/new-work.md preserves and documents the incumbent system for an ' + + 'extension or replaces it with the user for a redesign/rebrand. Other ' + + 'narrow refinement commands may read the CSS, tokens, components, and assets and proceed without blocking, then ' + + `offer \`${IMPECCABLE_COMMAND} init\` as a follow-up.`, + 'BUILD_INIT_REQUIRED: Before shape or any new-surface/redesign flow, init must capture PRODUCT.md with the human or structured ' + + 'simulated user. Init writes product truth only; reference/new-work.md owns every visual decision.', + 'SCOPED_EXISTING_ALLOWED: Narrow refinement commands may use the incumbent implementation as authority without ' + + 'blocking on context setup; they must preserve it and offer init afterward.', + 'EXISTING_VISUAL_SYSTEM: For refinement or extension, code and assets are incumbent design authority and missing ' + + 'DESIGN.md is a documentation gap. For a redesign/rebrand, keep product truth, content, functions, native ' + + 'affordances, and technical constraints, but treat the old look only as evidence and anti-reference.', ] : [ 'NO_PRODUCT_MD: This project has no PRODUCT.md yet. ' + - 'For `init`, `teach`, `craft`, `shape`, ' + + 'For `init`, `teach`, `shape`, ' + 'or wording that clearly maps to a from-scratch build/shape flow, load ' + - 'reference/init.md and write PRODUCT.md first, unless no user can ' + - 'respond (a one-shot or automated run, or the user said not to ask): ' + - 'then write a one-paragraph understanding of the product, audience, ' + - 'and the page\'s job from the brief, and continue. For any other ' + + 'reference/init.md, complete its human or structured simulated-user interview, and write PRODUCT.md before ' + + 'designing. If no answer mechanism truly exists, init may infer only from the explicit brief and must label its ' + + 'assumptions. It never writes DESIGN.md. For any other ' + '(scoped) command against existing code, proceed using the code as ' + `context and offer \`${IMPECCABLE_COMMAND} init\` as a suggestion (do not block).`, - 'NEW_WORK: No committed design context was found. If this task produces ' + - 'new design (a build from scratch, or a redesign that discards the ' + - 'current look), you MUST read reference/new-work.md before making any ' + - 'design decision. Scoped fixes to existing code do not need it.', + 'PRODUCT_INIT_REQUIRED: No product context or visual authority was found. New builds and redesigns ' + + 'must finish reference/init.md for PRODUCT.md, then reference/new-work.md establishes the world and surface. Scoped ' + + 'fixes to existing code do not need the new-surface flow.', ]; + appendSurfaceBriefContext(parts, ctx); parts.push(buildResolvedContextDirective(ctx, cliOptions, { targetExists })); + appendHookFallback(parts, ctx); if (shouldWarnMissingTarget(ctx, targetProvided, targetExists)) { parts.push(buildMissingTargetDirective()); } @@ -1020,37 +1064,29 @@ async function cli() { if (ctx.hasDesign) { parts.push(`# DESIGN.md\n\n${ctx.design.trim()}`); } + appendSurfaceBriefContext(parts, ctx); parts.push(buildResolvedContextDirective(ctx, cliOptions, { targetExists })); + appendHookFallback(parts, ctx); if (shouldWarnMissingTarget(ctx, targetProvided, targetExists)) { parts.push(buildMissingTargetDirective()); } - const register = extractRegister(ctx.product); - // The register field survives as a family hint (brand = Persuade/Experience, - // product = Operate/Read); SKILL.md's mode section carries the essentials - // inline, so no register-file read is mandated here. What IS mandated: - // the new-work playbook when no committed design system exists yet. if (!ctx.hasDesign) { parts.push(ctx.hasVisualImplementation - ? 'EXISTING_VISUAL_SYSTEM: PRODUCT.md exists and DESIGN.md is missing, but code contains incumbent visual decisions. ' + - 'Treat CSS, tokens, components, and assets as design authority. Do not route to new identity work merely because the ' + - 'document is absent; preserve and extend the implementation unless the user explicitly asks to discard it.' - : 'NEW_WORK: PRODUCT.md exists but no DESIGN.md or incumbent visual implementation was found. If this task produces ' + - 'new design or a redesign that discards the current look, you MUST read reference/new-work.md before making any ' + - 'design decision. Scoped fixes to existing code do not need it.'); + ? 'INCUMBENT_WORLD_UNDOCUMENTED: PRODUCT.md exists and DESIGN.md is missing, but code contains incumbent visual decisions. ' + + 'For shape or a new-surface/redesign request, load reference/new-work.md: an extension documents and preserves the code-defined world; ' + + 'a redesign replaces it with the user and uses the old look only as evidence and anti-reference. Narrow refinement ' + + 'commands may proceed using the implementation directly.' + : 'WORLD_DISCOVERY_REQUIRED: PRODUCT.md exists but no DESIGN.md or incumbent visual implementation was found. ' + + 'For a new build or redesign, load reference/new-work.md and establish the visual world with the human or structured ' + + 'simulated user before developing the task concept. Scoped fixes to existing code do not need this flow.'); } - if (register) { - parts.push(`REGISTER: \`${register}\` (family hint: brand = Persuade/Experience surfaces, product = Operate/Read). Derive the visitor's mode per SKILL.md.`); - } - const platform = extractPlatform(ctx.product); - const nativeRefs = - platform === 'adaptive' ? ['ios', 'android'] : platform === 'ios' || platform === 'android' ? [platform] : []; - if (nativeRefs.length) { - const refList = nativeRefs.map(p => `\`reference/${p}.md\``).join(' and '); - const label = platform === 'adaptive' ? '`adaptive` (both iOS and Android)' : `\`${platform}\``; + const platformReferences = loadNativePlatformReferences(ctx.platform); + for (const reference of platformReferences) { parts.push( - `NEXT STEP: This project targets ${label}. Also read ${refList} for native conventions, in addition to SKILL.md's mode guidance.`, + `# NATIVE PLATFORM REFERENCE: ${reference.name.toUpperCase()} (reference/${reference.name}.md)\n\n${reference.content.trim()}`, ); - } else if (!platform) { + } + if (!ctx.platform) { // A `## Platform` section that names something we don't recognize (a // toolchain like `flutter`, a typo) would otherwise silently fall back to // web — the wrong default exactly when the user tried to say "native". @@ -1078,6 +1114,72 @@ function pathExistsForTarget(cwd, targetPath) { return fs.existsSync(abs); } +const HOOK_MANIFESTS_BY_PROVIDER = Object.freeze({ + 'claude-code': ['.claude/settings.local.json', '.claude/settings.json'], + codex: ['.codex/hooks.json'], + agents: ['.codex/hooks.json'], + cursor: ['.cursor/hooks.json'], + github: ['.github/hooks/impeccable.json'], +}); + +function truthyEnv(value) { + return typeof value === 'string' && /^(1|true|yes|on)$/i.test(value.trim()); +} + +function valueHasHookMarker(value) { + if (typeof value === 'string') { + return value.includes('skills/impeccable/scripts/hook.mjs') + || value.includes('skills/impeccable/scripts/hook-before-edit.mjs'); + } + if (Array.isArray(value)) return value.some(valueHasHookMarker); + if (value && typeof value === 'object') return Object.values(value).some(valueHasHookMarker); + return false; +} + +function hookEnabledAt(root) { + if (truthyEnv(process.env.IMPECCABLE_HOOK_DISABLED)) return false; + let enabled = true; + for (const name of ['.impeccable/config.json', '.impeccable/config.local.json']) { + const raw = readJson(path.join(root, name)); + if (raw?.hook && Object.prototype.hasOwnProperty.call(raw.hook, 'enabled')) { + enabled = raw.hook.enabled !== false; + } + } + return enabled; +} + +const STOP_REVIEW_PROVIDERS = new Set(['claude-code', 'codex', 'agents']); + +function automaticHookMode(ctx) { + if (ctx.platform === 'ios' || ctx.platform === 'android' || ctx.platform === 'adaptive') { + return 'none'; + } + const activeRoot = path.resolve(ctx.projectRoot || process.cwd()); + if (!hookEnabledAt(activeRoot)) return 'none'; + const manifests = HOOK_MANIFESTS_BY_PROVIDER[IMPECCABLE_PROVIDER_ID] || []; + const roots = [...new Set([process.cwd(), ctx.projectRoot, ctx.repoRoot].filter(Boolean).map((root) => path.resolve(root)))]; + for (const root of roots) { + for (const rel of manifests) { + const raw = readJson(path.join(root, rel)); + if (raw?.hooks && valueHasHookMarker(raw.hooks)) { + return STOP_REVIEW_PROVIDERS.has(IMPECCABLE_PROVIDER_ID) ? 'stop' : 'per-edit'; + } + } + } + return 'none'; +} + +function appendHookFallback(parts, ctx) { + const hookMode = automaticHookMode(ctx); + if (hookMode === 'stop') return; + const native = ctx.platform === 'ios' || ctx.platform === 'android' || ctx.platform === 'adaptive'; + parts.push(renderLlmOnlySlopReview({ + automaticDetector: hookMode === 'per-edit', + manualDetector: hookMode === 'none' && !native, + scriptsPath: path.dirname(fileURLToPath(import.meta.url)), + })); +} + function buildResolvedContextDirective(ctx, options, { targetExists = null } = {}) { const targetPath = hasTargetOption(options) ? options.targetPath : null; return `RESOLVED_CONTEXT:\n${JSON.stringify({ @@ -1087,10 +1189,29 @@ function buildResolvedContextDirective(ctx, options, { targetExists = null } = { repoRoot: ctx.repoRoot, productPath: ctx.productPath, designPath: ctx.designPath, + surfaceBriefPath: ctx.surfaceBriefPath, + surfaceBriefReason: ctx.surfaceBriefReason, + surfaceBriefCandidates: ctx.surfaceBriefCandidates, hasVisualImplementation: ctx.hasVisualImplementation, + platform: ctx.platform, }, null, 2)}`; } +function appendSurfaceBriefContext(parts, ctx) { + if (ctx.hasSurfaceBrief && ctx.surfaceBrief) { + parts.push(`# SURFACE BRIEF (${ctx.surfaceBriefPath})\n\n${ctx.surfaceBrief.trim()}`); + return; + } + if (!ctx.surfaceBriefCandidates?.length) return; + const helper = path.join(path.dirname(fileURLToPath(import.meta.url)), 'surface-brief.mjs'); + parts.push( + 'SURFACE_CONTEXT_AVAILABLE: Persisted surface briefs exist, but none was selected unambiguously for this invocation. ' + + 'Resolve the requested surface to its concrete primary or related source path, then run ' + + `\`node ${helper} read \` once before changing that surface. Candidates:\n` + + JSON.stringify(ctx.surfaceBriefCandidates, null, 2), + ); +} + function shouldWarnMissingTarget(ctx, targetProvided, targetExists = null) { if (ctx.isMonorepo && targetProvided && targetExists === false) return true; return !!( diff --git a/.agents/skills/impeccable/scripts/critique-storage.mjs b/.agents/skills/impeccable/scripts/critique-storage.mjs index 6b4d225cf..38674285a 100644 --- a/.agents/skills/impeccable/scripts/critique-storage.mjs +++ b/.agents/skills/impeccable/scripts/critique-storage.mjs @@ -29,8 +29,9 @@ import fs from 'node:fs'; import path from 'node:path'; import { fileURLToPath, pathToFileURL } from 'node:url'; import { getCritiqueDir } from './lib/impeccable-paths.mjs'; +import { slugFromTarget } from './lib/target-slug.mjs'; -const SLUG_MAX = 50; +export { slugFromTarget } from './lib/target-slug.mjs'; /** * Mechanically derive a slug from a resolved target. Returns null if the @@ -40,46 +41,6 @@ const SLUG_MAX = 50; * concrete artifact before calling this — we never slug a natural-language * phrase. */ -export function slugFromTarget(resolved, { cwd = process.cwd() } = {}) { - if (!resolved || typeof resolved !== 'string') return null; - const trimmed = resolved.trim(); - if (!trimmed) return null; - - // URL - if (/^https?:\/\//i.test(trimmed)) { - let url; - try { url = new URL(trimmed); } catch { return null; } - const hostPath = `${url.hostname}${url.pathname}`; - return kebab(hostPath); - } - - // File path. Make it project-relative so two devs critiquing the same - // checkout get the same slug regardless of where their repo is cloned. - const abs = path.isAbsolute(trimmed) ? trimmed : path.resolve(cwd, trimmed); - let rel = path.relative(cwd, abs); - // If the target is outside cwd, fall back to the basename so we still - // produce a stable slug (vs the absolute path, which would include - // home dirs / usernames). - if (rel.startsWith('..') || path.isAbsolute(rel)) { - rel = path.basename(abs); - } - if (!rel || rel === '.' || rel === '') return null; - return kebab(rel); -} - -function kebab(s) { - const slug = s - .toLowerCase() - .replace(/[/\\.]+/g, '-') - .replace(/[^a-z0-9-]+/g, '-') - .replace(/-+/g, '-') - .replace(/^-|-$/g, ''); - if (!slug) return null; - // Cap from the tail — the tail (filename) is more identifying than the - // top-level directory. - return slug.length <= SLUG_MAX ? slug : slug.slice(slug.length - SLUG_MAX).replace(/^-/, ''); -} - /** * Filename-safe UTC ISO timestamp: hyphens for separators, trailing Z. * Plain colons aren't allowed on Windows filesystems. diff --git a/.agents/skills/impeccable/scripts/detector/detect-antipatterns-browser.js b/.agents/skills/impeccable/scripts/detector/detect-antipatterns-browser.js index 033573e36..76bf4ddeb 100644 --- a/.agents/skills/impeccable/scripts/detector/detect-antipatterns-browser.js +++ b/.agents/skills/impeccable/scripts/detector/detect-antipatterns-browser.js @@ -312,17 +312,6 @@ const ANTIPATTERNS = [ skillSection: 'Layout & Space', skillGuideline: 'numbered section markers', }, - { - id: 'numbered-section-markers', - category: 'slop', - scopes: ['layout'], - severity: 'advisory', - name: 'Numbered section markers (01 / 02 / 03)', - description: - 'Numbered display markers as section labels (01, 02, 03) are the AI editorial scaffold one tier deeper than tracked eyebrow chips. If you find yourself reaching for them, choose a different section cadence.', - skillSection: 'Layout & Space', - skillGuideline: 'numbered section markers', - }, { id: 'em-dash-overuse', category: 'slop', diff --git a/.agents/skills/impeccable/scripts/detector/engines/regex/detect-text.mjs b/.agents/skills/impeccable/scripts/detector/engines/regex/detect-text.mjs index 81c30ab99..f641d5917 100644 --- a/.agents/skills/impeccable/scripts/detector/engines/regex/detect-text.mjs +++ b/.agents/skills/impeccable/scripts/detector/engines/regex/detect-text.mjs @@ -280,22 +280,6 @@ const REGEX_ANALYZERS = [ if (count === 0) return []; return [finding('marketing-buzzword', filePath, `${count} buzzword phrase${count === 1 ? '' : 's'}: "${firstSample}"`)]; }, - // Numbered section markers (01 / 02 / 03 ...) - (content, filePath) => { - const text = stripHtmlToText(content); - const re = /\b(0[1-9]|1[0-2])\b/g; - const seen = new Set(); - let m; - while ((m = re.exec(text)) !== null) seen.add(m[1]); - if (seen.size < 3) return []; - const sorted = [...seen].sort(); - let sequential = 0; - for (let i = 1; i < sorted.length; i++) { - if (parseInt(sorted[i], 10) === parseInt(sorted[i - 1], 10) + 1) sequential++; - } - if (sequential < 2) return []; - return [finding('numbered-section-markers', filePath, `Sequence: ${sorted.slice(0, 6).join(', ')}`)]; - }, // Aphoristic cadence: manufactured-contrast + short-rebuttal (content, filePath) => { const text = stripHtmlToText(content); @@ -430,21 +414,20 @@ function runRegexMatchers(lines, filePath, lineOffset = 0, blockContext = null, } /** Page-level analyzers that scan rendered text content (em-dash use, - * buzzword phrases, numbered section markers, aphoristic cadence). + * buzzword phrases, aphoristic cadence). * These are detector-agnostic — they work on any HTML/text source * and don't need a parsed DOM. Exported so detectHtml can call them * for `.html` files (which otherwise skip the regex engine). */ const TEXT_CONTENT_ANALYZER_IDS = [ 'em-dash-overuse', 'marketing-buzzword', - 'numbered-section-markers', 'aphoristic-cadence', ]; function runTextContentAnalyzers(content, filePath, options = {}) { const profile = options?.profile; if (!shouldRunPageAnalyzers(content, filePath)) return []; - // The 4 text-content analyzers are at indices 3-6 in REGEX_ANALYZERS. + // The 3 text-content analyzers are at indices 3-5 in REGEX_ANALYZERS. const findings = []; for (let i = 0; i < TEXT_CONTENT_ANALYZER_IDS.length; i++) { const analyzer = REGEX_ANALYZERS[3 + i]; @@ -535,7 +518,6 @@ function detectText(content, filePath, options = {}) { 'monotonous-spacing', 'em-dash-overuse', 'marketing-buzzword', - 'numbered-section-markers', 'aphoristic-cadence', 'dark-glow', ]; diff --git a/.agents/skills/impeccable/scripts/detector/registry/antipatterns.mjs b/.agents/skills/impeccable/scripts/detector/registry/antipatterns.mjs index b85f7e62f..84c69b0b8 100644 --- a/.agents/skills/impeccable/scripts/detector/registry/antipatterns.mjs +++ b/.agents/skills/impeccable/scripts/detector/registry/antipatterns.mjs @@ -210,17 +210,6 @@ const ANTIPATTERNS = [ skillSection: 'Layout & Space', skillGuideline: 'numbered section markers', }, - { - id: 'numbered-section-markers', - category: 'slop', - scopes: ['layout'], - severity: 'advisory', - name: 'Numbered section markers (01 / 02 / 03)', - description: - 'Numbered display markers as section labels (01, 02, 03) are the AI editorial scaffold one tier deeper than tracked eyebrow chips. If you find yourself reaching for them, choose a different section cadence.', - skillSection: 'Layout & Space', - skillGuideline: 'numbered section markers', - }, { id: 'em-dash-overuse', category: 'slop', diff --git a/.agents/skills/impeccable/scripts/detector/rules/checks.mjs b/.agents/skills/impeccable/scripts/detector/rules/checks.mjs index 3fdd6b2dd..9bace11b3 100644 --- a/.agents/skills/impeccable/scripts/detector/rules/checks.mjs +++ b/.agents/skills/impeccable/scripts/detector/rules/checks.mjs @@ -352,27 +352,57 @@ function isAccentColor(cssColor) { return false; } +function resolveHeroHeadingSizePx(value) { + const input = String(value || '').trim().toLowerCase(); + if (!input) return 0; + + const simpleLengthPx = (token) => { + const match = /^(-?\d*\.?\d+)\s*(px|rem|em|%)?$/.exec(String(token || '').trim()); + if (!match) return null; + const amount = Number(match[1]); + if (!Number.isFinite(amount)) return null; + if (match[2] === 'rem' || match[2] === 'em') return amount * 16; + if (match[2] === '%') return amount * 0.16; + return amount; + }; + + const direct = simpleLengthPx(input); + if (direct !== null) return direct; + + // Static CSS engines cannot resolve viewport units, but clamp's min/max + // bounds still tell us whether the heading can ever reach hero scale. + const clamp = /^clamp\((.*)\)$/.exec(input); + if (clamp) { + const parts = clamp[1].split(','); + if (parts.length === 3) { + const bounds = [simpleLengthPx(parts[0]), simpleLengthPx(parts[2])] + .filter((candidate) => candidate !== null); + if (bounds.length > 0) return Math.max(...bounds); + } + } + + return 0; +} + // Sibling-relationship rule. Anchor on a hero-scale h1, look at the // previousElementSibling, and gate on EITHER the classic tracked- // uppercase eyebrow OR the modern accent-colored bold eyebrow. function checkHeroEyebrow(opts) { const { headingTag, headingText, headingFontSize, + headingInApplicationContext, siblingTag, siblingText, siblingTextTransform, siblingFontSize, siblingLetterSpacing, siblingFontWeight, siblingColor, siblingHasAccentDashPseudo, } = opts; if (headingTag !== 'h1') return []; - // We previously gated on headingFontSize >= 48 to anchor "hero scale". - // But modern hero h1s use clamp() / vw / var(--text-*), none of which - // jsdom can resolve — the computed value comes back as "2em" or - // "var(--text-9xl)" and parseFloat returns 2 or NaN. The gate fails - // on virtually every Tailwind v4 / framework build. The other gates - // (sibling text 2-60 chars, font-size ≤ 14px, accent-bold OR - // tracked-caps) are tight enough to avoid false positives on non- - // hero h1s — a tiny tan label directly above any h1 is the - // antipattern regardless of how big the h1 ends up. + // This is specifically a marketing-hero cliché, not a ban on compact + // context labels in product UI (for example, a station name inside a tab + // panel). Browser-computed sizes are reliable; the static adapter also + // resolves ordinary px/rem/em and clamp() bounds before reaching here. + if (headingInApplicationContext) return []; + if (!(headingFontSize >= 48)) return []; if (!siblingTag) return []; // An h2 above an h1 is a different anti-pattern (heading hierarchy / dual // headings) — never an eyebrow. @@ -1938,6 +1968,7 @@ function checkElementHeroEyebrowDOM(el) { headingTag: tag, headingText: el.textContent || '', headingFontSize: parseFloat(headStyle.fontSize) || 0, + headingInApplicationContext: !!el.closest('[role="tabpanel"], [role="dialog"], [role="application"], dialog'), siblingTag: sibling.tagName.toLowerCase(), siblingText: sibling.textContent || '', siblingTextTransform: sibStyle.textTransform || '', @@ -3367,7 +3398,8 @@ function checkElementHeroEyebrow(el, style, tag, window, customPropMap) { return checkHeroEyebrow({ headingTag: tag, headingText: el.textContent || '', - headingFontSize: parseFloat(headingFontSizeRaw) || 0, + headingFontSize: resolveHeroHeadingSizePx(headingFontSizeRaw), + headingInApplicationContext: !!el.closest?.('[role="tabpanel"], [role="dialog"], [role="application"], dialog'), siblingTag: sibling.tagName.toLowerCase(), siblingText: sibling.textContent || '', siblingTextTransform: sibStyle.textTransform || '', diff --git a/.agents/skills/impeccable/scripts/hook-lib.mjs b/.agents/skills/impeccable/scripts/hook-lib.mjs index e61ea692b..50aa2f8fb 100644 --- a/.agents/skills/impeccable/scripts/hook-lib.mjs +++ b/.agents/skills/impeccable/scripts/hook-lib.mjs @@ -46,6 +46,7 @@ import path from 'node:path'; import { pathToFileURL, fileURLToPath } from 'node:url'; import { extractPlatform, loadContext } from './context.mjs'; import { IMPECCABLE_COMMAND } from './lib/provider.mjs'; +import { renderStopSlopReview } from './lib/slop-review.mjs'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); @@ -1868,22 +1869,42 @@ export const CONTRACT_MAX_CHARS = 1800; // Cap contract sections per Stop emission so many touched artifacts cannot // stack an unbounded message. export const CONTRACT_AUDIT_MAX_FILES = 3; +export const CONTRACT_EXTS = new Set(['.html', '.htm', '.astro', '.svelte', '.vue', '.jsx', '.tsx']); +export const CONTRACT_REQUIRED_FIELDS = ['UNIQUE', 'NOT-TEMPLATE', 'OWN-WORLD', 'STORY', 'FIRST VIEWPORT', 'FORM']; /** - * Extract the artifact's own direction-contract comment: the first HTML - * comment in the head of the file, when its opening chars identify it as a - * contract/concept block. Returns the trimmed, length-capped body, or null - * when the file carries none (no comment, unclosed comment, marker missing, - * or the comment starts past the head window). + * Extract the artifact's own direction-contract comment from the head of an + * HTML or component file. Supports HTML-family comments and JSX block + * comments so the contract works in the Astro/Svelte/Vue/React scaffolds the + * skill actually builds. Returns the trimmed, length-capped body, or null + * when the file carries no valid contract block. */ export function extractDirectionContract(content) { if (typeof content !== 'string' || !content) return null; const head = content.slice(0, CONTRACT_HEAD_CHARS); - const m = //.exec(head); - if (!m) return null; - const body = m[1].trim(); - if (!body || !/contract|concept/i.test(body.slice(0, CONTRACT_MARKER_CHARS))) return null; - return body.slice(0, CONTRACT_MAX_CHARS); + const candidates = []; + for (const pattern of [//g, /\{\/\*([\s\S]*?)\*\/\}/g]) { + for (const match of head.matchAll(pattern)) { + const index = match.index ?? 0; + const linePrefix = head.slice(head.lastIndexOf('\n', index - 1) + 1, index).trim(); + if (linePrefix.startsWith('//')) continue; + candidates.push({ index, body: match[1].trim() }); + } + } + candidates.sort((a, b) => a.index - b.index); + for (const candidate of candidates) { + if (!candidate.body || !/direction\s+contract|concept\s+contract/i.test(candidate.body.slice(0, CONTRACT_MARKER_CHARS))) continue; + return candidate.body.slice(0, CONTRACT_MAX_CHARS); + } + return null; +} + +export function missingDirectionContractFields(contract) { + const body = typeof contract === 'string' ? contract : ''; + return CONTRACT_REQUIRED_FIELDS.filter((field) => { + const label = field.replace(/[.*+?^${}()|[\]\\]/g, '\\$&').replace(/\s+/g, '\\s+'); + return !new RegExp(`\\b${label}\\s*:`, 'i').test(body); + }); } /** @@ -1899,7 +1920,11 @@ export function renderContractAudit(entries, opts = {}) { const shown = entries.slice(0, CONTRACT_AUDIT_MAX_FILES); const blocks = shown.map(({ filePath, contract }) => { const display = relativize(filePath, cwd); - return `${display} opens with this direction contract, written when the direction was decided:\n\n${contract}`; + const missing = missingDirectionContractFields(contract); + const integrity = missing.length > 0 + ? `\n\nContract integrity defect: missing ${missing.join(', ')}. Repair the contract and the implementation together.` + : ''; + return `${display} opens with this direction contract, written when the direction was decided:\n\n${contract}${integrity}`; }); return [ `${ENVELOPE_PREFIX} Direction-contract audit. Before finishing, audit the rendered page against the contract it opens with, promise by promise.`, @@ -1984,9 +2009,22 @@ export async function runStopHook({ stdinJson, env = {}, cwd = process.cwd(), no return result({ skipped: 'native-platform', platform, durationMs: Date.now() - started }); } + const session = ensureSession(cache, sessionId); + const needsSlopReview = session.llmSlopReviewed !== true; + const det = detector || await loadDetector(); if (!det || typeof det.detectText !== 'function') { - return result({ skipped: 'detector-missing', durationMs: Date.now() - started }); + if (!needsSlopReview) return result({ skipped: 'detector-missing', durationMs: Date.now() - started }); + session.llmSlopReviewed = true; + session.updatedAt = Date.now(); + persistCache(projectCwd, cache); + const text = renderStopSlopReview(); + return { + exitCode: 0, + stdout: payload(text, 'Stop', harness), + emission: { kind: 'stop-llm-slop-review', llmSlopReview: true }, + audit: { ...audit, emitted: true, detectorMissing: true, llmSlopReview: true, chars: text.length, durationMs: Date.now() - started }, + }; } const scanOptions = designSystemOptions(config, det, projectCwd); @@ -2013,10 +2051,11 @@ export async function runStopHook({ stdinJson, env = {}, cwd = process.cwd(), no ? configuredExt.engine === 'html' : (ext === '.html' || ext === '.htm'); - // Direction-contract audit: HTML artifacts only, at most once per file + // Direction-contract audit: HTML and component artifacts, at most once per file // per session. The flag lives on the same session cache entry the // finding dedupe uses, so a second Stop fire stays quiet about it. - if (useHtmlEngine) { + const contractCapable = useHtmlEngine || CONTRACT_EXTS.has(ext); + if (contractCapable) { const fileEntry = ensureFile(cache, sessionId, filePath); if (!fileEntry.contractAudited) { const contract = extractDirectionContract(content); @@ -2046,10 +2085,15 @@ export async function runStopHook({ stdinJson, env = {}, cwd = process.cwd(), no } audit.scannedFiles = scanned; - if (freshGroups.length === 0 && contractEntries.length === 0) { + if (freshGroups.length === 0 && contractEntries.length === 0 && !needsSlopReview) { return result({ emitted: false, skipped: 'stop-clean', durationMs: Date.now() - started }); } + if (needsSlopReview) { + session.llmSlopReviewed = true; + session.updatedAt = Date.now(); + } + // Fresh findings and first-time contract audits earn the cache write; // both mark this batch as surfaced so the next Stop fire is silent // unless new issues appear. @@ -2064,6 +2108,9 @@ export async function runStopHook({ stdinJson, env = {}, cwd = process.cwd(), no if (contractEntries.length > 0) { parts.push(renderContractAudit(contractEntries, { cwd: projectCwd })); } + if (needsSlopReview) { + parts.push(renderStopSlopReview()); + } const text = appendDesignSystemNote(parts.join('\n\n'), scanOptions); return { exitCode: 0, @@ -2074,6 +2121,7 @@ export async function runStopHook({ stdinJson, env = {}, cwd = process.cwd(), no ...(contractEntries.length > 0 ? { contractFiles: contractEntries.map((entry) => entry.filePath) } : {}), + ...(needsSlopReview ? { llmSlopReview: true } : {}), }, audit: { ...audit, @@ -2081,6 +2129,7 @@ export async function runStopHook({ stdinJson, env = {}, cwd = process.cwd(), no freshFiles: freshGroups.length, freshFindings: freshGroups.reduce((sum, group) => sum + group.findings.length, 0), ...(contractEntries.length > 0 ? { contractAudits: contractEntries.length } : {}), + ...(needsSlopReview ? { llmSlopReview: true } : {}), chars: text.length, durationMs: Date.now() - started, }, diff --git a/.agents/skills/impeccable/scripts/lib/provider.mjs b/.agents/skills/impeccable/scripts/lib/provider.mjs index 57d2797b0..5397c4bca 100644 --- a/.agents/skills/impeccable/scripts/lib/provider.mjs +++ b/.agents/skills/impeccable/scripts/lib/provider.mjs @@ -1,4 +1,5 @@ // Source scripts default to slash commands. The provider build replaces only // this exact declaration, avoiding heuristic rewrites across executable code. export const IMPECCABLE_COMMAND_PREFIX = "$"; +export const IMPECCABLE_PROVIDER_ID = "agents"; export const IMPECCABLE_COMMAND = `${IMPECCABLE_COMMAND_PREFIX}impeccable`; diff --git a/.agents/skills/impeccable/scripts/lib/slop-review.mjs b/.agents/skills/impeccable/scripts/lib/slop-review.mjs new file mode 100644 index 000000000..30f03e34d --- /dev/null +++ b/.agents/skills/impeccable/scripts/lib/slop-review.mjs @@ -0,0 +1,30 @@ +export const LLM_ONLY_SLOP_ITEMS = Object.freeze([ + 'Monospace used merely to signal “technical” or “developer.”', + 'Light or dark mode chosen by category habit rather than the actual use scene.', + 'Everything wrapped in cards, or identical icon-heading-text cards repeated as the page structure.', + 'Hero-metric scaffolds: one oversized number, a small label, supporting stats, and an accent treatment.', + 'Decorative glassmorphism, meaningless sparklines, or generic rounded rectangles with drop shadows.', + 'A modal chosen by reflex when the task does not require interruption or protected focus.', +]); + +export function renderLlmOnlySlopReview({ manualDetector = false, automaticDetector = false, scriptsPath = null } = {}) { + const lines = [ + automaticDetector + ? 'AI_SLOP_REVIEW_REQUIRED: The automatic detector covers mechanical rules, but this harness has no reliable late review. Before finishing changed UI, inspect the authored result for detector-blind model reflexes:' + : 'AI_SLOP_REVIEW_REQUIRED: The automatic Impeccable design hook is not available for this session. Before finishing changed UI, inspect the authored result for detector-blind model reflexes:', + ...LLM_ONLY_SLOP_ITEMS.map((item) => `- ${item}`), + 'Fix reflexes, not intentional choices required by the brief or established visual authority.', + ]; + if (manualDetector && scriptsPath) { + lines.push(`Then run the mechanical detector once over the changed web UI: \`node ${scriptsPath}/detect.mjs --json \`. Do not run it earlier during concept selection.`); + } + return lines.join('\n'); +} + +export function renderStopSlopReview() { + return [ + '[impeccable@1] Detector-blind AI-slop review. The mechanical pass is complete; now inspect the rendered result for model reflexes it cannot reliably detect:', + ...LLM_ONLY_SLOP_ITEMS.map((item) => `- ${item}`), + 'Fix reflexes, not intentional choices required by the brief or established visual authority. Do not rerun the detector; this is the authored judgment layer.', + ].join('\n'); +} diff --git a/.agents/skills/impeccable/scripts/lib/surface-briefs.mjs b/.agents/skills/impeccable/scripts/lib/surface-briefs.mjs new file mode 100644 index 000000000..f83416f69 --- /dev/null +++ b/.agents/skills/impeccable/scripts/lib/surface-briefs.mjs @@ -0,0 +1,151 @@ +import fs from 'node:fs'; +import path from 'node:path'; +import { slugFromTarget } from './target-slug.mjs'; + +export const SURFACE_BRIEF_VERSION = 1; + +export function getSurfaceBriefDir(projectRoot) { + return path.join(projectRoot, '.impeccable', 'surfaces'); +} + +export function normalizeSurfaceTarget(target, { projectRoot = process.cwd() } = {}) { + if (!target || typeof target !== 'string' || !target.trim()) return null; + const trimmed = target.trim(); + if (/^https?:\/\//i.test(trimmed)) { + try { + const url = new URL(trimmed); + url.hash = ''; + url.search = ''; + return url.toString().replace(/\/$/, '') || url.origin; + } catch { + return null; + } + } + if (/^route:/i.test(trimmed)) { + const route = trimmed.slice(trimmed.indexOf(':') + 1).trim(); + if (!route.startsWith('/') || route.includes('..')) return null; + const normalizedRoute = route.split(/[?#]/, 1)[0].replace(/\/{2,}/g, '/').replace(/\/$/, '') || '/'; + return `route:${normalizedRoute}`; + } + if (trimmed === '/') return 'route:/'; + if (trimmed.startsWith('/')) { + const absolute = path.resolve(trimmed); + const relativeToProject = path.relative(projectRoot, absolute); + const isProjectFile = relativeToProject && !relativeToProject.startsWith('..') && !path.isAbsolute(relativeToProject); + if (!isProjectFile && !fs.existsSync(absolute) && !trimmed.includes('..')) { + const normalizedRoute = trimmed.split(/[?#]/, 1)[0].replace(/\/{2,}/g, '/').replace(/\/$/, '') || '/'; + return `route:${normalizedRoute}`; + } + } + const abs = path.isAbsolute(trimmed) ? trimmed : path.resolve(projectRoot, trimmed); + const rel = path.relative(projectRoot, abs); + if (!rel || rel === '.' || rel.startsWith('..') || path.isAbsolute(rel)) return null; + return rel.split(path.sep).join('/'); +} + +export function surfaceBriefPathForTarget(target, { projectRoot = process.cwd() } = {}) { + const normalized = normalizeSurfaceTarget(target, { projectRoot }); + if (!normalized) return null; + const slugInput = normalized.startsWith('route:') ? `route${normalized.slice('route:'.length)}` : normalized; + const slug = slugFromTarget(slugInput, { cwd: projectRoot }); + return slug ? path.join(getSurfaceBriefDir(projectRoot), `${slug}.md`) : null; +} + +export function parseSurfaceBrief(text, filePath = null) { + const match = String(text || '').match(/^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/); + const meta = {}; + if (match) { + for (const line of match[1].split(/\r?\n/)) { + const colon = line.indexOf(':'); + if (colon < 0) continue; + const key = line.slice(0, colon).trim(); + const raw = line.slice(colon + 1).trim(); + if (!key) continue; + if (/^(?:\[|\{|\")/.test(raw) || /^(?:true|false|null|-?\d+(?:\.\d+)?)$/.test(raw)) { + try { meta[key] = JSON.parse(raw); continue; } catch { /* keep string */ } + } + meta[key] = raw.replace(/^['"]|['"]$/g, ''); + } + } + const primaryTarget = typeof meta.primary_target === 'string' ? meta.primary_target : null; + const relatedTargets = Array.isArray(meta.related_targets) + ? meta.related_targets.filter((value) => typeof value === 'string') + : []; + return { + path: filePath, + text: String(text || ''), + body: match ? String(text || '').slice(match[0].length).trim() : String(text || '').trim(), + meta, + slug: typeof meta.slug === 'string' ? meta.slug : filePath ? path.basename(filePath, '.md') : null, + primaryTarget, + relatedTargets, + targets: [primaryTarget, ...relatedTargets].filter(Boolean), + }; +} + +export function listSurfaceBriefs(projectRoot = process.cwd()) { + const dir = getSurfaceBriefDir(projectRoot); + let names; + try { + names = fs.readdirSync(dir).filter((name) => name.endsWith('.md')).sort(); + } catch { + return []; + } + return names.flatMap((name) => { + const filePath = path.join(dir, name); + try { + return [parseSurfaceBrief(fs.readFileSync(filePath, 'utf-8'), filePath)]; + } catch { + return []; + } + }); +} + +export function resolveSurfaceBrief(projectRoot = process.cwd(), target = null) { + const briefs = listSurfaceBriefs(projectRoot); + if (!target) { + return { + brief: briefs.length === 1 ? briefs[0] : null, + candidates: briefs, + reason: briefs.length === 1 ? 'only-brief' : briefs.length > 1 ? 'ambiguous' : 'none', + }; + } + + const normalized = normalizeSurfaceTarget(target, { projectRoot }); + if (!normalized) return { brief: null, candidates: briefs, reason: 'invalid-target' }; + const exactPath = surfaceBriefPathForTarget(normalized, { projectRoot }); + const exact = briefs.find((brief) => brief.path === exactPath && (!brief.targets.length || brief.targets.includes(normalized))); + if (exact) return { brief: exact, candidates: briefs, reason: 'slug' }; + const mapped = briefs.filter((brief) => brief.targets.includes(normalized)); + return { + brief: mapped.length === 1 ? mapped[0] : null, + candidates: mapped.length > 1 ? mapped : briefs, + reason: mapped.length === 1 ? 'mapping' : mapped.length > 1 ? 'ambiguous-target' : 'not-found', + }; +} + +export function writeSurfaceBrief({ + projectRoot = process.cwd(), + primaryTarget, + relatedTargets = [], + body, +}) { + const normalizedPrimary = normalizeSurfaceTarget(primaryTarget, { projectRoot }); + if (!normalizedPrimary) throw new Error('surface brief requires a concrete project-relative primary target or URL'); + const normalizedRelated = [...new Set(relatedTargets + .map((target) => normalizeSurfaceTarget(target, { projectRoot })) + .filter((target) => target && target !== normalizedPrimary))]; + const slug = slugFromTarget(normalizedPrimary, { cwd: projectRoot }); + const filePath = surfaceBriefPathForTarget(normalizedPrimary, { projectRoot }); + fs.mkdirSync(path.dirname(filePath), { recursive: true }); + const frontmatter = [ + '---', + `version: ${SURFACE_BRIEF_VERSION}`, + `slug: ${JSON.stringify(slug)}`, + `primary_target: ${JSON.stringify(normalizedPrimary)}`, + `related_targets: ${JSON.stringify(normalizedRelated)}`, + '---', + ].join('\n'); + fs.writeFileSync(filePath, `${frontmatter}\n\n${String(body || '').trim()}\n`, 'utf-8'); + return filePath; +} diff --git a/.agents/skills/impeccable/scripts/lib/target-slug.mjs b/.agents/skills/impeccable/scripts/lib/target-slug.mjs new file mode 100644 index 000000000..025915ad5 --- /dev/null +++ b/.agents/skills/impeccable/scripts/lib/target-slug.mjs @@ -0,0 +1,33 @@ +import path from 'node:path'; + +const SLUG_MAX = 50; + +/** Derive one clone-stable slug from a concrete file path or URL. */ +export function slugFromTarget(resolved, { cwd = process.cwd() } = {}) { + if (!resolved || typeof resolved !== 'string') return null; + const trimmed = resolved.trim(); + if (!trimmed) return null; + + if (/^https?:\/\//i.test(trimmed)) { + let url; + try { url = new URL(trimmed); } catch { return null; } + return kebab(`${url.hostname}${url.pathname}`); + } + + const abs = path.isAbsolute(trimmed) ? trimmed : path.resolve(cwd, trimmed); + let rel = path.relative(cwd, abs); + if (rel.startsWith('..') || path.isAbsolute(rel)) rel = path.basename(abs); + if (!rel || rel === '.') return null; + return kebab(rel); +} + +export function kebab(value) { + const slug = String(value || '') + .toLowerCase() + .replace(/[/\\.]+/g, '-') + .replace(/[^a-z0-9-]+/g, '-') + .replace(/-+/g, '-') + .replace(/^-|-$/g, ''); + if (!slug) return null; + return slug.length <= SLUG_MAX ? slug : slug.slice(slug.length - SLUG_MAX).replace(/^-/, ''); +} diff --git a/.agents/skills/impeccable/scripts/live-browser.js b/.agents/skills/impeccable/scripts/live-browser.js index 616f1290a..47bbfefbf 100644 --- a/.agents/skills/impeccable/scripts/live-browser.js +++ b/.agents/skills/impeccable/scripts/live-browser.js @@ -2592,7 +2592,7 @@ minWidth: '16px', height: '16px', padding: '0 4px', borderRadius: '999px', background: tuneOpen ? C.brand : BP.hairline, - color: tuneOpen ? 'oklch(98% 0 0)' : 'inherit', + color: tuneOpen ? C.ink : 'inherit', fontFamily: MONO, fontSize: '9.5px', fontWeight: '600', lineHeight: '1', boxSizing: 'border-box', @@ -3131,7 +3131,7 @@ position: 'absolute', top: '2px', left: initial ? '18px' : '2px', width: '16px', height: '16px', borderRadius: '50%', - background: 'oklch(98% 0 0)', + background: C.ink, transition: 'left 0.18s ' + EASE, boxShadow: '0 1px 2px oklch(0% 0 0 / 0.2)', }); @@ -3165,7 +3165,7 @@ const b = el('button', { padding: '5px 4px', border: 'none', borderRadius: '3px', background: active ? C.brand : 'transparent', - color: active ? 'oklch(98% 0 0)' : P.text, + color: active ? C.ink : P.text, fontFamily: FONT, fontSize: '10.5px', fontWeight: '500', cursor: 'pointer', whiteSpace: 'nowrap', transition: 'background 0.1s ease, color 0.1s ease', @@ -3178,7 +3178,7 @@ segBtns.forEach(({ btn, val }) => { const on = val === o.value; btn.style.background = on ? C.brand : 'transparent'; - btn.style.color = on ? 'oklch(98% 0 0)' : P.text; + btn.style.color = on ? C.ink : P.text; }); applyParamValue(variantEl, p, o.value); queueCheckpoint('param_changed'); diff --git a/.agents/skills/impeccable/scripts/surface-brief.mjs b/.agents/skills/impeccable/scripts/surface-brief.mjs new file mode 100644 index 000000000..723f7c1b4 --- /dev/null +++ b/.agents/skills/impeccable/scripts/surface-brief.mjs @@ -0,0 +1,74 @@ +#!/usr/bin/env node +import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath, pathToFileURL } from 'node:url'; +import { resolveProjectRoot } from './context.mjs'; +import { + listSurfaceBriefs, + resolveSurfaceBrief, + surfaceBriefPathForTarget, + writeSurfaceBrief, +} from './lib/surface-briefs.mjs'; + +function summary(brief, projectRoot) { + return { + slug: brief.slug, + path: path.relative(projectRoot, brief.path).split(path.sep).join('/'), + primaryTarget: brief.primaryTarget, + relatedTargets: brief.relatedTargets, + }; +} + +function main(argv) { + const [command, target, bodyFile, ...relatedTargets] = argv; + const projectRoot = resolveProjectRoot(process.cwd(), target ? { targetPath: target } : {}); + if (command === 'path') { + const filePath = surfaceBriefPathForTarget(target, { projectRoot }); + if (!filePath) throw new Error('surface brief path requires a concrete target'); + process.stdout.write(`${path.relative(process.cwd(), filePath) || filePath}\n`); + return; + } + if (command === 'list') { + process.stdout.write(`${JSON.stringify(listSurfaceBriefs(projectRoot).map((brief) => summary(brief, projectRoot)), null, 2)}\n`); + return; + } + if (command === 'read') { + const result = resolveSurfaceBrief(projectRoot, target || null); + if (result.brief) { + process.stdout.write(result.brief.text); + return; + } + if (result.candidates.length) process.stderr.write(`${JSON.stringify(result.candidates.map((brief) => summary(brief, projectRoot)), null, 2)}\n`); + process.exit(2); + } + if (command === 'write') { + if (!target || !bodyFile) throw new Error('usage: surface-brief.mjs write '); + const filePath = writeSurfaceBrief({ + projectRoot, + primaryTarget: target, + relatedTargets, + body: fs.readFileSync(bodyFile, 'utf-8'), + }); + process.stdout.write(`${path.relative(process.cwd(), filePath) || filePath}\n`); + return; + } + throw new Error('usage: surface-brief.mjs [target] [body-file] [related-target ...]'); +} + +function isMainModule() { + if (!process.argv[1]) return false; + try { + return fs.realpathSync(fileURLToPath(import.meta.url)) === fs.realpathSync(process.argv[1]); + } catch { + return import.meta.url === pathToFileURL(process.argv[1]).href; + } +} + +if (isMainModule()) { + try { + main(process.argv.slice(2)); + } catch (error) { + process.stderr.write(`${error?.message || error}\n`); + process.exit(1); + } +} diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 4a194ffab..11e68c4e2 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -12,7 +12,7 @@ { "name": "impeccable", "description": "Design fluency for frontend development. 1 skill with 23 commands (/impeccable polish, /impeccable audit, /impeccable critique, etc.) and curated anti-pattern detection.", - "version": "4.0.0-alpha.9", + "version": "4.0.0-alpha.10", "author": { "name": "Paul Bakaus", "email": "paul@paulbakaus.com" diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index 9f6fcb153..7996b8ecf 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "impeccable", "description": "Design fluency for frontend development. 1 skill with 23 commands (/impeccable polish, /impeccable audit, /impeccable critique, etc.) and curated anti-pattern detection.", - "version": "4.0.0-alpha.9", + "version": "4.0.0-alpha.10", "author": { "name": "Paul Bakaus", "email": "paul@paulbakaus.com" diff --git a/.claude/skills/impeccable/SKILL.md b/.claude/skills/impeccable/SKILL.md index 80a242b4e..aaff837d5 100644 --- a/.claude/skills/impeccable/SKILL.md +++ b/.claude/skills/impeccable/SKILL.md @@ -1,7 +1,7 @@ --- name: impeccable description: Use when the user wants to design, redesign, shape, critique, audit, polish, clarify, distill, harden, optimize, adapt, animate, colorize, extract, or otherwise improve a frontend interface. Covers websites, landing pages, dashboards, product UI, app shells, components, forms, settings, onboarding, and empty states. Handles UX review, visual hierarchy, information architecture, cognitive load, accessibility, performance, responsive behavior, theming, anti-patterns, typography, fonts, spacing, layout, alignment, color, motion, micro-interactions, UX copy, error states, edge cases, i18n, and reusable design systems or tokens. Also use for bland designs that need to become bolder or more delightful, loud designs that should become quieter, live browser iteration on UI elements, or ambitious visual effects that should feel technically extraordinary. Not for backend-only or non-UI tasks. -version: 4.0.0-alpha.9 +version: 4.0.0-alpha.10 user-invocable: true argument-hint: "[craft|shape · audit|critique · animate|bolder|colorize|delight|layout|overdrive|quieter|typeset · adapt|clarify|distill · harden|onboard|optimize|polish · init|document|extract|live] [target]" license: Apache 2.0 @@ -10,56 +10,55 @@ allowed-tools: - Bash(node .claude/skills/impeccable/scripts/*) --- -Designs and iterates production-grade frontend interfaces. Real working code, committed design choices, exceptional craft. +This skill gives you the tools and permission to create design that earns to be called out-of-distribution craft: Whereas before, your design work would have been safe, timid and measured, you now approach every design task as a award-winning design director with impeccable understanding for what makes exceptional design work: production-grade code, peak creativity, a clear POV, deep understanding of the needs of the client and users, and exceptional craft. -Approach every design task as the design lead at a small studio known for giving every client a visual identity that could not be mistaken for anyone else's. The client has already rejected work that felt templated; they are paying for a point of view. Claude is capable of extraordinary work. Don't hold back. +Core principles: +- Go all out. No hedging, no shortcuts. The deliverable must be complete (except assets the user must provide). +- Dream big and bold. Distinct, beautiful, outstanding and highly inspiring work. +- Iterate with tools available to you (e.g. visual understanding, browser screenshots) until you think this meets the bar. ## Setup -1. Run `node .claude/skills/impeccable/scripts/context.mjs` once per session (if the runtime shows this skill's loaded base directory, run `node /scripts/context.mjs`; keep cwd at the user's project). It prints the project's context and its directives; follow what it prints. Once its output is in the conversation, never rerun it on a later turn. -2. `craft` and `shape` are build-path exceptions: the new-work gate below owns their flow, and on unattended runs its checkpoints resolve without pausing. For any other invoked sub-command (`audit`, `polish`, `live`, ...), immediately read **`reference/.md`** after `context.mjs` (the `.native` variant from the Commands table when the platform is `ios`/`android`/`adaptive`) and follow it. This read is a hard gate: do not inspect the target, run command-specific scripts, or edit files until the reference is loaded. -3. Read at least one project file (CSS / tokens / theme / a representative component) to learn what world you're in. If PRODUCT.md's `## Platform` is `ios` or `android`, also read `reference/.md` (`adaptive` reads both). +1. Run `node .claude/skills/impeccable/scripts/context.mjs` once per session (if the runtime shows this skill's loaded base directory, run `node /scripts/context.mjs`; keep cwd at the user's project). Pass a named source file or route as `--target `. It loads PRODUCT.md, DESIGN.md, the matching surface brief, and native-platform guidance when applicable; follow its directives and do not rerun it. +2. Before acting, load the one playbook that owns the request: the Commands table's reference for an explicit or clearly implied sub-command, or [reference/new-work.md](reference/new-work.md) for a new surface or replacement visual world. Then inspect the target and at least one representative source of incumbent visual truth (tokens, theme, CSS, component, or asset) before editing. ## How to design -**The brief wins.** Where the brief pins down a direction (a named aesthetic, an era, a place, a material, a specific font or palette), follow it exactly, including when it asks for a look this skill warns is saturated. Redirecting a pinned direction toward your own taste is a failure, not a save. - -**Existing worlds are sacred.** Most of impeccable's work happens inside a site or app that already exists. When the surface has a committed design system (real tokens, deliberately chosen faces, a palette the brand owns), work inside that world: extend it, sharpen it, leave it unmistakably the same brand, and never degrade a working page's performance. Inventing parallel colors, fonts, or styles on an existing surface is a defect, not creativity. A scoped refinement keeps the named scope's content and media footprint unless the brief explicitly changes them; build the added emphasis by recomposing what is already there through hierarchy, proportion, rhythm, and the system's own motifs. - -**New identity work reads the playbook first.** When nothing committed exists (greenfield, or a codebase with no real tokens or chosen faces), or the user explicitly asks for a redesign that discards the current look, you MUST read [reference/new-work.md](reference/new-work.md) before making any design decision. Missing DESIGN.md alone does not make work greenfield: code, tokens, chosen type, components, and assets are incumbent design authority. Preserve and extend them unless the user asked to replace them. Producing a genuinely new identity without the playbook yields the generic default this skill exists to prevent. Scoped fixes inside an existing world don't need it; the craft floor below governs them. `context.mjs` prints the appropriate authority directive. +- **The brief wins.** Honor pinned aesthetics, eras, materials, fonts, and palettes even when they conflict with a saturated-pattern warning. Redirecting a clear brief toward your taste is failure. +- **Refinement preserves; redesign replaces.** Refinement keeps the incumbent identity, behavior, copy, and everything outside scope. Ask before replacing factual copy or adding claims. Redesign keeps product truth, content, function, native affordances, and constraints, but treats the old look as evidence and anti-reference; choose a replacement world in new-work and replace DESIGN.md. Never split the difference into polish on the discarded look. +- **Visual authority is evidence, not a filename.** Missing DESIGN.md alone does not make a project greenfield; new-work decides whether to preserve, expand, or replace the incumbent world. ## Modes -Name the visitor's mode before designing; the page's grammar follows from it, and most ruined pages are one mode wearing another mode's grammar. **The mode belongs to the surface, not the subject**: a landing page for a dense technical tool is still Persuade, with Persuade's full permission to be striking; a docs page for a fashion house is still Read. Deciding a page can be plain because its subject is workmanlike is the same category error in reverse. The brief and the surface decide the mode; PRODUCT.md's `register` field survives only as a family hint (`brand` covers Persuade and Experience, `product` covers Operate and Read). Depth beyond the paragraphs below: [reference/new-work.md](reference/new-work.md) when inventing identity, [reference/operate.md](reference/operate.md) for substantial Operate and Read work. +Choose the mode from the requested surface, not the product, and persist it only in that surface brief. A tool's landing page is still Persuade; a fashion house's documentation is still Read. See [new-work.md](reference/new-work.md) for new surfaces and [operate.md](reference/operate.md) for deeper Operate/Read guidance. -**Persuade** (the surface exists to win someone over; design IS the product). The deliverable is an impression that stops the scroll, earns the click, converts. Spans every genre; don't collapse them into one look. On new surfaces, briefs that imply imagery must ship real, verified imagery; a colored rectangle where a photo belongs reads as incomplete. New Persuade surfaces take their typeface procedure and reject list from [reference/new-work.md](reference/new-work.md). - -**Operate** (the surface is a tool someone works in; design SERVES the task). A person getting something done: scanability and consistency outrank expressiveness. These surfaces earn trust by feeling native to their platform: system font stacks and workhorse UI faces are legitimate and often correct here (the Persuade reject list does not apply). The brand lives in the details: focus states, empty states, microcopy, one owned accent. The usage scene is part of the spec: an interface read outdoors, in motion, or at a glance must survive its real ambient light, and the theme follows the scene, not the category's habit. - -**Read** (the surface exists to be understood; long-form, reference, guidance). The deliverable is comprehension, and comprehension is earned twice: a structure the reader can hold in their head with nothing standing between them and the answer, and a reading experience good enough to stay in, through typographic quality and whatever visual or interactive support genuinely helps the reader follow. The brand lives in type, spacing, and small accents. - -**Experience** (the surface presents a body of work; the page IS the work). The artifact leads, the interface recedes, and the visitor meets the work itself in the first viewport at every screen size. Boldness here means trusting the work. +- **Persuade:** win someone over; design is the product. Earn attention and action. Ship real imagery when the brief needs it; follow the committed world, not category habit. +- **Operate:** help someone do work. Scanability, consistency, native expectations, and the real usage scene outrank expression. Brand lives in precise details. +- **Read:** make something understood. Structure for comprehension, then make the reading experience worth staying in. +- **Experience:** present a body of work. Let the artifact lead from the first viewport; the interface recedes. ## Craft floor -Build to this floor without announcing it. The design detector (the project hook, `node .claude/skills/impeccable/scripts/detect.mjs --json `, or `audit`) verifies most of it mechanically; resolve every finding before finalizing. Fix real defects, but use context judgment rather than distorting intentional design to appease a false positive. Classify any intentional exception explicitly and use the hook system's narrowest appropriate waiver when it must persist. +Build to this floor without announcing it. -- Contrast: body text ≥4.5:1 against its background (placeholders too); large text ≥3:1. Gray text on a colored background looks washed out: use a darker shade of the background's own hue, or a transparency of the text color. -- Shadows describe real light: an offset and a soft blur. A zero-offset colored halo is decoration announcing itself. -- Spacing has rhythm: generous separations, tight groupings; cramped padding reads as broken; the space above a heading exceeds the space below it. Verify computed spacing, not intended spacing. -- Type: body line length 65-75ch; display clamp() max ≤6rem; letter-spacing ≥-0.04em; `text-wrap: balance` on headings; modular scale ≥1.25 between steps; light-on-dark adds 0.05-0.1 line-height. Pair faces on a contrast axis, never two similar-but-not-identical ones; one family with committed weight contrast beats a timid pair. Test headings at every breakpoint; overflow means reduce the clamp or rewrite the copy. -- Motion is part of the build: one orchestrated moment beats scattered effects; ease-out exponential curves; reveals enhance an already-visible default (content gated on a class-triggered transition ships blank in hidden tabs and headless renderers). Responsive down to mobile and visible keyboard focus are part of the floor. -- Ship real content (no placeholders, dead links, or fake controls) and cover the interaction states people will actually hit (hover, focus, disabled, loading, error, empty). -- Copy is design material: name things the way the page's own people speak, make every control say what it does, and make every error say what happened and what to do next. -- Before finishing, re-read the brief: every requirement it names must exist on the page, findable in seconds. A beautiful page missing an asked-for feature is unfinished. +- **Contrast:** body and placeholder text ≥4.5:1; large text ≥3:1. On colored surfaces, tint secondary text from that hue or the foreground instead of using gray. +- **Depth:** shadows describe light with offset and soft blur; zero-offset colored halos are decoration. +- **Spacing:** tight groups, generous separation, no cramped containers; space above a heading exceeds space below. Verify computed values. +- **Type:** body measure 65–75ch; display max 6rem and tracking floor -0.04em; balance headings; use clear scale/weight contrast; test overflow at every breakpoint. +- **Motion:** author one coherent moment instead of scattered effects. Use exponential ease-out and an already-visible default. Premium motion is not transform/opacity alone: it may add focus, depth, masks, light, or material change through blur/filter, backdrop-filter, clip-path/masks, or shadow when smooth. Always provide reduced motion. +- **Shipping:** real content, working controls, responsive composition, keyboard focus, and the states users hit: hover, disabled, loading, error, and empty. +- **Copy:** use the product's language; controls name their action, errors name the problem and recovery. +- **Coverage:** every brief requirement must exist and be findable within seconds. + +Before finishing changed UI, follow the quality guidance supplied by `context.mjs` and hooks. Context requests a manual scan only when no automatic detector is active; never add a second detector pass. ## Commands | Command | Category | Description | Reference | |---|---|---|---| -| `craft [feature]` | Build | The standard build flow with attended checkpoints | [reference/new-work.md](reference/new-work.md) | +| `craft [feature]` | Build | Deprecated alias for an ordinary new-work request | [reference/craft.md](reference/craft.md) | | `shape [feature]` | Build | Plan UX/UI before writing code | [reference/shape.md](reference/shape.md) | -| `init` | Build | Set up project context: PRODUCT.md, DESIGN.md, live config, next steps | [reference/init.md](reference/init.md) | +| `init` | Build | Capture durable product context in PRODUCT.md | [reference/init.md](reference/init.md) | | `document` | Build | Generate DESIGN.md from existing project code | [reference/document.md](reference/document.md) | | `extract [target]` | Build | Pull reusable tokens and components into design system | [reference/extract.md](reference/extract.md) | | `critique [target]` | Evaluate | UX design review with heuristic scoring | [reference/critique.md](reference/critique.md) | @@ -81,7 +80,14 @@ Build to this floor without announcing it. The design detector (the project hook | `optimize [target]` | Fix | Diagnose and fix UI performance | [reference/optimize.md](reference/optimize.md) | | `live` | Iterate | Visual variant mode: pick elements in the browser, generate alternatives | [reference/live.md](reference/live.md) | -Routing: **no argument** → read [reference/routing.md](reference/routing.md) and present the context-aware menu (never auto-run a command). **First word matches a command** (or `pin` / `unpin` / `hooks`) → load its reference (native variant on native platforms) and follow it; everything after the command name is the target. **Intent clearly maps to one command** ("fix the spacing" → `layout`, "rewrite this error" → `clarify`) → same; if two fit, ask once. **Otherwise** → general design invocation: apply Setup and this file's guidance; builds flow through the new-work gate above, whose playbook carries the direction checkpoint and the finishing pass. `teach` routes to `init`, and `craft` routes to the standard build flow with attended checkpoints. If setup diverted into `init` for a build request, finish init, refresh context, then resume. +Routing: + +- **No argument:** read [routing.md](reference/routing.md) and present its context-aware menu; never auto-run a command. +- **Explicit or clearly implied command:** load its reference (native variant on native platforms) and follow it. Ask once if two commands fit. +- **Otherwise:** treat the request as general design work. Missing PRODUCT.md routes through init; new surfaces and replacement worlds use new-work. +- `teach` aliases `init`. `craft` is a deprecated alias for ordinary new-work and adds nothing. `shape` owns task discovery, then enters new-work only for visual-world and surface-concept decisions. + +After init writes PRODUCT.md, resume without rerunning `context.mjs`. **Pin / Unpin:** `node .claude/skills/impeccable/scripts/pin.mjs ` creates or removes a standalone `/` shortcut. Report the script's result concisely; relay stderr verbatim on error. diff --git a/.claude/skills/impeccable/reference/android.md b/.claude/skills/impeccable/reference/android.md index 3575c31fb..6337b9018 100644 --- a/.claude/skills/impeccable/reference/android.md +++ b/.claude/skills/impeccable/reference/android.md @@ -2,7 +2,7 @@ For native Android apps: Jetpack Compose, Android Views, React Native, Expo, Flutter shipping to Android hardware. -On native, register narrows. Material Design 3 governs structure, navigation, and interaction whatever the register; brand expresses through Material's theming (color roles, type scale, shape, motion). A Material-everywhere cross-platform app that also ships to iPhone still owes iOS its OS guarantees on that hardware: safe-area insets, Reduce Motion, edge-swipe back. +On native, the visitor mode narrows what expression may override. Material Design 3 governs structure, navigation, and interaction in every mode; brand expresses through Material's theming (color roles, type scale, shape, motion). A Material-everywhere cross-platform app that also ships to iPhone still owes iOS its OS guarantees on that hardware: safe-area insets, Reduce Motion, edge-swipe back. ## The Android slop test diff --git a/.claude/skills/impeccable/reference/animate.md b/.claude/skills/impeccable/reference/animate.md index c57e65d1b..d51bb6d63 100644 --- a/.claude/skills/impeccable/reference/animate.md +++ b/.claude/skills/impeccable/reference/animate.md @@ -1,203 +1,88 @@ > **Additional context needed**: performance constraints. -Add motion that conveys state, gives feedback, and clarifies hierarchy. Cut motion that exists only for decoration. Animation fatigue is a real cost; spend the budget on the moments that need it. +Use motion to explain state, relationship, and hierarchy, or to create one authored moment the surface has earned. Decoration without purpose is animation debt. --- -## Register +## Visitor mode -Persuade + Experience: 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. +- **Persuade + Experience:** motion may carry the voice. Prefer one rehearsed focal sequence to repeated section reveals. +- **Operate + Read:** motion serves feedback, state, and continuity. Keep routine transitions fast and do not make users wait through page-load choreography. +- **Native (`ios` / `android` / `adaptive`):** follow the Motion section of [ios.md](ios.md) or [android.md](android.md), including the platform's Reduce Motion behavior. Do not apply the web tooling below. -Operate + Read: 150–250 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. +## Find the job -Native (`ios` / `android` / `adaptive`): implementation follows the Motion section of [ios.md](ios.md) / [android.md](android.md) (read it first if Setup hasn't already): system transitions and OS Reduce Motion, never the web tooling below. +Inspect the existing motion language, interaction states, target devices, and performance budget. Find only the places where motion would: ---- +- acknowledge an action; +- make a state change or spatial relationship legible; +- preserve continuity through navigation or layout change; +- direct attention at a meaningful moment; +- embody the selected visual world. -## Assess Animation Opportunities +Ask only when a material constraint cannot be inferred. Do not animate a static area merely because it exists. -Analyze where motion would improve the experience: +## Set the motion thesis -1. **Identify static areas**: - - **Missing feedback**: Actions without visual acknowledgment (button clicks, form submission, etc.) - - **Jarring transitions**: Instant state changes that feel abrupt (show/hide, page loads, route changes) - - **Unclear relationships**: Spatial or hierarchical relationships that aren't obvious - - **Lack of delight**: Functional but joyless interactions - - **Missed guidance**: Opportunities to direct attention or explain behavior +Write a short plan before implementation: -2. **Understand the context**: - - What's the personality? (Playful vs serious, energetic vs calm) - - What's the performance budget? (Mobile-first? Complex page?) - - Who's the audience? (Motion-sensitive users? Power users who want speed?) - - What matters most? (One hero animation vs many micro-interactions?) +- **Focal moment:** the one sequence or interaction that deserves authorship, if any. +- **Continuity:** the state, layout, or navigation changes that need explanation. +- **Feedback:** the controls and outcomes that need acknowledgment. +- **Budget:** which effects may be expensive, how often they run, and the reduced-motion equivalent. -If any of these are unclear from the codebase, STOP and call the AskUserQuestion tool to clarify. +The focal moment must come from this product and surface concept. A generic fade-and-rise, hover lift, parallax layer, or scroll reveal is not a thesis. -**CRITICAL**: Respect `prefers-reduced-motion`. Always provide non-animated alternatives for users who need them. +## Choose material by meaning -## Plan Animation Strategy +Transform and opacity are reliable foundations, not the entire palette. Choose properties for what the transition communicates: -Create a purposeful animation plan: +- **Continuity and relationship:** shared-element motion, FLIP-style transforms, view transitions, or deliberate spatial movement. +- **Focus and depth:** bounded blur, filter, backdrop, light, or shadow changes. +- **Reveal and composition:** masks, clip paths, cropping, or controlled occlusion. +- **Material and energy:** color, gradient position, texture, distortion, or shader effects when the world and runtime support them. +- **State and feedback:** the smallest change that makes cause and result unmistakable. -- **Feedback layer**: Which interactions need acknowledgment? -- **Transition layer**: Which state changes need smoothing? -- **Entrance moment**: The ONE entrance worth rehearsing, where the mode invites it. Not every surface wants one. -- **Delight layer**: Where can we surprise and delight? +Do not stack techniques for spectacle. One strong material idea, carried through the focal sequence and quiet supporting states, is usually enough. -**IMPORTANT**: One well-orchestrated experience beats scattered animations everywhere. Focus on high-impact moments. +Sibling stagger is appropriate when a list appears as a list. Cap the total delay, and never reinterpret every scrolled section as a staggered list. -## Implement Animations +## Timing and easing -Add motion systematically across these categories: +Timing should express distance and consequence: -### Micro-interactions -- **Button feedback**: - - Hover: Subtle scale (1.02-1.05), color shift, shadow increase - - Click: Quick scale down then up (0.95 → 1), ripple effect - - Loading: Spinner or pulse state -- **Form interactions**: - - Input focus: Border color transition, slight scale or glow - - Validation: Shake on error, check mark on success, smooth color transitions -- **Toggle switches**: Smooth slide + color transition (200-300ms) -- **Checkboxes/radio**: Check mark animation, ripple effect -- **Like/favorite**: Scale + rotation, particle effects, color transition +| Duration | Typical use | +|---|---| +| 100–150 ms | immediate feedback | +| 150–300 ms | routine state change | +| 300–500 ms | layout, overlay, or view transition | +| 500–800 ms | a deliberately authored focal entrance | -### State Transitions -- **Show/hide**: Fade + slide (not instant), appropriate timing (200-300ms) -- **Expand/collapse**: Height transition with overflow handling, icon rotation -- **Loading states**: Skeleton screen fades, spinner animations, progress bars -- **Success/error**: Color transitions, icon animations, gentle scale pulse -- **Enable/disable**: Opacity transitions, cursor changes +Exit faster than entrance. Use natural deceleration such as `cubic-bezier(0.16, 1, 0.3, 1)` for confident arrivals; do not use bounce or elastic curves by reflex. Long feedback feels like latency. -### Entrance Animations -- **The one entrance moment** (where the mode invites it): a committed entrance for primary content (scale, parallax, or a creative effect) -- **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. +## Implement to the runtime - 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. +- Use CSS transitions and keyframes for declarative state and bounded sequences. +- Use Web Animations API or the project's existing motion library for interruption, sequencing, and dynamic values. +- Use View Transitions or shared-element techniques when continuity across states is the point. +- Use scroll-driven motion only when the scroll relationship itself carries meaning, with a robust fallback. +- Do not add a dependency for an effect the existing stack can express cleanly. -### Navigation & Flow -- **Page transitions**: Crossfade between routes, shared element transitions -- **Tab switching**: Slide indicator, content fade/slide -- **Carousel/slider**: Smooth transforms, snap points, momentum -- **Scroll effects**: Parallax layers, sticky headers with state changes, scroll progress indicators +Keep content visible in the default state so failed scripts do not hide the page. Avoid casually animating layout-driving properties such as `width`, `height`, `top`, `left`, and margins; use FLIP, transforms, or grid techniques when appropriate. Bound blur, filter, shadow, canvas, and shader work to isolated regions. Apply `will-change` only during known animation. Measure on target viewports and devices rather than assuming transform means fast. -### Feedback & Guidance -- **Hover hints**: Tooltip fade-ins, cursor changes, element highlights -- **Drag & drop**: Lift effect (shadow + scale), drop zone highlights, smooth repositioning -- **Copy/paste**: Brief highlight flash on paste, "copied" confirmation -- **Focus flow**: Highlight path through form or workflow +## Accessibility and control -### Delight Moments -- **Empty states**: Subtle floating animations on illustrations -- **Completed actions**: Confetti, check mark flourish, success celebrations -- **Easter eggs**: Hidden interactions for discovery -- **Contextual animation**: Weather effects, time-of-day themes, seasonal touches +Honor `prefers-reduced-motion` with an intentional alternative. Preserve state change and hierarchy while removing travel, parallax, flashing, or prolonged sequences. Do not replace every animation globally with `0.01ms` if that destroys useful feedback. Motion must never block focus, interaction, reading, or task completion. -## Technical Implementation +Respect autoplay and sound preferences. Any nonessential loop must stop when offscreen or hidden. -Use appropriate techniques for each animation: +## Verify -### Timing & Easing +- The focal motion is specific to the selected world and surface. +- Every supporting animation explains feedback, state, or relationship. +- Interruption and repeated use behave correctly. +- Desktop, mobile, keyboard, and reduced-motion paths remain usable. +- Expensive effects stay smooth on the target device. +- Removing an animation would lose meaning or authored character, not merely decoration. -**Duration: the 100/300/500 rule.** Timing matters more than easing for "feels right": - -| Duration | Use Case | Examples | -|----------|----------|----------| -| **100–150ms** | Instant feedback | Button press, toggle, color change | -| **200–300ms** | State changes | Menu open, tooltip, hover state | -| **300–500ms** | Layout changes | Accordion, modal, drawer | -| **500–800ms** | Entrance animations | Page load, hero reveal | - -**Easing curves (use these, not CSS defaults):** -```css -/* Recommended: natural deceleration */ ---ease-out-quart: cubic-bezier(0.25, 1, 0.5, 1); /* Smooth */ ---ease-out-quint: cubic-bezier(0.22, 1, 0.36, 1); /* Slightly snappier */ ---ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1); /* Confident, decisive */ - -/* AVOID: feel dated and tacky */ -/* bounce: cubic-bezier(0.34, 1.56, 0.64, 1); */ -/* elastic: cubic-bezier(0.68, -0.6, 0.32, 1.6); */ -``` - -**Exit animations are faster than entrances.** Use ~75% of enter duration. - -### CSS Animations -```css -/* Prefer for simple, declarative animations */ -- transitions for state changes -- @keyframes for complex sequences -- transform and opacity for reliable movement -- blur, filters, masks, clip paths, shadows, and color shifts for premium atmospheric effects when verified smooth -``` - -### JavaScript Animation -```javascript -/* Use for complex, interactive animations */ -- Web Animations API for programmatic control -- Framer Motion for React -- 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 -- **Layout safety**: Avoid casual animation of layout-driving properties (`width`, `height`, `top`, `left`, margins) -- **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) { - * { - animation-duration: 0.01ms !important; - animation-iteration-count: 1 !important; - transition-duration: 0.01ms !important; - } -} -``` - -**NEVER**: -- Use bounce or elastic easing curves; they feel dated and draw attention to the animation itself -- Animate layout properties casually (`width`, `height`, `top`, `left`, margins) when transform, FLIP, or grid-based techniques would work -- Use durations over 500ms for feedback (it feels laggy) -- Animate without purpose (every animation needs a reason) -- Ignore `prefers-reduced-motion` (this is an accessibility violation) -- Animate everything (animation fatigue makes interfaces feel exhausting) -- Block interaction during animations unless intentional - -## Verify Quality - -Test animations thoroughly: - -- **Smooth at 60fps**: No jank on target devices -- **Feels natural**: Easing curves feel organic, not robotic -- **Appropriate timing**: Not too fast (jarring) or too slow (laggy) -- **Reduced motion works**: Animations disabled or simplified appropriately -- **Doesn't block**: Users can interact during/after animations -- **Adds value**: Makes interface clearer or more delightful - -When the motion clarifies state instead of decorating it, hand off to `/impeccable polish` for the final pass. +When motion earns its place, hand off to `/impeccable polish` for the final pass. diff --git a/.claude/skills/impeccable/reference/audit.md b/.claude/skills/impeccable/reference/audit.md index 72815af69..70d80771a 100644 --- a/.claude/skills/impeccable/reference/audit.md +++ b/.claude/skills/impeccable/reference/audit.md @@ -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 - diff --git a/.claude/skills/impeccable/reference/audit.native.md b/.claude/skills/impeccable/reference/audit.native.md index 737d50e4f..0126fa157 100644 --- a/.claude/skills/impeccable/reference/audit.native.md +++ b/.claude/skills/impeccable/reference/audit.native.md @@ -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) diff --git a/.claude/skills/impeccable/reference/bolder.md b/.claude/skills/impeccable/reference/bolder.md index 044c31931..fced49456 100644 --- a/.claude/skills/impeccable/reference/bolder.md +++ b/.claude/skills/impeccable/reference/bolder.md @@ -8,12 +8,12 @@ ## Why it reads flat -A section usually reads flat for reasons its neighbors have already solved. Look at what the rest of the page does that this section does not: the display type at full strength, the structural devices that carry meaning, the signature motif, the density and pacing, the specificity of the copy. A flat section is typically one that quietly opts out of the system's own strongest moves. The most reliable bolder pass brings the target up to the expressive level its neighbors already reach, in the system's own vocabulary rather than a new one. +A section usually reads flat for reasons its neighbors have already solved. Look at what the rest of the page does that this section does not: the display type at full strength, the structural devices that carry meaning, the signature motif, the density and pacing. A flat section is typically one that quietly opts out of the system's own strongest moves. The most reliable bolder pass brings the target up to the expressive level its neighbors already reach, in the system's own vocabulary rather than a new one. ## The amplification - **Amplify what the system already owns.** Reuse its motif and its type scale at full strength, turned up for this section rather than invented for it. The bolder version should look more like the same brand, not less. -- **Let the content carry the weight.** Flat copy makes a flat design, and no amount of size fixes a generic claim. Make every word specific enough to earn its place, and let the section's real evidence do the work that a decorative addition would otherwise fake. +- **Keep content true.** Existing claims are part of the scope: preserve them unless the user supplies replacements. If real evidence is essential to the direction but absent, ask for it. - **Commit, then clarify.** Half-measures read as noise. Make the one decisive move completely, then quiet everything around it so the move is legible. If every element got louder, the section got flatter. - **Give it its own rhythm.** The target should read as a peak in the scroll, a shift in density or pace from what surrounds it, not simply more of the same. diff --git a/.claude/skills/impeccable/reference/clarify.md b/.claude/skills/impeccable/reference/clarify.md index 00ee978c3..3047a9d82 100644 --- a/.claude/skills/impeccable/reference/clarify.md +++ b/.claude/skills/impeccable/reference/clarify.md @@ -1,288 +1,94 @@ -> **Additional context needed**: audience technical level and users' mental state in context. +> **Additional context needed**: audience knowledge and emotional state. -Find the unclear, confusing, or poorly written interface text and rewrite it. Vague copy creates support tickets and abandonment; specific copy gets users through the task. +Rewrite unclear interface text so users understand what happened, what matters, and what to do next. Preserve factual meaning, product terminology, and brand voice. +## Audit the language ---- +Read the entire interaction path, not isolated strings. Identify: -## Assess Current Copy +- ambiguous nouns, verbs, and actions; +- internal jargon or assumed knowledge; +- vague labels, outcomes, and system states; +- missing consequences, recovery, or timing; +- inconsistent terminology and capitalization; +- redundant headings, intros, helper text, and confirmations; +- text that breaks at realistic widths or in translation; +- tone that ignores stress, risk, success, or urgency. -Identify what makes the text unclear or ineffective: +Infer audience and task from product context and surrounding UI. Ask before changing factual claims, legal meaning, or a term that may be domain-specific. -1. **Find clarity problems**: - - **Jargon**: Technical terms users won't understand - - **Ambiguity**: Multiple interpretations possible - - **Passive voice**: "Your file has been uploaded" vs "We uploaded your file" - - **Length**: Too wordy or too terse - - **Assumptions**: Assuming user knowledge they don't have - - **Missing context**: Users don't know what to do or why - - **Tone mismatch**: Too formal, too casual, or inappropriate for situation +## Set the message hierarchy -2. **Understand the context**: - - Who's the audience? (Technical? General? First-time users?) - - What's the user's mental state? (Stressed during error? Confident during success?) - - What's the action? (What do we want users to do?) - - What's the constraint? (Character limits? Space limitations?) +For each state, decide: -**CRITICAL**: Clear copy helps users succeed. Unclear copy creates frustration, errors, and support tickets. +1. the one fact the user needs now; +2. the action available next; +3. supporting context that changes the decision; +4. the appropriate tone for this moment. -## Plan Copy Improvements +Say each idea once. If the heading already explains the state, the introduction should add new information or disappear. -Create a strategy for clearer communication: +## Rewrite by function -- **Primary message**: What's the ONE thing users need to know? -- **Action needed**: What should users do next (if anything)? -- **Tone**: How should this feel? (Helpful? Apologetic? Encouraging?) -- **Constraints**: Length limits, brand voice, localization considerations +### Actions and navigation -**IMPORTANT**: Good UX writing is invisible. Users should understand immediately without noticing the words. +Use a specific verb and object when the outcome is not already obvious. Labels should describe what will happen, not the gesture used to trigger it. Keep the same noun and verb for the same concept throughout the product. -## Improve Copy Systematically +For destructive actions, name the object and consequence. Prefer undo over confirmation when recovery is safe. When confirmation is necessary, name the action on both the message and button instead of using `Yes`, `No`, `OK`, or `Submit`. -Refine text across these common areas: +### Forms -### Error Messages -**Bad**: "Error 403: Forbidden" -**Good**: "You don't have permission to view this page. Contact your admin for access." +Use persistent labels; placeholders are examples, not labels. Put format and eligibility requirements before submission. Explain why information is requested only when it is not obvious. Required and optional treatment should be consistent. -**Bad**: "Invalid input" -**Good**: "Email addresses need an @ symbol. Try: name@example.com" +Validation says what needs attention and how to correct it without blaming the user. Keep related instructions near the field and announce errors accessibly. -**Principles**: -- Explain what went wrong in plain language -- Suggest how to fix it -- Don't blame the user -- Include examples when helpful -- Link to help/support if applicable - -### Form Labels & Instructions -**Bad**: "DOB (MM/DD/YYYY)" -**Good**: "Date of birth" (with placeholder showing format) - -**Bad**: "Enter value here" -**Good**: "Your email address" or "Company name" - -**Principles**: -- Use clear, specific labels (not generic placeholders) -- Show format expectations with examples -- Explain why you're asking (when not obvious) -- Put instructions before the field, not after -- Keep required field indicators clear - -### Button & CTA Text -**Bad**: "Click here" | "Submit" | "OK" -**Good**: "Create account" | "Save changes" | "Got it, thanks" +### Errors and permissions -**Principles**: -- Describe the action specifically -- Use active voice (verb + noun) -- Match user's mental model -- Be specific ("Save" is better than "OK") +An actionable error answers: -### Help Text & Tooltips -**Bad**: "This is the username field" -**Good**: "Choose a username. You can change this later in Settings." +1. what failed; +2. why, when known and useful; +3. how to recover or what alternative remains. -**Principles**: -- Add value (don't just repeat the label) -- Answer the implicit question ("What is this?" or "Why do you need this?") -- Keep it brief but complete -- Link to detailed docs if needed +Do not expose internal codes as the primary message. Do not promise a cause or resolution the system cannot know. Treat privacy, payment, deletion, access loss, and blocked work seriously; warmth is welcome, jokes are not. -### Empty States -**Bad**: "No items" -**Good**: "No projects yet. Create your first project to get started." +### Loading, empty, and success states -**Principles**: -- Explain why it's empty (if not obvious) -- Show next action clearly -- Make it welcoming, not dead-end +Loading text names the real operation and sets an honest expectation when the wait is meaningful. Show determinate progress when available; never invent progress. -### Success Messages -**Bad**: "Success" -**Good**: "Settings saved! Your changes will take effect immediately." +An empty state distinguishes first use, no results, filters, permissions, and failure. Explain the state and provide the next useful action. -**Principles**: -- Confirm what happened -- Explain what happens next (if relevant) -- Be brief but complete -- Match the user's emotional moment (celebrate big wins) - -### Loading States -**Bad**: "Loading..." (for 30+ seconds) -**Good**: "Analyzing your data... this usually takes 30-60 seconds" - -**Principles**: -- Set expectations (how long?) -- Explain what's happening (when it's not obvious) -- Show progress when possible -- Offer escape hatch if appropriate ("Cancel") +Success confirms the completed outcome and mentions the next consequence only when it changes what the user should do. Routine success should be brief. -### Confirmation Dialogs -**Bad**: "Are you sure?" -**Good**: "Delete 'Project Alpha'? This can't be undone." +### Help and instructional text -**Principles**: -- State the specific action -- Explain consequences (especially for destructive actions) -- Use clear button labels ("Delete project" not "Yes") -- Don't overuse confirmations (only for risky actions) +Helper text answers an implicit question instead of restating the control. Use progressive disclosure for uncommon detail. Link text must make sense out of context; icon-only controls need accessible names. -### Navigation & Wayfinding -**Bad**: Generic labels like "Items" | "Things" | "Stuff" -**Good**: Specific labels like "Your projects" | "Team members" | "Settings" +## Voice, accessibility, and localization -**Principles**: -- Be specific and descriptive -- Use language users understand (not internal jargon) -- Make hierarchy clear -- Consider information scent (breadcrumbs, current location) +Voice stays consistent; tone adapts to the moment. Use plain language without flattening terminology the audience genuinely knows. -## Apply Clarity Principles +- Write complete translatable messages rather than concatenated fragments. +- Keep variables and numbers structured so translators can reorder them. +- Allow expansion instead of abbreviating prematurely. +- Make alt text convey the image's information; use empty alt for decoration. +- Keep screen-reader names aligned with visible labels and outcomes. +- Do not rely on punctuation, color, or iconography to carry the message alone. -Every piece of copy should follow these rules: +Maintain a short terminology glossary when inconsistency spans the product. Do not vary words for literary effect in an interface. -1. **Be specific**: "Enter email" not "Enter value" -2. **Be concise**: Cut unnecessary words (but don't sacrifice clarity) -3. **Be active**: "Save changes" not "Changes will be saved" -4. **Be human**: "Oops, something went wrong" not "System error encountered" -5. **Tell users what to do**, not just what happened -6. **Be consistent**: Use same terms throughout (don't vary for variety) +## Verify -**NEVER**: -- Use jargon without explanation -- Blame users ("You made an error" → "This field is required") -- Be vague ("Something went wrong" without explanation) -- Use passive voice unnecessarily -- Write overly long explanations (be concise) -- Use humor for errors (be empathetic instead) -- Assume technical knowledge -- Vary terminology (pick one term and stick with it) -- Repeat information (headers restating intros, redundant explanations) -- Use placeholders as the only labels (they disappear when users type) +Read the flow in context and test: -## Verify Improvements +- comprehension without hidden product knowledge; +- actionability at errors, empty states, and decision points; +- factual accuracy and consistent terminology; +- scanability at target widths and 200% zoom; +- long names, localization expansion, pluralization, and dynamic values; +- accessible names and announced state changes; +- tone appropriate to consequence and emotional context. -Test that copy improvements work: +The final copy is as short as it can be without removing meaning or recovery. -- **Comprehension**: Can users understand without context? -- **Actionability**: Do users know what to do next? -- **Brevity**: Is it as short as possible while remaining clear? -- **Consistency**: Does it match terminology elsewhere? -- **Tone**: Is it appropriate for the situation? - -When the copy reads cleanly, hand off to `/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. +When the language reads cleanly, hand off to `/impeccable polish` for the final pass. diff --git a/.claude/skills/impeccable/reference/codex.md b/.claude/skills/impeccable/reference/codex.md index 18a924a91..6c550f1dc 100644 --- a/.claude/skills/impeccable/reference/codex.md +++ b/.claude/skills/impeccable/reference/codex.md @@ -1,105 +1,38 @@ -# Codex: Visual Direction & Asset Production +# Codex: Surface Probes & Asset Production -This file is loaded by `/impeccable craft` when the harness has native image generation (currently Codex via `image_gen`). Other harnesses skip it. It covers the two craft steps that depend on real image generation: landing the visual direction, and producing the raster assets the implementation will compose. +Load this from [new-work.md](new-work.md) only when the harness has native image generation and a substantial, high-fidelity surface would benefit from seeing the shortlisted concept before code. PRODUCT.md and DESIGN.md are preconditions. New-work has already resolved the visual world; this file must not reopen it. -Read this *before* generating any images. The order matters, and the per-step user pauses are what keep generated imagery from drifting away from the brief. +The purpose of a probe is to test composition, narrative, hierarchy, density, focal moment, signature use, and image requirements. It is not a second identity workshop. Keep DESIGN.md's palette, typography direction, material language, component character, imagery stance, and motion grammar fixed. -### Four stop points before code +## Generate the smallest useful probe set -Steps A through D each end with the user. Do not advance past any of them on your own read of the situation. +Generate one to three high-fidelity north-star comps using the native image-generation capability. Base them on the real content and the surface concepts already developed with the user. -1. **STOP after Step A questions.** Wait for answers. -2. **STOP after Step B palette generation.** Wait for "confirm palette." -3. **STOP after Step C mocks.** Wait for direction approval or delegation. -4. **Only after Step D approves a direction** do you return to the build (new-work.md's plan, then code). +- When the user shortlisted multiple concepts, show one clear expression of each. +- When one concept is already selected, vary only the structural uncertainty that the image can resolve: topology, sequence, density, hierarchy, focal composition, or interaction framing. +- Show enough beyond the opening moment to prove the concept can govern the whole requested surface. +- Do not generate a palette artifact, ask new atmosphere questions, introduce a different type voice, or invent a new motif. If the committed world cannot support the concept, return to the concept shortlist rather than changing the world. -Prior shape approval does **not** satisfy any of these. Shape's "confirm or override" advances you into Step A; it is not a substitute for it. +Treat each comp as a direction test, not a screenshot specification. Core UI text, responsive behavior, accessibility, semantics, and interaction states remain implementation responsibilities. -## Step A: Explore Directions with the User +## One approval point -Before generating anything, run a brief direction conversation grounded in the shape brief. +Show the probes together and ask what should carry forward, what feels false to the world, and whether the selected surface concept should be approved, combined, revised, or rejected. Then stop and wait. A structured simulated user counts as attended and receives the same question. -**Step A is required even when shape just produced a confirmed brief.** The shape questions and Step A questions cover different ground: shape pins purpose, content, scope; Step A pins palette, atmosphere, and named visual references for the comps you're about to generate. The only time you can skip Step A is when the user has already answered these exact palette/atmosphere/reference questions in the same session. +Do not begin code until the user approves a direction or explicitly delegates the choice. If they delegate, choose using the task brief, PRODUCT.md, and DESIGN.md, and state the evidence. Approval refines the task concept; it does not modify DESIGN.md. -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: +After approval, summarize the composition and the parts of the comp that must not be literalized. Return to new-work.md and write the direction contract from the approved surface concept before code. -- "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.)" +## Inventory implementation fidelity -**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. +Before building, inventory the approved comp's major visible ingredients and choose an implementation medium for each: semantic HTML/CSS/SVG, existing project asset, generated raster, sourced raster, icon library, canvas/WebGL, or accepted omission. -## Step B: Generate the Brand Palette First +Pay special attention to the dominant composition, signature use, image-native content, second-fold system, and any interaction the still image only implies. If the concept depends on a photograph, architectural scene, product object, portrait, or other raster-native material, do not silently replace it with generic CSS scenery. -Generate **one** palette artifact before any mocks. This is a small, focused image: typography pairing on the chosen background, primary + accent color swatches, one signature ornament or motif. Single image, single pass. +Treat the comp as a north star, not something to trace. Do not rasterize core UI text or controls. Do not substitute a different visual driver after approval without asking. -Why palette first: mocks generated against a vague color sense produce noise that drowns out the structural decisions. A confirmed palette is the first concrete contract for everything downstream. +## Produce only the assets the build needs -Show the palette to the user. Ask one question: "This is the palette I'm locking in for the mocks. Confirm, or call out what to shift?" +When clean raster ingredients are required and a scoped subagent is available and authorized, use `impeccable_asset_producer`. Give it the approved comp, output paths, required dimensions and formats, transparency needs, crop notes, and what must remain semantic code. Otherwise produce the minimum required assets with the native image-generation capability in the current thread. -**STOP and wait for confirmation.** Do not generate mocks against an unconfirmed palette. "Probably good enough" is the wrong call here; the palette is the contract for everything downstream. - -## Step C: Generate 1-3 Visual Mocks Against the Palette - -Once the palette is confirmed, generate **1 to 3** high-fidelity north-star comps. Each mock must use the confirmed palette and typography. Mocks differ in *structural* direction (hierarchy, topology, density, composition), not in color or motif. - -- Brand work: push visual identity, composition, mood, and signature motifs. -- Product work: push hierarchy, topology, density, tone, grounded in realistic product structure. -- Landing pages and long-form brand surfaces: show enough of the second fold to establish the system beyond the hero. - -Use the `image_gen` tool directly (or via the imagegen skill when available). Don't ask the user to install anything. - -## Step D: Approval Loop - -Show the comps. Ask what carries forward. Iterate until **one direction is approved** or the user explicitly delegates. - -**STOP and wait for the approval or the delegation.** Do not begin Step E or return to the build until a single direction is named. If the user delegates, pick the strongest direction and explain it from the brief, not personal taste. - -Before moving to assets, summarize what to carry into code and what *not* to literalize from the mock. This is the handoff between visual exploration and semantic implementation. - -## Step E: Mock Fidelity Inventory - -Inventory the approved mock's major visible ingredients. For each, decide implementation: semantic HTML/CSS/SVG, generated raster, sourced raster, icon library, canvas/WebGL, or accepted omission. - -Common ingredients to inventory: - -- Hero silhouette and dominant composition -- Signature motifs (planets, devices, portraits, charts, route lines, insets, badges, etc.) -- Nav and primary-action treatment (when the surface has one) -- Section sequence, especially the second fold -- Image-native content the concept depends on -- Typography, density, color/material treatment, motion cues - -Treat the mock as a north star, not a screenshot to trace. Don't rasterize core UI text. But if the live result lacks the mock's major ingredients, the implementation is wrong. - -If a photographic, architectural, product, or place-led mock becomes generic CSS scenery, decorative diagrams, bullets, or copy, stop and fix it. That's a broken implementation, not a harmless interpretation. - -Don't substitute a different hero composition or visual driver post-approval without user sign-off. - -## Step F: Asset Slicing via the Asset Producer - -Raster ingredients identified in Step E need clean production assets. Use the bundled `impeccable_asset_producer` subagent rather than producing inline. - -Spawn it as a scoped subagent. If you do not have explicit permission to use agents, stop and ask: - -```text -Asset production will work better as a scoped subagent job. Should I spawn the Impeccable asset producer subagent for this step? -``` - -Pass to the agent: - -- Approved mock path or screenshot reference -- Crop paths or a contact sheet with crop ids -- Output directory -- Required dimensions, format, transparency needs -- Avoid list -- Notes on what should remain semantic HTML/CSS/SVG instead of raster - -Attach image generation capability to the spawned agent when the harness supports it. Do **not** load image-generation reference material into the parent thread. - -Inline asset production is allowed only if the user declines subagents, the harness cannot spawn the authorized agent, or the user explicitly asks for single-thread mode. - -Prefer HTML/CSS/SVG/canvas when they can credibly reproduce an ingredient; reach for real, generated, or stock imagery when the mock or subject matter calls for actual visual content. - -## After This File - -Once Steps A through F are complete, return to the build: implement per new-work.md and SKILL.md's craft floor. The implementation builds against the confirmed palette, approved mock, and the assets the producer wrote. +Return to [new-work.md](new-work.md) for the direction contract, implementation, and finishing pass. diff --git a/.claude/skills/impeccable/reference/colorize.md b/.claude/skills/impeccable/reference/colorize.md index b0a61dade..dc45f88c5 100644 --- a/.claude/skills/impeccable/reference/colorize.md +++ b/.claude/skills/impeccable/reference/colorize.md @@ -1,257 +1,86 @@ > **Additional context needed**: existing brand colors. -Replace timid grayscale or single-accent designs with a strategic palette: pick a color strategy, choose a hue family that fits the brand, then apply color with intent. More color ≠ better. Strategic color beats rainbow vomit. +Introduce color as hierarchy, meaning, and atmosphere. Preserve confirmed brand and semantic conventions; do not replace a visual world under the guise of colorizing it. --- -## Register +## Visitor mode -Persuade + Experience: palette IS voice. Pick a color strategy first per SKILL.md (Restrained / Committed / Full palette / Drenched) and follow its dosage. Committed, Full palette, and Drenched deliberately exceed the ≤10% rule; that rule is Restrained only. Unexpected combinations are allowed; a dominant color can own the page when the chosen strategy calls for it. +- **Persuade + Experience:** color may carry the voice and own large regions when the selected world calls for it. +- **Operate + Read:** color primarily encodes action, selection, status, wayfinding, and reading hierarchy. Rarity gives an accent force. -Operate + Read: semantic-first and almost always Restrained. Accent color is reserved for primary action, current selection, and state indicators. Not decoration. Every color has a consistent meaning across every screen. +## Audit before choosing ---- +Read DESIGN.md, tokens, assets, current themes, and representative states. Identify: -## Assess Color Opportunity +- which colors are confirmed brand commitments; +- current surface, text, action, and semantic roles; +- places where grayscale obscures hierarchy or state; +- contrast failures and color-only communication; +- light/dark or data-visualization requirements; +- whether the task asks for more color or a new identity. -Analyze the current state and identify opportunities: +If a new identity is required, use [new-work.md](new-work.md). Ask only when a binding brand decision cannot be inferred. -1. **Understand current state**: - - **Color absence**: Pure grayscale? Limited neutrals? One timid accent? - - **Missed opportunities**: Where could color add meaning, hierarchy, or delight? - - **Context**: What's appropriate for this domain and audience? - - **Brand**: Are there existing brand colors we should use? +## Choose a strategy -2. **Identify where color adds value**: - - **Semantic meaning**: Success (green), error (red), warning (yellow/orange), info (blue) - - **Hierarchy**: Drawing attention to important elements - - **Categorization**: Different sections, types, or states - - **Emotional tone**: Warmth, energy, trust, creativity - - **Wayfinding**: Helping users navigate and understand structure - - **Delight**: Moments of visual interest and personality +Name the intended emotional temperature, dominant relationship, contrast range, and color dosage before editing. The strategy may be restrained or immersive; it must follow the brief and selected world rather than a fixed percentage rule. -If any of these are unclear from the codebase, STOP and call the AskUserQuestion tool to clarify. +Build roles, not a bag of swatches: -**CRITICAL**: More color ≠ better. Strategic color beats rainbow vomit every time. Every color should have a purpose. +- canvas and elevated surfaces; +- primary and secondary text; +- action, focus, and selection; +- borders and separators; +- success, warning, error, and information; +- data categories or scales when needed. -## Plan Color Strategy +Use the project's existing color space. For a new web palette, prefer OKLCH because lightness and chroma can be adjusted predictably. Choose hue from product meaning and visual direction, never from a default category association. -Create a purposeful color introduction plan: +## Apply at system scale -- **Color palette**: What colors match the brand/context? (Choose 2-4 colors max beyond neutrals) -- **Dominant color**: Which color owns 60% of colored elements? -- **Accent colors**: Which colors provide contrast and highlights? (30% and 10%) -- **Application strategy**: Where does each color appear and why? +- Let the strongest color own a deliberate region or role instead of scattering tiny accents. +- Keep the primary action easy to find; do not spend its color on decoration. +- Tint neutrals only when the brand hue genuinely creates cohesion. Neutral gray is valid when it serves the world. +- On colored surfaces, derive secondary text from the foreground or surface hue rather than using washed-out generic gray. +- Keep semantic meanings consistent, but respect platform and domain conventions instead of assuming fixed hues. +- For data, use distinct lightness, chroma, shape, label, or pattern so color is not the only code. +- 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. -**IMPORTANT**: Color should enhance hierarchy and meaning, not create chaos. Less is more when it matters more. +Decoration without a relationship to hierarchy, state, content, or the visual world is not a color strategy. -## Introduce Color Strategically +## Contrast and perception -Add color systematically across these dimensions: +Verify computed foreground/background pairs: -### Semantic Color -- **State indicators**: - - Success: Green tones (emerald, forest, mint) - - Error: Red/pink tones (rose, crimson, coral) - - Warning: Orange/amber tones - - Info: Blue tones (sky, ocean, indigo) - - Neutral: Gray/slate for inactive states +| Content | WCAG AA minimum | +|---|---| +| body text | 4.5:1 | +| large text | 3:1 | +| controls, icons, focus indicators | 3:1 | -- **Status badges**: Colored backgrounds or borders for states (active, pending, completed, etc.) -- **Progress indicators**: Colored bars, rings, or charts showing completion or health +Do not rely on eyesight alone. Check interactive states, overlays, text on images, disabled content, and both themes. Simulate common vision deficiencies. Information conveyed by color also needs text, shape, iconography, or position. -### Accent Color Application -- **Primary actions**: Color the most important buttons/CTAs -- **Links**: Add color to clickable text (maintain accessibility) -- **Icons**: Colorize key icons for recognition and personality -- **Headers/titles**: Add color to section headers or key labels -- **Hover states**: Introduce color on interaction +When deriving OKLCH ramps, vary lightness and reduce chroma near white and black. Do not keep high chroma at extreme lightness merely to make the math uniform. Prefer explicit colors over chains of translucent overlays when alpha would make contrast context-dependent. -### Background & Surfaces -- **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 toward the brand, not "for warmth" by reflex +## Verify -**Use OKLCH for color**: It's perceptually uniform, meaning equal steps in lightness *look* equal. Great for generating harmonious scales. - -### Data Visualization -- **Charts & graphs**: Use color to encode categories or values -- **Heatmaps**: Color intensity shows density or importance -- **Comparison**: Color coding for different datasets or timeframes - -### Borders & Accents -- **Hairline borders**: 1px colored borders on full perimeter (not side-stripes; see the absolute ban on `border-left/right > 1px`) -- **Underlines**: Color underlines for emphasis or active states -- **Dividers**: Subtle colored dividers instead of gray lines -- **Focus rings**: Colored focus indicators matching brand -- **Surface tints**: A 4-8% background wash of the accent color instead of a stripe - -**NEVER**: `border-left` or `border-right` greater than 1px as a colored accent stripe. This is one of the three absolute bans in the parent skill. If you want to mark a card as "active" or "warning", use a full hairline border, a background tint, a leading glyph, or a numbered prefix. Not a side stripe. - -### Typography Color -- **Colored headings**: Use brand colors for section headings (maintain contrast) -- **Highlight text**: Color for emphasis or categories -- **Labels & tags**: Small colored labels for metadata or categories - -### Decorative Elements -- **Illustrations**: Add colored illustrations or icons -- **Shapes**: Geometric shapes in brand colors as background elements -- **Gradients**: Colorful gradient overlays or mesh backgrounds -- **Blobs/organic shapes**: Soft colored shapes for visual interest - -## Balance & Refinement - -Ensure color addition improves rather than overwhelms: - -### Maintain Hierarchy -- **Dominant color** (60%): Primary brand color or most used accent -- **Secondary color** (30%): Supporting color for variety -- **Accent color** (10%): High contrast for key moments -- **Neutrals** (remaining): Gray/black/white for structure - -### Accessibility -- **Contrast ratios**: Ensure WCAG compliance (4.5:1 for text, 3:1 for UI components) -- **Don't rely on color alone**: Use icons, labels, or patterns alongside color -- **Test for color blindness**: Verify red/green combinations work for all users - -### Cohesion -- **Consistent palette**: Use colors from defined palette, not arbitrary choices -- **Systematic application**: Same color meanings throughout (green always = success) -- **Temperature consistency**: Warm palette stays warm, cool stays cool - -**NEVER**: -- 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 -- Violate WCAG contrast requirements -- Use color as the only indicator (accessibility issue) -- Make everything colorful (defeats the purpose) -- Default to purple-blue gradients (AI slop aesthetic) - -## Verify Color Addition - -Test that colorization improves the experience: - -- **Better hierarchy**: Does color guide attention appropriately? -- **Clearer meaning**: Does color help users understand states/categories? -- **More engaging**: Does the interface feel warmer and more inviting? -- **Still accessible**: Do all color combinations meet WCAG standards? -- **Not overwhelming**: Is color balanced and purposeful? +- Every color has a stable role or a world-specific atmospheric purpose. +- Attention lands on the intended action, content, or state. +- The palette works across quiet, dense, interactive, error, and empty states. +- Light and dark themes are each composed, not mechanically inverted. +- Contrast and non-color cues pass in all relevant states. +- The result is recognizably this product, not a generic “colorful” treatment. When the palette earns its place, hand off to `/impeccable polish` for the final pass. ## Live-mode signature params -When invoked from live mode, each variant MUST declare a `color-amount` param so the user can dial between a restrained accent and a drenched surface without regeneration. Author the variant's CSS against `var(--p-color-amount, 0.5)`, typically as the alpha multiplier on backgrounds, or as a scaling factor on the chroma axis in an OKLCH expression. 0 = neutral/monochrome, 1 = full saturation / dominant coverage. +When invoked from live mode, every variant declares a `color-amount` parameter. Author CSS against `var(--p-color-amount, 0.5)` so the user can move from neutral to the variant's full color strategy without regeneration. ```json {"id":"color-amount","kind":"range","min":0,"max":1,"step":0.05,"default":0.5,"label":"Color amount"} ``` -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). +Add at most two variant-specific parameters, such as palette, temperature, or tint behavior. Follow [live.md](live.md)'s parameter contract. diff --git a/.claude/skills/impeccable/reference/craft.md b/.claude/skills/impeccable/reference/craft.md index cc7f98d37..dbbc9402c 100644 --- a/.claude/skills/impeccable/reference/craft.md +++ b/.claude/skills/impeccable/reference/craft.md @@ -1,3 +1,5 @@ # Craft (deprecated alias) -`craft` is a deprecated alias for the standard build flow. Builds route through SKILL.md's new-work gate; [new-work.md](new-work.md) carries the direction checkpoint ("Decide, then build"), the identity playbook, and the finishing pass. Invoking `craft` explicitly forces the attended checkpoints: pause at the stated direction for confirmation, and follow [codex.md](codex.md)'s mock flow when the harness generates images. Nothing else differs from a plain build request. +`craft` is a deprecated alias for an ordinary request to make new visual work. It adds no setup, interview, checkpoint, tool, or quality behavior. Apply SKILL.md's normal routing: create missing PRODUCT.md through [init.md](init.md), then follow [new-work.md](new-work.md) for visual authority, world and surface decisions, implementation, and finish. + +Do not tell users they need to invoke `craft`. Natural requests such as “build this feature,” “make a landing page,” or “redesign this screen” use the same flow. diff --git a/.claude/skills/impeccable/reference/critique.md b/.claude/skills/impeccable/reference/critique.md index 429b6c4fc..9306f4420 100644 --- a/.claude/skills/impeccable/reference/critique.md +++ b/.claude/skills/impeccable/reference/critique.md @@ -43,13 +43,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 @@ -115,11 +115,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. @@ -176,7 +176,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. 2. **Pass the structured metadata** through `IMPECCABLE_CRITIQUE_META` (JSON), then run the write command: ```bash diff --git a/.claude/skills/impeccable/reference/delight.md b/.claude/skills/impeccable/reference/delight.md index 3fa4b3205..40aaddc34 100644 --- a/.claude/skills/impeccable/reference/delight.md +++ b/.claude/skills/impeccable/reference/delight.md @@ -1,302 +1,70 @@ -> **Additional context needed**: what's appropriate for the domain (playful vs professional vs quirky vs elegant). +> **Additional context needed**: the brand's emotional range. -Find the moments where personality and unexpected polish would turn a functional interface into one users remember and tell other people about. Add only where the moment earns it; delight everywhere reads as noise. +Make the experience memorable at moments that earn it. Delight is not a layer of generic whimsy; it is product character revealed through a useful interaction, a humane response, or an unexpectedly considered detail. --- -## Register +## Visitor mode -Persuade + Experience: delight can be distributed across copy voice, section transitions, discovery rewards, seasonal touches, personality across the whole surface. +- **Persuade + Experience:** personality may run through voice, composition, motion, and discovery, provided the artifact remains the focus. +- **Operate + Read:** concentrate delight at meaningful moments such as first use, completion, recovery, or mastery. Reliability carries everything else. -Operate + Read: delight at specific moments, not pages. Completion, first-time actions, error recovery, milestone crossings. Reliability and consistency carry the rest of the experience; delight pushed everywhere reads as noise. +## Find the opportunity ---- +Inspect the target, DESIGN.md, product voice, repeated-use frequency, and emotional context. Look for: -## Assess Delight Opportunities +- effort worth acknowledging; +- waiting that can become informative; +- an empty or first-use state that can orient; +- an error or recovery moment that needs empathy; +- an interaction whose physical or verbal response could express the brand; +- a useful capability people might enjoy discovering. -Identify where delight would enhance (not distract from) the experience: +Do not manufacture a celebration for an ordinary click. Ask only when the brand's emotional range or the stakes cannot be inferred. -1. **Find natural delight moments**: - - **Success states**: Completed actions (save, send, publish) - - **Empty states**: First-time experiences, onboarding - - **Loading states**: Waiting periods that could be entertaining - - **Achievements**: Milestones, streaks, completions - - **Interactions**: Hover states, clicks, drags - - **Errors**: Softening frustrating moments - - **Easter eggs**: Hidden discoveries for curious users +## Define one delight thesis -2. **Understand the context**: - - What's the brand personality? (Playful? Professional? Quirky? Elegant?) - - Who's the audience? (Tech-savvy? Creative? Corporate?) - - What's the emotional context? (Accomplishment? Exploration? Frustration?) - - What's appropriate? (Banking app ≠ gaming app) +State in one sentence what the user should feel and why that feeling belongs to this product. Then choose the smallest system that can deliver it: -3. **Define delight strategy**: - - **Subtle sophistication**: Refined micro-interactions (luxury brands) - - **Playful personality**: Whimsical illustrations and copy (consumer apps) - - **Helpful surprises**: Anticipating needs before users ask (productivity tools) - - **Sensory richness**: Satisfying sounds, smooth animations (creative tools) +- a distinctive response to a meaningful action; +- product-specific language that clarifies while carrying voice; +- an interaction or transition with a recognizable material behavior; +- an illustration, sound, haptic, or environmental detail grounded in the product world; +- a discovery reward that reveals real utility. -If any of these are unclear from the codebase, STOP and call the AskUserQuestion tool to clarify. +Derive the treatment from product mechanism and visual world, not a stock catalog. -**CRITICAL**: Delight should enhance usability, never obscure it. If users notice the delight more than accomplishing their goal, you've gone too far. +## Build for the emotional moment -## Delight Principles +- **Success:** match the response to the effort and consequence. Major milestones can expand; routine saves should simply feel certain. +- **Waiting:** show truthful progress, useful context, or product-specific activity. Never fake work or delay completion to stage a flourish. +- **Empty and first use:** make the next action clear before adding personality. +- **Error and recovery:** lead with the problem and recovery. Warmth may reduce stress; jokes must not trivialize loss, money, privacy, or blocked work. +- **Repeated interaction:** keep the response satisfying after the hundredth use. Variation is useful only when it remains coherent and predictable enough to trust. +- **Discovery:** reward curiosity without hiding required functionality. -Follow these guidelines: +Copy must use the product's language. Generic whimsy is worse than neutral clarity. -### Delight Amplifies, Never Blocks -- Delight moments should be quick (< 1 second) -- Never delay core functionality for delight -- Make delight skippable or subtle -- Respect user's time and task focus +## Protect the experience -### Surprise and Discovery -- Hide delightful details for users to discover -- Reward exploration and curiosity -- Don't announce every delight moment -- Let users share discoveries with others +Delight must not: -### Appropriate to Context -- Match delight to emotional moment (celebrate success, empathize with errors) -- Respect the user's state (don't be playful during critical errors) -- Match brand personality and audience expectations -- Cultural sensitivity (what's delightful varies by culture) +- delay, block, or obscure the primary task; +- override platform conventions or accessibility; +- add unrequested factual claims; +- play sound without consent or ignore mute settings; +- become mandatory, unskippable, or exhausting on repeat; +- add a dependency or asset cost disproportionate to the moment. -### Compound Over Time -- Delight should remain fresh with repeated use -- Vary responses (not same animation every time) -- Reveal deeper layers with continued use -- Build anticipation through patterns +For authored motion, load [animate.md](animate.md). Respect reduced motion, screen readers, keyboard use, touch, localization, and cultural context. Nonessential loops stop when hidden. Make celebration intensity proportional to frequency and consequence. -## Delight Techniques +## Verify -Add personality and joy through these methods: +- The moment is specific enough that a neighboring product could not use it unchanged. +- It improves comprehension, confidence, motivation, or emotional recovery. +- The interface remains fast and obvious without the flourish. +- Repetition does not turn charm into friction. +- Reduced-motion, muted, keyboard, touch, and localized paths work. +- The result feels like the selected world, not a generic “delight” treatment. -### Micro-interactions & Animation - -**Button delight**: -```css -/* Satisfying button press */ -.button { - transition: transform 0.1s, box-shadow 0.1s; -} -.button:active { - transform: translateY(2px); - box-shadow: 0 2px 4px rgba(0,0,0,0.2); -} - -/* Ripple effect on click */ -/* Smooth lift on hover */ -.button:hover { - transform: translateY(-2px); - transition: transform 0.2s cubic-bezier(0.25, 1, 0.5, 1); /* ease-out-quart */ -} -``` - -**Loading delight**: -- Playful loading animations (not just spinners) -- Personality in loading messages (write product-specific ones, not generic AI filler) -- Progress indication with encouraging messages -- Skeleton screens with subtle animations - -**Success animations**: -- Checkmark draw animation -- Confetti burst for major achievements -- Gentle scale + fade for confirmation -- Satisfying sound effects (subtle) - -**Hover surprises**: -- Icons that animate on hover -- Color shifts or glow effects -- Tooltip reveals with personality -- Cursor changes (custom cursors for branded experiences) - -### Personality in Copy - -**Playful error messages**: -``` -"Error 404" -"This page is playing hide and seek. (And winning)" - -"Connection failed" -"Looks like the internet took a coffee break. Want to retry?" -``` - -**Encouraging empty states**: -``` -"No projects" -"Your canvas awaits. Create something amazing." - -"No saved articles" -"Nothing on the nightstand yet. Save a story for later." -``` - -**Playful labels & tooltips**: -``` -"Delete" -"Send to void" (for playful brand) - -"Help" -"Rescue me" (tooltip) -``` - -**IMPORTANT**: Match copy personality to brand. Banks shouldn't be wacky, but they can be warm. - -### Illustrations & Visual Personality - -**Custom illustrations**: -- Empty state illustrations (not stock icons) -- Error state illustrations (friendly monsters, quirky characters) -- Loading state illustrations (animated characters) -- Success state illustrations (celebrations) - -**Icon personality**: -- Custom icon set matching brand personality -- Animated icons (subtle motion on hover/click) -- Illustrative icons (more detailed than generic) -- Consistent style across all icons - -**Background effects**: -- Subtle particle effects -- Gradient mesh backgrounds -- Geometric patterns -- Parallax depth -- Time-of-day themes (morning vs night) - -### Satisfying Interactions - -**Drag and drop delight**: -- Lift effect on drag (shadow, scale) -- Snap animation when dropped -- Satisfying placement sound -- Undo toast ("Dropped in wrong place? [Undo]") - -**Toggle switches**: -- Smooth slide with spring physics -- Color transition -- Haptic feedback on mobile -- Optional sound effect - -**Progress & achievements** (Operate surfaces with recurring tasks only; gamification on editorial, portfolio, or one-visit surfaces reads as noise): -- Streak counters with celebratory milestones -- Progress bars that "celebrate" at 100% -- Badge unlocks with animation -- Playful stats ("You're on fire! 5 days in a row") - -**Form interactions**: -- Input fields that animate on focus -- Checkboxes with a satisfying scale pulse when checked -- Success state that celebrates valid input -- Auto-grow textareas - -### Sound Design - -**Subtle audio cues** (when appropriate): -- Notification sounds (distinctive but not annoying) -- Success sounds (satisfying "ding") -- Error sounds (empathetic, not harsh) -- Typing sounds for chat/messaging -- Ambient background audio (very subtle) - -**IMPORTANT**: -- Respect system sound settings -- Provide mute option -- Keep volumes quiet (subtle cues, not alarms) -- Don't play on every interaction (sound fatigue is real) - -### Easter Eggs & Hidden Delights - -**Discovery rewards**: -- Konami code unlocks special theme -- Hidden keyboard shortcuts (Cmd+K for special features) -- Hover reveals on logos or illustrations -- Alt text jokes on images (for screen reader users too!) -- A view-source or console colophon (a note on the typefaces, a thank-you, a hiring line) - -**Seasonal touches**: -- Holiday themes (subtle, tasteful) -- Seasonal color shifts -- Weather-based variations -- Time-based changes (dark at night, light during day) - -**Contextual personality**: -- Different messages based on time of day -- Responses to specific user actions -- Randomized variations (not same every time) -- Progressive reveals with continued use - -### Loading & Waiting States - -**Make waiting engaging**: -- Interesting loading messages that rotate -- Progress bars with personality -- Mini-games during long loads -- Fun facts or tips while waiting -- Countdown with encouraging messages - -``` -Loading messages: write ones specific to your product, not generic AI filler: -- "Syncing with your team's changes..." -- "Fetching this week's issue..." -- "Developing your photos..." -- "Checking tomorrow's tide tables..." -``` - -**WARNING**: Avoid cliched loading messages like "Herding pixels", "Teaching robots to dance", "Consulting the magic 8-ball", "Counting backwards from infinity". These are AI-slop copy, instantly recognizable as machine-generated. Write messages that are specific to what your product actually does. - -### Celebration Moments - -**Success celebrations**: -- Confetti for major milestones -- Animated checkmarks for completions -- Progress bar celebrations at 100% -- "Achievement unlocked" style notifications -- Personalized messages ("You published your 10th article!") - -**Milestone recognition** (same scoping as Progress & achievements: Operate surfaces with recurring tasks): -- First-time actions get special treatment -- Streak tracking and celebration -- Progress toward goals -- Anniversary celebrations - -## Implementation Patterns - -**Animation libraries**: -- Framer Motion (React) -- GSAP (universal) -- Lottie (After Effects animations) -- Canvas confetti (party effects) - -**Sound libraries**: -- Howler.js (audio management) -- Use-sound (React hook) - -**Physics libraries**: -- React Spring (spring physics) -- Popmotion (animation primitives) - -**IMPORTANT**: File size matters. Compress images, optimize animations, lazy load delight features. - -**NEVER**: -- Delay core functionality for delight -- Force users through delightful moments (make skippable) -- Use delight to hide poor UX -- Overdo it (less is more) -- Ignore accessibility (animate responsibly, provide alternatives) -- Make every interaction delightful (special moments should be special) -- Sacrifice performance for delight -- Be inappropriate for context (read the room) - -## Verify Delight Quality - -Test that delight actually delights: - -- **User reactions**: Do users smile? Share screenshots? -- **Doesn't annoy**: Still pleasant after 100th time? -- **Doesn't block**: Can users opt out or skip? -- **Performant**: No jank, no slowdown -- **Appropriate**: Matches brand and context -- **Accessible**: Works with reduced motion, screen readers - -When the moments feel earned, hand off to `/impeccable polish` for the final pass. +When the personality feels earned, hand off to `/impeccable polish` for the final pass. diff --git a/.claude/skills/impeccable/reference/document.md b/.claude/skills/impeccable/reference/document.md index 4c52e92c5..6bcf9b824 100644 --- a/.claude/skills/impeccable/reference/document.md +++ b/.claude/skills/impeccable/reference/document.md @@ -1,6 +1,6 @@ Generate a `DESIGN.md` file at the project root that captures the current visual design system, so AI agents generating new screens stay on-brand. -DESIGN.md follows the [official DESIGN.md format spec](https://raw.githubusercontent.com/google-labs-code/design.md/main/docs/spec.md): YAML frontmatter carrying machine-readable design tokens, followed by a markdown body with exactly six sections in a fixed order. **Tokens are normative; prose provides context for how to apply them.** Sections may be omitted when not relevant, but **do not reorder them and do not rename them**. Section headers must match the spec character-for-character so the file stays parseable by other DESIGN.md-aware tools (Stitch itself, awesome-design-md, skill-rest, etc.). +DESIGN.md follows the [official DESIGN.md format spec](https://raw.githubusercontent.com/google-labs-code/design.md/main/docs/spec.md): optional YAML frontmatter carrying machine-readable design tokens, followed by up to eight markdown sections in a fixed order. **Tokens are normative; prose provides context for how to apply them.** Sections may be omitted when not relevant, but those present stay in the specified order. Use the canonical headings below so the file remains portable across DESIGN.md-aware tools. ## The frontmatter: token schema @@ -43,26 +43,28 @@ components: 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. +- **Colors accept any valid CSS color string.** Hex is the recommended default for portability, but preserve an incumbent `rgb()`, `hsl()`, `oklch()`, wide-gamut, or mixed-color value when it is the project's normative source. 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 (`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) +## The markdown body: eight sections (canonical order) 1. `## Overview` 2. `## Colors` 3. `## Typography` -4. `## Elevation` -5. `## Components` -6. `## Do's and Don'ts` +4. `## Layout` +5. `## Elevation & Depth` +6. `## Shapes` +7. `## Components` +8. `## Do's and Don'ts` -Optional evocative subtitles are allowed in the form `## 2. Colors: The [Name] Palette` (Stitch's own outputs do this), but the literal word in each header (Overview, Colors, Typography, Elevation, Components, Do's and Don'ts) must be present. Do NOT add extra top-level sections (Layout Principles, Responsive Behavior, Motion, Agent Prompt Guide). Fold that content into the six spec sections where it naturally belongs. +Omit irrelevant sections rather than filling them with invented rules. Put responsive layout in Layout, depth in Elevation & Depth, radius and form language in Shapes, and per-component behavior in Components. Unknown sections are preserved by the format, but new visual guidance should use the canonical structure whenever it fits. ## When to run -- 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. +- New-work found a coherent incumbent visual system but no `DESIGN.md`. +- The first implementation of a new world is complete and its provisional decisions need to be carbonized. - An existing `DESIGN.md` is stale (the design has drifted). - Before a large redesign, to capture the current state as a reference. @@ -71,9 +73,9 @@ 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 init, nothing built yet). Interview for five high-level answers, write a minimal DESIGN.md marked ``. Re-run in scan mode once there's code. +- **Seed mode**: the project is pre-implementation. Ensure PRODUCT.md exists, then reuse new-work's visual-world workshop and write its directional DESIGN.md 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. +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` requests new-work's world workshop, but it does not authorize replacing coherent code: when an incumbent system exists, offer scan mode or route an explicit identity-replacement request through new-work. ## Scan mode (approach C: auto-extract, then confirm descriptive language) @@ -97,7 +99,8 @@ Build a structured draft from the discovered tokens. For each token class: - **Typography**: Map observed sizes and weights to the Material hierarchy (display / headline / title / body / label). Note font-family stacks and the scale ratio. - **Elevation**: Catalogue the shadow vocabulary. If the project is flat and uses tonal layering instead, that's a valid answer; state it explicitly. - **Components**: For each common component (button, card, input, chip, list item, tooltip, nav), extract shape (radius), color assignment, hover/focus treatment, internal padding. -- **Spacing + layout**: Fold into Overview or relevant Components. The spec does NOT have a Layout section. +- **Layout + spacing**: Extract grid, container, breakpoint, rhythm, and density behavior into Layout. +- **Shapes**: Extract radius, corner, border, clipping, and recurring form behavior into Shapes. ### Step 2b: Stage the frontmatter @@ -112,19 +115,19 @@ Skip anything the project doesn't have. Empty scale keys or fabricated tokens po ### Step 3: Ask the user for qualitative language -The following require creative input that cannot be auto-extracted. Group them into one `AskUserQuestion` interaction: +The following require creative input that cannot be auto-extracted. Ask them in two structured rounds of no more than three questions each (or the harness's lower limit), waiting between rounds: - **Creative North Star**: a single named metaphor for the whole system ("The Editorial Sanctuary", "The Golden State Curator", "The Lab Notebook"). Offer 2-3 options that honor PRODUCT.md's brand personality. -- **Overview voice**: mood adjectives, aesthetic philosophy in 2-3 sentences, anti-references (what the system should not feel like). +- **Overview voice**: mood adjectives, aesthetic philosophy in 2-3 sentences, and any confirmed visual anti-reference. - **Color character** (for auto-extracted colors): descriptive names ("Deep Muted Teal-Navy", not "blue-800"). Suggest 2-3 options per key color based on hue/saturation. - **Elevation philosophy**: flat/layered/lifted. If shadows exist, is their role ambient or structural? - **Component philosophy**: the feel of buttons, cards, inputs in one phrase ("tactile and confident" vs. "refined and restrained"). -Quote a line from PRODUCT.md when possible so the user sees their own strategic language carry forward. +Carry a line from PRODUCT.md only when it is a durable brand commitment that actually constrains the visual system. Page strategy and surface concepts do not belong here. ### Step 4: Write DESIGN.md -The file opens with the YAML frontmatter staged in Step 2b (schema documented at the top of this reference), then the markdown body using the structure below. Headers must match character-for-character. Optional evocative subtitles (e.g. `## 2. Colors: The Coastal Palette`) are allowed. +The file opens with the YAML frontmatter staged in Step 2b (schema documented at the top of this reference), then the markdown body using the canonical structure below. ```markdown --- @@ -136,13 +139,13 @@ colors: # Design System: [Project Title] -## 1. Overview +## Overview **Creative North Star: "[Named metaphor in quotes]"** -[2-3 paragraph holistic description: personality, density, aesthetic philosophy. Start from the North Star and work outward. State what this system explicitly rejects (pulled from PRODUCT.md's anti-references). End with a short **Key Characteristics:** bullet list.] +[2-3 paragraph holistic description: personality, density, and aesthetic philosophy. Start from the North Star and work outward. State only confirmed visual rejections. End with a short **Key Characteristics:** bullet list.] -## 2. Colors +## Colors [Describe the palette character in one sentence.] @@ -162,7 +165,7 @@ colors: ### Named Rules (optional, powerful) **The [Rule Name] Rule.** [Short, forceful prohibition or doctrine, e.g. "The One Voice Rule. The primary accent is used on ≤10% of any given screen. Its rarity is the point."] -## 3. Typography +## Typography **Display Font:** [Family] (with [fallback]) **Body Font:** [Family] (with [fallback]) @@ -180,7 +183,11 @@ colors: ### Named Rules (optional) **The [Rule Name] Rule.** [Short doctrine about type use.] -## 4. Elevation +## Layout + +[Describe the grid or spatial model, container behavior, density, responsive changes, and the spacing rhythm. Include exact values only when observed.] + +## Elevation & Depth [One paragraph: does this system use shadows, tonal layering, or a hybrid? If "no shadows", say so explicitly and describe how depth is conveyed instead.] @@ -191,7 +198,11 @@ colors: ### Named Rules (optional) **The [Rule Name] Rule.** [e.g. "The Flat-By-Default Rule. Surfaces are flat at rest. Shadows appear only as a response to state (hover, elevation, focus)."] -## 5. Components +## Shapes + +[Describe the form language: corner/radius strategy, borders, clipping, and any recurring silhouette or geometry.] + +## Components For each component, lead with a short character line, then specify shape, color assignment, states, and any distinctive behavior. @@ -223,16 +234,16 @@ For each component, lead with a short character line, then specify shape, color ### [Signature Component] (optional; if the project has a distinctive custom component worth documenting) [Description.] -## 6. Do's and Don'ts +## Do's and Don'ts -Concrete, forceful guardrails. Lead each with "Do" or "Don't". Be specific: include exact colors, pixel values, and named anti-patterns the user mentioned in PRODUCT.md. **Every anti-reference in PRODUCT.md should show up here as a "Don't" with the same language**, so the visual spec carries the strategic line through. Quote PRODUCT.md directly where possible: if PRODUCT.md says *"avoid dark mode with purple gradients, neon accents, glassmorphism"*, the Don'ts here should repeat that by name. +Concrete visual guardrails grounded in the incumbent implementation or the user's chosen world. Lead each with "Do" or "Don't" and include exact values only when established. Do not turn a task-specific concept or surface strategy into a system-wide prohibition. ### Do: - **Do** [specific prescription with exact values / named rule]. - **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** [...] ``` @@ -307,7 +318,7 @@ The `html` and `css` fields must be **self-contained, drop-in snippets** that re Aim for a tight set of **5-10 components** that best represent the visual system: - **Canonical primitives (always include if the project has them):** button (each variant as a separate component entry), input/text field, navigation, chip/tag, card. -- **Signature components (include if distinctive):** hero CTA, featured card, filter pill, a distinctive table or list-row treatment, any custom pattern the user mentioned as important in PRODUCT.md. +- **Signature components (include if distinctive):** the recurring custom patterns that actually define the implemented system. - **Skip the rest.** Utility components, form building blocks, wrapper layouts: not worth documenting unless visually distinctive. If the project has **no component library yet** (bare landing page, new project), synthesize canonical primitives from the tokens using best-practice defaults consistent with the DESIGN.md's rules. Every `.impeccable/design.json` has *something* to render, even on day zero. @@ -338,64 +349,40 @@ Your own write is the freshest source; subsequent commands in this session don't ## Seed mode -For projects with no visual system to extract yet. Produces a minimal scaffold, not a full spec. +For projects with no visual system to extract yet. Produces a user-chosen visual-world scaffold, not a fabricated token spec. -### Step 1: Confirm seed mode +### Step 1: Route through new-work's workshop -Before interviewing: "There's no existing visual system to scan. I'll ask five quick questions to seed a starter DESIGN.md. You can re-run `/impeccable document` once there's code, to capture the real tokens and components. OK?" +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 the user prefers to skip, stop. No file. +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. -### Step 2: Five questions +If new-work already completed the workshop in this session, use its chosen direction directly. Do not ask again. -Group into one `AskUserQuestion` interaction. Options must be concrete. +### Step 2: Write seed DESIGN.md -1. **Color strategy.** Pick one: - - Restrained: tinted neutrals + one accent ≤10% - - Committed: one saturated color carries 30–60% of the surface - - Full palette: 3–4 named color roles, each deliberate - - Drenched: the surface IS the color - - Then: one hue family or anchor reference ("deep teal", "mustard", "Klim #ff4500 orange"). - -2. **Typography direction.** Pick one (specific fonts come later): - - Serif display + sans body - - Single sans (warm / technical / geometric / humanist; pick a feel) - - Display + mono - - Mono-forward - - Editorial script + sans - -3. **Motion energy.** Pick one: - - Restrained: state changes only - - Responsive: feedback + transitions, no choreography - - Choreographed: orchestrated entrances, scroll-driven sequences - -4. **Three named references.** Brands, products, printed objects. Not adjectives. - -5. **One anti-reference.** What it should NOT feel like. Also named. - -### Step 3: Write seed DESIGN.md - -Use the six-section spec from Scan mode. Populate what the interview answers; leave the rest as honest placeholders. The seed is a scaffold, not a fabricated spec. +Use the canonical section order from Scan mode. Populate the selected workshop direction and leave unresolved implementation facts as honest placeholders. The seed commits a world and its invariants; it does not pretend implementation tokens already exist. Lead the file with: ```markdown - + ``` Per-section guidance in seed mode: -- **Overview**: Creative North Star and philosophy phrased from the answers (color strategy + motion energy + references). Reference the user's anti-reference directly. -- **Colors**: Color strategy as a Named Rule (e.g. *"The Drenched Rule. The surface IS the color."*). Hue family or anchor reference. No hex values; mark as `[to be resolved during implementation]`. -- **Typography**: the direction the user picked (e.g. "Serif display + sans body"). No font names yet: `[font pairing to be chosen at implementation]`. -- **Elevation**: inferred from motion energy. Restrained/Responsive → flat by default; Choreographed → layered. One sentence. +- **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. +- **Elevation & Depth**: the selected material and depth behavior, stated as an invariant rather than inferred from a generic preset. +- **Shapes**: the selected form and corner language. - **Components**: omit entirely; no components exist yet. -- **Do's and Don'ts**: carry PRODUCT.md's anti-references directly plus the anti-reference named in Q5. +- **Do's and Don'ts**: record the durable guardrails confirmed during the world choice, not task-local refusals. 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 +### Step 3: 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." @@ -405,15 +392,15 @@ Your own write is the freshest source; no reload needed. ## Style guidelines - **Frontmatter first, prose second.** Tokens go in the YAML frontmatter; prose contextualizes them. Don't redefine a token value in two places; the frontmatter is normative. -- **Cite PRODUCT.md anti-references by name** in the Do's and Don'ts section. If PRODUCT.md lists "SaaS landing-page clichés" or "generic AI tool marketing" as anti-references, the DESIGN.md Don'ts should repeat those phrases verbatim so the visual spec enforces the strategic line. -- **Match the spec, don't invent new sections.** The six section names are fixed. If you have Layout/Motion/Responsive content to document, fold it into Overview (philosophy-level rules) or Components (per-component behavior). +- **Carry only durable product constraints.** A binding logo, identity asset, accessibility need, or brand commitment from PRODUCT.md may constrain DESIGN.md. Surface strategy stays in its surface brief. +- **Match the spec.** Use its eight canonical sections in order and omit any that are irrelevant. Put motion guidance with the world or component it affects rather than creating a token group the schema does not support. - **Descriptive > technical**: "Gently curved edges (8px radius)" > "rounded-lg". Include the technical value in parens, lead with the description. - **Functional > decorative**: for each token, explain WHERE and WHY it's used, not just WHAT it is. - **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 forceful**. The voice of a design director. "Prohibited", "forbidden", "never", "always", not "consider", "might", "prefer". Match PRODUCT.md's tone. -- **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. -- **Reference PRODUCT.md**. The anti-references section of PRODUCT.md should directly inform the Do's and Don'ts section here. Quote or paraphrase. +- **Be decisive where evidence is decisive.** Use hard language for actual invariants and softer language for provisional guidance. +- **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. ## Pitfalls @@ -423,7 +410,7 @@ Your own write is the freshest source; no reload needed. - Don't invent components that don't exist. If the project only has buttons and cards, only document those. - Don't overwrite an existing DESIGN.md without asking. - Don't duplicate content from PRODUCT.md. DESIGN.md is strictly visual. -- Don't add a "Layout Principles" or "Motion" or "Responsive Behavior" top-level section. The spec has six, not nine. Fold that content where it belongs. +- Don't replace canonical sections with near-synonyms. Put layout and responsive behavior in `Layout`; put motion with the affected world or component. - Don't rename sections even slightly. "Colors" not "Color Palette & Roles". "Typography" not "Typography Rules". Tooling parsing depends on exact headers. - Don't duplicate token values between frontmatter and prose. If a color is in `colors.primary` as hex, the prose can name it and describe its role but should not reassert a different hex. The frontmatter is normative. - Don't invent frontmatter token groups outside Stitch's schema (no `motion:`, `breakpoints:`, `shadows:` at the top level). Stitch's Zod schema only accepts `colors`, `typography`, `rounded`, `spacing`, `components`. Anything else belongs in the sidecar's `extensions`. diff --git a/.claude/skills/impeccable/reference/hooks.md b/.claude/skills/impeccable/reference/hooks.md index ed277ad6e..8eaae2852 100644 --- a/.claude/skills/impeccable/reference/hooks.md +++ b/.claude/skills/impeccable/reference/hooks.md @@ -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. diff --git a/.claude/skills/impeccable/reference/init.md b/.claude/skills/impeccable/reference/init.md index e2b1df04e..2e6736137 100644 --- a/.claude/skills/impeccable/reference/init.md +++ b/.claude/skills/impeccable/reference/init.md @@ -1,224 +1,114 @@ -# Init Flow +# Init flow -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 [DESIGN.md format spec](https://raw.githubusercontent.com/google-labs-code/design.md/main/docs/spec.md). Answers "how it looks". -- **`.impeccable/live/config.json`** (live mode): pre-configured so `/impeccable live` boots straight into variant mode with no first-time detour. - -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. +`init` captures durable product truth in PRODUCT.md. It does not invent a visual world and does not write DESIGN.md; [new-work.md](new-work.md) creates or expands one, and [document.md](document.md) records an incumbent one. Existing runnable web projects may also receive `.impeccable/live/config.json`. ## Step 1: Load current state -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). +Use the PRODUCT.md path resolved by context.mjs. Update it instead of creating a competing authority. In a child app inheriting root context, confirm shared versus app-specific scope before writing. -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. -- **PRODUCT.md exists, DESIGN.md missing**: skip to Step 5 and offer to run `/impeccable document` for DESIGN.md. -- **PRODUCT.md exists but has no `## Register` section (legacy)**: add it. Infer a hypothesis from the codebase (see Step 2), confirm with the user, write the field. -- **PRODUCT.md exists but has no `## Platform` section (legacy)**: add it the same way, but only when the project is native (`ios` / `android` / `adaptive`) or the user wants it explicit; a missing field already means `web`. -- **Both exist**: STOP and call the AskUserQuestion tool to clarify. Ask which file to refresh. Skip the one the user doesn't want changed. -- **Just DESIGN.md exists (unusual)**: do Steps 2-4 to produce PRODUCT.md. +- **No PRODUCT.md:** explore, interview, and write it. +- **PRODUCT.md exists:** ask what product knowledge is stale or missing; do not reopen confirmed fields without a reason. +- **Legacy PRODUCT.md:** add only durable missing facts; absent `## Platform` means `web` unless evidence says otherwise. +- **Only DESIGN.md exists:** leave it untouched and create PRODUCT.md. +- **Redesign/rebrand request:** preserve confirmed product truth unless the user changes it. Visual replacement happens later in new-work, not here. -Never silently overwrite an existing file. Always confirm first. +Never silently overwrite an existing file or offer DESIGN.md during init. If another request invoked init, finish PRODUCT.md and resume it. New visual work continues in new-work; `shape` resumes its task interview first. -If init was invoked as a setup blocker by another command, such as `/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 project -## Step 2: Explore the codebase +Before asking, scan enough to avoid making the user repeat known facts: product docs and copy; package/config and app boundaries; features, workflows, routes, and roles; names, logos, legal/proof assets, and brand commitments; platform/accessibility signals; and the dev command/entry when live mode applies. -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: +Treat repository evidence as a hypothesis, not user approval. Note visual maturity without documenting, extending, or replacing the world. -- **README and docs**: Project purpose, target audience, any stated goals -- **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 -- **Any style guides or brand documentation** +Form a platform hypothesis: `web`, `ios`, `android`, or `adaptive` (one product that genuinely adapts its design language per OS). Mobile web remains `web`; a native wrapper around a website does not make its design language native. -Also form a **register hypothesis** from what you find: +## Step 3: Interview for product truth -- Brand signals: `/`, `/about`, `/pricing`, `/blog/*`, `/docs/*`, hero sections, big typography, scroll-driven sections, landing-page-shaped content. -- Product signals: `/app/*`, `/dashboard`, `/settings`, `/(auth)`, forms, data tables, side/top nav, app-shell components. +STOP and call the AskUserQuestion tool to clarify. Ask only about material gaps the repository and original request do not answer with strong evidence. -Register is a hypothesis at this point, not a decision; Step 3 confirms it. +Use the structured question tool when available; otherwise ask and wait. Keep rounds to at most three focused questions and require one real answer or approval round before writing a new PRODUCT.md. Confirm inferences. -Also form a **platform hypothesis**: +Start with the unknowns that most change future product decisions: -- Native signals: React Native / Expo (`react-native`, `expo`), Flutter (`pubspec.yaml`, `flutter`), SwiftUI / UIKit (`.swift`, `.xcodeproj`, an `ios/` app target), Jetpack Compose / Android (`build.gradle`, an `android/` app module, `AndroidManifest.xml`). An `ios/` and/or `android/` directory that is a real app target, not just a Capacitor/Cordova wrapper around a website. -- Web signals (the default): a web framework (Vite, Next, Nuxt, SvelteKit, Astro), an HTML entry, a CSS/Tailwind setup, no native app target. +1. Who is the primary user, in what situation, and what job are they doing? +2. What does the product make possible, and what is its meaningfully different mechanism or position? +3. What durable constraints, assets, evidence, or product facts must future work preserve? -Values: `web` / `ios` / `android` / `adaptive` (one codebase, ships both, adapts per OS). Mobile web is still `web`. Like register, this is a hypothesis; Step 3 confirms it. +Confirm ambiguous platform separately. Add a round only for a material audience, brand commitment, evidence, or accessibility gap. Record undecided facts instead of inventing them. -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. +Do not ask for an aesthetic direction, emotional feel, visual references, colors, typography, or style during init. If the user volunteers a binding visual constraint, record it without expanding it. -## Step 3: Ask strategic questions (for PRODUCT.md) +### What belongs here -STOP and call the AskUserQuestion tool to clarify. Ask about anything the codebase doesn't answer with strong, explicit evidence. +- users, jobs, workflows, purpose, success, positioning, and operating context; +- capabilities, constraints, terminology, evidence, platform, and accessibility; +- confirmed voice, assets, and brand commitments. -### Interview mode, not confirmation mode +### What does not belong here -If the repo is empty or the user's brief is sparse, run a short interview before proposing PRODUCT.md. Do **not** turn a one-sentence request into a complete inferred PRODUCT.md and ask for blanket confirmation. - -- Use the harness's structured question tool when one exists. Otherwise, ask directly in chat and stop: one question at a time, with lettered options where the crawl suggests likely answers, waiting for each answer before the next. -- Keep skill vocabulary (register, belief ladder, anti-references) out of question text; ask for the thing in words the user would use. For the brand register, ask like a magazine editor profiling the brand: curious and narrative, drawing out the story, the feel, and what a visitor should come to believe. -- Ask in focused rounds and wait for answers between them. Keep **one topic per question**; add rounds rather than fold several topics into one either-or choice. Options obey the same rule: an option answers only the question asked; never write a compound option that bundles a feeling with a business outcome or names an additional audience. -- Use inferred answers as hypotheses or options, not as finished facts. -- Complete at least one real user-answer round before drafting PRODUCT.md, unless every required answer is directly discoverable from repo docs. -- Round 1 should establish register, platform, users, purpose, positioning, and desired outcome. -- Round 2 should establish brand personality or references, anti-references, and accessibility needs, plus conversion & proof for Persuade surfaces. - -### Minimum viable interview - -Ask enough to complete PRODUCT.md. At minimum, cover register confirmation, **platform confirmation** (`web` / `ios` / `android` / `adaptive`), users, purpose, positioning, brand personality, anti-references, and accessibility needs (plus conversion & proof for Persuade surfaces) unless each answer is directly discoverable from repo context. Never let the template's default `web` stand unconfirmed for a native or cross-platform repo. After at least one interview round, you may propose inferred answers, but the user must confirm them before you write PRODUCT.md. Never synthesize PRODUCT.md from the original task prompt alone. - -### Register (ask first; it shapes everything below) - -Every design task serves one of four visitor modes (see SKILL.md's Registers): **Persuade** (marketing, landing, campaigns), **Experience** (portfolios, albums, bodies of work), **Operate** (app UI, admin, dashboards, tools), and **Read** (documentation, editorial, long-form). PRODUCT.md stores the two-value family that covers them: `brand` covers Persuade and Experience, `product` covers Operate and Read. The brief decides the exact mode per task; the stored register only sets the default family. - -If Step 2 produced a clear hypothesis, lead with it: *"From the codebase, this looks like a [brand / product] surface. Does that match your intent, or should we treat it differently?"* - -If the signal is genuinely split (e.g. a product with a big marketing landing), STOP and call the AskUserQuestion tool to clarify. Ask which register describes the **primary** surface. The register can be overridden per task later, but PRODUCT.md carries one default. Settle the default before drafting any register-dependent questions; never batch Persuade-only questions (Conversion & proof) into the same round as the question that decides the register. - -### Platform (ask right after register) - -Every project targets **web** (includes responsive mobile web), **ios**, **android**, or **adaptive** (one codebase, ships both, adapts per OS: Flutter, React Native, KMP). Platform picks the native rulebook: HIG for `ios`, Material 3 for `android`, both for `adaptive`, none for `web`. - -If Step 2 produced a clear hypothesis, lead with it: *"From the codebase, this looks like a [web / ios / android / adaptive] project. Does that match?"* For cross-platform apps, decide by the **design language the app renders**, not the toolchain: one look on both platforms (Flutter's Material-everywhere default) takes that platform's value; genuine per-OS adaptation (Cupertino on iOS, Material on Android) is `adaptive`. When in doubt, `web`. - -A monorepo shipping both a website and a native app gets a PRODUCT.md per app, each with its own `## Platform`; the root PRODUCT.md carries the primary surface's platform. - -### Users & Purpose -- Who uses this? What's their context when using it? -- What job are they trying to get done? -- What is this for? A purpose stated in README or docs is a hypothesis, not strong evidence; confirm it, don't transcribe it. -- What does success look like? -- If more than one kind of user is plausible, confirm a primary and secondary audience; don't manufacture a split that isn't there. An audience implied by another answer (a success metric, a CTA) is still unconfirmed; ask before writing it as secondary. -- If the surface speaks to a different audience than the people who use the product, ask the user to name both. -- For brand: what emotions should the interface evoke? (confidence, delight, calm, urgency) Ask this standalone; don't fold emotions into the success question. -- For product: what workflow are they in? What's the primary task on any given screen? - -### Positioning -- In one line, what does this do that nothing else does? The single strategic claim every screen reinforces. - -### 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? - - 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? - -### Conversion & proof (Persuade surfaces only) - -Ask these only when the primary surface is a Persuade one (marketing, landing, campaigns). Experience and Read surfaces (portfolios, albums, long-form writing, documentation) get no CTA, belief-ladder, or proof questions even when the stored register is `brand`; skip this section and its PRODUCT.md counterpart. - -- What's the primary CTA? -- What's the secondary fallback, for visitors not ready for the primary? -- The one line a visitor should remember after 10 seconds. -- What must the visitor believe, in order, before taking the primary CTA? (The template's belief ladder.) -- What proof is on hand? Ask the user to hand over any testimonials, case studies, press, or client/partner logos they already have. If you can receive files directly, collect them; otherwise create `.impeccable/assets/proof/` and ask the user to add files there. Reference supplied files by path; record text proof inline. - -### Accessibility & Inclusion -- Specific accessibility requirements? (WCAG level, known user needs) -- Considerations for reduced motion, color blindness, or other accommodations? - -Skip questions where the answer is already clear. **Do NOT ask about colors, fonts, radii, or visual styling here.** Those belong in DESIGN.md, not PRODUCT.md. +- visual worlds, palettes, typography, components, or page concepts; +- visitor mode, narrative, CTA/proof sequence, or other surface strategy; +- invented testimonials, customers, benchmarks, pricing, licensing, or deployment claims; +- a requirement to decide every optional field. ## Step 4: Write PRODUCT.md -Write PRODUCT.md only after the user has confirmed the strategic answers from Step 3. If an inferred answer is uncertain or unconfirmed, ask before writing. Confirmed means what the user actually said yes to; do not pad a confirmed answer with extras they never picked (additional anti-references, audiences, roadmap claims, a WCAG level), whether drawn from the crawl, another answer, or your own option text. If an extra belongs in the doc, ask about it first. - -Synthesize into a strategic document: +Write only confirmed facts and explicitly marked open decisions. Omit irrelevant sections rather than filling them with generic prose. ```markdown # Product -## Register - -product - ## Platform web ## Users -[Who they are, their context, the job to be done. Primary audience; a secondary audience or a surface-vs-user split only when they apply.] +[Primary users, their situation, and job. Add other audiences only when confirmed.] ## Product Purpose -[What this product does, why it exists, what success looks like] +[What the product does, why it exists, and what success means.] ## Positioning -[The single strategic claim every screen reinforces. Not a visual rule, not an anti-reference.] +[The product mechanism or claim a neighboring product could not truthfully copy.] -## Conversion & proof -[Persuade surfaces only (marketing, landing, campaigns). Omit this section entirely, heading included, for the product register and for Experience or Read surfaces such as portfolios, albums, or long-form writing.] -- Primary and secondary CTA: [...] -- The line a visitor remembers after 10 seconds: [...] -- Belief ladder: [...] -- Proof on hand: [testimonials, case studies, press, or logos, referenced by path] +## Operating Context +[Workflows, environments, tools, documents, materials, and rituals that are factual parts of using or evaluating the product.] -## Brand Personality -[Voice, tone, 3-word personality, emotional goals] +## Capabilities and Constraints +[Confirmed functionality, technical constraints, terminology, and explicitly undecided product facts.] -## Anti-references -[What this should NOT look like. Specific bad-example sites or patterns to avoid.] +## Brand Commitments +[Existing name, voice, assets, personality, identity constraints, and references the user explicitly made binding. Omit when none exist.] -## Design Principles -[3-5 strategic principles derived from the conversation. Principles like "practice what you preach", "show, don't tell", "expert confidence". NOT visual rules like "use OKLCH" or "magenta accent".] +## Evidence on Hand +[Real content, data, demonstrations, testimonials, case studies, press, or assets, with paths where applicable. State absences that future work must not fabricate.] + +## Product Principles +[Three to five durable strategic principles derived from confirmed answers; no visual recipes.] ## Accessibility & Inclusion -[WCAG level, known user needs, considerations] +[Known user needs or required standard. Omit when no product-specific requirement was established.] ``` -Register is either `brand` or `product` as a bare value. No prose, no commentary. Platform is `web`, `ios`, `android`, or `adaptive`, also a bare value; omit the section only on legacy files you're leaving untouched, otherwise write `web` explicitly. +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. -Write fields as prose, and use bold sparingly: only where a word carries a decision, never as a label lead-in on every line. +### Completion gate -Write to `PROJECT_ROOT/PRODUCT.md`. If `.impeccable.md` existed, the loader already renamed it; merge into that content rather than starting from scratch. +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: Decide on DESIGN.md +## Step 5: Configure live mode when useful -Offer `/impeccable document` either way. Two paths: +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. -- **Code exists** (CSS tokens, components, a running site): "I can generate a DESIGN.md that captures your visual system (colors, typography, components) so variants stay on-brand. Want to do that now?" -- **Pre-implementation** (empty project): "I can seed a starter DESIGN.md from five quick questions about color strategy, type direction, motion energy, and references. You can re-run once there's code, to capture the real tokens. Want to do that now?" +## Step 6: Wrap up or resume -If the user agrees, delegate to `/impeccable document` (it auto-detects scan vs seed). Load its reference and follow that flow. +Summarize captured and deliberately undecided facts. Do not offer DESIGN.md merely because it is missing. -If the user prefers to skip, mention they can run `/impeccable document` any time later. +Recommend the next action from the actual project state: -## Step 6: Configure live mode (when code exists) +- Empty or early project: ask naturally for the surface to be built, or use `/impeccable shape ` when the user wants a confirmed brief without implementation. New-work will establish a visual world only when the requested work needs one. +- Existing coherent interface without DESIGN.md: `/impeccable document` if the user wants the incumbent system recorded independently of a new build. +- Existing surface needing work: name the most relevant scoped command. +- Web project ready for visual iteration: `/impeccable live` when configured. -**Skip this step when the platform is native** (`ios` / `android` / `adaptive`): live mode drives a browser overlay. A hybrid wrapper or Expo web target serving HTML doesn't change that. - -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 .claude/skills/impeccable/scripts/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) and platform captured (web / ios / android / adaptive) -- 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 or live config is pending, one line on how to set it up later - -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 platform**, offer the 2-4 most relevant (not a menu dump), and give the exact command to type. Group by intent: - -- **Build something new**: `/impeccable craft ` (shape, then build end-to-end) or `/impeccable shape ` (plan first). Lead with this for empty or early-stage projects. -- **Improve what's there**: name the specific surface. `/impeccable critique ` for a scored UX review; `/impeccable audit ` for a11y / perf / responsive checks; `/impeccable polish ` 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** (web only): `/impeccable live` (configured in Step 6) to pick elements in the browser and generate variants in place. **Skip this group for native platforms.** - -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 STOP and call the AskUserQuestion tool to clarify. Ask whether they'd like a brief summary of PRODUCT.md appended to CLAUDE.md for easier agent reference. If yes, append a short **Design Context** pointer section there. +If init was invoked by another request, resume without rerunning context.mjs; new-work owns later visual decisions. diff --git a/.claude/skills/impeccable/reference/ios.md b/.claude/skills/impeccable/reference/ios.md index ef10ee0d2..ccef5d2c4 100644 --- a/.claude/skills/impeccable/reference/ios.md +++ b/.claude/skills/impeccable/reference/ios.md @@ -2,7 +2,7 @@ For native iOS / iPadOS apps: SwiftUI, UIKit, React Native, Expo, Flutter shipping to Apple hardware. -On native, register narrows. HIG conformance governs structure, navigation, and interaction whatever the register; brand expresses through the expressive layer the platform provides (tint, type, motion, content). Calm, Duolingo, and Spotify carry strong identity entirely inside HIG conventions. +On native, the visitor mode narrows what expression may override. HIG conformance governs structure, navigation, and interaction in every mode; brand expresses through the layer the platform leaves open (tint, type, motion, content). ## The iOS slop test diff --git a/.claude/skills/impeccable/reference/layout.md b/.claude/skills/impeccable/reference/layout.md index 23e9f4b74..c8404091d 100644 --- a/.claude/skills/impeccable/reference/layout.md +++ b/.claude/skills/impeccable/reference/layout.md @@ -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. --- -## Register +## 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, register, 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 .claude/skills/impeccable/scripts/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 `/impeccable polish` for the final pass. +When the structure holds, hand off to `/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) * )`: 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. diff --git a/.claude/skills/impeccable/reference/live.md b/.claude/skills/impeccable/reference/live.md index 8c2cccd0a..d47a556c0 100644 --- a/.claude/skills/impeccable/reference/live.md +++ b/.claude/skills/impeccable/reference/live.md @@ -33,7 +33,7 @@ Chat is overhead. No recap, no tutorial output, no pasting PRODUCT / DESIGN bodi node .claude/skills/impeccable/scripts/live.mjs ``` -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. +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, DESIGN.md, and any surface brief already loaded by Setup in mind for variant generation: **DESIGN.md wins on visual decisions; PRODUCT.md wins on durable product and voice decisions; the surface brief wins on this surface's strategy.** 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 requires the user's explicit redesign/replacement intent. `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). @@ -187,7 +187,7 @@ All three carry `fallback: "agent-driven"`. Follow **Handle fallback** below. ### 3. Load the action's reference -If `event.action` is `impeccable` (the default freeform action), use SKILL.md's shared laws plus the loaded register reference (`brand.md` or `product.md`). Do not load a sub-command reference. **Freeform is not a pass to skip parameters:** you still follow the composition budget and the freeform bias in **§7 Parameters** below. Sub-command files list MUST-have signature knobs; freeform has no such file, so sizing knobs from surface weight and primary axes is entirely on you. +If `event.action` is `impeccable` (the default freeform action), use SKILL.md's shared laws and decide the visitor mode from the selected surface. Do not load a sub-command reference. **Freeform is not a pass to skip parameters:** you still follow the composition budget and the freeform bias in **§7 Parameters** below. Sub-command files list MUST-have signature knobs; freeform has no such file, so sizing knobs from surface weight and primary axes is entirely on you. Any other `event.action` (`bolder`, `quieter`, `distill`, `polish`, `typeset`, `colorize`, `layout`, `adapt`, `animate`, `delight`, `overdrive`): Read `reference/.md` before planning. Each sub-command encodes a specific discipline; skipping its reference produces generic output. Those files may require specific params; layer them on top of the §7 budget, not instead of it. @@ -216,7 +216,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. @@ -224,10 +224,7 @@ This sentence is the **identity lock**. Every variant must be readable as the sa **Default mode**: the existing identity is preserved. Variants vary expression axes within it. *This is the right mode for ~90% of live sessions.* The user picked an element on a real product they're shipping; they expect variants of *their* hero, not three different brands' heroes. -**Departure mode**: the existing identity is rejected. Variants propose alternatives consistent with PRODUCT.md voice. Trigger only when at least one is true: - -- PRODUCT.md anti-references explicitly call out the current surface ("the current `index.html` is itself an example"; "diffuse away from this"; "the page on screen is the failure"). Generic anti-references that describe what to avoid in general do **not** trigger departure mode; only ones that point at *this* surface specifically. -- The user's freeform prompt explicitly asks for departure ("rebuild this from scratch", "what if it weren't editorial at all", "show me something completely different"). +**Departure mode**: the existing identity is rejected. Variants propose alternatives consistent with durable product and brand truth. Trigger only when the user explicitly asks for departure in the current request or freeform prompt ("redesign this", "rebuild this from scratch", "what if it weren't editorial at all", "show me something completely different"). A stale page critique or an old task note is not replacement authorization. If you're unsure, you're in default mode. The cost of being wrong about default is "three on-brand variants with similar feel": recoverable, the user picks none. The cost of being wrong about departure is "three off-brand variants": unrecoverable, the user is annoyed. @@ -246,13 +243,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 the brand's stated voice and register in PRODUCT.md. Do NOT pick from a fixed catalog of lane categories. The right three directions for this brand are not the same as the right three for another brand, 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. @@ -262,7 +259,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. @@ -289,7 +286,7 @@ In **default mode**, the prompt narrows the axes you choose, not the identity. * In **departure mode**, the prompt narrows the lanes you draw from, not the families. *"Make it feel like a newspaper front page"* would itself be a departure-mode prompt; honor it but pick three meaningfully different newspaper-adjacent lanes (broadsheet vs. tabloid vs. trade journal), and run the family pass to confirm they don't collapse into one. -When the prompt and PRODUCT.md anti-references conflict (the prompt asks for X, the anti-references ban X), the anti-references win; they describe the brand's standing position, the prompt is one moment. +When the prompt conflicts with a confirmed binding brand commitment or DESIGN.md invariant, preserve the invariant unless the user explicitly revokes or replaces it. Task-local strategy from the matching surface brief may change when the user changes that surface's goal. ### 6. Write all variants in a single edit diff --git a/.claude/skills/impeccable/reference/new-work.md b/.claude/skills/impeccable/reference/new-work.md index 00c4dfdc5..3cf5547bf 100644 --- a/.claude/skills/impeccable/reference/new-work.md +++ b/.claude/skills/impeccable/reference/new-work.md @@ -1,47 +1,178 @@ -# New identity work +# New visual work -Read this only when the project has no committed visual identity, or the user explicitly asked to replace the current one. A missing DESIGN.md is not enough: code, tokens, chosen type, components, and assets are incumbent design authority. If they exist, preserve and extend them unless the brief says to discard them. +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. -This playbook is task-scoped. PRODUCT.md owns durable product facts and DESIGN.md owns durable visual invariants; the direction for one page or feature belongs in the current task, not in either document. +## 1. Name the intent -## Choose the authority +- **Greenfield:** no coherent visual implementation. +- **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. -Classify the work before inventing anything: +A plain “redesign this page/site” authorizes replacement. “Redesign this within the current brand/system” means extension or refinement. Ask once only when the wording is genuinely ambiguous. -- **Extend**: an incumbent system exists and the task adds or improves a surface. Preserve its lineage, semantics, and affordances. -- **Surface redesign**: the current identity remains, but this surface may change its topology, hierarchy, and expression. -- **New identity**: nothing committed exists, or the user explicitly authorized a rebrand. +## 2. Resolve visual authority -When the evidence is mixed, preserve. Novelty is not permission to erase authored decisions. +Read DESIGN.md and representative code, tokens, components, and assets. Choose one path: -## Find the concept +### A. Explicit redesign -Name the subject, audience, page job, and visitor mode from SKILL.md. Then identify the product's unique mechanism: what it does, proves, or enables that a neighboring product cannot truthfully claim. +The old DESIGN.md and implementation are not authority. Keep only unrevoked product facts, content, function, native expectations, constraints, and brand commitments. Establish a replacement world. -Derive the concept from that mechanism through one primary transformation: information topology, evidence, temporal behavior, or spatial mapping. The transformation must change how the surface is organized or behaves, not merely how it is decorated. This produces stranger and more defensible work than imitating a culturally specific document or object. Borrowing a literal cultural form is allowed only when it is genuinely native to the product and audience, and never as costume. +### B. DESIGN.md covers this kind of surface -For **Operate** and **Read**, start with the task and information structure. Native controls remain native controls; expression comes from hierarchy, density, rhythm, state, and the system around them. For **Persuade** and **Experience** creating a new identity, explore more freely, but keep the product legible and the primary action obvious. +Use its invariants and normative tokens. Skip world-building and discover the surface. -Run `node .claude/skills/impeccable/scripts/concept-seed.mjs` only for Persuade or Experience new-identity work when two or more directions remain equally credible. It is an entropy source, not an authority: never let the draw overrule the strongest fit. When color is genuinely unconstrained, `node .claude/skills/impeccable/scripts/palette.mjs` may break a reflex palette; subject evidence and incumbent color still win. +### C. A coherent implementation exists but DESIGN.md does not -## State the direction +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. -Before code, write a task-scoped direction contract of at most 120 words in your reasoning: +### D. The brand exists, but a whole-surface family is unresolved -- the thesis this surface owns; -- the structural or behavioral transformation; -- what authority is preserved; -- the first viewport's composition and primary action; -- the signature element the surface will be remembered by. +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. -Do not embed this contract as a comment in the production artifact or turn it into a second design system. In an attended substantial build, confirm it once. In an unattended run, record it and continue. +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. -## Build once, commit fully +### E. No confirmed visual authority exists -Plan one compact token system and one layout source: the concept or the content's real structure. Build the strongest coherent direction once. Commitment means the transformation governs the whole surface; it does not mean every control must be rebuilt as a metaphor. +Establish a world. Scaffolds, framework defaults, and stray utilities are not identity. -Make the first viewport demonstrate the product's mechanism. Pace long surfaces through contrast in density and scale, and cut sections that only repeat claims. Briefs that depend on imagery ship real, verified imagery. Preserve semantic HTML, familiar interaction behavior, accessibility, and the project's technical conventions. +## 3. Discover the requested surface -## Finish proportionally +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. -Inspect desktop and mobile, write one honest critique against the brief and direction, patch material defects, then run the detector once. Repeat only when a real defect remains. A separate reviewer is optional when the harness already provides one and the extra cost is earned by the risk; it is not a default step and its presence is not evidence of quality. +Ask one attended round of at most three material questions without repeating durable facts. CTA hierarchy, proof sequence, content gaps, and interaction outcomes belong here, not PRODUCT.md. For a fully specified narrow request, state the interpretation and invite correction. + +When `shape` has already completed its discovery interview, reuse those confirmed answers and do not ask this round again. + +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. Each candidate joins a durable visual system (identity thesis, information/layout grammar, material and type behavior, color strategy, imagery, motion, reusable signature) to a concrete expression for this surface (topology, sequence, focal moment, and implementation consequence). Do not rank yet. +3. **Break the ranking rut once.** Run `node .claude/skills/impeccable/scripts/concept-seed.mjs --scope direction`. Promote the assigned grounded pair and weigh its challengers only when they can become both a coherent system and a strong solution to this task. +4. **Test coupling and breadth.** Reject a pair if its surface could swap into another candidate unchanged, or if its world is merely the surface motif repeated. Test each survivor on the requested surface plus navigation, quiet and dense content, interaction/state, and one unlike future surface. Compare it with both the category habit and predictable contrarian response. +5. **Offer coupled choices.** Present two or three materially different pairs without recommendation cues. For each, show the world rules, this surface's 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. 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 .claude/skills/impeccable/scripts/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 promoted candidate when a roll ran; otherwise use the strongest grounded structure. It must survive both tests. + +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). + +### Write or update DESIGN.md + +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: + +`` + +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 .claude/skills/impeccable/scripts/surface-brief.mjs read ` + +Exit 0: preserve still-valid decisions and change only what the user changed. For redesign, retain valid product strategy, content, function, and open decisions; replace visual direction and contract. Exit 2: no brief. After the contract, write with `node .claude/skills/impeccable/scripts/surface-brief.mjs write [related-target ...]`. + +The body is concise and contains: + +```markdown +# Surface brief: [name] + +## Scope +[Primary/related route or artifact, visitor mode, and what this surface owns.] + +## Product strategy +[Surface-specific audience and job, desired outcome, primary/secondary action or task, content and proof sequence, factual constraints, and explicitly open decisions.] + +## Selected direction +[Reference to the applicable DESIGN.md world or expression range, selected surface concept, focal moment, narrative/interaction sequence, and implementation consequence.] + +## Direction contract +[The six contract blocks below.] + +## Open decisions +[Only unresolved items that later work must not silently invent. Omit when empty.] +``` + +Commit `.impeccable/surfaces/.md` as stable later-work context. Exclude global truth, exact tokens, transient notes, and work logs. + +## 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. + +Before code, write a direction contract of at most 150 words in an opening HTML or framework comment. The first 200 characters must name `DIRECTION CONTRACT`. + +- `UNIQUE`: the task thesis tied to the product mechanism; +- `NOT-TEMPLATE`: the category-default arrangement this structure refuses; +- `OWN-WORLD`: the confirmed DESIGN.md invariants, tokens, and materials it uses; +- `STORY`: what the visitor understands, believes, and does; +- `FIRST VIEWPORT`: exact composition, hierarchy, action, and where the concept exceeds competent convention; +- `FORM`: chosen structure or behavior, signature, implementation consequence, and seed key. + +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. + +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. + +## 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. + +Build the strongest coherent direction once. Its grammar governs navigation, actions, controls, content, and transitions without disguising affordances. Give the focal form the scale that gives it force; do not trap it inside a standard hero panel. + +**Make the opening a thesis.** Demonstrate the mechanism immediately; leave an idea, interaction, or evidence, not merely mood. + +**Commit before correcting.** Land the hard move at full strength before refining it. In unattended work, safety is the known risk. + +**Commit at page scale.** Let color, material, image, or type own a region when the world calls for it. Scattered signature decoration is not commitment. + +**Pace the whole surface.** Vary density, scale, image, motion, and quiet inside one grammar. Cut repeated claims; prove the mechanism with real artifact, interaction, data, transformation, or content. + +**Author motion as material.** Motion expresses the world and task. Premium moments go beyond repeated transform/opacity through earned focus, depth, masks, continuity, light, or material change. Bound expensive effects, test in-browser, keep content visible by default, and design reduced motion. + +Preserve semantics, affordances, accessibility, performance, responsiveness, and project conventions. Operate/Read express through topology, hierarchy, density, rhythm, and state; Persuade/Experience may earn drama. + +## 8. Solidify the visual record + +After first implementation of a new/replacement world or approved expansion, refresh DESIGN.md from the build: + +- replace provisional direction with the exact type, color roles, tokens, spacing/radii, components, states, and motion that survived; +- add normative YAML tokens only for values the implementation actually uses; +- remove the directional-seed status once the record and implementation agree; +- preserve broader world invariants and expression ranges; +- do not promote the task's story, hero composition, or one-off motif into a global rule unless it is intentionally reusable. + +Ordinary extension does not rewrite DESIGN.md; only approved durable changes do. + +## 9. Finish like a 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. diff --git a/.claude/skills/impeccable/reference/operate.md b/.claude/skills/impeccable/reference/operate.md index 04ee9d7d2..f420cbaa2 100644 --- a/.claude/skills/impeccable/reference/operate.md +++ b/.claude/skills/impeccable/reference/operate.md @@ -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. - No orchestrated page-load sequences. Product loads into a task; users don't want to watch it load. -## Product bans (on top of the shared absolute bans) +## Product constraints - Decorative motion that doesn't convey state. - Inconsistent component vocabulary across screens. If the "save" button looks different in two places, one is wrong. @@ -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. diff --git a/.claude/skills/impeccable/reference/polish.md b/.claude/skills/impeccable/reference/polish.md index 22c18c157..5d38db9be 100644 --- a/.claude/skills/impeccable/reference/polish.md +++ b/.claude/skills/impeccable/reference/polish.md @@ -1,241 +1,98 @@ -> **Additional context needed**: quality bar (MVP vs flagship). +> **Additional context needed**: quality bar and shipping constraints. -Perform a meticulous final pass to catch all the small details that separate good work from great work. The difference between shipped and polished. +Polish is refinement, never concealed redesign. Preserve the incumbent visual world, content, behavior, and everything outside scope. If the concept itself is wrong, say so and recommend redesign or `bolder` instead of smuggling in a replacement. -Detector and automated QA output are defect evidence only. A clean script result is never proof that the design is strong; gather browser evidence and inspect the real interaction path. +A detector result is defect evidence, not proof of quality. Inspect the rendered experience and real interaction path. -## Design System Discovery +## 1. Establish the system -Aligning the feature to the design system is **not optional**. Polish without alignment is decoration on top of drift, and it makes the next person's job harder. Discovery comes before any other polish work. +Read DESIGN.md and representative tokens, shared components, patterns, and neighboring flows. If no formal system exists, use coherent project conventions. -1. **Find the design system**: Search for design system documentation, component libraries, style guides, or token definitions. Study the core patterns: design principles, target audience, color tokens, spacing scale, typography styles, component API, motion conventions. -2. **Note the conventions**: How are shared components imported? What spacing scale is used? Which colors come from tokens vs hard-coded values? What motion and interaction patterns are established? What flow shapes are used for comparable actions (modal vs full-page, inline vs route, save-on-blur vs explicit submit)? -3. **Identify drift, then name the root cause**: For every deviation, classify it as a **missing token** (the value should exist in the system but doesn't), a **one-off implementation** (a shared component already exists but wasn't used), or a **conceptual misalignment** (the feature's flow, IA, or hierarchy doesn't match neighboring features). The fix differs by category: patch the value, swap to the shared component, or rework the flow. Fixing the symptom without naming the cause is how drift compounds. +Classify each drift before fixing it: -If a design system exists, polish **must** align the feature with it. If none exists, polish against the conventions visible in the codebase. **If anything about the system is ambiguous, ask. Never guess at design system principles.** +- **missing token:** the system needs a reusable value; +- **one-off implementation:** an existing shared component or pattern should replace it; +- **conceptual mismatch:** the flow, information architecture, or hierarchy differs from comparable product areas; +- **local defect:** the implementation is simply incomplete or inconsistent. -## Pre-Polish Assessment +Fix the cause at the narrowest correct level. Ask when a binding system principle cannot be inferred. -Understand the current state and goals before touching anything: +## 2. Gather the evidence -1. **Review completeness**: - - Is it functionally complete? - - Are there known issues to preserve (mark with TODOs)? - - What's the quality bar? (MVP vs flagship feature?) - - When does it ship? (How much time for polish?) +Use the feature yourself at representative desktop and mobile sizes. Determine: -2. **Think experience-first**: Who actually uses this, and what's the best possible experience for them? Effective design beats decorative polish; a feature that looks beautiful but fights the user's flow is not polished. Walk the path from their perspective before opening DevTools. +- whether the path is functionally complete; +- the intended quality bar and time available; +- known constraints or deliberately unfinished work; +- the states, content lengths, roles, and input methods users will actually encounter. -3. **Identify polish areas**: - - Visual inconsistencies - - Spacing and alignment issues - - Interaction state gaps - - Copy inconsistencies - - Edge cases and error states - - Loading and transition smoothness - - Information architecture and flow drift (does this feature reveal complexity the way neighboring features do?) +If a prior critique exists, use it as one input: -4. **Pull in any prior critique** (optional signal): If `/impeccable critique` has been run on the same target, its priority issues are a useful prior for what to address first. Resolve the target to a file path or URL, then: - ```bash - slug=$(node .claude/skills/impeccable/scripts/critique-storage.mjs slug "") - node .claude/skills/impeccable/scripts/critique-storage.mjs latest "$slug" - ``` - Exit 0 with body = found; fold the P0/P1 items into your polish list and mention the snapshot path so the user sees what you read. Exit 2 = no snapshot, continue without it. The critique is one input among many. Do your own pass either way. +```bash +slug=$(node .claude/skills/impeccable/scripts/critique-storage.mjs slug "") +node .claude/skills/impeccable/scripts/critique-storage.mjs latest "$slug" +``` -5. **Triage cosmetic vs functional**: Classify each issue as **cosmetic** (looks off, doesn't impede the user) or **functional** (breaks, blocks, or confuses the experience). When polish time is tight, functional issues ship first; cosmetic ones can land in a follow-up. Quality should be consistent; never perfect one corner while leaving another rough. +Exit 0 returns the latest snapshot; incorporate relevant P0/P1 findings and name the snapshot read. Exit 2 means none exists. Perform an independent pass either way. -**CRITICAL**: Polish is the last step, not the first. Don't polish work that's not functionally complete. +## 3. Triage -## Polish Systematically +Separate functional defects from cosmetic ones and fix in this order: -Work through these dimensions methodically: +1. broken or blocked tasks, data loss, misleading state, and inaccessible paths; +2. missing loading, empty, error, success, disabled, and permission states; +3. flow, hierarchy, responsive, and design-system drift; +4. visual and motion inconsistencies; +5. code and asset cleanup. -### Visual Alignment & Spacing +Do not perfect one corner while leaving the rest below the same quality bar. -- **Pixel-perfect alignment**: Everything lines up to grid -- **Consistent spacing**: All gaps use spacing scale (no random 13px gaps) -- **Optical alignment**: Adjust for visual weight (icons may need offset for optical centering) -- **Responsive consistency**: Spacing and alignment work at all breakpoints -- **Grid adherence**: Elements snap to baseline grid +## 4. Polish the whole path -**Check**: -- Enable grid overlay and verify alignment -- Check spacing with browser inspector -- Test at multiple viewport sizes -- Look for elements that "feel" off +### Flow and hierarchy -### Information Architecture & Flow +- Match neighboring mental models, terminology, disclosure, routing, save behavior, and optimistic or pessimistic patterns. +- Make the primary task and current state obvious without flattening every element to equal weight. +- Ensure arrival, transition, empty, and recovery paths connect instead of behaving as isolated screens. -Visual polish on a misshapen flow is wasted work. Match the *shape* of the experience to the system, not just the surface. +### Layout and type -- **Progressive disclosure**: Match how much is revealed when, compared to neighboring features. A settings page exposing 40 fields when the rest of the app reveals 5 at a time is drift, even if every field is perfectly styled. -- **Established user flows**: Multi-step actions follow the same shape as comparable flows elsewhere: modal vs full-page, inline edit vs separate route, save-on-blur vs explicit submit, optimistic vs pessimistic updates. -- **Hierarchy & complexity**: The same conceptual weight gets the same visual weight throughout. Primary actions don't become tertiary in one corner of the product, and tertiary actions don't shout. -- **Empty, loading, and arrival transitions**: How content arrives, updates, and leaves matches how it does in adjacent features. -- **Naming and mental model**: The feature uses the same nouns and verbs as the rest of the system. A "Workspace" here shouldn't be a "Project" three screens away. +- Align to the project's grid and spacing scale; fix optical as well as mathematical alignment. +- Group related content tightly and separate distinct groups generously. +- Keep same-role typography consistent; test measure, wrapping, localization expansion, zoom, and font loading. +- Verify every supported viewport rather than correcting only the current screenshot. -### Typography Refinement +### Color, imagery, and icons -- **Hierarchy consistency**: Same elements use same sizes/weights throughout -- **Line length**: 45-75 characters for body text -- **Line height**: Appropriate for font size and context -- **Widows & orphans**: No single words on last line -- **Hyphenation**: Appropriate for language and column width -- **Kerning**: Adjust letter spacing where needed (especially headlines) -- **Font loading**: No FOUT/FOIT flashes +- Use semantic tokens and stable color meanings across themes. +- Verify text, control, and focus contrast in every state. +- Keep icon families, stroke/weight, sizing, and optical alignment coherent. +- Prevent image layout shift; use correct aspect ratios, responsive sources, and useful alt text. -### Color & Contrast +### Interaction and state -- **Contrast ratios**: All text meets WCAG standards -- **Consistent token usage**: No hard-coded colors, all use design tokens -- **Theme consistency**: Works in all theme variants -- **Color meaning**: Same colors mean same things throughout -- **Accessible focus**: Focus indicators visible with sufficient contrast -- **Gray on color**: Never put gray text on colored backgrounds; use a shade of that color or transparency +- Every control needs appropriate default, hover, focus, active, disabled, loading, error, and success behavior. +- Preserve visible keyboard focus, logical tab order, labels, and platform-appropriate touch targets. +- Keep motion coherent, interruptible, performant, and reduced-motion aware. Do not add animation merely to make polish visible. +- Validate long, missing, localized, offline, slow, and permission-limited content where the product can encounter it. -### Interaction States +### Content and code -Every interactive element needs all states: +- Keep terminology, capitalization, punctuation, and factual copy consistent. Ask before changing claims. +- Remove debug output, dead code, unused imports, obsolete styles, and polish-created duplication. +- Replace custom implementations with shared components where the system owns the pattern. +- Promote genuinely reusable values to tokens; do not create a system abstraction for one local exception. -- **Default**: Resting state -- **Hover**: Subtle feedback (color, scale, shadow) -- **Focus**: Keyboard focus indicator (never remove without replacement) -- **Active**: Click/tap feedback -- **Disabled**: Clearly non-interactive -- **Loading**: Async action feedback -- **Error**: Validation or error state -- **Success**: Successful completion +## 5. Verify and finish -**Missing states create confusion and broken experiences**. +Walk the complete path again with mouse, keyboard, and touch where applicable. Check: -### Micro-interactions & Transitions +- mobile, intermediate, and wide layouts; +- loading, empty, error, success, disabled, long-content, and missing-content states; +- zoom, contrast, focus, semantics, screen-reader names, and reduced motion; +- console errors, layout shift, interaction latency, image loading, and supported browsers; +- agreement with DESIGN.md, neighboring features, and the user's scope. -- **Smooth transitions**: All state changes animated appropriately (150-300ms) -- **Consistent easing**: Use ease-out-quart/quint/expo for natural deceleration. Never bounce or elastic; they feel dated. -- **No jank**: Smooth animations; use atmospheric blur/filter/mask/shadow effects when they add polish, but bound expensive paint areas and avoid casual layout-property animation -- **Appropriate motion**: Motion serves purpose, not decoration -- **Reduced motion**: Respects `prefers-reduced-motion` +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. -### Content & Copy - -- **Consistent terminology**: Same things called same names throughout -- **Consistent capitalization**: Title Case vs Sentence case applied consistently -- **Grammar & spelling**: No typos -- **Appropriate length**: Not too wordy, not too terse -- **Punctuation consistency**: Periods on sentences, not on labels (unless all labels have them) - -### Icons & Images - -- **Consistent style**: All icons from same family or matching style -- **Appropriate sizing**: Icons sized consistently for context -- **Proper alignment**: Icons align with adjacent text optically -- **Alt text**: All images have descriptive alt text -- **Loading states**: Images don't cause layout shift, proper aspect ratios -- **Retina support**: 2x assets for high-DPI screens - -### Forms & Inputs - -- **Label consistency**: All inputs properly labeled -- **Required indicators**: Clear and consistent -- **Error messages**: Helpful and consistent -- **Tab order**: Logical keyboard navigation -- **Auto-focus**: Appropriate (don't overuse) -- **Validation timing**: Consistent (on blur vs on submit) - -### Edge Cases & Error States - -- **Loading states**: All async actions have loading feedback -- **Empty states**: Helpful empty states, not just blank space -- **Error states**: Clear error messages with recovery paths -- **Success states**: Confirmation of successful actions -- **Long content**: Handles very long names, descriptions, etc. -- **No content**: Handles missing data gracefully -- **Offline**: Appropriate offline handling (if applicable) - -### Responsiveness - -- **All breakpoints**: Test mobile, tablet, desktop -- **Touch targets**: 44x44px minimum on touch devices -- **Readable text**: No text smaller than 14px on mobile -- **No horizontal scroll**: Content fits viewport -- **Appropriate reflow**: Content adapts logically - -### Performance - -- **Fast initial load**: Optimize critical path -- **No layout shift**: Elements don't jump after load (CLS) -- **Smooth interactions**: No lag or jank -- **Optimized images**: Appropriate formats and sizes -- **Lazy loading**: Off-screen content loads lazily - -### Code Quality - -- **Remove console logs**: No debug logging in production -- **Remove commented code**: Clean up dead code -- **Remove unused imports**: Clean up unused dependencies -- **Consistent naming**: Variables and functions follow conventions -- **Type safety**: No TypeScript `any` or ignored errors -- **Accessibility**: Proper ARIA labels and semantic HTML - -## Polish Checklist - -Go through systematically: - -- [ ] Aligned to the design system (drift named and resolved by root cause) -- [ ] Information architecture and flow shape match neighboring features -- [ ] Visual alignment perfect at all breakpoints -- [ ] Spacing uses design tokens consistently -- [ ] Typography hierarchy consistent -- [ ] All interactive states implemented -- [ ] All transitions smooth (60fps) -- [ ] Copy is consistent and polished -- [ ] Icons are consistent and properly sized -- [ ] All forms properly labeled and validated -- [ ] Error states are helpful -- [ ] Loading states are clear -- [ ] Empty states are welcoming -- [ ] Touch targets are 44x44px minimum -- [ ] Contrast ratios meet WCAG AA -- [ ] Keyboard navigation works -- [ ] Focus indicators visible -- [ ] No console errors or warnings -- [ ] No layout shift on load -- [ ] Works in all supported browsers -- [ ] Respects reduced motion preference -- [ ] Code is clean (no TODOs, console.logs, commented code) - -**IMPORTANT**: Polish is about details. Zoom in. Squint at it. Use it yourself. The little things add up. - -Sweat the details. Zoom in until the alignment is right and the spacing reads as deliberate. Then ship. - -**NEVER**: -- Polish before it's functionally complete -- Polish without aligning to the design system; that's decoration on drift -- Guess at design system principles instead of asking when something is ambiguous -- Spend hours on polish if it ships in 30 minutes (triage) -- Introduce bugs while polishing (test thoroughly) -- Ignore systematic issues (if spacing is off everywhere, fix the system, not just one screen) -- Perfect one thing while leaving others rough (consistent quality level) -- Create new one-off components when design system equivalents exist -- Hard-code values that should use design tokens -- Introduce new patterns or flows that diverge from established ones - -## Final Verification - -Before marking as done: - -- **Use it yourself**: Actually interact with the feature. -- **Test on real devices**: Not just browser DevTools. -- **Ask someone else to review**: Fresh eyes catch things. -- **Compare to design**: Match intended design. -- **Check all states**: Don't just test happy path. -- **Treat automation carefully**: Run detector or QA commands when they are available and relevant, fix their defects, but never cite a clean result as proof that the work is polished. - -## Clean Up - -After polishing, ensure code quality: - -- **Replace custom implementations**: If the design system provides a component you reimplemented, switch to the shared version. -- **Remove orphaned code**: Delete unused styles, components, or files made obsolete by polish. -- **Consolidate tokens**: If you introduced new values, check whether they should be tokens. -- **Verify DRYness**: Look for duplication introduced during polishing and consolidate. +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. diff --git a/.claude/skills/impeccable/reference/quieter.md b/.claude/skills/impeccable/reference/quieter.md index ea5b54925..c20b38fb3 100644 --- a/.claude/skills/impeccable/reference/quieter.md +++ b/.claude/skills/impeccable/reference/quieter.md @@ -2,7 +2,7 @@ Quiet design is harder than bold design. Subtlety needs precision. Reduce visual --- -## Register +## Visitor mode Persuade + Experience: "quieter" means more restrained palette, more whitespace, more typographic air. Drama is reduced, not eliminated; the POV stays intact. diff --git a/.claude/skills/impeccable/reference/routing.md b/.claude/skills/impeccable/reference/routing.md index 0b107da93..da49eb993 100644 --- a/.claude/skills/impeccable/reference/routing.md +++ b/.claude/skills/impeccable/reference/routing.md @@ -11,7 +11,7 @@ Reason over the signals; there is no score to obey: - `critique.latest` with a low `score` or non-zero `p0` / `p1` → `polish` (it reads that snapshot as its backlog), or re-run `critique` if the snapshot looks stale. - `git.changedFiles` pointing at one surface → scope `audit` or `polish` to those files specifically, naming them. - `devServer.running` true → `live` is available for in-browser iteration; if false, don't lead with `live`. **`live` and the bundled `detect.mjs` are web-only.** If `setup.platform` is `ios`, `android`, or `adaptive`, don't lead with either; the browser overlay and the HTML rule engine don't apply to native app code. -- Otherwise group by intent exactly as init's "Recommend starting points" step does (build new / improve what's there / iterate visually), tailored to `setup.register`. +- Otherwise group by intent (build new / improve what's there / iterate visually), tailored to the current surface and `setup.platform`. **If `scan.targets` is non-empty and `setup.platform` is not `ios`/`android`/`adaptive`, run `node .claude/skills/impeccable/scripts/detect.mjs --json ` once** (the bundled detector over local files: no network, no npx; it reads HTML/CSS, so skip it for native projects). `scan.via` tells you what they are: `git-changes` (the markup/style files in your dirty tree, the most relevant set), `source-dir` (e.g. `src`, `app`), `html`, or `root`. Fold the hits into your picks: many quality / contrast hits → `audit` or `polish`; a specific slop family → the matching command (gradient text or eyebrows → `quieter` / `typeset`, flat or gray palette → `colorize`, and so on). It's a real, current signal that beats guessing. If detect errors or the tree is large and slow, skip it and recommend the user run `audit` themselves; never block the suggestion on it. diff --git a/.claude/skills/impeccable/reference/shape.md b/.claude/skills/impeccable/reference/shape.md index 23e286df5..acbb26e5b 100644 --- a/.claude/skills/impeccable/reference/shape.md +++ b/.claude/skills/impeccable/reference/shape.md @@ -1,167 +1,61 @@ -Shape the UX and UI for a feature before any code is written. This command produces a **design brief**: a structured artifact that guides implementation through discovery, not guesswork. +# Shape -**Scope**: Design planning only. This command does NOT write code. It produces the thinking that makes code good. +Discover what should be made and how it should work, then return a confirmed design brief without code. -**Output**: A design brief that can be handed off to /impeccable craft, or directly to /impeccable for freeform implementation. When visual direction probes are used, the images are supporting artifacts, not the primary output. +**Product gate:** when context reports that PRODUCT.md is missing, load and complete [init.md](init.md) before Phase 1. Do not substitute shape questions for the product interview. Once PRODUCT.md exists, return here; product context does not replace task discovery. -## Philosophy +## Phase 1: Discovery interview -Most AI-generated UIs fail not because of bad code, but because of skipped thinking. They jump to "here's a card grid" without asking "what is the user trying to accomplish?" This command inverts that: understand deeply first, so implementation is precise. +Do not write code or choose visual direction yet. -## Phase 1: Discovery Interview +### Cadence -**Do NOT write any code or make any design decisions during this phase.** Your only job is to understand the feature deeply enough to make excellent design decisions later. +- Use the structured question tool when available; otherwise ask and stop. +- Ask two or three related questions per round, then wait. One round is the default; add a second only when the answers expose a material gap. +- Do not dump a questionnaire, repeat settled facts, or turn obvious facts into menus. Assert the likely reading and invite correction. +- A sparse prompt requires at least one answer round. A precise prompt may need only a compact confirmation. -**Unattended runs:** when no user can respond (a one-shot task, an automated run, or an explicit instruction not to ask questions), answer the interview questions yourself from the brief, record the answers as the design brief, and continue without pausing at any confirmation gate in this file. The thinking is still required; only the waiting is waived. +### Round 1: purpose, people, and outcome -This is a required interaction, not optional guidance. Ask these questions in conversation, adapting based on answers. Don't dump them all at once; have a natural dialogue. STOP and call the AskUserQuestion tool to clarify. +Choose the two or three questions that most change the result: -### Interview cadence +- What is this surface or feature for, and what problem must it solve? +- Who specifically reaches it, in what situation and state of mind? +- What is the primary thing they must understand or do? What would success look like? +- What is uniquely true here that a neighboring product or generic template could not claim? -Discovery includes at least one user-answer round unless PRODUCT.md, DESIGN.md, or an already-confirmed brief directly answers the needed inputs. With a sparse prompt, do **not** synthesize a complete brief for confirmation on the first response. +### Round 2: material, behavior, and boundaries -- Use the harness's structured question tool when one exists. Otherwise, ask directly in chat and stop. -- Ask **2-3 questions per round**, then wait for answers. -- Treat PRODUCT.md and DESIGN.md as anchors; they reduce repeated questions but do **not** replace shape for craft. Shape is task-specific. -- One round is the default. Add a second only if the first answers leave material gaps. Don't run a second round just to feel thorough. -- Round 1 should clarify purpose, audience/context, content/scope, and (for brand) visual direction. -- Round 2, when needed, fills in whatever's still genuinely missing. +Run only for material unresolved decisions: -**Assert-then-confirm, not menu-with-escape.** When PRODUCT.md and the user's prompt make one option obvious, name it and ask the user to confirm or override. Don't enumerate "Restrained / Committed / Or something else?" as a real choice; "This reads as Restrained, confirm?" beats a four-option menu when the answer is already clear. +- What real content, evidence, data, and assets must the experience carry? What are realistic minimum, typical, and maximum ranges? +- Which states and transitions matter: first-run, empty, loading, error, success, permissions, overflow, or expert use? +- What is the intended fidelity, breadth, and interactivity: exploration, production-ready screen, full flow, or broader surface? +- What must remain untouched? What would make the result feel wrong even if it looked polished? +- Which platform, framework, performance, accessibility, localization, or delivery constraints are binding? -### Purpose & Context -- What is this feature for? What problem does it solve? -- Who specifically will use it? (Not "users"; be specific: role, context, frequency) -- What does success look like? How will you know this feature is working? -- What's the user's state of mind when they reach this feature? (Rushed? Exploring? Anxious? Focused?) +Never ask for CSS values or canned aesthetic lanes. New-work owns visual-world and concept choices. -### Content & Data -- What content or data does this feature display or collect? -- What are the realistic ranges? (Minimum, typical, maximum, e.g., 0 items, 5 items, 500 items) -- What are the edge cases? (Empty state, error state, first-time use, power user) -- Is any content dynamic? What changes and how often? -- What visual assets are real content here? Note required images, product shots, illustrations, maps, textures, diagrams, generated objects, or existing project assets. +## Phase 2: Resolve the design direction -### Design Direction +For new surfaces, brand expansion, or replacement, follow [new-work.md](new-work.md) through visual authority, any world workshop, and concept choice. Reuse discovery, then return before its contract, persistence, or implementation. Inside an established world, use its concept process only when composition or interaction remains materially open. -Force a visual decision on three fronts. Skip anything PRODUCT.md or DESIGN.md already answers; ask only what's missing. +## Phase 3: Write the brief -- **Color strategy for this surface.** Pick one: Restrained / Committed / Full palette / Drenched. Can override the project default if the surface earns it (e.g. a drenched hero inside an otherwise Restrained product). -- **Theme via scene sentence.** Write one sentence of physical context for this surface: who uses it, where, under what ambient light, in what mood. The sentence forces dark vs light. If it doesn't, add detail until it does. -- **Two or three named anchor references.** Specific products, brands, objects. Not adjectives like "modern" or "clean." +Write the smallest useful brief: -### Scope +1. **Job and audience:** who arrives, their context, need, and visitor mode. +2. **Outcome and proof:** primary task/action, success, real evidence, and product-specific truth. +3. **Selected direction:** visual authority, structural/interaction thesis, sequence, focal moment, and implementation consequence. +4. **Scope and boundaries:** fidelity, breadth, interactivity, named target, what remains untouched, and explicit anti-goals. +5. **States and ranges:** realistic content/data ranges and material states. +6. **Interaction and layout:** hierarchy, topology, responsiveness, affordances, feedback, and transitions; intent, not CSS. +7. **Constraints and open decisions:** platform, delivery, accessibility, localization, reusable components, and choices a builder must not invent. -Always ask. Sketch quality and shipped quality are different outputs; don't guess between them. +Use three to five bullets when the task is settled; use the full structure only for ambiguous, multi-screen, or standalone planning. Do not restate the conversation. -- **Fidelity.** Sketch / mid-fi / high-fi / production-ready? -- **Breadth.** One screen / a flow / a whole surface? -- **Interactivity.** Static visual / interactive prototype / shipped-quality component? -- **Time intent.** Quick exploration, or polish until it ships? +## Confirm and stop -Scope answers are task-scoped. Don't write them to PRODUCT.md or DESIGN.md; carry them through the design brief only. +Present the brief for explicit confirmation or one correction round, then stop: shape never writes code or a direction contract. -### Constraints -- Are there technical constraints? (Framework, performance budget, browser support) -- Are there content constraints? (Localization, dynamic text length, user-generated content) -- Mobile/responsive requirements? -- Accessibility requirements beyond WCAG AA? - -### Anti-Goals -- What should this NOT be? What would be a wrong direction? -- What's the biggest risk of getting this wrong? - -## Phase 1.5: Visual Direction Probe (Capability-Gated) - -After the discovery interview, generate a small set of visual direction probes **before** writing the final brief when all of these are true: - -- The work is **net-new** or directionally ambiguous enough that visual exploration will clarify the brief. -- The requested fidelity is **mid-fi, high-fi, or production-ready**. Skip for sketch-only planning. -- The current harness gives you native image generation (Codex's `image_gen`, an equivalent MCP tool, or similar). Don't ask the user to install APIs or tooling. - -When those conditions are met, this step is mandatory. If image generation isn't natively available, do not ask the user to install APIs or tooling. State in one line that the image step is skipped because the harness lacks native image generation, then proceed. The one-line announcement is required, not optional; it forces a conscious decision instead of letting the step quietly evaporate. - -Use probes to explore visual lanes, not to replace the brief. - -Do not skip probes because the final UI will be semantic, editable, code-native, responsive, or accessible. Those are implementation requirements, not reasons to avoid visual exploration. - -### What to generate - -Generate **2 to 4** distinct direction probes based on the discovery answers, especially: - -- Color strategy -- Theme scene sentence -- Named anchor references -- Scope and fidelity - -The probes should differ in primary visual direction (hierarchy, topology, density, typographic voice, or color strategy), not just palette tweaks. - -### How to use the probes - -- Treat them as **direction tests**, not final designs. -- Use them to pressure-test whether the brief is pointing at the right lane. -- Ask the user which direction feels closest, what feels off, and what should carry forward. -- If the probes reveal a mismatch, revise the brief inputs before finalizing the brief. - -### Important limits - -- Do **not** skip discovery because image generation is available. -- Do **not** treat generated imagery as final UX specification, final copy, or final accessibility behavior. -- Do **not** use this step for minor refinements of existing work. It's for shaping a new surface or clarifying a big directional choice. - -If image generation isn't natively available, announce the skip in one line and proceed to the design brief. - -## Phase 2: Design Brief - -After the interview and any required probes, present a brief and **end your response**. The user must confirm before any implementation runs. Do not present a brief and then continue to code in the same response, even if the brief feels obvious to you. The user's confirmation is the gate. - -**Choose the brief shape based on how clear the answers are:** - -- **Compact form (3-5 bullets)** when discovery was crisp and the original prompt + PRODUCT.md already pinned scope, content, and direction. State what you're building, the visual lane, and end with one or two specific questions or a clear "confirm or override?" prompt. This is the default for typical craft requests with a clear prompt. -- **Full structured form (sections below)** when the task is genuinely ambiguous, multi-screen, or when the user asked for shape as a standalone step. Use this when the discipline of structure earns its weight. - -Don't pad a clear brief into a long one to look thorough. A 70-line brief restating answers the user just gave is noise, not rigor. Equally, don't skip the confirmation pause to look efficient: the pause is the point. - -Present the brief, then **stop and wait for explicit confirmation**. You are not the judge of whether the user already approved. Even when the brief feels obviously right, ask once and wait. The pause is what separates shape from premature implementation. - -### Brief Structure - -**1. Feature Summary** (2-3 sentences) -What this is, who it's for, what it needs to accomplish. - -**2. Primary User Action** -The single most important thing a user should do or understand here. - -**3. Design Direction** -Color strategy (Restrained / Committed / Full palette / Drenched) + the theme scene sentence + 2–3 named anchor references. Reference PRODUCT.md and DESIGN.md where they already answer, and note any per-surface overrides. - -If you ran the Visual Direction Probe step, name which probe direction won and what changed in the brief because of it. - -**4. Scope** -Fidelity, breadth, interactivity, and time intent from the Scope section of the interview. Task-scoped; these don't persist beyond the brief. - -**5. Layout Strategy** -High-level spatial approach: what gets emphasis, what's secondary, how information flows. Describe the visual hierarchy and rhythm, not specific CSS. - -**6. Key States** -List every state the feature needs: default, empty, loading, error, success, edge cases. For each, note what the user needs to see and feel. - -**7. Interaction Model** -How users interact with this feature. What happens on click, hover, scroll? What feedback do they get? What's the flow from entry to completion? - -**8. Content Requirements** -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., 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. - ---- - -STOP and call the AskUserQuestion tool to clarify. Ask for explicit confirmation of the brief before finishing. - -If the user disagrees with any part, revisit the relevant discovery questions. A shape run is incomplete until the user confirms direction. - -Once confirmed, the brief is complete. The user can now hand it to /impeccable, or use it to guide any other implementation approach. (If the user wants the full discovery-then-build flow in one step, they should use /impeccable craft instead, which runs this command internally.) +When no human or structured answer mechanism exists, mark assumptions plainly, return the brief, and stop. diff --git a/.claude/skills/impeccable/reference/typeset.md b/.claude/skills/impeccable/reference/typeset.md index fefa95b0d..7b54de2d5 100644 --- a/.claude/skills/impeccable/reference/typeset.md +++ b/.claude/skills/impeccable/reference/typeset.md @@ -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. --- -## Register +## Visitor mode -New identity work: run the font selection procedure in [new-work.md](new-work.md). Fluid `clamp()` scale, ≥1.25 ratio between steps. +- **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, register, **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 .claude/skills/impeccable/scripts/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 `/impeccable polish` for the final pass. +When the hierarchy holds, hand off to `/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) * )` 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 - -The tactical selection procedure and the reflex-reject list live in [new-work.md](new-work.md) (loaded for new identity work). 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 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. diff --git a/.claude/skills/impeccable/scripts/command-metadata.json b/.claude/skills/impeccable/scripts/command-metadata.json index 83796d54b..dad8ef2e0 100644 --- a/.claude/skills/impeccable/scripts/command-metadata.json +++ b/.claude/skills/impeccable/scripts/command-metadata.json @@ -1,6 +1,6 @@ { "craft": { - "description": "Full confirmed-brief-then-build flow. Runs multi-round shape discovery first, resolves visual probe and north-star mock gates when available, then builds and visually iterates. Use when building a new feature end-to-end.", + "description": "Deprecated compatibility alias for an ordinary Impeccable new-work request. It adds no behavior; natural build and redesign requests use the same flow.", "argumentHint": "[feature description]" }, "init": { diff --git a/.claude/skills/impeccable/scripts/concept-seed.mjs b/.claude/skills/impeccable/scripts/concept-seed.mjs index 1f141a80f..25f20d9ab 100644 --- a/.claude/skills/impeccable/scripts/concept-seed.mjs +++ b/.claude/skills/impeccable/scripts/concept-seed.mjs @@ -1,6 +1,7 @@ #!/usr/bin/env node /** - * Concept-seed picker: the dice half of the new-work concept procedure. + * External concept seed: the dice half of new-work's coupled-direction and + * established-world surface procedures. * * The model derives a grounded shortlist of candidate FORMS from the * audience's world and the subject's cultural home (see @@ -12,10 +13,9 @@ * * This script rolls them from outside, the same trick that made the * palette seed work: - * - BUILD INDEX (2-5): which entry of the model's own resonance-ordered - * shortlist to build. The dice never choose an ungrounded ingredient; - * they only refuse the argmax rut. (Index 1 is excluded: that's the - * concept every run would ship anyway.) + * - PROMOTED INDEX: which entry of the model's own resonance-ordered + * shortlist must be taken seriously beside its favorites. The dice never + * choose an ungrounded ingredient; they only refuse the argmax rut. * - CHALLENGERS (3): outside forms from concept-ingredients.json, weighed * against the derived candidates on exactly two axes — audience * identification and product clarity. They win only when they beat the @@ -23,8 +23,8 @@ * material and win over thin categories, which is the intended shape. * * Usage: - * node scripts/concept-seed.mjs # roll at random - * node scripts/concept-seed.mjs --from # deterministic (hash key) + * node scripts/concept-seed.mjs --scope direction + * node scripts/concept-seed.mjs --scope surface --from * * Env vars: * IMPECCABLE_CONCEPT_SEED — same as --from; for reproducible eval runs. @@ -40,6 +40,12 @@ const pool = JSON.parse(readFileSync(join(here, 'concept-ingredients.json'), 'ut const args = process.argv.slice(2); const fromIdx = args.indexOf('--from'); +const scopeIdx = args.indexOf('--scope'); +const scope = scopeIdx !== -1 ? args[scopeIdx + 1] : 'surface'; +if (scope !== 'surface' && scope !== 'direction') { + process.stderr.write('concept-seed: --scope must be direction or surface\n'); + process.exit(1); +} // When no key is supplied, generate one and print it: a user reporting a // bad outcome can hand us the key and we replay the exact roll. const key = fromIdx !== -1 @@ -47,7 +53,7 @@ const key = fromIdx !== -1 : (process.env.IMPECCABLE_CONCEPT_SEED || crypto.randomBytes(4).toString('hex')); function hashUnit(k, salt) { - const h = crypto.createHash('sha256').update(`${salt}:${k}`).digest(); + const h = crypto.createHash('sha256').update(`${scope}:${salt}:${k}`).digest(); return h.readUInt32BE(0) / 0xffffffff; } const unit = (salt) => hashUnit(key, salt); @@ -67,22 +73,48 @@ for (let i = 0; picks.length < 3 && i < 60; i++) { } } -process.stdout.write(`CONCEPT SEED (key: ${key}; rerun with --from ${key} to reproduce this roll) -BUILD INDEX: ${buildIndex} - After ordering your derived candidates by resonance, build the page whose - form comes from candidate number ${buildIndex}, exactly as if it had ranked - first: full commitment. Your top-ranked candidate is what every run in this - category would ship; the assignment exists to refuse that rut, not to - punish it. -CHALLENGERS (weigh against your derived candidates on the same two axes, -audience identification and product clarity; a challenger wins only when -it beats the grounded list on both): +const promotedInstruction = scope === 'direction' + ? `After ordering the grounded coupled directions by product fit, promote + candidate ${buildIndex} into the serious shortlist. Each candidate must join + a durable visual system to a concrete expression for the requested first + surface; select or revise that pair as one decision. It must survive the + current task plus navigation, quiet and dense content, interaction and state, + and a substantially different future surface.` + : `After ordering the task's grounded structural candidates by resonance, + promote candidate ${buildIndex} into the serious shortlist. In an attended + run, present it beside the strongest materially different candidates and + let the user select or revise the surface concept. In a truly unattended + run, use it when it survives audience identification and product clarity.`; + +const challengerInstruction = scope === 'direction' + ? `A challenger enters only when it can supply both reusable identity grammar + and a strong first-surface structure, not a one-page costume or a style pasted + onto an unrelated layout. Weigh audience identification, product clarity, + current-surface force, and cross-surface system breadth.` + : `A challenger wins only when it beats the grounded list on both audience + identification and product clarity. It may change task topology or + interaction, but never the committed visual identity.`; + +const authorityInstruction = scope === 'direction' + ? `PRODUCT.md and explicit incumbent brand commitments constrain every coupled +direction. The seed never chooses exact colors, fonts, tokens, or a user +preference, and it never permits the world and first surface to be selected +independently.` + : `PRODUCT.md and DESIGN.md constrain every surface candidate's identity +vocabulary; they do not cancel task-level composition. The seed never +authorizes a new palette, type system, material world, or unfamiliar control +behavior.`; + +process.stdout.write(`${scope.toUpperCase()} CONCEPT SEED (key: ${key}; rerun with --scope ${scope} --from ${key} to reproduce this roll) +PROMOTED INDEX: ${buildIndex} + ${promotedInstruction} + The promotion exists to refuse the model's ranking rut, not to outrank the + user or the brief. +CHALLENGERS: 1. ${picks[0]} 2. ${picks[1]} 3. ${picks[2]} -If a challenger wins, it replaces the assigned candidate. If the surface is -an existing world whose incumbent carries a deliberate, ownable idea, the -incumbent IS the chosen candidate: intensify its lineage and ignore the -roll entirely. The same override applies when the user, PRODUCT.md, or -DESIGN.md pins a direction: pinned direction beats the roll, always. +${challengerInstruction} +${authorityInstruction} +A user- or brief-pinned decision beats the roll, always. `); diff --git a/.claude/skills/impeccable/scripts/context-signals.mjs b/.claude/skills/impeccable/scripts/context-signals.mjs index 2fc27bea7..d296320d7 100644 --- a/.claude/skills/impeccable/scripts/context-signals.mjs +++ b/.claude/skills/impeccable/scripts/context-signals.mjs @@ -11,7 +11,7 @@ * output is always valid JSON. * * Signals: - * - setup: PRODUCT.md / DESIGN.md presence, register, whether code exists + * - setup: PRODUCT.md / DESIGN.md presence and whether code exists * - critique: the latest cached critique score (.impeccable/critique) * - git: branch + files changed vs the default branch (a scope hint) * - devServer: whether a local dev server answers on a common port (gates live) @@ -21,7 +21,7 @@ import net from 'node:net'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { execFileSync } from 'node:child_process'; -import { loadContext, extractRegister, extractPlatform } from './context.mjs'; +import { loadContext, extractPlatform } from './context.mjs'; import { getCritiqueDir } from './lib/impeccable-paths.mjs'; /** Is there code here at all, or just context files / an empty repo? */ @@ -196,7 +196,6 @@ export async function gatherSignals(cwd = process.cwd()) { hasDesign: ctx.hasDesign, designPath: ctx.designPath, hasCode: hasCode(cwd), - register: extractRegister(ctx.product), platform: extractPlatform(ctx.product), }, critique: { latest: latestCritique(cwd) }, diff --git a/.claude/skills/impeccable/scripts/context.mjs b/.claude/skills/impeccable/scripts/context.mjs index 2ef2d4ce8..080e7ee3a 100644 --- a/.claude/skills/impeccable/scripts/context.mjs +++ b/.claude/skills/impeccable/scripts/context.mjs @@ -1,8 +1,10 @@ /** - * Context loader: prints PRODUCT.md (and DESIGN.md if present) as one - * markdown block on stdout, or prints a `NO_PRODUCT_MD:` message when no + * Context loader: prints PRODUCT.md, DESIGN.md when present, the matching + * persisted surface brief when one can be resolved, and native-platform + * guidance selected from PRODUCT.md. It prints a + * `NO_PRODUCT_MD:` message when no * PRODUCT.md is found anywhere. The skill keys off that message to branch: - * from-scratch build commands (init / teach / craft / shape) and clear + * from-scratch build requests (plus init / teach / shape) and clear * build/shape intent divert into the init flow, while scoped commands proceed * using the existing code as context. * @@ -23,10 +25,13 @@ import os from 'node:os'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { parseTargetOptions } from './lib/target-args.mjs'; -import { IMPECCABLE_COMMAND } from './lib/provider.mjs'; +import { IMPECCABLE_COMMAND, IMPECCABLE_PROVIDER_ID } from './lib/provider.mjs'; +import { renderLlmOnlySlopReview } from './lib/slop-review.mjs'; +import { resolveSurfaceBrief } from './lib/surface-briefs.mjs'; const PRODUCT_NAMES = ['PRODUCT.md', 'Product.md', 'product.md']; const DESIGN_NAMES = ['DESIGN.md', 'Design.md', 'design.md']; +const SKILL_REFERENCE_DIR = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', 'reference'); const FALLBACK_DIRS = ['.agents/context', 'docs']; const MONOREPO_MARKER_FILES = ['pnpm-workspace.yaml', 'turbo.json', 'nx.json', 'lerna.json']; const MONOREPO_FALLBACK_PROJECT_DIRS = ['apps', 'packages']; @@ -41,6 +46,8 @@ const WORKSPACE_DISCOVERY_IGNORED_DIRS = new Set([ '.turbo', '.cache', 'coverage', + 'vendor', + 'vendors', ]); const VISUAL_SOURCE_DIRS = ['src', 'app', 'pages', 'components', 'site', 'public', 'styles']; const STYLE_EXTENSIONS = new Set(['.css', '.scss', '.sass', '.less', '.styl']); @@ -73,6 +80,12 @@ export function loadContext(cwd = process.cwd(), options = {}) { const designPath = resolved.designPath; const product = productPath ? safeRead(productPath) : null; const design = designPath ? safeRead(designPath) : null; + const platform = extractPlatform(product); + const surfaceResolution = resolveSurfaceBrief( + resolved.projectRoot, + hasTargetOption(options) ? options.targetPath : null, + ); + const surfaceBrief = surfaceResolution.brief; return { hasProduct: !!product, product, @@ -83,7 +96,18 @@ export function loadContext(cwd = process.cwd(), options = {}) { contextDir: resolved.contextDir, productContextDir: productPath ? path.dirname(productPath) : null, designContextDir: designPath ? path.dirname(designPath) : null, + hasSurfaceBrief: !!surfaceBrief, + surfaceBrief: surfaceBrief?.text ?? null, + surfaceBriefPath: surfaceBrief?.path ? path.relative(absCwd, surfaceBrief.path) : null, + surfaceBriefReason: surfaceResolution.reason, + surfaceBriefCandidates: surfaceResolution.candidates.map((brief) => ({ + slug: brief.slug, + path: path.relative(absCwd, brief.path), + primaryTarget: brief.primaryTarget, + relatedTargets: brief.relatedTargets, + })), hasVisualImplementation: hasVisualImplementation(resolved.projectRoot), + platform, projectRoot: resolved.projectRoot, repoRoot: resolved.repoRoot, isMonorepo: resolved.isMonorepo, @@ -696,6 +720,19 @@ function safeRead(p) { } } +function loadNativePlatformReferences(platform) { + const names = platform === 'adaptive' + ? ['ios', 'android'] + : platform === 'ios' || platform === 'android' + ? [platform] + : []; + return names.flatMap((name) => { + const filePath = path.join(SKILL_REFERENCE_DIR, `${name}.md`); + const content = safeRead(filePath); + return content ? [{ name, filePath, content }] : []; + }); +} + /** * Best-effort evidence that the project already has an incumbent visual * implementation. DESIGN.md is documentation, not the only source of design @@ -719,9 +756,11 @@ export function hasVisualImplementation(projectRoot) { let styledComponents = 0; const inspectFile = (filePath) => { - if (scannedFiles++ >= VISUAL_SCAN_FILE_LIMIT) return false; const ext = path.extname(filePath).toLowerCase(); if (!STYLE_EXTENSIONS.has(ext) && !UI_EXTENSIONS.has(ext)) return false; + const base = path.basename(filePath).toLowerCase(); + if (/\.min\.[a-z]+$/.test(base)) return false; + if (scannedFiles++ >= VISUAL_SCAN_FILE_LIMIT) return false; let body; try { body = fs.readFileSync(filePath, 'utf-8').slice(0, 64 * 1024); @@ -729,18 +768,28 @@ export function hasVisualImplementation(projectRoot) { return false; } - const base = path.basename(filePath).toLowerCase(); + const evidence = body + .replace(/\/\*[\s\S]*?\*\//g, '') + .replace(//g, '') + .replace(/^\s*\/\/.*$/gm, ''); if (STYLE_EXTENSIONS.has(ext)) { - const customProperties = body.match(/--[a-z0-9_-]+\s*:/gi)?.length ?? 0; - const visualDeclarations = body.match(/\b(?:color|background(?:-color)?|border(?:-color)?|font-family)\s*:/gi)?.length ?? 0; - if (/\b(?:tokens?|theme|design-system)\b/.test(base) && body.trim().length > 80) return true; + const customProperties = evidence.match(/--[a-z0-9_-]+\s*:/gi)?.length ?? 0; + const visualDeclarations = evidence.match(/\b(?:color|background(?:-color)?|border(?:-color)?|font-family)\s*:/gi)?.length ?? 0; + if (/\b(?:tokens?|theme|design-system)\b/.test(base) && evidence.trim().length > 80) return true; if (customProperties >= 3 || visualDeclarations >= 5) return true; } - if ((ext === '.html' || ext === '.htm') && body.length > 600 && /]+stylesheet/i.test(body)) { + if ((ext === '.html' || ext === '.htm') && evidence.length > 600 && /]+stylesheet/i.test(evidence)) { return true; } - if (!['.html', '.htm'].includes(ext) && body.length > 300 && /class(?:Name)?\s*=|style\s*=|styled\(|css`/i.test(body)) { + if (!['.html', '.htm'].includes(ext) && evidence.length > 300) { + const embeddedCustomProperties = evidence.match(/--[a-z0-9_-]+\s*:/gi)?.length ?? 0; + const embeddedVisualDeclarations = evidence.match(/\b(?:color|background(?:-color)?|border(?:-color)?|font-family)\s*:/gi)?.length ?? 0; + const classTokens = [...evidence.matchAll(/class(?:Name)?\s*=\s*["'`]([^"'`]+)["'`]/gi)] + .reduce((count, match) => count + match[1].trim().split(/\s+/).length, 0); + if ((embeddedCustomProperties >= 3 && embeddedVisualDeclarations >= 3) || embeddedVisualDeclarations >= 5 || classTokens >= 12) return true; + } + if (!['.html', '.htm'].includes(ext) && evidence.length > 300 && /class(?:Name)?\s*=|style\s*=|styled\(|css`/i.test(evidence)) { styledComponents += 1; if (styledComponents >= 3) return true; } @@ -781,9 +830,9 @@ function escapeRegExp(value) { /** * Read the first non-empty line under a bare `## ` section of - * PRODUCT.md (e.g. `## Register`, `## Platform`). Returns null when the + * PRODUCT.md (for example `## Platform`). Returns null when the * section is absent. The heading match is exact (`\s*$`) so near-miss - * headings like `## Register guidelines` don't shadow the real field. + * near-miss headings don't shadow the real field. */ export function extractSectionValue(product, heading) { if (!product) return null; @@ -802,16 +851,6 @@ export function extractSectionValue(product, heading) { return null; } -/** - * Pull the register (`brand` or `product`) out of PRODUCT.md by looking - * for a `## Register` section and reading the first non-empty line that - * follows it. Returns null when the file is legacy / register-less. - */ -export function extractRegister(product) { - const word = (extractSectionValue(product, 'Register') || '').toLowerCase(); - return word === 'brand' || word === 'product' ? word : null; -} - /** * Pull the platform (`web`, `ios`, `android`, or `adaptive`) out of PRODUCT.md * by looking for a `## Platform` section and reading the first non-empty line @@ -985,30 +1024,35 @@ async function cli() { const parts = ctx.hasVisualImplementation ? [ 'NO_PRODUCT_MD: This project has no PRODUCT.md yet, but it does have an incumbent visual implementation. ' + - 'For `init` or `teach`, load reference/init.md and create PRODUCT.md. For `craft`, `shape`, or other work ' + - 'against the existing product, read its CSS, tokens, components, and assets first and proceed without blocking; ' + - `offer \`${IMPECCABLE_COMMAND} init\` as a follow-up. Only treat the work as greenfield when the user explicitly ` + - 'asks to discard or replace the current identity.', - 'EXISTING_VISUAL_SYSTEM: Code and assets are the incumbent design authority. Missing DESIGN.md is a documentation ' + - 'gap, not permission to invent a replacement identity. Preserve and extend the implementation unless the user ' + - 'explicitly requests a rebrand, or inspection proves the detected files contain no coherent visual decisions.', + 'For `init`, `teach`, `shape`, or any request to create a new surface or replacement visual world, load reference/init.md and create PRODUCT.md with the user first. ' + + 'After init writes PRODUCT.md, reference/new-work.md preserves and documents the incumbent system for an ' + + 'extension or replaces it with the user for a redesign/rebrand. Other ' + + 'narrow refinement commands may read the CSS, tokens, components, and assets and proceed without blocking, then ' + + `offer \`${IMPECCABLE_COMMAND} init\` as a follow-up.`, + 'BUILD_INIT_REQUIRED: Before shape or any new-surface/redesign flow, init must capture PRODUCT.md with the human or structured ' + + 'simulated user. Init writes product truth only; reference/new-work.md owns every visual decision.', + 'SCOPED_EXISTING_ALLOWED: Narrow refinement commands may use the incumbent implementation as authority without ' + + 'blocking on context setup; they must preserve it and offer init afterward.', + 'EXISTING_VISUAL_SYSTEM: For refinement or extension, code and assets are incumbent design authority and missing ' + + 'DESIGN.md is a documentation gap. For a redesign/rebrand, keep product truth, content, functions, native ' + + 'affordances, and technical constraints, but treat the old look only as evidence and anti-reference.', ] : [ 'NO_PRODUCT_MD: This project has no PRODUCT.md yet. ' + - 'For `init`, `teach`, `craft`, `shape`, ' + + 'For `init`, `teach`, `shape`, ' + 'or wording that clearly maps to a from-scratch build/shape flow, load ' + - 'reference/init.md and write PRODUCT.md first, unless no user can ' + - 'respond (a one-shot or automated run, or the user said not to ask): ' + - 'then write a one-paragraph understanding of the product, audience, ' + - 'and the page\'s job from the brief, and continue. For any other ' + + 'reference/init.md, complete its human or structured simulated-user interview, and write PRODUCT.md before ' + + 'designing. If no answer mechanism truly exists, init may infer only from the explicit brief and must label its ' + + 'assumptions. It never writes DESIGN.md. For any other ' + '(scoped) command against existing code, proceed using the code as ' + `context and offer \`${IMPECCABLE_COMMAND} init\` as a suggestion (do not block).`, - 'NEW_WORK: No committed design context was found. If this task produces ' + - 'new design (a build from scratch, or a redesign that discards the ' + - 'current look), you MUST read reference/new-work.md before making any ' + - 'design decision. Scoped fixes to existing code do not need it.', + 'PRODUCT_INIT_REQUIRED: No product context or visual authority was found. New builds and redesigns ' + + 'must finish reference/init.md for PRODUCT.md, then reference/new-work.md establishes the world and surface. Scoped ' + + 'fixes to existing code do not need the new-surface flow.', ]; + appendSurfaceBriefContext(parts, ctx); parts.push(buildResolvedContextDirective(ctx, cliOptions, { targetExists })); + appendHookFallback(parts, ctx); if (shouldWarnMissingTarget(ctx, targetProvided, targetExists)) { parts.push(buildMissingTargetDirective()); } @@ -1020,37 +1064,29 @@ async function cli() { if (ctx.hasDesign) { parts.push(`# DESIGN.md\n\n${ctx.design.trim()}`); } + appendSurfaceBriefContext(parts, ctx); parts.push(buildResolvedContextDirective(ctx, cliOptions, { targetExists })); + appendHookFallback(parts, ctx); if (shouldWarnMissingTarget(ctx, targetProvided, targetExists)) { parts.push(buildMissingTargetDirective()); } - const register = extractRegister(ctx.product); - // The register field survives as a family hint (brand = Persuade/Experience, - // product = Operate/Read); SKILL.md's mode section carries the essentials - // inline, so no register-file read is mandated here. What IS mandated: - // the new-work playbook when no committed design system exists yet. if (!ctx.hasDesign) { parts.push(ctx.hasVisualImplementation - ? 'EXISTING_VISUAL_SYSTEM: PRODUCT.md exists and DESIGN.md is missing, but code contains incumbent visual decisions. ' + - 'Treat CSS, tokens, components, and assets as design authority. Do not route to new identity work merely because the ' + - 'document is absent; preserve and extend the implementation unless the user explicitly asks to discard it.' - : 'NEW_WORK: PRODUCT.md exists but no DESIGN.md or incumbent visual implementation was found. If this task produces ' + - 'new design or a redesign that discards the current look, you MUST read reference/new-work.md before making any ' + - 'design decision. Scoped fixes to existing code do not need it.'); + ? 'INCUMBENT_WORLD_UNDOCUMENTED: PRODUCT.md exists and DESIGN.md is missing, but code contains incumbent visual decisions. ' + + 'For shape or a new-surface/redesign request, load reference/new-work.md: an extension documents and preserves the code-defined world; ' + + 'a redesign replaces it with the user and uses the old look only as evidence and anti-reference. Narrow refinement ' + + 'commands may proceed using the implementation directly.' + : 'WORLD_DISCOVERY_REQUIRED: PRODUCT.md exists but no DESIGN.md or incumbent visual implementation was found. ' + + 'For a new build or redesign, load reference/new-work.md and establish the visual world with the human or structured ' + + 'simulated user before developing the task concept. Scoped fixes to existing code do not need this flow.'); } - if (register) { - parts.push(`REGISTER: \`${register}\` (family hint: brand = Persuade/Experience surfaces, product = Operate/Read). Derive the visitor's mode per SKILL.md.`); - } - const platform = extractPlatform(ctx.product); - const nativeRefs = - platform === 'adaptive' ? ['ios', 'android'] : platform === 'ios' || platform === 'android' ? [platform] : []; - if (nativeRefs.length) { - const refList = nativeRefs.map(p => `\`reference/${p}.md\``).join(' and '); - const label = platform === 'adaptive' ? '`adaptive` (both iOS and Android)' : `\`${platform}\``; + const platformReferences = loadNativePlatformReferences(ctx.platform); + for (const reference of platformReferences) { parts.push( - `NEXT STEP: This project targets ${label}. Also read ${refList} for native conventions, in addition to SKILL.md's mode guidance.`, + `# NATIVE PLATFORM REFERENCE: ${reference.name.toUpperCase()} (reference/${reference.name}.md)\n\n${reference.content.trim()}`, ); - } else if (!platform) { + } + if (!ctx.platform) { // A `## Platform` section that names something we don't recognize (a // toolchain like `flutter`, a typo) would otherwise silently fall back to // web — the wrong default exactly when the user tried to say "native". @@ -1078,6 +1114,72 @@ function pathExistsForTarget(cwd, targetPath) { return fs.existsSync(abs); } +const HOOK_MANIFESTS_BY_PROVIDER = Object.freeze({ + 'claude-code': ['.claude/settings.local.json', '.claude/settings.json'], + codex: ['.codex/hooks.json'], + agents: ['.codex/hooks.json'], + cursor: ['.cursor/hooks.json'], + github: ['.github/hooks/impeccable.json'], +}); + +function truthyEnv(value) { + return typeof value === 'string' && /^(1|true|yes|on)$/i.test(value.trim()); +} + +function valueHasHookMarker(value) { + if (typeof value === 'string') { + return value.includes('skills/impeccable/scripts/hook.mjs') + || value.includes('skills/impeccable/scripts/hook-before-edit.mjs'); + } + if (Array.isArray(value)) return value.some(valueHasHookMarker); + if (value && typeof value === 'object') return Object.values(value).some(valueHasHookMarker); + return false; +} + +function hookEnabledAt(root) { + if (truthyEnv(process.env.IMPECCABLE_HOOK_DISABLED)) return false; + let enabled = true; + for (const name of ['.impeccable/config.json', '.impeccable/config.local.json']) { + const raw = readJson(path.join(root, name)); + if (raw?.hook && Object.prototype.hasOwnProperty.call(raw.hook, 'enabled')) { + enabled = raw.hook.enabled !== false; + } + } + return enabled; +} + +const STOP_REVIEW_PROVIDERS = new Set(['claude-code', 'codex', 'agents']); + +function automaticHookMode(ctx) { + if (ctx.platform === 'ios' || ctx.platform === 'android' || ctx.platform === 'adaptive') { + return 'none'; + } + const activeRoot = path.resolve(ctx.projectRoot || process.cwd()); + if (!hookEnabledAt(activeRoot)) return 'none'; + const manifests = HOOK_MANIFESTS_BY_PROVIDER[IMPECCABLE_PROVIDER_ID] || []; + const roots = [...new Set([process.cwd(), ctx.projectRoot, ctx.repoRoot].filter(Boolean).map((root) => path.resolve(root)))]; + for (const root of roots) { + for (const rel of manifests) { + const raw = readJson(path.join(root, rel)); + if (raw?.hooks && valueHasHookMarker(raw.hooks)) { + return STOP_REVIEW_PROVIDERS.has(IMPECCABLE_PROVIDER_ID) ? 'stop' : 'per-edit'; + } + } + } + return 'none'; +} + +function appendHookFallback(parts, ctx) { + const hookMode = automaticHookMode(ctx); + if (hookMode === 'stop') return; + const native = ctx.platform === 'ios' || ctx.platform === 'android' || ctx.platform === 'adaptive'; + parts.push(renderLlmOnlySlopReview({ + automaticDetector: hookMode === 'per-edit', + manualDetector: hookMode === 'none' && !native, + scriptsPath: path.dirname(fileURLToPath(import.meta.url)), + })); +} + function buildResolvedContextDirective(ctx, options, { targetExists = null } = {}) { const targetPath = hasTargetOption(options) ? options.targetPath : null; return `RESOLVED_CONTEXT:\n${JSON.stringify({ @@ -1087,10 +1189,29 @@ function buildResolvedContextDirective(ctx, options, { targetExists = null } = { repoRoot: ctx.repoRoot, productPath: ctx.productPath, designPath: ctx.designPath, + surfaceBriefPath: ctx.surfaceBriefPath, + surfaceBriefReason: ctx.surfaceBriefReason, + surfaceBriefCandidates: ctx.surfaceBriefCandidates, hasVisualImplementation: ctx.hasVisualImplementation, + platform: ctx.platform, }, null, 2)}`; } +function appendSurfaceBriefContext(parts, ctx) { + if (ctx.hasSurfaceBrief && ctx.surfaceBrief) { + parts.push(`# SURFACE BRIEF (${ctx.surfaceBriefPath})\n\n${ctx.surfaceBrief.trim()}`); + return; + } + if (!ctx.surfaceBriefCandidates?.length) return; + const helper = path.join(path.dirname(fileURLToPath(import.meta.url)), 'surface-brief.mjs'); + parts.push( + 'SURFACE_CONTEXT_AVAILABLE: Persisted surface briefs exist, but none was selected unambiguously for this invocation. ' + + 'Resolve the requested surface to its concrete primary or related source path, then run ' + + `\`node ${helper} read \` once before changing that surface. Candidates:\n` + + JSON.stringify(ctx.surfaceBriefCandidates, null, 2), + ); +} + function shouldWarnMissingTarget(ctx, targetProvided, targetExists = null) { if (ctx.isMonorepo && targetProvided && targetExists === false) return true; return !!( diff --git a/.claude/skills/impeccable/scripts/critique-storage.mjs b/.claude/skills/impeccable/scripts/critique-storage.mjs index 6b4d225cf..38674285a 100644 --- a/.claude/skills/impeccable/scripts/critique-storage.mjs +++ b/.claude/skills/impeccable/scripts/critique-storage.mjs @@ -29,8 +29,9 @@ import fs from 'node:fs'; import path from 'node:path'; import { fileURLToPath, pathToFileURL } from 'node:url'; import { getCritiqueDir } from './lib/impeccable-paths.mjs'; +import { slugFromTarget } from './lib/target-slug.mjs'; -const SLUG_MAX = 50; +export { slugFromTarget } from './lib/target-slug.mjs'; /** * Mechanically derive a slug from a resolved target. Returns null if the @@ -40,46 +41,6 @@ const SLUG_MAX = 50; * concrete artifact before calling this — we never slug a natural-language * phrase. */ -export function slugFromTarget(resolved, { cwd = process.cwd() } = {}) { - if (!resolved || typeof resolved !== 'string') return null; - const trimmed = resolved.trim(); - if (!trimmed) return null; - - // URL - if (/^https?:\/\//i.test(trimmed)) { - let url; - try { url = new URL(trimmed); } catch { return null; } - const hostPath = `${url.hostname}${url.pathname}`; - return kebab(hostPath); - } - - // File path. Make it project-relative so two devs critiquing the same - // checkout get the same slug regardless of where their repo is cloned. - const abs = path.isAbsolute(trimmed) ? trimmed : path.resolve(cwd, trimmed); - let rel = path.relative(cwd, abs); - // If the target is outside cwd, fall back to the basename so we still - // produce a stable slug (vs the absolute path, which would include - // home dirs / usernames). - if (rel.startsWith('..') || path.isAbsolute(rel)) { - rel = path.basename(abs); - } - if (!rel || rel === '.' || rel === '') return null; - return kebab(rel); -} - -function kebab(s) { - const slug = s - .toLowerCase() - .replace(/[/\\.]+/g, '-') - .replace(/[^a-z0-9-]+/g, '-') - .replace(/-+/g, '-') - .replace(/^-|-$/g, ''); - if (!slug) return null; - // Cap from the tail — the tail (filename) is more identifying than the - // top-level directory. - return slug.length <= SLUG_MAX ? slug : slug.slice(slug.length - SLUG_MAX).replace(/^-/, ''); -} - /** * Filename-safe UTC ISO timestamp: hyphens for separators, trailing Z. * Plain colons aren't allowed on Windows filesystems. diff --git a/.claude/skills/impeccable/scripts/detector/detect-antipatterns-browser.js b/.claude/skills/impeccable/scripts/detector/detect-antipatterns-browser.js index 033573e36..76bf4ddeb 100644 --- a/.claude/skills/impeccable/scripts/detector/detect-antipatterns-browser.js +++ b/.claude/skills/impeccable/scripts/detector/detect-antipatterns-browser.js @@ -312,17 +312,6 @@ const ANTIPATTERNS = [ skillSection: 'Layout & Space', skillGuideline: 'numbered section markers', }, - { - id: 'numbered-section-markers', - category: 'slop', - scopes: ['layout'], - severity: 'advisory', - name: 'Numbered section markers (01 / 02 / 03)', - description: - 'Numbered display markers as section labels (01, 02, 03) are the AI editorial scaffold one tier deeper than tracked eyebrow chips. If you find yourself reaching for them, choose a different section cadence.', - skillSection: 'Layout & Space', - skillGuideline: 'numbered section markers', - }, { id: 'em-dash-overuse', category: 'slop', diff --git a/.claude/skills/impeccable/scripts/detector/engines/regex/detect-text.mjs b/.claude/skills/impeccable/scripts/detector/engines/regex/detect-text.mjs index 81c30ab99..f641d5917 100644 --- a/.claude/skills/impeccable/scripts/detector/engines/regex/detect-text.mjs +++ b/.claude/skills/impeccable/scripts/detector/engines/regex/detect-text.mjs @@ -280,22 +280,6 @@ const REGEX_ANALYZERS = [ if (count === 0) return []; return [finding('marketing-buzzword', filePath, `${count} buzzword phrase${count === 1 ? '' : 's'}: "${firstSample}"`)]; }, - // Numbered section markers (01 / 02 / 03 ...) - (content, filePath) => { - const text = stripHtmlToText(content); - const re = /\b(0[1-9]|1[0-2])\b/g; - const seen = new Set(); - let m; - while ((m = re.exec(text)) !== null) seen.add(m[1]); - if (seen.size < 3) return []; - const sorted = [...seen].sort(); - let sequential = 0; - for (let i = 1; i < sorted.length; i++) { - if (parseInt(sorted[i], 10) === parseInt(sorted[i - 1], 10) + 1) sequential++; - } - if (sequential < 2) return []; - return [finding('numbered-section-markers', filePath, `Sequence: ${sorted.slice(0, 6).join(', ')}`)]; - }, // Aphoristic cadence: manufactured-contrast + short-rebuttal (content, filePath) => { const text = stripHtmlToText(content); @@ -430,21 +414,20 @@ function runRegexMatchers(lines, filePath, lineOffset = 0, blockContext = null, } /** Page-level analyzers that scan rendered text content (em-dash use, - * buzzword phrases, numbered section markers, aphoristic cadence). + * buzzword phrases, aphoristic cadence). * These are detector-agnostic — they work on any HTML/text source * and don't need a parsed DOM. Exported so detectHtml can call them * for `.html` files (which otherwise skip the regex engine). */ const TEXT_CONTENT_ANALYZER_IDS = [ 'em-dash-overuse', 'marketing-buzzword', - 'numbered-section-markers', 'aphoristic-cadence', ]; function runTextContentAnalyzers(content, filePath, options = {}) { const profile = options?.profile; if (!shouldRunPageAnalyzers(content, filePath)) return []; - // The 4 text-content analyzers are at indices 3-6 in REGEX_ANALYZERS. + // The 3 text-content analyzers are at indices 3-5 in REGEX_ANALYZERS. const findings = []; for (let i = 0; i < TEXT_CONTENT_ANALYZER_IDS.length; i++) { const analyzer = REGEX_ANALYZERS[3 + i]; @@ -535,7 +518,6 @@ function detectText(content, filePath, options = {}) { 'monotonous-spacing', 'em-dash-overuse', 'marketing-buzzword', - 'numbered-section-markers', 'aphoristic-cadence', 'dark-glow', ]; diff --git a/.claude/skills/impeccable/scripts/detector/registry/antipatterns.mjs b/.claude/skills/impeccable/scripts/detector/registry/antipatterns.mjs index b85f7e62f..84c69b0b8 100644 --- a/.claude/skills/impeccable/scripts/detector/registry/antipatterns.mjs +++ b/.claude/skills/impeccable/scripts/detector/registry/antipatterns.mjs @@ -210,17 +210,6 @@ const ANTIPATTERNS = [ skillSection: 'Layout & Space', skillGuideline: 'numbered section markers', }, - { - id: 'numbered-section-markers', - category: 'slop', - scopes: ['layout'], - severity: 'advisory', - name: 'Numbered section markers (01 / 02 / 03)', - description: - 'Numbered display markers as section labels (01, 02, 03) are the AI editorial scaffold one tier deeper than tracked eyebrow chips. If you find yourself reaching for them, choose a different section cadence.', - skillSection: 'Layout & Space', - skillGuideline: 'numbered section markers', - }, { id: 'em-dash-overuse', category: 'slop', diff --git a/.claude/skills/impeccable/scripts/detector/rules/checks.mjs b/.claude/skills/impeccable/scripts/detector/rules/checks.mjs index 3fdd6b2dd..9bace11b3 100644 --- a/.claude/skills/impeccable/scripts/detector/rules/checks.mjs +++ b/.claude/skills/impeccable/scripts/detector/rules/checks.mjs @@ -352,27 +352,57 @@ function isAccentColor(cssColor) { return false; } +function resolveHeroHeadingSizePx(value) { + const input = String(value || '').trim().toLowerCase(); + if (!input) return 0; + + const simpleLengthPx = (token) => { + const match = /^(-?\d*\.?\d+)\s*(px|rem|em|%)?$/.exec(String(token || '').trim()); + if (!match) return null; + const amount = Number(match[1]); + if (!Number.isFinite(amount)) return null; + if (match[2] === 'rem' || match[2] === 'em') return amount * 16; + if (match[2] === '%') return amount * 0.16; + return amount; + }; + + const direct = simpleLengthPx(input); + if (direct !== null) return direct; + + // Static CSS engines cannot resolve viewport units, but clamp's min/max + // bounds still tell us whether the heading can ever reach hero scale. + const clamp = /^clamp\((.*)\)$/.exec(input); + if (clamp) { + const parts = clamp[1].split(','); + if (parts.length === 3) { + const bounds = [simpleLengthPx(parts[0]), simpleLengthPx(parts[2])] + .filter((candidate) => candidate !== null); + if (bounds.length > 0) return Math.max(...bounds); + } + } + + return 0; +} + // Sibling-relationship rule. Anchor on a hero-scale h1, look at the // previousElementSibling, and gate on EITHER the classic tracked- // uppercase eyebrow OR the modern accent-colored bold eyebrow. function checkHeroEyebrow(opts) { const { headingTag, headingText, headingFontSize, + headingInApplicationContext, siblingTag, siblingText, siblingTextTransform, siblingFontSize, siblingLetterSpacing, siblingFontWeight, siblingColor, siblingHasAccentDashPseudo, } = opts; if (headingTag !== 'h1') return []; - // We previously gated on headingFontSize >= 48 to anchor "hero scale". - // But modern hero h1s use clamp() / vw / var(--text-*), none of which - // jsdom can resolve — the computed value comes back as "2em" or - // "var(--text-9xl)" and parseFloat returns 2 or NaN. The gate fails - // on virtually every Tailwind v4 / framework build. The other gates - // (sibling text 2-60 chars, font-size ≤ 14px, accent-bold OR - // tracked-caps) are tight enough to avoid false positives on non- - // hero h1s — a tiny tan label directly above any h1 is the - // antipattern regardless of how big the h1 ends up. + // This is specifically a marketing-hero cliché, not a ban on compact + // context labels in product UI (for example, a station name inside a tab + // panel). Browser-computed sizes are reliable; the static adapter also + // resolves ordinary px/rem/em and clamp() bounds before reaching here. + if (headingInApplicationContext) return []; + if (!(headingFontSize >= 48)) return []; if (!siblingTag) return []; // An h2 above an h1 is a different anti-pattern (heading hierarchy / dual // headings) — never an eyebrow. @@ -1938,6 +1968,7 @@ function checkElementHeroEyebrowDOM(el) { headingTag: tag, headingText: el.textContent || '', headingFontSize: parseFloat(headStyle.fontSize) || 0, + headingInApplicationContext: !!el.closest('[role="tabpanel"], [role="dialog"], [role="application"], dialog'), siblingTag: sibling.tagName.toLowerCase(), siblingText: sibling.textContent || '', siblingTextTransform: sibStyle.textTransform || '', @@ -3367,7 +3398,8 @@ function checkElementHeroEyebrow(el, style, tag, window, customPropMap) { return checkHeroEyebrow({ headingTag: tag, headingText: el.textContent || '', - headingFontSize: parseFloat(headingFontSizeRaw) || 0, + headingFontSize: resolveHeroHeadingSizePx(headingFontSizeRaw), + headingInApplicationContext: !!el.closest?.('[role="tabpanel"], [role="dialog"], [role="application"], dialog'), siblingTag: sibling.tagName.toLowerCase(), siblingText: sibling.textContent || '', siblingTextTransform: sibStyle.textTransform || '', diff --git a/.claude/skills/impeccable/scripts/hook-lib.mjs b/.claude/skills/impeccable/scripts/hook-lib.mjs index e61ea692b..50aa2f8fb 100644 --- a/.claude/skills/impeccable/scripts/hook-lib.mjs +++ b/.claude/skills/impeccable/scripts/hook-lib.mjs @@ -46,6 +46,7 @@ import path from 'node:path'; import { pathToFileURL, fileURLToPath } from 'node:url'; import { extractPlatform, loadContext } from './context.mjs'; import { IMPECCABLE_COMMAND } from './lib/provider.mjs'; +import { renderStopSlopReview } from './lib/slop-review.mjs'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); @@ -1868,22 +1869,42 @@ export const CONTRACT_MAX_CHARS = 1800; // Cap contract sections per Stop emission so many touched artifacts cannot // stack an unbounded message. export const CONTRACT_AUDIT_MAX_FILES = 3; +export const CONTRACT_EXTS = new Set(['.html', '.htm', '.astro', '.svelte', '.vue', '.jsx', '.tsx']); +export const CONTRACT_REQUIRED_FIELDS = ['UNIQUE', 'NOT-TEMPLATE', 'OWN-WORLD', 'STORY', 'FIRST VIEWPORT', 'FORM']; /** - * Extract the artifact's own direction-contract comment: the first HTML - * comment in the head of the file, when its opening chars identify it as a - * contract/concept block. Returns the trimmed, length-capped body, or null - * when the file carries none (no comment, unclosed comment, marker missing, - * or the comment starts past the head window). + * Extract the artifact's own direction-contract comment from the head of an + * HTML or component file. Supports HTML-family comments and JSX block + * comments so the contract works in the Astro/Svelte/Vue/React scaffolds the + * skill actually builds. Returns the trimmed, length-capped body, or null + * when the file carries no valid contract block. */ export function extractDirectionContract(content) { if (typeof content !== 'string' || !content) return null; const head = content.slice(0, CONTRACT_HEAD_CHARS); - const m = //.exec(head); - if (!m) return null; - const body = m[1].trim(); - if (!body || !/contract|concept/i.test(body.slice(0, CONTRACT_MARKER_CHARS))) return null; - return body.slice(0, CONTRACT_MAX_CHARS); + const candidates = []; + for (const pattern of [//g, /\{\/\*([\s\S]*?)\*\/\}/g]) { + for (const match of head.matchAll(pattern)) { + const index = match.index ?? 0; + const linePrefix = head.slice(head.lastIndexOf('\n', index - 1) + 1, index).trim(); + if (linePrefix.startsWith('//')) continue; + candidates.push({ index, body: match[1].trim() }); + } + } + candidates.sort((a, b) => a.index - b.index); + for (const candidate of candidates) { + if (!candidate.body || !/direction\s+contract|concept\s+contract/i.test(candidate.body.slice(0, CONTRACT_MARKER_CHARS))) continue; + return candidate.body.slice(0, CONTRACT_MAX_CHARS); + } + return null; +} + +export function missingDirectionContractFields(contract) { + const body = typeof contract === 'string' ? contract : ''; + return CONTRACT_REQUIRED_FIELDS.filter((field) => { + const label = field.replace(/[.*+?^${}()|[\]\\]/g, '\\$&').replace(/\s+/g, '\\s+'); + return !new RegExp(`\\b${label}\\s*:`, 'i').test(body); + }); } /** @@ -1899,7 +1920,11 @@ export function renderContractAudit(entries, opts = {}) { const shown = entries.slice(0, CONTRACT_AUDIT_MAX_FILES); const blocks = shown.map(({ filePath, contract }) => { const display = relativize(filePath, cwd); - return `${display} opens with this direction contract, written when the direction was decided:\n\n${contract}`; + const missing = missingDirectionContractFields(contract); + const integrity = missing.length > 0 + ? `\n\nContract integrity defect: missing ${missing.join(', ')}. Repair the contract and the implementation together.` + : ''; + return `${display} opens with this direction contract, written when the direction was decided:\n\n${contract}${integrity}`; }); return [ `${ENVELOPE_PREFIX} Direction-contract audit. Before finishing, audit the rendered page against the contract it opens with, promise by promise.`, @@ -1984,9 +2009,22 @@ export async function runStopHook({ stdinJson, env = {}, cwd = process.cwd(), no return result({ skipped: 'native-platform', platform, durationMs: Date.now() - started }); } + const session = ensureSession(cache, sessionId); + const needsSlopReview = session.llmSlopReviewed !== true; + const det = detector || await loadDetector(); if (!det || typeof det.detectText !== 'function') { - return result({ skipped: 'detector-missing', durationMs: Date.now() - started }); + if (!needsSlopReview) return result({ skipped: 'detector-missing', durationMs: Date.now() - started }); + session.llmSlopReviewed = true; + session.updatedAt = Date.now(); + persistCache(projectCwd, cache); + const text = renderStopSlopReview(); + return { + exitCode: 0, + stdout: payload(text, 'Stop', harness), + emission: { kind: 'stop-llm-slop-review', llmSlopReview: true }, + audit: { ...audit, emitted: true, detectorMissing: true, llmSlopReview: true, chars: text.length, durationMs: Date.now() - started }, + }; } const scanOptions = designSystemOptions(config, det, projectCwd); @@ -2013,10 +2051,11 @@ export async function runStopHook({ stdinJson, env = {}, cwd = process.cwd(), no ? configuredExt.engine === 'html' : (ext === '.html' || ext === '.htm'); - // Direction-contract audit: HTML artifacts only, at most once per file + // Direction-contract audit: HTML and component artifacts, at most once per file // per session. The flag lives on the same session cache entry the // finding dedupe uses, so a second Stop fire stays quiet about it. - if (useHtmlEngine) { + const contractCapable = useHtmlEngine || CONTRACT_EXTS.has(ext); + if (contractCapable) { const fileEntry = ensureFile(cache, sessionId, filePath); if (!fileEntry.contractAudited) { const contract = extractDirectionContract(content); @@ -2046,10 +2085,15 @@ export async function runStopHook({ stdinJson, env = {}, cwd = process.cwd(), no } audit.scannedFiles = scanned; - if (freshGroups.length === 0 && contractEntries.length === 0) { + if (freshGroups.length === 0 && contractEntries.length === 0 && !needsSlopReview) { return result({ emitted: false, skipped: 'stop-clean', durationMs: Date.now() - started }); } + if (needsSlopReview) { + session.llmSlopReviewed = true; + session.updatedAt = Date.now(); + } + // Fresh findings and first-time contract audits earn the cache write; // both mark this batch as surfaced so the next Stop fire is silent // unless new issues appear. @@ -2064,6 +2108,9 @@ export async function runStopHook({ stdinJson, env = {}, cwd = process.cwd(), no if (contractEntries.length > 0) { parts.push(renderContractAudit(contractEntries, { cwd: projectCwd })); } + if (needsSlopReview) { + parts.push(renderStopSlopReview()); + } const text = appendDesignSystemNote(parts.join('\n\n'), scanOptions); return { exitCode: 0, @@ -2074,6 +2121,7 @@ export async function runStopHook({ stdinJson, env = {}, cwd = process.cwd(), no ...(contractEntries.length > 0 ? { contractFiles: contractEntries.map((entry) => entry.filePath) } : {}), + ...(needsSlopReview ? { llmSlopReview: true } : {}), }, audit: { ...audit, @@ -2081,6 +2129,7 @@ export async function runStopHook({ stdinJson, env = {}, cwd = process.cwd(), no freshFiles: freshGroups.length, freshFindings: freshGroups.reduce((sum, group) => sum + group.findings.length, 0), ...(contractEntries.length > 0 ? { contractAudits: contractEntries.length } : {}), + ...(needsSlopReview ? { llmSlopReview: true } : {}), chars: text.length, durationMs: Date.now() - started, }, diff --git a/.claude/skills/impeccable/scripts/lib/provider.mjs b/.claude/skills/impeccable/scripts/lib/provider.mjs index 7fa928a9c..5f24d569b 100644 --- a/.claude/skills/impeccable/scripts/lib/provider.mjs +++ b/.claude/skills/impeccable/scripts/lib/provider.mjs @@ -1,4 +1,5 @@ // Source scripts default to slash commands. The provider build replaces only // this exact declaration, avoiding heuristic rewrites across executable code. export const IMPECCABLE_COMMAND_PREFIX = "/"; +export const IMPECCABLE_PROVIDER_ID = "claude-code"; export const IMPECCABLE_COMMAND = `${IMPECCABLE_COMMAND_PREFIX}impeccable`; diff --git a/.claude/skills/impeccable/scripts/lib/slop-review.mjs b/.claude/skills/impeccable/scripts/lib/slop-review.mjs new file mode 100644 index 000000000..30f03e34d --- /dev/null +++ b/.claude/skills/impeccable/scripts/lib/slop-review.mjs @@ -0,0 +1,30 @@ +export const LLM_ONLY_SLOP_ITEMS = Object.freeze([ + 'Monospace used merely to signal “technical” or “developer.”', + 'Light or dark mode chosen by category habit rather than the actual use scene.', + 'Everything wrapped in cards, or identical icon-heading-text cards repeated as the page structure.', + 'Hero-metric scaffolds: one oversized number, a small label, supporting stats, and an accent treatment.', + 'Decorative glassmorphism, meaningless sparklines, or generic rounded rectangles with drop shadows.', + 'A modal chosen by reflex when the task does not require interruption or protected focus.', +]); + +export function renderLlmOnlySlopReview({ manualDetector = false, automaticDetector = false, scriptsPath = null } = {}) { + const lines = [ + automaticDetector + ? 'AI_SLOP_REVIEW_REQUIRED: The automatic detector covers mechanical rules, but this harness has no reliable late review. Before finishing changed UI, inspect the authored result for detector-blind model reflexes:' + : 'AI_SLOP_REVIEW_REQUIRED: The automatic Impeccable design hook is not available for this session. Before finishing changed UI, inspect the authored result for detector-blind model reflexes:', + ...LLM_ONLY_SLOP_ITEMS.map((item) => `- ${item}`), + 'Fix reflexes, not intentional choices required by the brief or established visual authority.', + ]; + if (manualDetector && scriptsPath) { + lines.push(`Then run the mechanical detector once over the changed web UI: \`node ${scriptsPath}/detect.mjs --json \`. Do not run it earlier during concept selection.`); + } + return lines.join('\n'); +} + +export function renderStopSlopReview() { + return [ + '[impeccable@1] Detector-blind AI-slop review. The mechanical pass is complete; now inspect the rendered result for model reflexes it cannot reliably detect:', + ...LLM_ONLY_SLOP_ITEMS.map((item) => `- ${item}`), + 'Fix reflexes, not intentional choices required by the brief or established visual authority. Do not rerun the detector; this is the authored judgment layer.', + ].join('\n'); +} diff --git a/.claude/skills/impeccable/scripts/lib/surface-briefs.mjs b/.claude/skills/impeccable/scripts/lib/surface-briefs.mjs new file mode 100644 index 000000000..f83416f69 --- /dev/null +++ b/.claude/skills/impeccable/scripts/lib/surface-briefs.mjs @@ -0,0 +1,151 @@ +import fs from 'node:fs'; +import path from 'node:path'; +import { slugFromTarget } from './target-slug.mjs'; + +export const SURFACE_BRIEF_VERSION = 1; + +export function getSurfaceBriefDir(projectRoot) { + return path.join(projectRoot, '.impeccable', 'surfaces'); +} + +export function normalizeSurfaceTarget(target, { projectRoot = process.cwd() } = {}) { + if (!target || typeof target !== 'string' || !target.trim()) return null; + const trimmed = target.trim(); + if (/^https?:\/\//i.test(trimmed)) { + try { + const url = new URL(trimmed); + url.hash = ''; + url.search = ''; + return url.toString().replace(/\/$/, '') || url.origin; + } catch { + return null; + } + } + if (/^route:/i.test(trimmed)) { + const route = trimmed.slice(trimmed.indexOf(':') + 1).trim(); + if (!route.startsWith('/') || route.includes('..')) return null; + const normalizedRoute = route.split(/[?#]/, 1)[0].replace(/\/{2,}/g, '/').replace(/\/$/, '') || '/'; + return `route:${normalizedRoute}`; + } + if (trimmed === '/') return 'route:/'; + if (trimmed.startsWith('/')) { + const absolute = path.resolve(trimmed); + const relativeToProject = path.relative(projectRoot, absolute); + const isProjectFile = relativeToProject && !relativeToProject.startsWith('..') && !path.isAbsolute(relativeToProject); + if (!isProjectFile && !fs.existsSync(absolute) && !trimmed.includes('..')) { + const normalizedRoute = trimmed.split(/[?#]/, 1)[0].replace(/\/{2,}/g, '/').replace(/\/$/, '') || '/'; + return `route:${normalizedRoute}`; + } + } + const abs = path.isAbsolute(trimmed) ? trimmed : path.resolve(projectRoot, trimmed); + const rel = path.relative(projectRoot, abs); + if (!rel || rel === '.' || rel.startsWith('..') || path.isAbsolute(rel)) return null; + return rel.split(path.sep).join('/'); +} + +export function surfaceBriefPathForTarget(target, { projectRoot = process.cwd() } = {}) { + const normalized = normalizeSurfaceTarget(target, { projectRoot }); + if (!normalized) return null; + const slugInput = normalized.startsWith('route:') ? `route${normalized.slice('route:'.length)}` : normalized; + const slug = slugFromTarget(slugInput, { cwd: projectRoot }); + return slug ? path.join(getSurfaceBriefDir(projectRoot), `${slug}.md`) : null; +} + +export function parseSurfaceBrief(text, filePath = null) { + const match = String(text || '').match(/^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/); + const meta = {}; + if (match) { + for (const line of match[1].split(/\r?\n/)) { + const colon = line.indexOf(':'); + if (colon < 0) continue; + const key = line.slice(0, colon).trim(); + const raw = line.slice(colon + 1).trim(); + if (!key) continue; + if (/^(?:\[|\{|\")/.test(raw) || /^(?:true|false|null|-?\d+(?:\.\d+)?)$/.test(raw)) { + try { meta[key] = JSON.parse(raw); continue; } catch { /* keep string */ } + } + meta[key] = raw.replace(/^['"]|['"]$/g, ''); + } + } + const primaryTarget = typeof meta.primary_target === 'string' ? meta.primary_target : null; + const relatedTargets = Array.isArray(meta.related_targets) + ? meta.related_targets.filter((value) => typeof value === 'string') + : []; + return { + path: filePath, + text: String(text || ''), + body: match ? String(text || '').slice(match[0].length).trim() : String(text || '').trim(), + meta, + slug: typeof meta.slug === 'string' ? meta.slug : filePath ? path.basename(filePath, '.md') : null, + primaryTarget, + relatedTargets, + targets: [primaryTarget, ...relatedTargets].filter(Boolean), + }; +} + +export function listSurfaceBriefs(projectRoot = process.cwd()) { + const dir = getSurfaceBriefDir(projectRoot); + let names; + try { + names = fs.readdirSync(dir).filter((name) => name.endsWith('.md')).sort(); + } catch { + return []; + } + return names.flatMap((name) => { + const filePath = path.join(dir, name); + try { + return [parseSurfaceBrief(fs.readFileSync(filePath, 'utf-8'), filePath)]; + } catch { + return []; + } + }); +} + +export function resolveSurfaceBrief(projectRoot = process.cwd(), target = null) { + const briefs = listSurfaceBriefs(projectRoot); + if (!target) { + return { + brief: briefs.length === 1 ? briefs[0] : null, + candidates: briefs, + reason: briefs.length === 1 ? 'only-brief' : briefs.length > 1 ? 'ambiguous' : 'none', + }; + } + + const normalized = normalizeSurfaceTarget(target, { projectRoot }); + if (!normalized) return { brief: null, candidates: briefs, reason: 'invalid-target' }; + const exactPath = surfaceBriefPathForTarget(normalized, { projectRoot }); + const exact = briefs.find((brief) => brief.path === exactPath && (!brief.targets.length || brief.targets.includes(normalized))); + if (exact) return { brief: exact, candidates: briefs, reason: 'slug' }; + const mapped = briefs.filter((brief) => brief.targets.includes(normalized)); + return { + brief: mapped.length === 1 ? mapped[0] : null, + candidates: mapped.length > 1 ? mapped : briefs, + reason: mapped.length === 1 ? 'mapping' : mapped.length > 1 ? 'ambiguous-target' : 'not-found', + }; +} + +export function writeSurfaceBrief({ + projectRoot = process.cwd(), + primaryTarget, + relatedTargets = [], + body, +}) { + const normalizedPrimary = normalizeSurfaceTarget(primaryTarget, { projectRoot }); + if (!normalizedPrimary) throw new Error('surface brief requires a concrete project-relative primary target or URL'); + const normalizedRelated = [...new Set(relatedTargets + .map((target) => normalizeSurfaceTarget(target, { projectRoot })) + .filter((target) => target && target !== normalizedPrimary))]; + const slug = slugFromTarget(normalizedPrimary, { cwd: projectRoot }); + const filePath = surfaceBriefPathForTarget(normalizedPrimary, { projectRoot }); + fs.mkdirSync(path.dirname(filePath), { recursive: true }); + const frontmatter = [ + '---', + `version: ${SURFACE_BRIEF_VERSION}`, + `slug: ${JSON.stringify(slug)}`, + `primary_target: ${JSON.stringify(normalizedPrimary)}`, + `related_targets: ${JSON.stringify(normalizedRelated)}`, + '---', + ].join('\n'); + fs.writeFileSync(filePath, `${frontmatter}\n\n${String(body || '').trim()}\n`, 'utf-8'); + return filePath; +} diff --git a/.claude/skills/impeccable/scripts/lib/target-slug.mjs b/.claude/skills/impeccable/scripts/lib/target-slug.mjs new file mode 100644 index 000000000..025915ad5 --- /dev/null +++ b/.claude/skills/impeccable/scripts/lib/target-slug.mjs @@ -0,0 +1,33 @@ +import path from 'node:path'; + +const SLUG_MAX = 50; + +/** Derive one clone-stable slug from a concrete file path or URL. */ +export function slugFromTarget(resolved, { cwd = process.cwd() } = {}) { + if (!resolved || typeof resolved !== 'string') return null; + const trimmed = resolved.trim(); + if (!trimmed) return null; + + if (/^https?:\/\//i.test(trimmed)) { + let url; + try { url = new URL(trimmed); } catch { return null; } + return kebab(`${url.hostname}${url.pathname}`); + } + + const abs = path.isAbsolute(trimmed) ? trimmed : path.resolve(cwd, trimmed); + let rel = path.relative(cwd, abs); + if (rel.startsWith('..') || path.isAbsolute(rel)) rel = path.basename(abs); + if (!rel || rel === '.') return null; + return kebab(rel); +} + +export function kebab(value) { + const slug = String(value || '') + .toLowerCase() + .replace(/[/\\.]+/g, '-') + .replace(/[^a-z0-9-]+/g, '-') + .replace(/-+/g, '-') + .replace(/^-|-$/g, ''); + if (!slug) return null; + return slug.length <= SLUG_MAX ? slug : slug.slice(slug.length - SLUG_MAX).replace(/^-/, ''); +} diff --git a/.claude/skills/impeccable/scripts/live-browser.js b/.claude/skills/impeccable/scripts/live-browser.js index 616f1290a..47bbfefbf 100644 --- a/.claude/skills/impeccable/scripts/live-browser.js +++ b/.claude/skills/impeccable/scripts/live-browser.js @@ -2592,7 +2592,7 @@ minWidth: '16px', height: '16px', padding: '0 4px', borderRadius: '999px', background: tuneOpen ? C.brand : BP.hairline, - color: tuneOpen ? 'oklch(98% 0 0)' : 'inherit', + color: tuneOpen ? C.ink : 'inherit', fontFamily: MONO, fontSize: '9.5px', fontWeight: '600', lineHeight: '1', boxSizing: 'border-box', @@ -3131,7 +3131,7 @@ position: 'absolute', top: '2px', left: initial ? '18px' : '2px', width: '16px', height: '16px', borderRadius: '50%', - background: 'oklch(98% 0 0)', + background: C.ink, transition: 'left 0.18s ' + EASE, boxShadow: '0 1px 2px oklch(0% 0 0 / 0.2)', }); @@ -3165,7 +3165,7 @@ const b = el('button', { padding: '5px 4px', border: 'none', borderRadius: '3px', background: active ? C.brand : 'transparent', - color: active ? 'oklch(98% 0 0)' : P.text, + color: active ? C.ink : P.text, fontFamily: FONT, fontSize: '10.5px', fontWeight: '500', cursor: 'pointer', whiteSpace: 'nowrap', transition: 'background 0.1s ease, color 0.1s ease', @@ -3178,7 +3178,7 @@ segBtns.forEach(({ btn, val }) => { const on = val === o.value; btn.style.background = on ? C.brand : 'transparent'; - btn.style.color = on ? 'oklch(98% 0 0)' : P.text; + btn.style.color = on ? C.ink : P.text; }); applyParamValue(variantEl, p, o.value); queueCheckpoint('param_changed'); diff --git a/.claude/skills/impeccable/scripts/surface-brief.mjs b/.claude/skills/impeccable/scripts/surface-brief.mjs new file mode 100644 index 000000000..723f7c1b4 --- /dev/null +++ b/.claude/skills/impeccable/scripts/surface-brief.mjs @@ -0,0 +1,74 @@ +#!/usr/bin/env node +import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath, pathToFileURL } from 'node:url'; +import { resolveProjectRoot } from './context.mjs'; +import { + listSurfaceBriefs, + resolveSurfaceBrief, + surfaceBriefPathForTarget, + writeSurfaceBrief, +} from './lib/surface-briefs.mjs'; + +function summary(brief, projectRoot) { + return { + slug: brief.slug, + path: path.relative(projectRoot, brief.path).split(path.sep).join('/'), + primaryTarget: brief.primaryTarget, + relatedTargets: brief.relatedTargets, + }; +} + +function main(argv) { + const [command, target, bodyFile, ...relatedTargets] = argv; + const projectRoot = resolveProjectRoot(process.cwd(), target ? { targetPath: target } : {}); + if (command === 'path') { + const filePath = surfaceBriefPathForTarget(target, { projectRoot }); + if (!filePath) throw new Error('surface brief path requires a concrete target'); + process.stdout.write(`${path.relative(process.cwd(), filePath) || filePath}\n`); + return; + } + if (command === 'list') { + process.stdout.write(`${JSON.stringify(listSurfaceBriefs(projectRoot).map((brief) => summary(brief, projectRoot)), null, 2)}\n`); + return; + } + if (command === 'read') { + const result = resolveSurfaceBrief(projectRoot, target || null); + if (result.brief) { + process.stdout.write(result.brief.text); + return; + } + if (result.candidates.length) process.stderr.write(`${JSON.stringify(result.candidates.map((brief) => summary(brief, projectRoot)), null, 2)}\n`); + process.exit(2); + } + if (command === 'write') { + if (!target || !bodyFile) throw new Error('usage: surface-brief.mjs write '); + const filePath = writeSurfaceBrief({ + projectRoot, + primaryTarget: target, + relatedTargets, + body: fs.readFileSync(bodyFile, 'utf-8'), + }); + process.stdout.write(`${path.relative(process.cwd(), filePath) || filePath}\n`); + return; + } + throw new Error('usage: surface-brief.mjs [target] [body-file] [related-target ...]'); +} + +function isMainModule() { + if (!process.argv[1]) return false; + try { + return fs.realpathSync(fileURLToPath(import.meta.url)) === fs.realpathSync(process.argv[1]); + } catch { + return import.meta.url === pathToFileURL(process.argv[1]).href; + } +} + +if (isMainModule()) { + try { + main(process.argv.slice(2)); + } catch (error) { + process.stderr.write(`${error?.message || error}\n`); + process.exit(1); + } +} diff --git a/.cursor/skills/impeccable/SKILL.md b/.cursor/skills/impeccable/SKILL.md index e4f0b3bfc..00f2b81a9 100644 --- a/.cursor/skills/impeccable/SKILL.md +++ b/.cursor/skills/impeccable/SKILL.md @@ -1,60 +1,59 @@ --- name: impeccable description: Use when the user wants to design, redesign, shape, critique, audit, polish, clarify, distill, harden, optimize, adapt, animate, colorize, extract, or otherwise improve a frontend interface. Covers websites, landing pages, dashboards, product UI, app shells, components, forms, settings, onboarding, and empty states. Handles UX review, visual hierarchy, information architecture, cognitive load, accessibility, performance, responsive behavior, theming, anti-patterns, typography, fonts, spacing, layout, alignment, color, motion, micro-interactions, UX copy, error states, edge cases, i18n, and reusable design systems or tokens. Also use for bland designs that need to become bolder or more delightful, loud designs that should become quieter, live browser iteration on UI elements, or ambitious visual effects that should feel technically extraordinary. Not for backend-only or non-UI tasks. -version: 4.0.0-alpha.9 +version: 4.0.0-alpha.10 license: Apache 2.0 --- -Designs and iterates production-grade frontend interfaces. Real working code, committed design choices, exceptional craft. +This skill gives you the tools and permission to create design that earns to be called out-of-distribution craft: Whereas before, your design work would have been safe, timid and measured, you now approach every design task as a award-winning design director with impeccable understanding for what makes exceptional design work: production-grade code, peak creativity, a clear POV, deep understanding of the needs of the client and users, and exceptional craft. -Approach every design task as the design lead at a small studio known for giving every client a visual identity that could not be mistaken for anyone else's. The client has already rejected work that felt templated; they are paying for a point of view. the model is capable of extraordinary work. Don't hold back. +Core principles: +- Go all out. No hedging, no shortcuts. The deliverable must be complete (except assets the user must provide). +- Dream big and bold. Distinct, beautiful, outstanding and highly inspiring work. +- Iterate with tools available to you (e.g. visual understanding, browser screenshots) until you think this meets the bar. ## Setup -1. Run `node .cursor/skills/impeccable/scripts/context.mjs` once per session (if the runtime shows this skill's loaded base directory, run `node /scripts/context.mjs`; keep cwd at the user's project). It prints the project's context and its directives; follow what it prints. Once its output is in the conversation, never rerun it on a later turn. -2. `craft` and `shape` are build-path exceptions: the new-work gate below owns their flow, and on unattended runs its checkpoints resolve without pausing. For any other invoked sub-command (`audit`, `polish`, `live`, ...), immediately read **`reference/.md`** after `context.mjs` (the `.native` variant from the Commands table when the platform is `ios`/`android`/`adaptive`) and follow it. This read is a hard gate: do not inspect the target, run command-specific scripts, or edit files until the reference is loaded. -3. Read at least one project file (CSS / tokens / theme / a representative component) to learn what world you're in. If PRODUCT.md's `## Platform` is `ios` or `android`, also read `reference/.md` (`adaptive` reads both). +1. Run `node .cursor/skills/impeccable/scripts/context.mjs` once per session (if the runtime shows this skill's loaded base directory, run `node /scripts/context.mjs`; keep cwd at the user's project). Pass a named source file or route as `--target `. It loads PRODUCT.md, DESIGN.md, the matching surface brief, and native-platform guidance when applicable; follow its directives and do not rerun it. +2. Before acting, load the one playbook that owns the request: the Commands table's reference for an explicit or clearly implied sub-command, or [reference/new-work.md](reference/new-work.md) for a new surface or replacement visual world. Then inspect the target and at least one representative source of incumbent visual truth (tokens, theme, CSS, component, or asset) before editing. ## How to design -**The brief wins.** Where the brief pins down a direction (a named aesthetic, an era, a place, a material, a specific font or palette), follow it exactly, including when it asks for a look this skill warns is saturated. Redirecting a pinned direction toward your own taste is a failure, not a save. - -**Existing worlds are sacred.** Most of impeccable's work happens inside a site or app that already exists. When the surface has a committed design system (real tokens, deliberately chosen faces, a palette the brand owns), work inside that world: extend it, sharpen it, leave it unmistakably the same brand, and never degrade a working page's performance. Inventing parallel colors, fonts, or styles on an existing surface is a defect, not creativity. A scoped refinement keeps the named scope's content and media footprint unless the brief explicitly changes them; build the added emphasis by recomposing what is already there through hierarchy, proportion, rhythm, and the system's own motifs. - -**New identity work reads the playbook first.** When nothing committed exists (greenfield, or a codebase with no real tokens or chosen faces), or the user explicitly asks for a redesign that discards the current look, you MUST read [reference/new-work.md](reference/new-work.md) before making any design decision. Missing DESIGN.md alone does not make work greenfield: code, tokens, chosen type, components, and assets are incumbent design authority. Preserve and extend them unless the user asked to replace them. Producing a genuinely new identity without the playbook yields the generic default this skill exists to prevent. Scoped fixes inside an existing world don't need it; the craft floor below governs them. `context.mjs` prints the appropriate authority directive. +- **The brief wins.** Honor pinned aesthetics, eras, materials, fonts, and palettes even when they conflict with a saturated-pattern warning. Redirecting a clear brief toward your taste is failure. +- **Refinement preserves; redesign replaces.** Refinement keeps the incumbent identity, behavior, copy, and everything outside scope. Ask before replacing factual copy or adding claims. Redesign keeps product truth, content, function, native affordances, and constraints, but treats the old look as evidence and anti-reference; choose a replacement world in new-work and replace DESIGN.md. Never split the difference into polish on the discarded look. +- **Visual authority is evidence, not a filename.** Missing DESIGN.md alone does not make a project greenfield; new-work decides whether to preserve, expand, or replace the incumbent world. ## Modes -Name the visitor's mode before designing; the page's grammar follows from it, and most ruined pages are one mode wearing another mode's grammar. **The mode belongs to the surface, not the subject**: a landing page for a dense technical tool is still Persuade, with Persuade's full permission to be striking; a docs page for a fashion house is still Read. Deciding a page can be plain because its subject is workmanlike is the same category error in reverse. The brief and the surface decide the mode; PRODUCT.md's `register` field survives only as a family hint (`brand` covers Persuade and Experience, `product` covers Operate and Read). Depth beyond the paragraphs below: [reference/new-work.md](reference/new-work.md) when inventing identity, [reference/operate.md](reference/operate.md) for substantial Operate and Read work. +Choose the mode from the requested surface, not the product, and persist it only in that surface brief. A tool's landing page is still Persuade; a fashion house's documentation is still Read. See [new-work.md](reference/new-work.md) for new surfaces and [operate.md](reference/operate.md) for deeper Operate/Read guidance. -**Persuade** (the surface exists to win someone over; design IS the product). The deliverable is an impression that stops the scroll, earns the click, converts. Spans every genre; don't collapse them into one look. On new surfaces, briefs that imply imagery must ship real, verified imagery; a colored rectangle where a photo belongs reads as incomplete. New Persuade surfaces take their typeface procedure and reject list from [reference/new-work.md](reference/new-work.md). - -**Operate** (the surface is a tool someone works in; design SERVES the task). A person getting something done: scanability and consistency outrank expressiveness. These surfaces earn trust by feeling native to their platform: system font stacks and workhorse UI faces are legitimate and often correct here (the Persuade reject list does not apply). The brand lives in the details: focus states, empty states, microcopy, one owned accent. The usage scene is part of the spec: an interface read outdoors, in motion, or at a glance must survive its real ambient light, and the theme follows the scene, not the category's habit. - -**Read** (the surface exists to be understood; long-form, reference, guidance). The deliverable is comprehension, and comprehension is earned twice: a structure the reader can hold in their head with nothing standing between them and the answer, and a reading experience good enough to stay in, through typographic quality and whatever visual or interactive support genuinely helps the reader follow. The brand lives in type, spacing, and small accents. - -**Experience** (the surface presents a body of work; the page IS the work). The artifact leads, the interface recedes, and the visitor meets the work itself in the first viewport at every screen size. Boldness here means trusting the work. +- **Persuade:** win someone over; design is the product. Earn attention and action. Ship real imagery when the brief needs it; follow the committed world, not category habit. +- **Operate:** help someone do work. Scanability, consistency, native expectations, and the real usage scene outrank expression. Brand lives in precise details. +- **Read:** make something understood. Structure for comprehension, then make the reading experience worth staying in. +- **Experience:** present a body of work. Let the artifact lead from the first viewport; the interface recedes. ## Craft floor -Build to this floor without announcing it. The design detector (the project hook, `node .cursor/skills/impeccable/scripts/detect.mjs --json `, or `audit`) verifies most of it mechanically; resolve every finding before finalizing. Fix real defects, but use context judgment rather than distorting intentional design to appease a false positive. Classify any intentional exception explicitly and use the hook system's narrowest appropriate waiver when it must persist. +Build to this floor without announcing it. -- Contrast: body text ≥4.5:1 against its background (placeholders too); large text ≥3:1. Gray text on a colored background looks washed out: use a darker shade of the background's own hue, or a transparency of the text color. -- Shadows describe real light: an offset and a soft blur. A zero-offset colored halo is decoration announcing itself. -- Spacing has rhythm: generous separations, tight groupings; cramped padding reads as broken; the space above a heading exceeds the space below it. Verify computed spacing, not intended spacing. -- Type: body line length 65-75ch; display clamp() max ≤6rem; letter-spacing ≥-0.04em; `text-wrap: balance` on headings; modular scale ≥1.25 between steps; light-on-dark adds 0.05-0.1 line-height. Pair faces on a contrast axis, never two similar-but-not-identical ones; one family with committed weight contrast beats a timid pair. Test headings at every breakpoint; overflow means reduce the clamp or rewrite the copy. -- Motion is part of the build: one orchestrated moment beats scattered effects; ease-out exponential curves; reveals enhance an already-visible default (content gated on a class-triggered transition ships blank in hidden tabs and headless renderers). Responsive down to mobile and visible keyboard focus are part of the floor. -- Ship real content (no placeholders, dead links, or fake controls) and cover the interaction states people will actually hit (hover, focus, disabled, loading, error, empty). -- Copy is design material: name things the way the page's own people speak, make every control say what it does, and make every error say what happened and what to do next. -- Before finishing, re-read the brief: every requirement it names must exist on the page, findable in seconds. A beautiful page missing an asked-for feature is unfinished. +- **Contrast:** body and placeholder text ≥4.5:1; large text ≥3:1. On colored surfaces, tint secondary text from that hue or the foreground instead of using gray. +- **Depth:** shadows describe light with offset and soft blur; zero-offset colored halos are decoration. +- **Spacing:** tight groups, generous separation, no cramped containers; space above a heading exceeds space below. Verify computed values. +- **Type:** body measure 65–75ch; display max 6rem and tracking floor -0.04em; balance headings; use clear scale/weight contrast; test overflow at every breakpoint. +- **Motion:** author one coherent moment instead of scattered effects. Use exponential ease-out and an already-visible default. Premium motion is not transform/opacity alone: it may add focus, depth, masks, light, or material change through blur/filter, backdrop-filter, clip-path/masks, or shadow when smooth. Always provide reduced motion. +- **Shipping:** real content, working controls, responsive composition, keyboard focus, and the states users hit: hover, disabled, loading, error, and empty. +- **Copy:** use the product's language; controls name their action, errors name the problem and recovery. +- **Coverage:** every brief requirement must exist and be findable within seconds. + +Before finishing changed UI, follow the quality guidance supplied by `context.mjs` and hooks. Context requests a manual scan only when no automatic detector is active; never add a second detector pass. ## Commands | Command | Category | Description | Reference | |---|---|---|---| -| `craft [feature]` | Build | The standard build flow with attended checkpoints | [reference/new-work.md](reference/new-work.md) | +| `craft [feature]` | Build | Deprecated alias for an ordinary new-work request | [reference/craft.md](reference/craft.md) | | `shape [feature]` | Build | Plan UX/UI before writing code | [reference/shape.md](reference/shape.md) | -| `init` | Build | Set up project context: PRODUCT.md, DESIGN.md, live config, next steps | [reference/init.md](reference/init.md) | +| `init` | Build | Capture durable product context in PRODUCT.md | [reference/init.md](reference/init.md) | | `document` | Build | Generate DESIGN.md from existing project code | [reference/document.md](reference/document.md) | | `extract [target]` | Build | Pull reusable tokens and components into design system | [reference/extract.md](reference/extract.md) | | `critique [target]` | Evaluate | UX design review with heuristic scoring | [reference/critique.md](reference/critique.md) | @@ -76,7 +75,14 @@ Build to this floor without announcing it. The design detector (the project hook | `optimize [target]` | Fix | Diagnose and fix UI performance | [reference/optimize.md](reference/optimize.md) | | `live` | Iterate | Visual variant mode: pick elements in the browser, generate alternatives | [reference/live.md](reference/live.md) | -Routing: **no argument** → read [reference/routing.md](reference/routing.md) and present the context-aware menu (never auto-run a command). **First word matches a command** (or `pin` / `unpin` / `hooks`) → load its reference (native variant on native platforms) and follow it; everything after the command name is the target. **Intent clearly maps to one command** ("fix the spacing" → `layout`, "rewrite this error" → `clarify`) → same; if two fit, ask once. **Otherwise** → general design invocation: apply Setup and this file's guidance; builds flow through the new-work gate above, whose playbook carries the direction checkpoint and the finishing pass. `teach` routes to `init`, and `craft` routes to the standard build flow with attended checkpoints. If setup diverted into `init` for a build request, finish init, refresh context, then resume. +Routing: + +- **No argument:** read [routing.md](reference/routing.md) and present its context-aware menu; never auto-run a command. +- **Explicit or clearly implied command:** load its reference (native variant on native platforms) and follow it. Ask once if two commands fit. +- **Otherwise:** treat the request as general design work. Missing PRODUCT.md routes through init; new surfaces and replacement worlds use new-work. +- `teach` aliases `init`. `craft` is a deprecated alias for ordinary new-work and adds nothing. `shape` owns task discovery, then enters new-work only for visual-world and surface-concept decisions. + +After init writes PRODUCT.md, resume without rerunning `context.mjs`. **Pin / Unpin:** `node .cursor/skills/impeccable/scripts/pin.mjs ` creates or removes a standalone `/` shortcut. Report the script's result concisely; relay stderr verbatim on error. diff --git a/.cursor/skills/impeccable/reference/android.md b/.cursor/skills/impeccable/reference/android.md index 3575c31fb..6337b9018 100644 --- a/.cursor/skills/impeccable/reference/android.md +++ b/.cursor/skills/impeccable/reference/android.md @@ -2,7 +2,7 @@ For native Android apps: Jetpack Compose, Android Views, React Native, Expo, Flutter shipping to Android hardware. -On native, register narrows. Material Design 3 governs structure, navigation, and interaction whatever the register; brand expresses through Material's theming (color roles, type scale, shape, motion). A Material-everywhere cross-platform app that also ships to iPhone still owes iOS its OS guarantees on that hardware: safe-area insets, Reduce Motion, edge-swipe back. +On native, the visitor mode narrows what expression may override. Material Design 3 governs structure, navigation, and interaction in every mode; brand expresses through Material's theming (color roles, type scale, shape, motion). A Material-everywhere cross-platform app that also ships to iPhone still owes iOS its OS guarantees on that hardware: safe-area insets, Reduce Motion, edge-swipe back. ## The Android slop test diff --git a/.cursor/skills/impeccable/reference/animate.md b/.cursor/skills/impeccable/reference/animate.md index 1a1098a5b..d51bb6d63 100644 --- a/.cursor/skills/impeccable/reference/animate.md +++ b/.cursor/skills/impeccable/reference/animate.md @@ -1,203 +1,88 @@ > **Additional context needed**: performance constraints. -Add motion that conveys state, gives feedback, and clarifies hierarchy. Cut motion that exists only for decoration. Animation fatigue is a real cost; spend the budget on the moments that need it. +Use motion to explain state, relationship, and hierarchy, or to create one authored moment the surface has earned. Decoration without purpose is animation debt. --- -## Register +## Visitor mode -Persuade + Experience: 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. +- **Persuade + Experience:** motion may carry the voice. Prefer one rehearsed focal sequence to repeated section reveals. +- **Operate + Read:** motion serves feedback, state, and continuity. Keep routine transitions fast and do not make users wait through page-load choreography. +- **Native (`ios` / `android` / `adaptive`):** follow the Motion section of [ios.md](ios.md) or [android.md](android.md), including the platform's Reduce Motion behavior. Do not apply the web tooling below. -Operate + Read: 150–250 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. +## Find the job -Native (`ios` / `android` / `adaptive`): implementation follows the Motion section of [ios.md](ios.md) / [android.md](android.md) (read it first if Setup hasn't already): system transitions and OS Reduce Motion, never the web tooling below. +Inspect the existing motion language, interaction states, target devices, and performance budget. Find only the places where motion would: ---- +- acknowledge an action; +- make a state change or spatial relationship legible; +- preserve continuity through navigation or layout change; +- direct attention at a meaningful moment; +- embody the selected visual world. -## Assess Animation Opportunities +Ask only when a material constraint cannot be inferred. Do not animate a static area merely because it exists. -Analyze where motion would improve the experience: +## Set the motion thesis -1. **Identify static areas**: - - **Missing feedback**: Actions without visual acknowledgment (button clicks, form submission, etc.) - - **Jarring transitions**: Instant state changes that feel abrupt (show/hide, page loads, route changes) - - **Unclear relationships**: Spatial or hierarchical relationships that aren't obvious - - **Lack of delight**: Functional but joyless interactions - - **Missed guidance**: Opportunities to direct attention or explain behavior +Write a short plan before implementation: -2. **Understand the context**: - - What's the personality? (Playful vs serious, energetic vs calm) - - What's the performance budget? (Mobile-first? Complex page?) - - Who's the audience? (Motion-sensitive users? Power users who want speed?) - - What matters most? (One hero animation vs many micro-interactions?) +- **Focal moment:** the one sequence or interaction that deserves authorship, if any. +- **Continuity:** the state, layout, or navigation changes that need explanation. +- **Feedback:** the controls and outcomes that need acknowledgment. +- **Budget:** which effects may be expensive, how often they run, and the reduced-motion equivalent. -If any of these are unclear from the codebase, ask the user directly to clarify what you cannot infer. +The focal moment must come from this product and surface concept. A generic fade-and-rise, hover lift, parallax layer, or scroll reveal is not a thesis. -**CRITICAL**: Respect `prefers-reduced-motion`. Always provide non-animated alternatives for users who need them. +## Choose material by meaning -## Plan Animation Strategy +Transform and opacity are reliable foundations, not the entire palette. Choose properties for what the transition communicates: -Create a purposeful animation plan: +- **Continuity and relationship:** shared-element motion, FLIP-style transforms, view transitions, or deliberate spatial movement. +- **Focus and depth:** bounded blur, filter, backdrop, light, or shadow changes. +- **Reveal and composition:** masks, clip paths, cropping, or controlled occlusion. +- **Material and energy:** color, gradient position, texture, distortion, or shader effects when the world and runtime support them. +- **State and feedback:** the smallest change that makes cause and result unmistakable. -- **Feedback layer**: Which interactions need acknowledgment? -- **Transition layer**: Which state changes need smoothing? -- **Entrance moment**: The ONE entrance worth rehearsing, where the mode invites it. Not every surface wants one. -- **Delight layer**: Where can we surprise and delight? +Do not stack techniques for spectacle. One strong material idea, carried through the focal sequence and quiet supporting states, is usually enough. -**IMPORTANT**: One well-orchestrated experience beats scattered animations everywhere. Focus on high-impact moments. +Sibling stagger is appropriate when a list appears as a list. Cap the total delay, and never reinterpret every scrolled section as a staggered list. -## Implement Animations +## Timing and easing -Add motion systematically across these categories: +Timing should express distance and consequence: -### Micro-interactions -- **Button feedback**: - - Hover: Subtle scale (1.02-1.05), color shift, shadow increase - - Click: Quick scale down then up (0.95 → 1), ripple effect - - Loading: Spinner or pulse state -- **Form interactions**: - - Input focus: Border color transition, slight scale or glow - - Validation: Shake on error, check mark on success, smooth color transitions -- **Toggle switches**: Smooth slide + color transition (200-300ms) -- **Checkboxes/radio**: Check mark animation, ripple effect -- **Like/favorite**: Scale + rotation, particle effects, color transition +| Duration | Typical use | +|---|---| +| 100–150 ms | immediate feedback | +| 150–300 ms | routine state change | +| 300–500 ms | layout, overlay, or view transition | +| 500–800 ms | a deliberately authored focal entrance | -### State Transitions -- **Show/hide**: Fade + slide (not instant), appropriate timing (200-300ms) -- **Expand/collapse**: Height transition with overflow handling, icon rotation -- **Loading states**: Skeleton screen fades, spinner animations, progress bars -- **Success/error**: Color transitions, icon animations, gentle scale pulse -- **Enable/disable**: Opacity transitions, cursor changes +Exit faster than entrance. Use natural deceleration such as `cubic-bezier(0.16, 1, 0.3, 1)` for confident arrivals; do not use bounce or elastic curves by reflex. Long feedback feels like latency. -### Entrance Animations -- **The one entrance moment** (where the mode invites it): a committed entrance for primary content (scale, parallax, or a creative effect) -- **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. +## Implement to the runtime - 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. +- Use CSS transitions and keyframes for declarative state and bounded sequences. +- Use Web Animations API or the project's existing motion library for interruption, sequencing, and dynamic values. +- Use View Transitions or shared-element techniques when continuity across states is the point. +- Use scroll-driven motion only when the scroll relationship itself carries meaning, with a robust fallback. +- Do not add a dependency for an effect the existing stack can express cleanly. -### Navigation & Flow -- **Page transitions**: Crossfade between routes, shared element transitions -- **Tab switching**: Slide indicator, content fade/slide -- **Carousel/slider**: Smooth transforms, snap points, momentum -- **Scroll effects**: Parallax layers, sticky headers with state changes, scroll progress indicators +Keep content visible in the default state so failed scripts do not hide the page. Avoid casually animating layout-driving properties such as `width`, `height`, `top`, `left`, and margins; use FLIP, transforms, or grid techniques when appropriate. Bound blur, filter, shadow, canvas, and shader work to isolated regions. Apply `will-change` only during known animation. Measure on target viewports and devices rather than assuming transform means fast. -### Feedback & Guidance -- **Hover hints**: Tooltip fade-ins, cursor changes, element highlights -- **Drag & drop**: Lift effect (shadow + scale), drop zone highlights, smooth repositioning -- **Copy/paste**: Brief highlight flash on paste, "copied" confirmation -- **Focus flow**: Highlight path through form or workflow +## Accessibility and control -### Delight Moments -- **Empty states**: Subtle floating animations on illustrations -- **Completed actions**: Confetti, check mark flourish, success celebrations -- **Easter eggs**: Hidden interactions for discovery -- **Contextual animation**: Weather effects, time-of-day themes, seasonal touches +Honor `prefers-reduced-motion` with an intentional alternative. Preserve state change and hierarchy while removing travel, parallax, flashing, or prolonged sequences. Do not replace every animation globally with `0.01ms` if that destroys useful feedback. Motion must never block focus, interaction, reading, or task completion. -## Technical Implementation +Respect autoplay and sound preferences. Any nonessential loop must stop when offscreen or hidden. -Use appropriate techniques for each animation: +## Verify -### Timing & Easing +- The focal motion is specific to the selected world and surface. +- Every supporting animation explains feedback, state, or relationship. +- Interruption and repeated use behave correctly. +- Desktop, mobile, keyboard, and reduced-motion paths remain usable. +- Expensive effects stay smooth on the target device. +- Removing an animation would lose meaning or authored character, not merely decoration. -**Duration: the 100/300/500 rule.** Timing matters more than easing for "feels right": - -| Duration | Use Case | Examples | -|----------|----------|----------| -| **100–150ms** | Instant feedback | Button press, toggle, color change | -| **200–300ms** | State changes | Menu open, tooltip, hover state | -| **300–500ms** | Layout changes | Accordion, modal, drawer | -| **500–800ms** | Entrance animations | Page load, hero reveal | - -**Easing curves (use these, not CSS defaults):** -```css -/* Recommended: natural deceleration */ ---ease-out-quart: cubic-bezier(0.25, 1, 0.5, 1); /* Smooth */ ---ease-out-quint: cubic-bezier(0.22, 1, 0.36, 1); /* Slightly snappier */ ---ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1); /* Confident, decisive */ - -/* AVOID: feel dated and tacky */ -/* bounce: cubic-bezier(0.34, 1.56, 0.64, 1); */ -/* elastic: cubic-bezier(0.68, -0.6, 0.32, 1.6); */ -``` - -**Exit animations are faster than entrances.** Use ~75% of enter duration. - -### CSS Animations -```css -/* Prefer for simple, declarative animations */ -- transitions for state changes -- @keyframes for complex sequences -- transform and opacity for reliable movement -- blur, filters, masks, clip paths, shadows, and color shifts for premium atmospheric effects when verified smooth -``` - -### JavaScript Animation -```javascript -/* Use for complex, interactive animations */ -- Web Animations API for programmatic control -- Framer Motion for React -- 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 -- **Layout safety**: Avoid casual animation of layout-driving properties (`width`, `height`, `top`, `left`, margins) -- **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) { - * { - animation-duration: 0.01ms !important; - animation-iteration-count: 1 !important; - transition-duration: 0.01ms !important; - } -} -``` - -**NEVER**: -- Use bounce or elastic easing curves; they feel dated and draw attention to the animation itself -- Animate layout properties casually (`width`, `height`, `top`, `left`, margins) when transform, FLIP, or grid-based techniques would work -- Use durations over 500ms for feedback (it feels laggy) -- Animate without purpose (every animation needs a reason) -- Ignore `prefers-reduced-motion` (this is an accessibility violation) -- Animate everything (animation fatigue makes interfaces feel exhausting) -- Block interaction during animations unless intentional - -## Verify Quality - -Test animations thoroughly: - -- **Smooth at 60fps**: No jank on target devices -- **Feels natural**: Easing curves feel organic, not robotic -- **Appropriate timing**: Not too fast (jarring) or too slow (laggy) -- **Reduced motion works**: Animations disabled or simplified appropriately -- **Doesn't block**: Users can interact during/after animations -- **Adds value**: Makes interface clearer or more delightful - -When the motion clarifies state instead of decorating it, hand off to `/impeccable polish` for the final pass. +When motion earns its place, hand off to `/impeccable polish` for the final pass. diff --git a/.cursor/skills/impeccable/reference/audit.md b/.cursor/skills/impeccable/reference/audit.md index 72815af69..70d80771a 100644 --- a/.cursor/skills/impeccable/reference/audit.md +++ b/.cursor/skills/impeccable/reference/audit.md @@ -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 - diff --git a/.cursor/skills/impeccable/reference/audit.native.md b/.cursor/skills/impeccable/reference/audit.native.md index 737d50e4f..0126fa157 100644 --- a/.cursor/skills/impeccable/reference/audit.native.md +++ b/.cursor/skills/impeccable/reference/audit.native.md @@ -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) diff --git a/.cursor/skills/impeccable/reference/bolder.md b/.cursor/skills/impeccable/reference/bolder.md index 6b44f0e38..78f5e4811 100644 --- a/.cursor/skills/impeccable/reference/bolder.md +++ b/.cursor/skills/impeccable/reference/bolder.md @@ -8,12 +8,12 @@ ## Why it reads flat -A section usually reads flat for reasons its neighbors have already solved. Look at what the rest of the page does that this section does not: the display type at full strength, the structural devices that carry meaning, the signature motif, the density and pacing, the specificity of the copy. A flat section is typically one that quietly opts out of the system's own strongest moves. The most reliable bolder pass brings the target up to the expressive level its neighbors already reach, in the system's own vocabulary rather than a new one. +A section usually reads flat for reasons its neighbors have already solved. Look at what the rest of the page does that this section does not: the display type at full strength, the structural devices that carry meaning, the signature motif, the density and pacing. A flat section is typically one that quietly opts out of the system's own strongest moves. The most reliable bolder pass brings the target up to the expressive level its neighbors already reach, in the system's own vocabulary rather than a new one. ## The amplification - **Amplify what the system already owns.** Reuse its motif and its type scale at full strength, turned up for this section rather than invented for it. The bolder version should look more like the same brand, not less. -- **Let the content carry the weight.** Flat copy makes a flat design, and no amount of size fixes a generic claim. Make every word specific enough to earn its place, and let the section's real evidence do the work that a decorative addition would otherwise fake. +- **Keep content true.** Existing claims are part of the scope: preserve them unless the user supplies replacements. If real evidence is essential to the direction but absent, ask for it. - **Commit, then clarify.** Half-measures read as noise. Make the one decisive move completely, then quiet everything around it so the move is legible. If every element got louder, the section got flatter. - **Give it its own rhythm.** The target should read as a peak in the scroll, a shift in density or pace from what surrounds it, not simply more of the same. diff --git a/.cursor/skills/impeccable/reference/clarify.md b/.cursor/skills/impeccable/reference/clarify.md index 00ee978c3..3047a9d82 100644 --- a/.cursor/skills/impeccable/reference/clarify.md +++ b/.cursor/skills/impeccable/reference/clarify.md @@ -1,288 +1,94 @@ -> **Additional context needed**: audience technical level and users' mental state in context. +> **Additional context needed**: audience knowledge and emotional state. -Find the unclear, confusing, or poorly written interface text and rewrite it. Vague copy creates support tickets and abandonment; specific copy gets users through the task. +Rewrite unclear interface text so users understand what happened, what matters, and what to do next. Preserve factual meaning, product terminology, and brand voice. +## Audit the language ---- +Read the entire interaction path, not isolated strings. Identify: -## Assess Current Copy +- ambiguous nouns, verbs, and actions; +- internal jargon or assumed knowledge; +- vague labels, outcomes, and system states; +- missing consequences, recovery, or timing; +- inconsistent terminology and capitalization; +- redundant headings, intros, helper text, and confirmations; +- text that breaks at realistic widths or in translation; +- tone that ignores stress, risk, success, or urgency. -Identify what makes the text unclear or ineffective: +Infer audience and task from product context and surrounding UI. Ask before changing factual claims, legal meaning, or a term that may be domain-specific. -1. **Find clarity problems**: - - **Jargon**: Technical terms users won't understand - - **Ambiguity**: Multiple interpretations possible - - **Passive voice**: "Your file has been uploaded" vs "We uploaded your file" - - **Length**: Too wordy or too terse - - **Assumptions**: Assuming user knowledge they don't have - - **Missing context**: Users don't know what to do or why - - **Tone mismatch**: Too formal, too casual, or inappropriate for situation +## Set the message hierarchy -2. **Understand the context**: - - Who's the audience? (Technical? General? First-time users?) - - What's the user's mental state? (Stressed during error? Confident during success?) - - What's the action? (What do we want users to do?) - - What's the constraint? (Character limits? Space limitations?) +For each state, decide: -**CRITICAL**: Clear copy helps users succeed. Unclear copy creates frustration, errors, and support tickets. +1. the one fact the user needs now; +2. the action available next; +3. supporting context that changes the decision; +4. the appropriate tone for this moment. -## Plan Copy Improvements +Say each idea once. If the heading already explains the state, the introduction should add new information or disappear. -Create a strategy for clearer communication: +## Rewrite by function -- **Primary message**: What's the ONE thing users need to know? -- **Action needed**: What should users do next (if anything)? -- **Tone**: How should this feel? (Helpful? Apologetic? Encouraging?) -- **Constraints**: Length limits, brand voice, localization considerations +### Actions and navigation -**IMPORTANT**: Good UX writing is invisible. Users should understand immediately without noticing the words. +Use a specific verb and object when the outcome is not already obvious. Labels should describe what will happen, not the gesture used to trigger it. Keep the same noun and verb for the same concept throughout the product. -## Improve Copy Systematically +For destructive actions, name the object and consequence. Prefer undo over confirmation when recovery is safe. When confirmation is necessary, name the action on both the message and button instead of using `Yes`, `No`, `OK`, or `Submit`. -Refine text across these common areas: +### Forms -### Error Messages -**Bad**: "Error 403: Forbidden" -**Good**: "You don't have permission to view this page. Contact your admin for access." +Use persistent labels; placeholders are examples, not labels. Put format and eligibility requirements before submission. Explain why information is requested only when it is not obvious. Required and optional treatment should be consistent. -**Bad**: "Invalid input" -**Good**: "Email addresses need an @ symbol. Try: name@example.com" +Validation says what needs attention and how to correct it without blaming the user. Keep related instructions near the field and announce errors accessibly. -**Principles**: -- Explain what went wrong in plain language -- Suggest how to fix it -- Don't blame the user -- Include examples when helpful -- Link to help/support if applicable - -### Form Labels & Instructions -**Bad**: "DOB (MM/DD/YYYY)" -**Good**: "Date of birth" (with placeholder showing format) - -**Bad**: "Enter value here" -**Good**: "Your email address" or "Company name" - -**Principles**: -- Use clear, specific labels (not generic placeholders) -- Show format expectations with examples -- Explain why you're asking (when not obvious) -- Put instructions before the field, not after -- Keep required field indicators clear - -### Button & CTA Text -**Bad**: "Click here" | "Submit" | "OK" -**Good**: "Create account" | "Save changes" | "Got it, thanks" +### Errors and permissions -**Principles**: -- Describe the action specifically -- Use active voice (verb + noun) -- Match user's mental model -- Be specific ("Save" is better than "OK") +An actionable error answers: -### Help Text & Tooltips -**Bad**: "This is the username field" -**Good**: "Choose a username. You can change this later in Settings." +1. what failed; +2. why, when known and useful; +3. how to recover or what alternative remains. -**Principles**: -- Add value (don't just repeat the label) -- Answer the implicit question ("What is this?" or "Why do you need this?") -- Keep it brief but complete -- Link to detailed docs if needed +Do not expose internal codes as the primary message. Do not promise a cause or resolution the system cannot know. Treat privacy, payment, deletion, access loss, and blocked work seriously; warmth is welcome, jokes are not. -### Empty States -**Bad**: "No items" -**Good**: "No projects yet. Create your first project to get started." +### Loading, empty, and success states -**Principles**: -- Explain why it's empty (if not obvious) -- Show next action clearly -- Make it welcoming, not dead-end +Loading text names the real operation and sets an honest expectation when the wait is meaningful. Show determinate progress when available; never invent progress. -### Success Messages -**Bad**: "Success" -**Good**: "Settings saved! Your changes will take effect immediately." +An empty state distinguishes first use, no results, filters, permissions, and failure. Explain the state and provide the next useful action. -**Principles**: -- Confirm what happened -- Explain what happens next (if relevant) -- Be brief but complete -- Match the user's emotional moment (celebrate big wins) - -### Loading States -**Bad**: "Loading..." (for 30+ seconds) -**Good**: "Analyzing your data... this usually takes 30-60 seconds" - -**Principles**: -- Set expectations (how long?) -- Explain what's happening (when it's not obvious) -- Show progress when possible -- Offer escape hatch if appropriate ("Cancel") +Success confirms the completed outcome and mentions the next consequence only when it changes what the user should do. Routine success should be brief. -### Confirmation Dialogs -**Bad**: "Are you sure?" -**Good**: "Delete 'Project Alpha'? This can't be undone." +### Help and instructional text -**Principles**: -- State the specific action -- Explain consequences (especially for destructive actions) -- Use clear button labels ("Delete project" not "Yes") -- Don't overuse confirmations (only for risky actions) +Helper text answers an implicit question instead of restating the control. Use progressive disclosure for uncommon detail. Link text must make sense out of context; icon-only controls need accessible names. -### Navigation & Wayfinding -**Bad**: Generic labels like "Items" | "Things" | "Stuff" -**Good**: Specific labels like "Your projects" | "Team members" | "Settings" +## Voice, accessibility, and localization -**Principles**: -- Be specific and descriptive -- Use language users understand (not internal jargon) -- Make hierarchy clear -- Consider information scent (breadcrumbs, current location) +Voice stays consistent; tone adapts to the moment. Use plain language without flattening terminology the audience genuinely knows. -## Apply Clarity Principles +- Write complete translatable messages rather than concatenated fragments. +- Keep variables and numbers structured so translators can reorder them. +- Allow expansion instead of abbreviating prematurely. +- Make alt text convey the image's information; use empty alt for decoration. +- Keep screen-reader names aligned with visible labels and outcomes. +- Do not rely on punctuation, color, or iconography to carry the message alone. -Every piece of copy should follow these rules: +Maintain a short terminology glossary when inconsistency spans the product. Do not vary words for literary effect in an interface. -1. **Be specific**: "Enter email" not "Enter value" -2. **Be concise**: Cut unnecessary words (but don't sacrifice clarity) -3. **Be active**: "Save changes" not "Changes will be saved" -4. **Be human**: "Oops, something went wrong" not "System error encountered" -5. **Tell users what to do**, not just what happened -6. **Be consistent**: Use same terms throughout (don't vary for variety) +## Verify -**NEVER**: -- Use jargon without explanation -- Blame users ("You made an error" → "This field is required") -- Be vague ("Something went wrong" without explanation) -- Use passive voice unnecessarily -- Write overly long explanations (be concise) -- Use humor for errors (be empathetic instead) -- Assume technical knowledge -- Vary terminology (pick one term and stick with it) -- Repeat information (headers restating intros, redundant explanations) -- Use placeholders as the only labels (they disappear when users type) +Read the flow in context and test: -## Verify Improvements +- comprehension without hidden product knowledge; +- actionability at errors, empty states, and decision points; +- factual accuracy and consistent terminology; +- scanability at target widths and 200% zoom; +- long names, localization expansion, pluralization, and dynamic values; +- accessible names and announced state changes; +- tone appropriate to consequence and emotional context. -Test that copy improvements work: +The final copy is as short as it can be without removing meaning or recovery. -- **Comprehension**: Can users understand without context? -- **Actionability**: Do users know what to do next? -- **Brevity**: Is it as short as possible while remaining clear? -- **Consistency**: Does it match terminology elsewhere? -- **Tone**: Is it appropriate for the situation? - -When the copy reads cleanly, hand off to `/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. +When the language reads cleanly, hand off to `/impeccable polish` for the final pass. diff --git a/.cursor/skills/impeccable/reference/codex.md b/.cursor/skills/impeccable/reference/codex.md index 18a924a91..6c550f1dc 100644 --- a/.cursor/skills/impeccable/reference/codex.md +++ b/.cursor/skills/impeccable/reference/codex.md @@ -1,105 +1,38 @@ -# Codex: Visual Direction & Asset Production +# Codex: Surface Probes & Asset Production -This file is loaded by `/impeccable craft` when the harness has native image generation (currently Codex via `image_gen`). Other harnesses skip it. It covers the two craft steps that depend on real image generation: landing the visual direction, and producing the raster assets the implementation will compose. +Load this from [new-work.md](new-work.md) only when the harness has native image generation and a substantial, high-fidelity surface would benefit from seeing the shortlisted concept before code. PRODUCT.md and DESIGN.md are preconditions. New-work has already resolved the visual world; this file must not reopen it. -Read this *before* generating any images. The order matters, and the per-step user pauses are what keep generated imagery from drifting away from the brief. +The purpose of a probe is to test composition, narrative, hierarchy, density, focal moment, signature use, and image requirements. It is not a second identity workshop. Keep DESIGN.md's palette, typography direction, material language, component character, imagery stance, and motion grammar fixed. -### Four stop points before code +## Generate the smallest useful probe set -Steps A through D each end with the user. Do not advance past any of them on your own read of the situation. +Generate one to three high-fidelity north-star comps using the native image-generation capability. Base them on the real content and the surface concepts already developed with the user. -1. **STOP after Step A questions.** Wait for answers. -2. **STOP after Step B palette generation.** Wait for "confirm palette." -3. **STOP after Step C mocks.** Wait for direction approval or delegation. -4. **Only after Step D approves a direction** do you return to the build (new-work.md's plan, then code). +- When the user shortlisted multiple concepts, show one clear expression of each. +- When one concept is already selected, vary only the structural uncertainty that the image can resolve: topology, sequence, density, hierarchy, focal composition, or interaction framing. +- Show enough beyond the opening moment to prove the concept can govern the whole requested surface. +- Do not generate a palette artifact, ask new atmosphere questions, introduce a different type voice, or invent a new motif. If the committed world cannot support the concept, return to the concept shortlist rather than changing the world. -Prior shape approval does **not** satisfy any of these. Shape's "confirm or override" advances you into Step A; it is not a substitute for it. +Treat each comp as a direction test, not a screenshot specification. Core UI text, responsive behavior, accessibility, semantics, and interaction states remain implementation responsibilities. -## Step A: Explore Directions with the User +## One approval point -Before generating anything, run a brief direction conversation grounded in the shape brief. +Show the probes together and ask what should carry forward, what feels false to the world, and whether the selected surface concept should be approved, combined, revised, or rejected. Then stop and wait. A structured simulated user counts as attended and receives the same question. -**Step A is required even when shape just produced a confirmed brief.** The shape questions and Step A questions cover different ground: shape pins purpose, content, scope; Step A pins palette, atmosphere, and named visual references for the comps you're about to generate. The only time you can skip Step A is when the user has already answered these exact palette/atmosphere/reference questions in the same session. +Do not begin code until the user approves a direction or explicitly delegates the choice. If they delegate, choose using the task brief, PRODUCT.md, and DESIGN.md, and state the evidence. Approval refines the task concept; it does not modify DESIGN.md. -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: +After approval, summarize the composition and the parts of the comp that must not be literalized. Return to new-work.md and write the direction contract from the approved surface concept before code. -- "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.)" +## Inventory implementation fidelity -**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. +Before building, inventory the approved comp's major visible ingredients and choose an implementation medium for each: semantic HTML/CSS/SVG, existing project asset, generated raster, sourced raster, icon library, canvas/WebGL, or accepted omission. -## Step B: Generate the Brand Palette First +Pay special attention to the dominant composition, signature use, image-native content, second-fold system, and any interaction the still image only implies. If the concept depends on a photograph, architectural scene, product object, portrait, or other raster-native material, do not silently replace it with generic CSS scenery. -Generate **one** palette artifact before any mocks. This is a small, focused image: typography pairing on the chosen background, primary + accent color swatches, one signature ornament or motif. Single image, single pass. +Treat the comp as a north star, not something to trace. Do not rasterize core UI text or controls. Do not substitute a different visual driver after approval without asking. -Why palette first: mocks generated against a vague color sense produce noise that drowns out the structural decisions. A confirmed palette is the first concrete contract for everything downstream. +## Produce only the assets the build needs -Show the palette to the user. Ask one question: "This is the palette I'm locking in for the mocks. Confirm, or call out what to shift?" +When clean raster ingredients are required and a scoped subagent is available and authorized, use `impeccable_asset_producer`. Give it the approved comp, output paths, required dimensions and formats, transparency needs, crop notes, and what must remain semantic code. Otherwise produce the minimum required assets with the native image-generation capability in the current thread. -**STOP and wait for confirmation.** Do not generate mocks against an unconfirmed palette. "Probably good enough" is the wrong call here; the palette is the contract for everything downstream. - -## Step C: Generate 1-3 Visual Mocks Against the Palette - -Once the palette is confirmed, generate **1 to 3** high-fidelity north-star comps. Each mock must use the confirmed palette and typography. Mocks differ in *structural* direction (hierarchy, topology, density, composition), not in color or motif. - -- Brand work: push visual identity, composition, mood, and signature motifs. -- Product work: push hierarchy, topology, density, tone, grounded in realistic product structure. -- Landing pages and long-form brand surfaces: show enough of the second fold to establish the system beyond the hero. - -Use the `image_gen` tool directly (or via the imagegen skill when available). Don't ask the user to install anything. - -## Step D: Approval Loop - -Show the comps. Ask what carries forward. Iterate until **one direction is approved** or the user explicitly delegates. - -**STOP and wait for the approval or the delegation.** Do not begin Step E or return to the build until a single direction is named. If the user delegates, pick the strongest direction and explain it from the brief, not personal taste. - -Before moving to assets, summarize what to carry into code and what *not* to literalize from the mock. This is the handoff between visual exploration and semantic implementation. - -## Step E: Mock Fidelity Inventory - -Inventory the approved mock's major visible ingredients. For each, decide implementation: semantic HTML/CSS/SVG, generated raster, sourced raster, icon library, canvas/WebGL, or accepted omission. - -Common ingredients to inventory: - -- Hero silhouette and dominant composition -- Signature motifs (planets, devices, portraits, charts, route lines, insets, badges, etc.) -- Nav and primary-action treatment (when the surface has one) -- Section sequence, especially the second fold -- Image-native content the concept depends on -- Typography, density, color/material treatment, motion cues - -Treat the mock as a north star, not a screenshot to trace. Don't rasterize core UI text. But if the live result lacks the mock's major ingredients, the implementation is wrong. - -If a photographic, architectural, product, or place-led mock becomes generic CSS scenery, decorative diagrams, bullets, or copy, stop and fix it. That's a broken implementation, not a harmless interpretation. - -Don't substitute a different hero composition or visual driver post-approval without user sign-off. - -## Step F: Asset Slicing via the Asset Producer - -Raster ingredients identified in Step E need clean production assets. Use the bundled `impeccable_asset_producer` subagent rather than producing inline. - -Spawn it as a scoped subagent. If you do not have explicit permission to use agents, stop and ask: - -```text -Asset production will work better as a scoped subagent job. Should I spawn the Impeccable asset producer subagent for this step? -``` - -Pass to the agent: - -- Approved mock path or screenshot reference -- Crop paths or a contact sheet with crop ids -- Output directory -- Required dimensions, format, transparency needs -- Avoid list -- Notes on what should remain semantic HTML/CSS/SVG instead of raster - -Attach image generation capability to the spawned agent when the harness supports it. Do **not** load image-generation reference material into the parent thread. - -Inline asset production is allowed only if the user declines subagents, the harness cannot spawn the authorized agent, or the user explicitly asks for single-thread mode. - -Prefer HTML/CSS/SVG/canvas when they can credibly reproduce an ingredient; reach for real, generated, or stock imagery when the mock or subject matter calls for actual visual content. - -## After This File - -Once Steps A through F are complete, return to the build: implement per new-work.md and SKILL.md's craft floor. The implementation builds against the confirmed palette, approved mock, and the assets the producer wrote. +Return to [new-work.md](new-work.md) for the direction contract, implementation, and finishing pass. diff --git a/.cursor/skills/impeccable/reference/colorize.md b/.cursor/skills/impeccable/reference/colorize.md index abee1acf8..dc45f88c5 100644 --- a/.cursor/skills/impeccable/reference/colorize.md +++ b/.cursor/skills/impeccable/reference/colorize.md @@ -1,257 +1,86 @@ > **Additional context needed**: existing brand colors. -Replace timid grayscale or single-accent designs with a strategic palette: pick a color strategy, choose a hue family that fits the brand, then apply color with intent. More color ≠ better. Strategic color beats rainbow vomit. +Introduce color as hierarchy, meaning, and atmosphere. Preserve confirmed brand and semantic conventions; do not replace a visual world under the guise of colorizing it. --- -## Register +## Visitor mode -Persuade + Experience: palette IS voice. Pick a color strategy first per SKILL.md (Restrained / Committed / Full palette / Drenched) and follow its dosage. Committed, Full palette, and Drenched deliberately exceed the ≤10% rule; that rule is Restrained only. Unexpected combinations are allowed; a dominant color can own the page when the chosen strategy calls for it. +- **Persuade + Experience:** color may carry the voice and own large regions when the selected world calls for it. +- **Operate + Read:** color primarily encodes action, selection, status, wayfinding, and reading hierarchy. Rarity gives an accent force. -Operate + Read: semantic-first and almost always Restrained. Accent color is reserved for primary action, current selection, and state indicators. Not decoration. Every color has a consistent meaning across every screen. +## Audit before choosing ---- +Read DESIGN.md, tokens, assets, current themes, and representative states. Identify: -## Assess Color Opportunity +- which colors are confirmed brand commitments; +- current surface, text, action, and semantic roles; +- places where grayscale obscures hierarchy or state; +- contrast failures and color-only communication; +- light/dark or data-visualization requirements; +- whether the task asks for more color or a new identity. -Analyze the current state and identify opportunities: +If a new identity is required, use [new-work.md](new-work.md). Ask only when a binding brand decision cannot be inferred. -1. **Understand current state**: - - **Color absence**: Pure grayscale? Limited neutrals? One timid accent? - - **Missed opportunities**: Where could color add meaning, hierarchy, or delight? - - **Context**: What's appropriate for this domain and audience? - - **Brand**: Are there existing brand colors we should use? +## Choose a strategy -2. **Identify where color adds value**: - - **Semantic meaning**: Success (green), error (red), warning (yellow/orange), info (blue) - - **Hierarchy**: Drawing attention to important elements - - **Categorization**: Different sections, types, or states - - **Emotional tone**: Warmth, energy, trust, creativity - - **Wayfinding**: Helping users navigate and understand structure - - **Delight**: Moments of visual interest and personality +Name the intended emotional temperature, dominant relationship, contrast range, and color dosage before editing. The strategy may be restrained or immersive; it must follow the brief and selected world rather than a fixed percentage rule. -If any of these are unclear from the codebase, ask the user directly to clarify what you cannot infer. +Build roles, not a bag of swatches: -**CRITICAL**: More color ≠ better. Strategic color beats rainbow vomit every time. Every color should have a purpose. +- canvas and elevated surfaces; +- primary and secondary text; +- action, focus, and selection; +- borders and separators; +- success, warning, error, and information; +- data categories or scales when needed. -## Plan Color Strategy +Use the project's existing color space. For a new web palette, prefer OKLCH because lightness and chroma can be adjusted predictably. Choose hue from product meaning and visual direction, never from a default category association. -Create a purposeful color introduction plan: +## Apply at system scale -- **Color palette**: What colors match the brand/context? (Choose 2-4 colors max beyond neutrals) -- **Dominant color**: Which color owns 60% of colored elements? -- **Accent colors**: Which colors provide contrast and highlights? (30% and 10%) -- **Application strategy**: Where does each color appear and why? +- Let the strongest color own a deliberate region or role instead of scattering tiny accents. +- Keep the primary action easy to find; do not spend its color on decoration. +- Tint neutrals only when the brand hue genuinely creates cohesion. Neutral gray is valid when it serves the world. +- On colored surfaces, derive secondary text from the foreground or surface hue rather than using washed-out generic gray. +- Keep semantic meanings consistent, but respect platform and domain conventions instead of assuming fixed hues. +- For data, use distinct lightness, chroma, shape, label, or pattern so color is not the only code. +- 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. -**IMPORTANT**: Color should enhance hierarchy and meaning, not create chaos. Less is more when it matters more. +Decoration without a relationship to hierarchy, state, content, or the visual world is not a color strategy. -## Introduce Color Strategically +## Contrast and perception -Add color systematically across these dimensions: +Verify computed foreground/background pairs: -### Semantic Color -- **State indicators**: - - Success: Green tones (emerald, forest, mint) - - Error: Red/pink tones (rose, crimson, coral) - - Warning: Orange/amber tones - - Info: Blue tones (sky, ocean, indigo) - - Neutral: Gray/slate for inactive states +| Content | WCAG AA minimum | +|---|---| +| body text | 4.5:1 | +| large text | 3:1 | +| controls, icons, focus indicators | 3:1 | -- **Status badges**: Colored backgrounds or borders for states (active, pending, completed, etc.) -- **Progress indicators**: Colored bars, rings, or charts showing completion or health +Do not rely on eyesight alone. Check interactive states, overlays, text on images, disabled content, and both themes. Simulate common vision deficiencies. Information conveyed by color also needs text, shape, iconography, or position. -### Accent Color Application -- **Primary actions**: Color the most important buttons/CTAs -- **Links**: Add color to clickable text (maintain accessibility) -- **Icons**: Colorize key icons for recognition and personality -- **Headers/titles**: Add color to section headers or key labels -- **Hover states**: Introduce color on interaction +When deriving OKLCH ramps, vary lightness and reduce chroma near white and black. Do not keep high chroma at extreme lightness merely to make the math uniform. Prefer explicit colors over chains of translucent overlays when alpha would make contrast context-dependent. -### Background & Surfaces -- **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 toward the brand, not "for warmth" by reflex +## Verify -**Use OKLCH for color**: It's perceptually uniform, meaning equal steps in lightness *look* equal. Great for generating harmonious scales. - -### Data Visualization -- **Charts & graphs**: Use color to encode categories or values -- **Heatmaps**: Color intensity shows density or importance -- **Comparison**: Color coding for different datasets or timeframes - -### Borders & Accents -- **Hairline borders**: 1px colored borders on full perimeter (not side-stripes; see the absolute ban on `border-left/right > 1px`) -- **Underlines**: Color underlines for emphasis or active states -- **Dividers**: Subtle colored dividers instead of gray lines -- **Focus rings**: Colored focus indicators matching brand -- **Surface tints**: A 4-8% background wash of the accent color instead of a stripe - -**NEVER**: `border-left` or `border-right` greater than 1px as a colored accent stripe. This is one of the three absolute bans in the parent skill. If you want to mark a card as "active" or "warning", use a full hairline border, a background tint, a leading glyph, or a numbered prefix. Not a side stripe. - -### Typography Color -- **Colored headings**: Use brand colors for section headings (maintain contrast) -- **Highlight text**: Color for emphasis or categories -- **Labels & tags**: Small colored labels for metadata or categories - -### Decorative Elements -- **Illustrations**: Add colored illustrations or icons -- **Shapes**: Geometric shapes in brand colors as background elements -- **Gradients**: Colorful gradient overlays or mesh backgrounds -- **Blobs/organic shapes**: Soft colored shapes for visual interest - -## Balance & Refinement - -Ensure color addition improves rather than overwhelms: - -### Maintain Hierarchy -- **Dominant color** (60%): Primary brand color or most used accent -- **Secondary color** (30%): Supporting color for variety -- **Accent color** (10%): High contrast for key moments -- **Neutrals** (remaining): Gray/black/white for structure - -### Accessibility -- **Contrast ratios**: Ensure WCAG compliance (4.5:1 for text, 3:1 for UI components) -- **Don't rely on color alone**: Use icons, labels, or patterns alongside color -- **Test for color blindness**: Verify red/green combinations work for all users - -### Cohesion -- **Consistent palette**: Use colors from defined palette, not arbitrary choices -- **Systematic application**: Same color meanings throughout (green always = success) -- **Temperature consistency**: Warm palette stays warm, cool stays cool - -**NEVER**: -- 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 -- Violate WCAG contrast requirements -- Use color as the only indicator (accessibility issue) -- Make everything colorful (defeats the purpose) -- Default to purple-blue gradients (AI slop aesthetic) - -## Verify Color Addition - -Test that colorization improves the experience: - -- **Better hierarchy**: Does color guide attention appropriately? -- **Clearer meaning**: Does color help users understand states/categories? -- **More engaging**: Does the interface feel warmer and more inviting? -- **Still accessible**: Do all color combinations meet WCAG standards? -- **Not overwhelming**: Is color balanced and purposeful? +- Every color has a stable role or a world-specific atmospheric purpose. +- Attention lands on the intended action, content, or state. +- The palette works across quiet, dense, interactive, error, and empty states. +- Light and dark themes are each composed, not mechanically inverted. +- Contrast and non-color cues pass in all relevant states. +- The result is recognizably this product, not a generic “colorful” treatment. When the palette earns its place, hand off to `/impeccable polish` for the final pass. ## Live-mode signature params -When invoked from live mode, each variant MUST declare a `color-amount` param so the user can dial between a restrained accent and a drenched surface without regeneration. Author the variant's CSS against `var(--p-color-amount, 0.5)`, typically as the alpha multiplier on backgrounds, or as a scaling factor on the chroma axis in an OKLCH expression. 0 = neutral/monochrome, 1 = full saturation / dominant coverage. +When invoked from live mode, every variant declares a `color-amount` parameter. Author CSS against `var(--p-color-amount, 0.5)` so the user can move from neutral to the variant's full color strategy without regeneration. ```json {"id":"color-amount","kind":"range","min":0,"max":1,"step":0.05,"default":0.5,"label":"Color amount"} ``` -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). +Add at most two variant-specific parameters, such as palette, temperature, or tint behavior. Follow [live.md](live.md)'s parameter contract. diff --git a/.cursor/skills/impeccable/reference/craft.md b/.cursor/skills/impeccable/reference/craft.md index cc7f98d37..dbbc9402c 100644 --- a/.cursor/skills/impeccable/reference/craft.md +++ b/.cursor/skills/impeccable/reference/craft.md @@ -1,3 +1,5 @@ # Craft (deprecated alias) -`craft` is a deprecated alias for the standard build flow. Builds route through SKILL.md's new-work gate; [new-work.md](new-work.md) carries the direction checkpoint ("Decide, then build"), the identity playbook, and the finishing pass. Invoking `craft` explicitly forces the attended checkpoints: pause at the stated direction for confirmation, and follow [codex.md](codex.md)'s mock flow when the harness generates images. Nothing else differs from a plain build request. +`craft` is a deprecated alias for an ordinary request to make new visual work. It adds no setup, interview, checkpoint, tool, or quality behavior. Apply SKILL.md's normal routing: create missing PRODUCT.md through [init.md](init.md), then follow [new-work.md](new-work.md) for visual authority, world and surface decisions, implementation, and finish. + +Do not tell users they need to invoke `craft`. Natural requests such as “build this feature,” “make a landing page,” or “redesign this screen” use the same flow. diff --git a/.cursor/skills/impeccable/reference/critique.md b/.cursor/skills/impeccable/reference/critique.md index 6beb82dda..9b8feff94 100644 --- a/.cursor/skills/impeccable/reference/critique.md +++ b/.cursor/skills/impeccable/reference/critique.md @@ -43,13 +43,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 @@ -115,11 +115,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. @@ -176,7 +176,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. 2. **Pass the structured metadata** through `IMPECCABLE_CRITIQUE_META` (JSON), then run the write command: ```bash diff --git a/.cursor/skills/impeccable/reference/delight.md b/.cursor/skills/impeccable/reference/delight.md index 596cff09e..40aaddc34 100644 --- a/.cursor/skills/impeccable/reference/delight.md +++ b/.cursor/skills/impeccable/reference/delight.md @@ -1,302 +1,70 @@ -> **Additional context needed**: what's appropriate for the domain (playful vs professional vs quirky vs elegant). +> **Additional context needed**: the brand's emotional range. -Find the moments where personality and unexpected polish would turn a functional interface into one users remember and tell other people about. Add only where the moment earns it; delight everywhere reads as noise. +Make the experience memorable at moments that earn it. Delight is not a layer of generic whimsy; it is product character revealed through a useful interaction, a humane response, or an unexpectedly considered detail. --- -## Register +## Visitor mode -Persuade + Experience: delight can be distributed across copy voice, section transitions, discovery rewards, seasonal touches, personality across the whole surface. +- **Persuade + Experience:** personality may run through voice, composition, motion, and discovery, provided the artifact remains the focus. +- **Operate + Read:** concentrate delight at meaningful moments such as first use, completion, recovery, or mastery. Reliability carries everything else. -Operate + Read: delight at specific moments, not pages. Completion, first-time actions, error recovery, milestone crossings. Reliability and consistency carry the rest of the experience; delight pushed everywhere reads as noise. +## Find the opportunity ---- +Inspect the target, DESIGN.md, product voice, repeated-use frequency, and emotional context. Look for: -## Assess Delight Opportunities +- effort worth acknowledging; +- waiting that can become informative; +- an empty or first-use state that can orient; +- an error or recovery moment that needs empathy; +- an interaction whose physical or verbal response could express the brand; +- a useful capability people might enjoy discovering. -Identify where delight would enhance (not distract from) the experience: +Do not manufacture a celebration for an ordinary click. Ask only when the brand's emotional range or the stakes cannot be inferred. -1. **Find natural delight moments**: - - **Success states**: Completed actions (save, send, publish) - - **Empty states**: First-time experiences, onboarding - - **Loading states**: Waiting periods that could be entertaining - - **Achievements**: Milestones, streaks, completions - - **Interactions**: Hover states, clicks, drags - - **Errors**: Softening frustrating moments - - **Easter eggs**: Hidden discoveries for curious users +## Define one delight thesis -2. **Understand the context**: - - What's the brand personality? (Playful? Professional? Quirky? Elegant?) - - Who's the audience? (Tech-savvy? Creative? Corporate?) - - What's the emotional context? (Accomplishment? Exploration? Frustration?) - - What's appropriate? (Banking app ≠ gaming app) +State in one sentence what the user should feel and why that feeling belongs to this product. Then choose the smallest system that can deliver it: -3. **Define delight strategy**: - - **Subtle sophistication**: Refined micro-interactions (luxury brands) - - **Playful personality**: Whimsical illustrations and copy (consumer apps) - - **Helpful surprises**: Anticipating needs before users ask (productivity tools) - - **Sensory richness**: Satisfying sounds, smooth animations (creative tools) +- a distinctive response to a meaningful action; +- product-specific language that clarifies while carrying voice; +- an interaction or transition with a recognizable material behavior; +- an illustration, sound, haptic, or environmental detail grounded in the product world; +- a discovery reward that reveals real utility. -If any of these are unclear from the codebase, ask the user directly to clarify what you cannot infer. +Derive the treatment from product mechanism and visual world, not a stock catalog. -**CRITICAL**: Delight should enhance usability, never obscure it. If users notice the delight more than accomplishing their goal, you've gone too far. +## Build for the emotional moment -## Delight Principles +- **Success:** match the response to the effort and consequence. Major milestones can expand; routine saves should simply feel certain. +- **Waiting:** show truthful progress, useful context, or product-specific activity. Never fake work or delay completion to stage a flourish. +- **Empty and first use:** make the next action clear before adding personality. +- **Error and recovery:** lead with the problem and recovery. Warmth may reduce stress; jokes must not trivialize loss, money, privacy, or blocked work. +- **Repeated interaction:** keep the response satisfying after the hundredth use. Variation is useful only when it remains coherent and predictable enough to trust. +- **Discovery:** reward curiosity without hiding required functionality. -Follow these guidelines: +Copy must use the product's language. Generic whimsy is worse than neutral clarity. -### Delight Amplifies, Never Blocks -- Delight moments should be quick (< 1 second) -- Never delay core functionality for delight -- Make delight skippable or subtle -- Respect user's time and task focus +## Protect the experience -### Surprise and Discovery -- Hide delightful details for users to discover -- Reward exploration and curiosity -- Don't announce every delight moment -- Let users share discoveries with others +Delight must not: -### Appropriate to Context -- Match delight to emotional moment (celebrate success, empathize with errors) -- Respect the user's state (don't be playful during critical errors) -- Match brand personality and audience expectations -- Cultural sensitivity (what's delightful varies by culture) +- delay, block, or obscure the primary task; +- override platform conventions or accessibility; +- add unrequested factual claims; +- play sound without consent or ignore mute settings; +- become mandatory, unskippable, or exhausting on repeat; +- add a dependency or asset cost disproportionate to the moment. -### Compound Over Time -- Delight should remain fresh with repeated use -- Vary responses (not same animation every time) -- Reveal deeper layers with continued use -- Build anticipation through patterns +For authored motion, load [animate.md](animate.md). Respect reduced motion, screen readers, keyboard use, touch, localization, and cultural context. Nonessential loops stop when hidden. Make celebration intensity proportional to frequency and consequence. -## Delight Techniques +## Verify -Add personality and joy through these methods: +- The moment is specific enough that a neighboring product could not use it unchanged. +- It improves comprehension, confidence, motivation, or emotional recovery. +- The interface remains fast and obvious without the flourish. +- Repetition does not turn charm into friction. +- Reduced-motion, muted, keyboard, touch, and localized paths work. +- The result feels like the selected world, not a generic “delight” treatment. -### Micro-interactions & Animation - -**Button delight**: -```css -/* Satisfying button press */ -.button { - transition: transform 0.1s, box-shadow 0.1s; -} -.button:active { - transform: translateY(2px); - box-shadow: 0 2px 4px rgba(0,0,0,0.2); -} - -/* Ripple effect on click */ -/* Smooth lift on hover */ -.button:hover { - transform: translateY(-2px); - transition: transform 0.2s cubic-bezier(0.25, 1, 0.5, 1); /* ease-out-quart */ -} -``` - -**Loading delight**: -- Playful loading animations (not just spinners) -- Personality in loading messages (write product-specific ones, not generic AI filler) -- Progress indication with encouraging messages -- Skeleton screens with subtle animations - -**Success animations**: -- Checkmark draw animation -- Confetti burst for major achievements -- Gentle scale + fade for confirmation -- Satisfying sound effects (subtle) - -**Hover surprises**: -- Icons that animate on hover -- Color shifts or glow effects -- Tooltip reveals with personality -- Cursor changes (custom cursors for branded experiences) - -### Personality in Copy - -**Playful error messages**: -``` -"Error 404" -"This page is playing hide and seek. (And winning)" - -"Connection failed" -"Looks like the internet took a coffee break. Want to retry?" -``` - -**Encouraging empty states**: -``` -"No projects" -"Your canvas awaits. Create something amazing." - -"No saved articles" -"Nothing on the nightstand yet. Save a story for later." -``` - -**Playful labels & tooltips**: -``` -"Delete" -"Send to void" (for playful brand) - -"Help" -"Rescue me" (tooltip) -``` - -**IMPORTANT**: Match copy personality to brand. Banks shouldn't be wacky, but they can be warm. - -### Illustrations & Visual Personality - -**Custom illustrations**: -- Empty state illustrations (not stock icons) -- Error state illustrations (friendly monsters, quirky characters) -- Loading state illustrations (animated characters) -- Success state illustrations (celebrations) - -**Icon personality**: -- Custom icon set matching brand personality -- Animated icons (subtle motion on hover/click) -- Illustrative icons (more detailed than generic) -- Consistent style across all icons - -**Background effects**: -- Subtle particle effects -- Gradient mesh backgrounds -- Geometric patterns -- Parallax depth -- Time-of-day themes (morning vs night) - -### Satisfying Interactions - -**Drag and drop delight**: -- Lift effect on drag (shadow, scale) -- Snap animation when dropped -- Satisfying placement sound -- Undo toast ("Dropped in wrong place? [Undo]") - -**Toggle switches**: -- Smooth slide with spring physics -- Color transition -- Haptic feedback on mobile -- Optional sound effect - -**Progress & achievements** (Operate surfaces with recurring tasks only; gamification on editorial, portfolio, or one-visit surfaces reads as noise): -- Streak counters with celebratory milestones -- Progress bars that "celebrate" at 100% -- Badge unlocks with animation -- Playful stats ("You're on fire! 5 days in a row") - -**Form interactions**: -- Input fields that animate on focus -- Checkboxes with a satisfying scale pulse when checked -- Success state that celebrates valid input -- Auto-grow textareas - -### Sound Design - -**Subtle audio cues** (when appropriate): -- Notification sounds (distinctive but not annoying) -- Success sounds (satisfying "ding") -- Error sounds (empathetic, not harsh) -- Typing sounds for chat/messaging -- Ambient background audio (very subtle) - -**IMPORTANT**: -- Respect system sound settings -- Provide mute option -- Keep volumes quiet (subtle cues, not alarms) -- Don't play on every interaction (sound fatigue is real) - -### Easter Eggs & Hidden Delights - -**Discovery rewards**: -- Konami code unlocks special theme -- Hidden keyboard shortcuts (Cmd+K for special features) -- Hover reveals on logos or illustrations -- Alt text jokes on images (for screen reader users too!) -- A view-source or console colophon (a note on the typefaces, a thank-you, a hiring line) - -**Seasonal touches**: -- Holiday themes (subtle, tasteful) -- Seasonal color shifts -- Weather-based variations -- Time-based changes (dark at night, light during day) - -**Contextual personality**: -- Different messages based on time of day -- Responses to specific user actions -- Randomized variations (not same every time) -- Progressive reveals with continued use - -### Loading & Waiting States - -**Make waiting engaging**: -- Interesting loading messages that rotate -- Progress bars with personality -- Mini-games during long loads -- Fun facts or tips while waiting -- Countdown with encouraging messages - -``` -Loading messages: write ones specific to your product, not generic AI filler: -- "Syncing with your team's changes..." -- "Fetching this week's issue..." -- "Developing your photos..." -- "Checking tomorrow's tide tables..." -``` - -**WARNING**: Avoid cliched loading messages like "Herding pixels", "Teaching robots to dance", "Consulting the magic 8-ball", "Counting backwards from infinity". These are AI-slop copy, instantly recognizable as machine-generated. Write messages that are specific to what your product actually does. - -### Celebration Moments - -**Success celebrations**: -- Confetti for major milestones -- Animated checkmarks for completions -- Progress bar celebrations at 100% -- "Achievement unlocked" style notifications -- Personalized messages ("You published your 10th article!") - -**Milestone recognition** (same scoping as Progress & achievements: Operate surfaces with recurring tasks): -- First-time actions get special treatment -- Streak tracking and celebration -- Progress toward goals -- Anniversary celebrations - -## Implementation Patterns - -**Animation libraries**: -- Framer Motion (React) -- GSAP (universal) -- Lottie (After Effects animations) -- Canvas confetti (party effects) - -**Sound libraries**: -- Howler.js (audio management) -- Use-sound (React hook) - -**Physics libraries**: -- React Spring (spring physics) -- Popmotion (animation primitives) - -**IMPORTANT**: File size matters. Compress images, optimize animations, lazy load delight features. - -**NEVER**: -- Delay core functionality for delight -- Force users through delightful moments (make skippable) -- Use delight to hide poor UX -- Overdo it (less is more) -- Ignore accessibility (animate responsibly, provide alternatives) -- Make every interaction delightful (special moments should be special) -- Sacrifice performance for delight -- Be inappropriate for context (read the room) - -## Verify Delight Quality - -Test that delight actually delights: - -- **User reactions**: Do users smile? Share screenshots? -- **Doesn't annoy**: Still pleasant after 100th time? -- **Doesn't block**: Can users opt out or skip? -- **Performant**: No jank, no slowdown -- **Appropriate**: Matches brand and context -- **Accessible**: Works with reduced motion, screen readers - -When the moments feel earned, hand off to `/impeccable polish` for the final pass. +When the personality feels earned, hand off to `/impeccable polish` for the final pass. diff --git a/.cursor/skills/impeccable/reference/document.md b/.cursor/skills/impeccable/reference/document.md index 0d38eb568..121a00ab1 100644 --- a/.cursor/skills/impeccable/reference/document.md +++ b/.cursor/skills/impeccable/reference/document.md @@ -1,6 +1,6 @@ Generate a `DESIGN.md` file at the project root that captures the current visual design system, so AI agents generating new screens stay on-brand. -DESIGN.md follows the [official DESIGN.md format spec](https://raw.githubusercontent.com/google-labs-code/design.md/main/docs/spec.md): YAML frontmatter carrying machine-readable design tokens, followed by a markdown body with exactly six sections in a fixed order. **Tokens are normative; prose provides context for how to apply them.** Sections may be omitted when not relevant, but **do not reorder them and do not rename them**. Section headers must match the spec character-for-character so the file stays parseable by other DESIGN.md-aware tools (Stitch itself, awesome-design-md, skill-rest, etc.). +DESIGN.md follows the [official DESIGN.md format spec](https://raw.githubusercontent.com/google-labs-code/design.md/main/docs/spec.md): optional YAML frontmatter carrying machine-readable design tokens, followed by up to eight markdown sections in a fixed order. **Tokens are normative; prose provides context for how to apply them.** Sections may be omitted when not relevant, but those present stay in the specified order. Use the canonical headings below so the file remains portable across DESIGN.md-aware tools. ## The frontmatter: token schema @@ -43,26 +43,28 @@ components: 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. +- **Colors accept any valid CSS color string.** Hex is the recommended default for portability, but preserve an incumbent `rgb()`, `hsl()`, `oklch()`, wide-gamut, or mixed-color value when it is the project's normative source. 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 (`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) +## The markdown body: eight sections (canonical order) 1. `## Overview` 2. `## Colors` 3. `## Typography` -4. `## Elevation` -5. `## Components` -6. `## Do's and Don'ts` +4. `## Layout` +5. `## Elevation & Depth` +6. `## Shapes` +7. `## Components` +8. `## Do's and Don'ts` -Optional evocative subtitles are allowed in the form `## 2. Colors: The [Name] Palette` (Stitch's own outputs do this), but the literal word in each header (Overview, Colors, Typography, Elevation, Components, Do's and Don'ts) must be present. Do NOT add extra top-level sections (Layout Principles, Responsive Behavior, Motion, Agent Prompt Guide). Fold that content into the six spec sections where it naturally belongs. +Omit irrelevant sections rather than filling them with invented rules. Put responsive layout in Layout, depth in Elevation & Depth, radius and form language in Shapes, and per-component behavior in Components. Unknown sections are preserved by the format, but new visual guidance should use the canonical structure whenever it fits. ## When to run -- 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. +- New-work found a coherent incumbent visual system but no `DESIGN.md`. +- The first implementation of a new world is complete and its provisional decisions need to be carbonized. - An existing `DESIGN.md` is stale (the design has drifted). - Before a large redesign, to capture the current state as a reference. @@ -71,9 +73,9 @@ 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 init, nothing built yet). Interview for five high-level answers, write a minimal DESIGN.md marked ``. Re-run in scan mode once there's code. +- **Seed mode**: the project is pre-implementation. Ensure PRODUCT.md exists, then reuse new-work's visual-world workshop and write its directional DESIGN.md 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. +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` requests new-work's world workshop, but it does not authorize replacing coherent code: when an incumbent system exists, offer scan mode or route an explicit identity-replacement request through new-work. ## Scan mode (approach C: auto-extract, then confirm descriptive language) @@ -97,7 +99,8 @@ Build a structured draft from the discovered tokens. For each token class: - **Typography**: Map observed sizes and weights to the Material hierarchy (display / headline / title / body / label). Note font-family stacks and the scale ratio. - **Elevation**: Catalogue the shadow vocabulary. If the project is flat and uses tonal layering instead, that's a valid answer; state it explicitly. - **Components**: For each common component (button, card, input, chip, list item, tooltip, nav), extract shape (radius), color assignment, hover/focus treatment, internal padding. -- **Spacing + layout**: Fold into Overview or relevant Components. The spec does NOT have a Layout section. +- **Layout + spacing**: Extract grid, container, breakpoint, rhythm, and density behavior into Layout. +- **Shapes**: Extract radius, corner, border, clipping, and recurring form behavior into Shapes. ### Step 2b: Stage the frontmatter @@ -112,19 +115,19 @@ Skip anything the project doesn't have. Empty scale keys or fabricated tokens po ### Step 3: Ask the user for qualitative language -The following require creative input that cannot be auto-extracted. Group them into one `AskUserQuestion` interaction: +The following require creative input that cannot be auto-extracted. Ask them in two structured rounds of no more than three questions each (or the harness's lower limit), waiting between rounds: - **Creative North Star**: a single named metaphor for the whole system ("The Editorial Sanctuary", "The Golden State Curator", "The Lab Notebook"). Offer 2-3 options that honor PRODUCT.md's brand personality. -- **Overview voice**: mood adjectives, aesthetic philosophy in 2-3 sentences, anti-references (what the system should not feel like). +- **Overview voice**: mood adjectives, aesthetic philosophy in 2-3 sentences, and any confirmed visual anti-reference. - **Color character** (for auto-extracted colors): descriptive names ("Deep Muted Teal-Navy", not "blue-800"). Suggest 2-3 options per key color based on hue/saturation. - **Elevation philosophy**: flat/layered/lifted. If shadows exist, is their role ambient or structural? - **Component philosophy**: the feel of buttons, cards, inputs in one phrase ("tactile and confident" vs. "refined and restrained"). -Quote a line from PRODUCT.md when possible so the user sees their own strategic language carry forward. +Carry a line from PRODUCT.md only when it is a durable brand commitment that actually constrains the visual system. Page strategy and surface concepts do not belong here. ### Step 4: Write DESIGN.md -The file opens with the YAML frontmatter staged in Step 2b (schema documented at the top of this reference), then the markdown body using the structure below. Headers must match character-for-character. Optional evocative subtitles (e.g. `## 2. Colors: The Coastal Palette`) are allowed. +The file opens with the YAML frontmatter staged in Step 2b (schema documented at the top of this reference), then the markdown body using the canonical structure below. ```markdown --- @@ -136,13 +139,13 @@ colors: # Design System: [Project Title] -## 1. Overview +## Overview **Creative North Star: "[Named metaphor in quotes]"** -[2-3 paragraph holistic description: personality, density, aesthetic philosophy. Start from the North Star and work outward. State what this system explicitly rejects (pulled from PRODUCT.md's anti-references). End with a short **Key Characteristics:** bullet list.] +[2-3 paragraph holistic description: personality, density, and aesthetic philosophy. Start from the North Star and work outward. State only confirmed visual rejections. End with a short **Key Characteristics:** bullet list.] -## 2. Colors +## Colors [Describe the palette character in one sentence.] @@ -162,7 +165,7 @@ colors: ### Named Rules (optional, powerful) **The [Rule Name] Rule.** [Short, forceful prohibition or doctrine, e.g. "The One Voice Rule. The primary accent is used on ≤10% of any given screen. Its rarity is the point."] -## 3. Typography +## Typography **Display Font:** [Family] (with [fallback]) **Body Font:** [Family] (with [fallback]) @@ -180,7 +183,11 @@ colors: ### Named Rules (optional) **The [Rule Name] Rule.** [Short doctrine about type use.] -## 4. Elevation +## Layout + +[Describe the grid or spatial model, container behavior, density, responsive changes, and the spacing rhythm. Include exact values only when observed.] + +## Elevation & Depth [One paragraph: does this system use shadows, tonal layering, or a hybrid? If "no shadows", say so explicitly and describe how depth is conveyed instead.] @@ -191,7 +198,11 @@ colors: ### Named Rules (optional) **The [Rule Name] Rule.** [e.g. "The Flat-By-Default Rule. Surfaces are flat at rest. Shadows appear only as a response to state (hover, elevation, focus)."] -## 5. Components +## Shapes + +[Describe the form language: corner/radius strategy, borders, clipping, and any recurring silhouette or geometry.] + +## Components For each component, lead with a short character line, then specify shape, color assignment, states, and any distinctive behavior. @@ -223,16 +234,16 @@ For each component, lead with a short character line, then specify shape, color ### [Signature Component] (optional; if the project has a distinctive custom component worth documenting) [Description.] -## 6. Do's and Don'ts +## Do's and Don'ts -Concrete, forceful guardrails. Lead each with "Do" or "Don't". Be specific: include exact colors, pixel values, and named anti-patterns the user mentioned in PRODUCT.md. **Every anti-reference in PRODUCT.md should show up here as a "Don't" with the same language**, so the visual spec carries the strategic line through. Quote PRODUCT.md directly where possible: if PRODUCT.md says *"avoid dark mode with purple gradients, neon accents, glassmorphism"*, the Don'ts here should repeat that by name. +Concrete visual guardrails grounded in the incumbent implementation or the user's chosen world. Lead each with "Do" or "Don't" and include exact values only when established. Do not turn a task-specific concept or surface strategy into a system-wide prohibition. ### Do: - **Do** [specific prescription with exact values / named rule]. - **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** [...] ``` @@ -307,7 +318,7 @@ The `html` and `css` fields must be **self-contained, drop-in snippets** that re Aim for a tight set of **5-10 components** that best represent the visual system: - **Canonical primitives (always include if the project has them):** button (each variant as a separate component entry), input/text field, navigation, chip/tag, card. -- **Signature components (include if distinctive):** hero CTA, featured card, filter pill, a distinctive table or list-row treatment, any custom pattern the user mentioned as important in PRODUCT.md. +- **Signature components (include if distinctive):** the recurring custom patterns that actually define the implemented system. - **Skip the rest.** Utility components, form building blocks, wrapper layouts: not worth documenting unless visually distinctive. If the project has **no component library yet** (bare landing page, new project), synthesize canonical primitives from the tokens using best-practice defaults consistent with the DESIGN.md's rules. Every `.impeccable/design.json` has *something* to render, even on day zero. @@ -338,64 +349,40 @@ Your own write is the freshest source; subsequent commands in this session don't ## Seed mode -For projects with no visual system to extract yet. Produces a minimal scaffold, not a full spec. +For projects with no visual system to extract yet. Produces a user-chosen visual-world scaffold, not a fabricated token spec. -### Step 1: Confirm seed mode +### Step 1: Route through new-work's workshop -Before interviewing: "There's no existing visual system to scan. I'll ask five quick questions to seed a starter DESIGN.md. You can re-run `/impeccable document` once there's code, to capture the real tokens and components. OK?" +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 the user prefers to skip, stop. No file. +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. -### Step 2: Five questions +If new-work already completed the workshop in this session, use its chosen direction directly. Do not ask again. -Group into one `AskUserQuestion` interaction. Options must be concrete. +### Step 2: Write seed DESIGN.md -1. **Color strategy.** Pick one: - - Restrained: tinted neutrals + one accent ≤10% - - Committed: one saturated color carries 30–60% of the surface - - Full palette: 3–4 named color roles, each deliberate - - Drenched: the surface IS the color - - Then: one hue family or anchor reference ("deep teal", "mustard", "Klim #ff4500 orange"). - -2. **Typography direction.** Pick one (specific fonts come later): - - Serif display + sans body - - Single sans (warm / technical / geometric / humanist; pick a feel) - - Display + mono - - Mono-forward - - Editorial script + sans - -3. **Motion energy.** Pick one: - - Restrained: state changes only - - Responsive: feedback + transitions, no choreography - - Choreographed: orchestrated entrances, scroll-driven sequences - -4. **Three named references.** Brands, products, printed objects. Not adjectives. - -5. **One anti-reference.** What it should NOT feel like. Also named. - -### Step 3: Write seed DESIGN.md - -Use the six-section spec from Scan mode. Populate what the interview answers; leave the rest as honest placeholders. The seed is a scaffold, not a fabricated spec. +Use the canonical section order from Scan mode. Populate the selected workshop direction and leave unresolved implementation facts as honest placeholders. The seed commits a world and its invariants; it does not pretend implementation tokens already exist. Lead the file with: ```markdown - + ``` Per-section guidance in seed mode: -- **Overview**: Creative North Star and philosophy phrased from the answers (color strategy + motion energy + references). Reference the user's anti-reference directly. -- **Colors**: Color strategy as a Named Rule (e.g. *"The Drenched Rule. The surface IS the color."*). Hue family or anchor reference. No hex values; mark as `[to be resolved during implementation]`. -- **Typography**: the direction the user picked (e.g. "Serif display + sans body"). No font names yet: `[font pairing to be chosen at implementation]`. -- **Elevation**: inferred from motion energy. Restrained/Responsive → flat by default; Choreographed → layered. One sentence. +- **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. +- **Elevation & Depth**: the selected material and depth behavior, stated as an invariant rather than inferred from a generic preset. +- **Shapes**: the selected form and corner language. - **Components**: omit entirely; no components exist yet. -- **Do's and Don'ts**: carry PRODUCT.md's anti-references directly plus the anti-reference named in Q5. +- **Do's and Don'ts**: record the durable guardrails confirmed during the world choice, not task-local refusals. 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 +### Step 3: 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." @@ -405,15 +392,15 @@ Your own write is the freshest source; no reload needed. ## Style guidelines - **Frontmatter first, prose second.** Tokens go in the YAML frontmatter; prose contextualizes them. Don't redefine a token value in two places; the frontmatter is normative. -- **Cite PRODUCT.md anti-references by name** in the Do's and Don'ts section. If PRODUCT.md lists "SaaS landing-page clichés" or "generic AI tool marketing" as anti-references, the DESIGN.md Don'ts should repeat those phrases verbatim so the visual spec enforces the strategic line. -- **Match the spec, don't invent new sections.** The six section names are fixed. If you have Layout/Motion/Responsive content to document, fold it into Overview (philosophy-level rules) or Components (per-component behavior). +- **Carry only durable product constraints.** A binding logo, identity asset, accessibility need, or brand commitment from PRODUCT.md may constrain DESIGN.md. Surface strategy stays in its surface brief. +- **Match the spec.** Use its eight canonical sections in order and omit any that are irrelevant. Put motion guidance with the world or component it affects rather than creating a token group the schema does not support. - **Descriptive > technical**: "Gently curved edges (8px radius)" > "rounded-lg". Include the technical value in parens, lead with the description. - **Functional > decorative**: for each token, explain WHERE and WHY it's used, not just WHAT it is. - **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 forceful**. The voice of a design director. "Prohibited", "forbidden", "never", "always", not "consider", "might", "prefer". Match PRODUCT.md's tone. -- **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. -- **Reference PRODUCT.md**. The anti-references section of PRODUCT.md should directly inform the Do's and Don'ts section here. Quote or paraphrase. +- **Be decisive where evidence is decisive.** Use hard language for actual invariants and softer language for provisional guidance. +- **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. ## Pitfalls @@ -423,7 +410,7 @@ Your own write is the freshest source; no reload needed. - Don't invent components that don't exist. If the project only has buttons and cards, only document those. - Don't overwrite an existing DESIGN.md without asking. - Don't duplicate content from PRODUCT.md. DESIGN.md is strictly visual. -- Don't add a "Layout Principles" or "Motion" or "Responsive Behavior" top-level section. The spec has six, not nine. Fold that content where it belongs. +- Don't replace canonical sections with near-synonyms. Put layout and responsive behavior in `Layout`; put motion with the affected world or component. - Don't rename sections even slightly. "Colors" not "Color Palette & Roles". "Typography" not "Typography Rules". Tooling parsing depends on exact headers. - Don't duplicate token values between frontmatter and prose. If a color is in `colors.primary` as hex, the prose can name it and describe its role but should not reassert a different hex. The frontmatter is normative. - Don't invent frontmatter token groups outside Stitch's schema (no `motion:`, `breakpoints:`, `shadows:` at the top level). Stitch's Zod schema only accepts `colors`, `typography`, `rounded`, `spacing`, `components`. Anything else belongs in the sidecar's `extensions`. diff --git a/.cursor/skills/impeccable/reference/hooks.md b/.cursor/skills/impeccable/reference/hooks.md index 4a6b50ac6..29ec1c38c 100644 --- a/.cursor/skills/impeccable/reference/hooks.md +++ b/.cursor/skills/impeccable/reference/hooks.md @@ -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. diff --git a/.cursor/skills/impeccable/reference/init.md b/.cursor/skills/impeccable/reference/init.md index fad998fb9..d3e3a1fbd 100644 --- a/.cursor/skills/impeccable/reference/init.md +++ b/.cursor/skills/impeccable/reference/init.md @@ -1,224 +1,114 @@ -# Init Flow +# Init flow -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 [DESIGN.md format spec](https://raw.githubusercontent.com/google-labs-code/design.md/main/docs/spec.md). Answers "how it looks". -- **`.impeccable/live/config.json`** (live mode): pre-configured so `/impeccable live` boots straight into variant mode with no first-time detour. - -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. +`init` captures durable product truth in PRODUCT.md. It does not invent a visual world and does not write DESIGN.md; [new-work.md](new-work.md) creates or expands one, and [document.md](document.md) records an incumbent one. Existing runnable web projects may also receive `.impeccable/live/config.json`. ## Step 1: Load current state -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). +Use the PRODUCT.md path resolved by context.mjs. Update it instead of creating a competing authority. In a child app inheriting root context, confirm shared versus app-specific scope before writing. -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. -- **PRODUCT.md exists, DESIGN.md missing**: skip to Step 5 and offer to run `/impeccable document` for DESIGN.md. -- **PRODUCT.md exists but has no `## Register` section (legacy)**: add it. Infer a hypothesis from the codebase (see Step 2), confirm with the user, write the field. -- **PRODUCT.md exists but has no `## Platform` section (legacy)**: add it the same way, but only when the project is native (`ios` / `android` / `adaptive`) or the user wants it explicit; a missing field already means `web`. -- **Both exist**: ask the user directly to clarify what you cannot infer. Ask which file to refresh. Skip the one the user doesn't want changed. -- **Just DESIGN.md exists (unusual)**: do Steps 2-4 to produce PRODUCT.md. +- **No PRODUCT.md:** explore, interview, and write it. +- **PRODUCT.md exists:** ask what product knowledge is stale or missing; do not reopen confirmed fields without a reason. +- **Legacy PRODUCT.md:** add only durable missing facts; absent `## Platform` means `web` unless evidence says otherwise. +- **Only DESIGN.md exists:** leave it untouched and create PRODUCT.md. +- **Redesign/rebrand request:** preserve confirmed product truth unless the user changes it. Visual replacement happens later in new-work, not here. -Never silently overwrite an existing file. Always confirm first. +Never silently overwrite an existing file or offer DESIGN.md during init. If another request invoked init, finish PRODUCT.md and resume it. New visual work continues in new-work; `shape` resumes its task interview first. -If init was invoked as a setup blocker by another command, such as `/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 project -## Step 2: Explore the codebase +Before asking, scan enough to avoid making the user repeat known facts: product docs and copy; package/config and app boundaries; features, workflows, routes, and roles; names, logos, legal/proof assets, and brand commitments; platform/accessibility signals; and the dev command/entry when live mode applies. -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: +Treat repository evidence as a hypothesis, not user approval. Note visual maturity without documenting, extending, or replacing the world. -- **README and docs**: Project purpose, target audience, any stated goals -- **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 -- **Any style guides or brand documentation** +Form a platform hypothesis: `web`, `ios`, `android`, or `adaptive` (one product that genuinely adapts its design language per OS). Mobile web remains `web`; a native wrapper around a website does not make its design language native. -Also form a **register hypothesis** from what you find: +## Step 3: Interview for product truth -- Brand signals: `/`, `/about`, `/pricing`, `/blog/*`, `/docs/*`, hero sections, big typography, scroll-driven sections, landing-page-shaped content. -- Product signals: `/app/*`, `/dashboard`, `/settings`, `/(auth)`, forms, data tables, side/top nav, app-shell components. +ask the user directly to clarify what you cannot infer. Ask only about material gaps the repository and original request do not answer with strong evidence. -Register is a hypothesis at this point, not a decision; Step 3 confirms it. +Use the structured question tool when available; otherwise ask and wait. Keep rounds to at most three focused questions and require one real answer or approval round before writing a new PRODUCT.md. Confirm inferences. -Also form a **platform hypothesis**: +Start with the unknowns that most change future product decisions: -- Native signals: React Native / Expo (`react-native`, `expo`), Flutter (`pubspec.yaml`, `flutter`), SwiftUI / UIKit (`.swift`, `.xcodeproj`, an `ios/` app target), Jetpack Compose / Android (`build.gradle`, an `android/` app module, `AndroidManifest.xml`). An `ios/` and/or `android/` directory that is a real app target, not just a Capacitor/Cordova wrapper around a website. -- Web signals (the default): a web framework (Vite, Next, Nuxt, SvelteKit, Astro), an HTML entry, a CSS/Tailwind setup, no native app target. +1. Who is the primary user, in what situation, and what job are they doing? +2. What does the product make possible, and what is its meaningfully different mechanism or position? +3. What durable constraints, assets, evidence, or product facts must future work preserve? -Values: `web` / `ios` / `android` / `adaptive` (one codebase, ships both, adapts per OS). Mobile web is still `web`. Like register, this is a hypothesis; Step 3 confirms it. +Confirm ambiguous platform separately. Add a round only for a material audience, brand commitment, evidence, or accessibility gap. Record undecided facts instead of inventing them. -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. +Do not ask for an aesthetic direction, emotional feel, visual references, colors, typography, or style during init. If the user volunteers a binding visual constraint, record it without expanding it. -## Step 3: Ask strategic questions (for PRODUCT.md) +### What belongs here -ask the user directly to clarify what you cannot infer. Ask about anything the codebase doesn't answer with strong, explicit evidence. +- users, jobs, workflows, purpose, success, positioning, and operating context; +- capabilities, constraints, terminology, evidence, platform, and accessibility; +- confirmed voice, assets, and brand commitments. -### Interview mode, not confirmation mode +### What does not belong here -If the repo is empty or the user's brief is sparse, run a short interview before proposing PRODUCT.md. Do **not** turn a one-sentence request into a complete inferred PRODUCT.md and ask for blanket confirmation. - -- Use the harness's structured question tool when one exists. Otherwise, ask directly in chat and stop: one question at a time, with lettered options where the crawl suggests likely answers, waiting for each answer before the next. -- Keep skill vocabulary (register, belief ladder, anti-references) out of question text; ask for the thing in words the user would use. For the brand register, ask like a magazine editor profiling the brand: curious and narrative, drawing out the story, the feel, and what a visitor should come to believe. -- Ask in focused rounds and wait for answers between them. Keep **one topic per question**; add rounds rather than fold several topics into one either-or choice. Options obey the same rule: an option answers only the question asked; never write a compound option that bundles a feeling with a business outcome or names an additional audience. -- Use inferred answers as hypotheses or options, not as finished facts. -- Complete at least one real user-answer round before drafting PRODUCT.md, unless every required answer is directly discoverable from repo docs. -- Round 1 should establish register, platform, users, purpose, positioning, and desired outcome. -- Round 2 should establish brand personality or references, anti-references, and accessibility needs, plus conversion & proof for Persuade surfaces. - -### Minimum viable interview - -Ask enough to complete PRODUCT.md. At minimum, cover register confirmation, **platform confirmation** (`web` / `ios` / `android` / `adaptive`), users, purpose, positioning, brand personality, anti-references, and accessibility needs (plus conversion & proof for Persuade surfaces) unless each answer is directly discoverable from repo context. Never let the template's default `web` stand unconfirmed for a native or cross-platform repo. After at least one interview round, you may propose inferred answers, but the user must confirm them before you write PRODUCT.md. Never synthesize PRODUCT.md from the original task prompt alone. - -### Register (ask first; it shapes everything below) - -Every design task serves one of four visitor modes (see SKILL.md's Registers): **Persuade** (marketing, landing, campaigns), **Experience** (portfolios, albums, bodies of work), **Operate** (app UI, admin, dashboards, tools), and **Read** (documentation, editorial, long-form). PRODUCT.md stores the two-value family that covers them: `brand` covers Persuade and Experience, `product` covers Operate and Read. The brief decides the exact mode per task; the stored register only sets the default family. - -If Step 2 produced a clear hypothesis, lead with it: *"From the codebase, this looks like a [brand / product] surface. Does that match your intent, or should we treat it differently?"* - -If the signal is genuinely split (e.g. a product with a big marketing landing), ask the user directly to clarify what you cannot infer. Ask which register describes the **primary** surface. The register can be overridden per task later, but PRODUCT.md carries one default. Settle the default before drafting any register-dependent questions; never batch Persuade-only questions (Conversion & proof) into the same round as the question that decides the register. - -### Platform (ask right after register) - -Every project targets **web** (includes responsive mobile web), **ios**, **android**, or **adaptive** (one codebase, ships both, adapts per OS: Flutter, React Native, KMP). Platform picks the native rulebook: HIG for `ios`, Material 3 for `android`, both for `adaptive`, none for `web`. - -If Step 2 produced a clear hypothesis, lead with it: *"From the codebase, this looks like a [web / ios / android / adaptive] project. Does that match?"* For cross-platform apps, decide by the **design language the app renders**, not the toolchain: one look on both platforms (Flutter's Material-everywhere default) takes that platform's value; genuine per-OS adaptation (Cupertino on iOS, Material on Android) is `adaptive`. When in doubt, `web`. - -A monorepo shipping both a website and a native app gets a PRODUCT.md per app, each with its own `## Platform`; the root PRODUCT.md carries the primary surface's platform. - -### Users & Purpose -- Who uses this? What's their context when using it? -- What job are they trying to get done? -- What is this for? A purpose stated in README or docs is a hypothesis, not strong evidence; confirm it, don't transcribe it. -- What does success look like? -- If more than one kind of user is plausible, confirm a primary and secondary audience; don't manufacture a split that isn't there. An audience implied by another answer (a success metric, a CTA) is still unconfirmed; ask before writing it as secondary. -- If the surface speaks to a different audience than the people who use the product, ask the user to name both. -- For brand: what emotions should the interface evoke? (confidence, delight, calm, urgency) Ask this standalone; don't fold emotions into the success question. -- For product: what workflow are they in? What's the primary task on any given screen? - -### Positioning -- In one line, what does this do that nothing else does? The single strategic claim every screen reinforces. - -### 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? - - 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? - -### Conversion & proof (Persuade surfaces only) - -Ask these only when the primary surface is a Persuade one (marketing, landing, campaigns). Experience and Read surfaces (portfolios, albums, long-form writing, documentation) get no CTA, belief-ladder, or proof questions even when the stored register is `brand`; skip this section and its PRODUCT.md counterpart. - -- What's the primary CTA? -- What's the secondary fallback, for visitors not ready for the primary? -- The one line a visitor should remember after 10 seconds. -- What must the visitor believe, in order, before taking the primary CTA? (The template's belief ladder.) -- What proof is on hand? Ask the user to hand over any testimonials, case studies, press, or client/partner logos they already have. If you can receive files directly, collect them; otherwise create `.impeccable/assets/proof/` and ask the user to add files there. Reference supplied files by path; record text proof inline. - -### Accessibility & Inclusion -- Specific accessibility requirements? (WCAG level, known user needs) -- Considerations for reduced motion, color blindness, or other accommodations? - -Skip questions where the answer is already clear. **Do NOT ask about colors, fonts, radii, or visual styling here.** Those belong in DESIGN.md, not PRODUCT.md. +- visual worlds, palettes, typography, components, or page concepts; +- visitor mode, narrative, CTA/proof sequence, or other surface strategy; +- invented testimonials, customers, benchmarks, pricing, licensing, or deployment claims; +- a requirement to decide every optional field. ## Step 4: Write PRODUCT.md -Write PRODUCT.md only after the user has confirmed the strategic answers from Step 3. If an inferred answer is uncertain or unconfirmed, ask before writing. Confirmed means what the user actually said yes to; do not pad a confirmed answer with extras they never picked (additional anti-references, audiences, roadmap claims, a WCAG level), whether drawn from the crawl, another answer, or your own option text. If an extra belongs in the doc, ask about it first. - -Synthesize into a strategic document: +Write only confirmed facts and explicitly marked open decisions. Omit irrelevant sections rather than filling them with generic prose. ```markdown # Product -## Register - -product - ## Platform web ## Users -[Who they are, their context, the job to be done. Primary audience; a secondary audience or a surface-vs-user split only when they apply.] +[Primary users, their situation, and job. Add other audiences only when confirmed.] ## Product Purpose -[What this product does, why it exists, what success looks like] +[What the product does, why it exists, and what success means.] ## Positioning -[The single strategic claim every screen reinforces. Not a visual rule, not an anti-reference.] +[The product mechanism or claim a neighboring product could not truthfully copy.] -## Conversion & proof -[Persuade surfaces only (marketing, landing, campaigns). Omit this section entirely, heading included, for the product register and for Experience or Read surfaces such as portfolios, albums, or long-form writing.] -- Primary and secondary CTA: [...] -- The line a visitor remembers after 10 seconds: [...] -- Belief ladder: [...] -- Proof on hand: [testimonials, case studies, press, or logos, referenced by path] +## Operating Context +[Workflows, environments, tools, documents, materials, and rituals that are factual parts of using or evaluating the product.] -## Brand Personality -[Voice, tone, 3-word personality, emotional goals] +## Capabilities and Constraints +[Confirmed functionality, technical constraints, terminology, and explicitly undecided product facts.] -## Anti-references -[What this should NOT look like. Specific bad-example sites or patterns to avoid.] +## Brand Commitments +[Existing name, voice, assets, personality, identity constraints, and references the user explicitly made binding. Omit when none exist.] -## Design Principles -[3-5 strategic principles derived from the conversation. Principles like "practice what you preach", "show, don't tell", "expert confidence". NOT visual rules like "use OKLCH" or "magenta accent".] +## Evidence on Hand +[Real content, data, demonstrations, testimonials, case studies, press, or assets, with paths where applicable. State absences that future work must not fabricate.] + +## Product Principles +[Three to five durable strategic principles derived from confirmed answers; no visual recipes.] ## Accessibility & Inclusion -[WCAG level, known user needs, considerations] +[Known user needs or required standard. Omit when no product-specific requirement was established.] ``` -Register is either `brand` or `product` as a bare value. No prose, no commentary. Platform is `web`, `ios`, `android`, or `adaptive`, also a bare value; omit the section only on legacy files you're leaving untouched, otherwise write `web` explicitly. +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. -Write fields as prose, and use bold sparingly: only where a word carries a decision, never as a label lead-in on every line. +### Completion gate -Write to `PROJECT_ROOT/PRODUCT.md`. If `.impeccable.md` existed, the loader already renamed it; merge into that content rather than starting from scratch. +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: Decide on DESIGN.md +## Step 5: Configure live mode when useful -Offer `/impeccable document` either way. Two paths: +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. -- **Code exists** (CSS tokens, components, a running site): "I can generate a DESIGN.md that captures your visual system (colors, typography, components) so variants stay on-brand. Want to do that now?" -- **Pre-implementation** (empty project): "I can seed a starter DESIGN.md from five quick questions about color strategy, type direction, motion energy, and references. You can re-run once there's code, to capture the real tokens. Want to do that now?" +## Step 6: Wrap up or resume -If the user agrees, delegate to `/impeccable document` (it auto-detects scan vs seed). Load its reference and follow that flow. +Summarize captured and deliberately undecided facts. Do not offer DESIGN.md merely because it is missing. -If the user prefers to skip, mention they can run `/impeccable document` any time later. +Recommend the next action from the actual project state: -## Step 6: Configure live mode (when code exists) +- Empty or early project: ask naturally for the surface to be built, or use `/impeccable shape ` when the user wants a confirmed brief without implementation. New-work will establish a visual world only when the requested work needs one. +- Existing coherent interface without DESIGN.md: `/impeccable document` if the user wants the incumbent system recorded independently of a new build. +- Existing surface needing work: name the most relevant scoped command. +- Web project ready for visual iteration: `/impeccable live` when configured. -**Skip this step when the platform is native** (`ios` / `android` / `adaptive`): live mode drives a browser overlay. A hybrid wrapper or Expo web target serving HTML doesn't change that. - -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 .cursor/skills/impeccable/scripts/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) and platform captured (web / ios / android / adaptive) -- 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 or live config is pending, one line on how to set it up later - -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 platform**, offer the 2-4 most relevant (not a menu dump), and give the exact command to type. Group by intent: - -- **Build something new**: `/impeccable craft ` (shape, then build end-to-end) or `/impeccable shape ` (plan first). Lead with this for empty or early-stage projects. -- **Improve what's there**: name the specific surface. `/impeccable critique ` for a scored UX review; `/impeccable audit ` for a11y / perf / responsive checks; `/impeccable polish ` 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** (web only): `/impeccable live` (configured in Step 6) to pick elements in the browser and generate variants in place. **Skip this group for native platforms.** - -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 the user directly to clarify what you cannot infer. Ask whether they'd like a brief summary of PRODUCT.md appended to .cursorrules for easier agent reference. If yes, append a short **Design Context** pointer section there. +If init was invoked by another request, resume without rerunning context.mjs; new-work owns later visual decisions. diff --git a/.cursor/skills/impeccable/reference/ios.md b/.cursor/skills/impeccable/reference/ios.md index ef10ee0d2..ccef5d2c4 100644 --- a/.cursor/skills/impeccable/reference/ios.md +++ b/.cursor/skills/impeccable/reference/ios.md @@ -2,7 +2,7 @@ For native iOS / iPadOS apps: SwiftUI, UIKit, React Native, Expo, Flutter shipping to Apple hardware. -On native, register narrows. HIG conformance governs structure, navigation, and interaction whatever the register; brand expresses through the expressive layer the platform provides (tint, type, motion, content). Calm, Duolingo, and Spotify carry strong identity entirely inside HIG conventions. +On native, the visitor mode narrows what expression may override. HIG conformance governs structure, navigation, and interaction in every mode; brand expresses through the layer the platform leaves open (tint, type, motion, content). ## The iOS slop test diff --git a/.cursor/skills/impeccable/reference/layout.md b/.cursor/skills/impeccable/reference/layout.md index 537a61d84..76cc1e783 100644 --- a/.cursor/skills/impeccable/reference/layout.md +++ b/.cursor/skills/impeccable/reference/layout.md @@ -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. --- -## Register +## 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, register, 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 .cursor/skills/impeccable/scripts/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 `/impeccable polish` for the final pass. +When the structure holds, hand off to `/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) * )`: 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. diff --git a/.cursor/skills/impeccable/reference/live.md b/.cursor/skills/impeccable/reference/live.md index 7867eaa03..e30bede00 100644 --- a/.cursor/skills/impeccable/reference/live.md +++ b/.cursor/skills/impeccable/reference/live.md @@ -33,7 +33,7 @@ Chat is overhead. No recap, no tutorial output, no pasting PRODUCT / DESIGN bodi node .cursor/skills/impeccable/scripts/live.mjs ``` -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. +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, DESIGN.md, and any surface brief already loaded by Setup in mind for variant generation: **DESIGN.md wins on visual decisions; PRODUCT.md wins on durable product and voice decisions; the surface brief wins on this surface's strategy.** 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 requires the user's explicit redesign/replacement intent. `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). @@ -187,7 +187,7 @@ All three carry `fallback: "agent-driven"`. Follow **Handle fallback** below. ### 3. Load the action's reference -If `event.action` is `impeccable` (the default freeform action), use SKILL.md's shared laws plus the loaded register reference (`brand.md` or `product.md`). Do not load a sub-command reference. **Freeform is not a pass to skip parameters:** you still follow the composition budget and the freeform bias in **§7 Parameters** below. Sub-command files list MUST-have signature knobs; freeform has no such file, so sizing knobs from surface weight and primary axes is entirely on you. +If `event.action` is `impeccable` (the default freeform action), use SKILL.md's shared laws and decide the visitor mode from the selected surface. Do not load a sub-command reference. **Freeform is not a pass to skip parameters:** you still follow the composition budget and the freeform bias in **§7 Parameters** below. Sub-command files list MUST-have signature knobs; freeform has no such file, so sizing knobs from surface weight and primary axes is entirely on you. Any other `event.action` (`bolder`, `quieter`, `distill`, `polish`, `typeset`, `colorize`, `layout`, `adapt`, `animate`, `delight`, `overdrive`): Read `reference/.md` before planning. Each sub-command encodes a specific discipline; skipping its reference produces generic output. Those files may require specific params; layer them on top of the §7 budget, not instead of it. @@ -216,7 +216,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. @@ -224,10 +224,7 @@ This sentence is the **identity lock**. Every variant must be readable as the sa **Default mode**: the existing identity is preserved. Variants vary expression axes within it. *This is the right mode for ~90% of live sessions.* The user picked an element on a real product they're shipping; they expect variants of *their* hero, not three different brands' heroes. -**Departure mode**: the existing identity is rejected. Variants propose alternatives consistent with PRODUCT.md voice. Trigger only when at least one is true: - -- PRODUCT.md anti-references explicitly call out the current surface ("the current `index.html` is itself an example"; "diffuse away from this"; "the page on screen is the failure"). Generic anti-references that describe what to avoid in general do **not** trigger departure mode; only ones that point at *this* surface specifically. -- The user's freeform prompt explicitly asks for departure ("rebuild this from scratch", "what if it weren't editorial at all", "show me something completely different"). +**Departure mode**: the existing identity is rejected. Variants propose alternatives consistent with durable product and brand truth. Trigger only when the user explicitly asks for departure in the current request or freeform prompt ("redesign this", "rebuild this from scratch", "what if it weren't editorial at all", "show me something completely different"). A stale page critique or an old task note is not replacement authorization. If you're unsure, you're in default mode. The cost of being wrong about default is "three on-brand variants with similar feel": recoverable, the user picks none. The cost of being wrong about departure is "three off-brand variants": unrecoverable, the user is annoyed. @@ -246,13 +243,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 the brand's stated voice and register in PRODUCT.md. Do NOT pick from a fixed catalog of lane categories. The right three directions for this brand are not the same as the right three for another brand, 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. @@ -262,7 +259,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. @@ -289,7 +286,7 @@ In **default mode**, the prompt narrows the axes you choose, not the identity. * In **departure mode**, the prompt narrows the lanes you draw from, not the families. *"Make it feel like a newspaper front page"* would itself be a departure-mode prompt; honor it but pick three meaningfully different newspaper-adjacent lanes (broadsheet vs. tabloid vs. trade journal), and run the family pass to confirm they don't collapse into one. -When the prompt and PRODUCT.md anti-references conflict (the prompt asks for X, the anti-references ban X), the anti-references win; they describe the brand's standing position, the prompt is one moment. +When the prompt conflicts with a confirmed binding brand commitment or DESIGN.md invariant, preserve the invariant unless the user explicitly revokes or replaces it. Task-local strategy from the matching surface brief may change when the user changes that surface's goal. ### 6. Write all variants in a single edit diff --git a/.cursor/skills/impeccable/reference/new-work.md b/.cursor/skills/impeccable/reference/new-work.md index 5bd2e5660..aea520f97 100644 --- a/.cursor/skills/impeccable/reference/new-work.md +++ b/.cursor/skills/impeccable/reference/new-work.md @@ -1,47 +1,178 @@ -# New identity work +# New visual work -Read this only when the project has no committed visual identity, or the user explicitly asked to replace the current one. A missing DESIGN.md is not enough: code, tokens, chosen type, components, and assets are incumbent design authority. If they exist, preserve and extend them unless the brief says to discard them. +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. -This playbook is task-scoped. PRODUCT.md owns durable product facts and DESIGN.md owns durable visual invariants; the direction for one page or feature belongs in the current task, not in either document. +## 1. Name the intent -## Choose the authority +- **Greenfield:** no coherent visual implementation. +- **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. -Classify the work before inventing anything: +A plain “redesign this page/site” authorizes replacement. “Redesign this within the current brand/system” means extension or refinement. Ask once only when the wording is genuinely ambiguous. -- **Extend**: an incumbent system exists and the task adds or improves a surface. Preserve its lineage, semantics, and affordances. -- **Surface redesign**: the current identity remains, but this surface may change its topology, hierarchy, and expression. -- **New identity**: nothing committed exists, or the user explicitly authorized a rebrand. +## 2. Resolve visual authority -When the evidence is mixed, preserve. Novelty is not permission to erase authored decisions. +Read DESIGN.md and representative code, tokens, components, and assets. Choose one path: -## Find the concept +### A. Explicit redesign -Name the subject, audience, page job, and visitor mode from SKILL.md. Then identify the product's unique mechanism: what it does, proves, or enables that a neighboring product cannot truthfully claim. +The old DESIGN.md and implementation are not authority. Keep only unrevoked product facts, content, function, native expectations, constraints, and brand commitments. Establish a replacement world. -Derive the concept from that mechanism through one primary transformation: information topology, evidence, temporal behavior, or spatial mapping. The transformation must change how the surface is organized or behaves, not merely how it is decorated. This produces stranger and more defensible work than imitating a culturally specific document or object. Borrowing a literal cultural form is allowed only when it is genuinely native to the product and audience, and never as costume. +### B. DESIGN.md covers this kind of surface -For **Operate** and **Read**, start with the task and information structure. Native controls remain native controls; expression comes from hierarchy, density, rhythm, state, and the system around them. For **Persuade** and **Experience** creating a new identity, explore more freely, but keep the product legible and the primary action obvious. +Use its invariants and normative tokens. Skip world-building and discover the surface. -Run `node .cursor/skills/impeccable/scripts/concept-seed.mjs` only for Persuade or Experience new-identity work when two or more directions remain equally credible. It is an entropy source, not an authority: never let the draw overrule the strongest fit. When color is genuinely unconstrained, `node .cursor/skills/impeccable/scripts/palette.mjs` may break a reflex palette; subject evidence and incumbent color still win. +### C. A coherent implementation exists but DESIGN.md does not -## State the direction +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. -Before code, write a task-scoped direction contract of at most 120 words in your reasoning: +### D. The brand exists, but a whole-surface family is unresolved -- the thesis this surface owns; -- the structural or behavioral transformation; -- what authority is preserved; -- the first viewport's composition and primary action; -- the signature element the surface will be remembered by. +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. -Do not embed this contract as a comment in the production artifact or turn it into a second design system. In an attended substantial build, confirm it once. In an unattended run, record it and continue. +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. -## Build once, commit fully +### E. No confirmed visual authority exists -Plan one compact token system and one layout source: the concept or the content's real structure. Build the strongest coherent direction once. Commitment means the transformation governs the whole surface; it does not mean every control must be rebuilt as a metaphor. +Establish a world. Scaffolds, framework defaults, and stray utilities are not identity. -Make the first viewport demonstrate the product's mechanism. Pace long surfaces through contrast in density and scale, and cut sections that only repeat claims. Briefs that depend on imagery ship real, verified imagery. Preserve semantic HTML, familiar interaction behavior, accessibility, and the project's technical conventions. +## 3. Discover the requested surface -## Finish proportionally +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. -Inspect desktop and mobile, write one honest critique against the brief and direction, patch material defects, then run the detector once. Repeat only when a real defect remains. A separate reviewer is optional when the harness already provides one and the extra cost is earned by the risk; it is not a default step and its presence is not evidence of quality. +Ask one attended round of at most three material questions without repeating durable facts. CTA hierarchy, proof sequence, content gaps, and interaction outcomes belong here, not PRODUCT.md. For a fully specified narrow request, state the interpretation and invite correction. + +When `shape` has already completed its discovery interview, reuse those confirmed answers and do not ask this round again. + +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. Each candidate joins a durable visual system (identity thesis, information/layout grammar, material and type behavior, color strategy, imagery, motion, reusable signature) to a concrete expression for this surface (topology, sequence, focal moment, and implementation consequence). Do not rank yet. +3. **Break the ranking rut once.** Run `node .cursor/skills/impeccable/scripts/concept-seed.mjs --scope direction`. Promote the assigned grounded pair and weigh its challengers only when they can become both a coherent system and a strong solution to this task. +4. **Test coupling and breadth.** Reject a pair if its surface could swap into another candidate unchanged, or if its world is merely the surface motif repeated. Test each survivor on the requested surface plus navigation, quiet and dense content, interaction/state, and one unlike future surface. Compare it with both the category habit and predictable contrarian response. +5. **Offer coupled choices.** Present two or three materially different pairs without recommendation cues. For each, show the world rules, this surface's 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. 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 .cursor/skills/impeccable/scripts/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 promoted candidate when a roll ran; otherwise use the strongest grounded structure. It must survive both tests. + +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). + +### Write or update DESIGN.md + +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: + +`` + +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 .cursor/skills/impeccable/scripts/surface-brief.mjs read ` + +Exit 0: preserve still-valid decisions and change only what the user changed. For redesign, retain valid product strategy, content, function, and open decisions; replace visual direction and contract. Exit 2: no brief. After the contract, write with `node .cursor/skills/impeccable/scripts/surface-brief.mjs write [related-target ...]`. + +The body is concise and contains: + +```markdown +# Surface brief: [name] + +## Scope +[Primary/related route or artifact, visitor mode, and what this surface owns.] + +## Product strategy +[Surface-specific audience and job, desired outcome, primary/secondary action or task, content and proof sequence, factual constraints, and explicitly open decisions.] + +## Selected direction +[Reference to the applicable DESIGN.md world or expression range, selected surface concept, focal moment, narrative/interaction sequence, and implementation consequence.] + +## Direction contract +[The six contract blocks below.] + +## Open decisions +[Only unresolved items that later work must not silently invent. Omit when empty.] +``` + +Commit `.impeccable/surfaces/.md` as stable later-work context. Exclude global truth, exact tokens, transient notes, and work logs. + +## 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. + +Before code, write a direction contract of at most 150 words in an opening HTML or framework comment. The first 200 characters must name `DIRECTION CONTRACT`. + +- `UNIQUE`: the task thesis tied to the product mechanism; +- `NOT-TEMPLATE`: the category-default arrangement this structure refuses; +- `OWN-WORLD`: the confirmed DESIGN.md invariants, tokens, and materials it uses; +- `STORY`: what the visitor understands, believes, and does; +- `FIRST VIEWPORT`: exact composition, hierarchy, action, and where the concept exceeds competent convention; +- `FORM`: chosen structure or behavior, signature, implementation consequence, and seed key. + +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. + +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. + +## 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. + +Build the strongest coherent direction once. Its grammar governs navigation, actions, controls, content, and transitions without disguising affordances. Give the focal form the scale that gives it force; do not trap it inside a standard hero panel. + +**Make the opening a thesis.** Demonstrate the mechanism immediately; leave an idea, interaction, or evidence, not merely mood. + +**Commit before correcting.** Land the hard move at full strength before refining it. In unattended work, safety is the known risk. + +**Commit at page scale.** Let color, material, image, or type own a region when the world calls for it. Scattered signature decoration is not commitment. + +**Pace the whole surface.** Vary density, scale, image, motion, and quiet inside one grammar. Cut repeated claims; prove the mechanism with real artifact, interaction, data, transformation, or content. + +**Author motion as material.** Motion expresses the world and task. Premium moments go beyond repeated transform/opacity through earned focus, depth, masks, continuity, light, or material change. Bound expensive effects, test in-browser, keep content visible by default, and design reduced motion. + +Preserve semantics, affordances, accessibility, performance, responsiveness, and project conventions. Operate/Read express through topology, hierarchy, density, rhythm, and state; Persuade/Experience may earn drama. + +## 8. Solidify the visual record + +After first implementation of a new/replacement world or approved expansion, refresh DESIGN.md from the build: + +- replace provisional direction with the exact type, color roles, tokens, spacing/radii, components, states, and motion that survived; +- add normative YAML tokens only for values the implementation actually uses; +- remove the directional-seed status once the record and implementation agree; +- preserve broader world invariants and expression ranges; +- do not promote the task's story, hero composition, or one-off motif into a global rule unless it is intentionally reusable. + +Ordinary extension does not rewrite DESIGN.md; only approved durable changes do. + +## 9. Finish like a 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. diff --git a/.cursor/skills/impeccable/reference/operate.md b/.cursor/skills/impeccable/reference/operate.md index 04ee9d7d2..f420cbaa2 100644 --- a/.cursor/skills/impeccable/reference/operate.md +++ b/.cursor/skills/impeccable/reference/operate.md @@ -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. - No orchestrated page-load sequences. Product loads into a task; users don't want to watch it load. -## Product bans (on top of the shared absolute bans) +## Product constraints - Decorative motion that doesn't convey state. - Inconsistent component vocabulary across screens. If the "save" button looks different in two places, one is wrong. @@ -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. diff --git a/.cursor/skills/impeccable/reference/polish.md b/.cursor/skills/impeccable/reference/polish.md index da5c541bc..ba00d9443 100644 --- a/.cursor/skills/impeccable/reference/polish.md +++ b/.cursor/skills/impeccable/reference/polish.md @@ -1,241 +1,98 @@ -> **Additional context needed**: quality bar (MVP vs flagship). +> **Additional context needed**: quality bar and shipping constraints. -Perform a meticulous final pass to catch all the small details that separate good work from great work. The difference between shipped and polished. +Polish is refinement, never concealed redesign. Preserve the incumbent visual world, content, behavior, and everything outside scope. If the concept itself is wrong, say so and recommend redesign or `bolder` instead of smuggling in a replacement. -Detector and automated QA output are defect evidence only. A clean script result is never proof that the design is strong; gather browser evidence and inspect the real interaction path. +A detector result is defect evidence, not proof of quality. Inspect the rendered experience and real interaction path. -## Design System Discovery +## 1. Establish the system -Aligning the feature to the design system is **not optional**. Polish without alignment is decoration on top of drift, and it makes the next person's job harder. Discovery comes before any other polish work. +Read DESIGN.md and representative tokens, shared components, patterns, and neighboring flows. If no formal system exists, use coherent project conventions. -1. **Find the design system**: Search for design system documentation, component libraries, style guides, or token definitions. Study the core patterns: design principles, target audience, color tokens, spacing scale, typography styles, component API, motion conventions. -2. **Note the conventions**: How are shared components imported? What spacing scale is used? Which colors come from tokens vs hard-coded values? What motion and interaction patterns are established? What flow shapes are used for comparable actions (modal vs full-page, inline vs route, save-on-blur vs explicit submit)? -3. **Identify drift, then name the root cause**: For every deviation, classify it as a **missing token** (the value should exist in the system but doesn't), a **one-off implementation** (a shared component already exists but wasn't used), or a **conceptual misalignment** (the feature's flow, IA, or hierarchy doesn't match neighboring features). The fix differs by category: patch the value, swap to the shared component, or rework the flow. Fixing the symptom without naming the cause is how drift compounds. +Classify each drift before fixing it: -If a design system exists, polish **must** align the feature with it. If none exists, polish against the conventions visible in the codebase. **If anything about the system is ambiguous, ask. Never guess at design system principles.** +- **missing token:** the system needs a reusable value; +- **one-off implementation:** an existing shared component or pattern should replace it; +- **conceptual mismatch:** the flow, information architecture, or hierarchy differs from comparable product areas; +- **local defect:** the implementation is simply incomplete or inconsistent. -## Pre-Polish Assessment +Fix the cause at the narrowest correct level. Ask when a binding system principle cannot be inferred. -Understand the current state and goals before touching anything: +## 2. Gather the evidence -1. **Review completeness**: - - Is it functionally complete? - - Are there known issues to preserve (mark with TODOs)? - - What's the quality bar? (MVP vs flagship feature?) - - When does it ship? (How much time for polish?) +Use the feature yourself at representative desktop and mobile sizes. Determine: -2. **Think experience-first**: Who actually uses this, and what's the best possible experience for them? Effective design beats decorative polish; a feature that looks beautiful but fights the user's flow is not polished. Walk the path from their perspective before opening DevTools. +- whether the path is functionally complete; +- the intended quality bar and time available; +- known constraints or deliberately unfinished work; +- the states, content lengths, roles, and input methods users will actually encounter. -3. **Identify polish areas**: - - Visual inconsistencies - - Spacing and alignment issues - - Interaction state gaps - - Copy inconsistencies - - Edge cases and error states - - Loading and transition smoothness - - Information architecture and flow drift (does this feature reveal complexity the way neighboring features do?) +If a prior critique exists, use it as one input: -4. **Pull in any prior critique** (optional signal): If `/impeccable critique` has been run on the same target, its priority issues are a useful prior for what to address first. Resolve the target to a file path or URL, then: - ```bash - slug=$(node .cursor/skills/impeccable/scripts/critique-storage.mjs slug "") - node .cursor/skills/impeccable/scripts/critique-storage.mjs latest "$slug" - ``` - Exit 0 with body = found; fold the P0/P1 items into your polish list and mention the snapshot path so the user sees what you read. Exit 2 = no snapshot, continue without it. The critique is one input among many. Do your own pass either way. +```bash +slug=$(node .cursor/skills/impeccable/scripts/critique-storage.mjs slug "") +node .cursor/skills/impeccable/scripts/critique-storage.mjs latest "$slug" +``` -5. **Triage cosmetic vs functional**: Classify each issue as **cosmetic** (looks off, doesn't impede the user) or **functional** (breaks, blocks, or confuses the experience). When polish time is tight, functional issues ship first; cosmetic ones can land in a follow-up. Quality should be consistent; never perfect one corner while leaving another rough. +Exit 0 returns the latest snapshot; incorporate relevant P0/P1 findings and name the snapshot read. Exit 2 means none exists. Perform an independent pass either way. -**CRITICAL**: Polish is the last step, not the first. Don't polish work that's not functionally complete. +## 3. Triage -## Polish Systematically +Separate functional defects from cosmetic ones and fix in this order: -Work through these dimensions methodically: +1. broken or blocked tasks, data loss, misleading state, and inaccessible paths; +2. missing loading, empty, error, success, disabled, and permission states; +3. flow, hierarchy, responsive, and design-system drift; +4. visual and motion inconsistencies; +5. code and asset cleanup. -### Visual Alignment & Spacing +Do not perfect one corner while leaving the rest below the same quality bar. -- **Pixel-perfect alignment**: Everything lines up to grid -- **Consistent spacing**: All gaps use spacing scale (no random 13px gaps) -- **Optical alignment**: Adjust for visual weight (icons may need offset for optical centering) -- **Responsive consistency**: Spacing and alignment work at all breakpoints -- **Grid adherence**: Elements snap to baseline grid +## 4. Polish the whole path -**Check**: -- Enable grid overlay and verify alignment -- Check spacing with browser inspector -- Test at multiple viewport sizes -- Look for elements that "feel" off +### Flow and hierarchy -### Information Architecture & Flow +- Match neighboring mental models, terminology, disclosure, routing, save behavior, and optimistic or pessimistic patterns. +- Make the primary task and current state obvious without flattening every element to equal weight. +- Ensure arrival, transition, empty, and recovery paths connect instead of behaving as isolated screens. -Visual polish on a misshapen flow is wasted work. Match the *shape* of the experience to the system, not just the surface. +### Layout and type -- **Progressive disclosure**: Match how much is revealed when, compared to neighboring features. A settings page exposing 40 fields when the rest of the app reveals 5 at a time is drift, even if every field is perfectly styled. -- **Established user flows**: Multi-step actions follow the same shape as comparable flows elsewhere: modal vs full-page, inline edit vs separate route, save-on-blur vs explicit submit, optimistic vs pessimistic updates. -- **Hierarchy & complexity**: The same conceptual weight gets the same visual weight throughout. Primary actions don't become tertiary in one corner of the product, and tertiary actions don't shout. -- **Empty, loading, and arrival transitions**: How content arrives, updates, and leaves matches how it does in adjacent features. -- **Naming and mental model**: The feature uses the same nouns and verbs as the rest of the system. A "Workspace" here shouldn't be a "Project" three screens away. +- Align to the project's grid and spacing scale; fix optical as well as mathematical alignment. +- Group related content tightly and separate distinct groups generously. +- Keep same-role typography consistent; test measure, wrapping, localization expansion, zoom, and font loading. +- Verify every supported viewport rather than correcting only the current screenshot. -### Typography Refinement +### Color, imagery, and icons -- **Hierarchy consistency**: Same elements use same sizes/weights throughout -- **Line length**: 45-75 characters for body text -- **Line height**: Appropriate for font size and context -- **Widows & orphans**: No single words on last line -- **Hyphenation**: Appropriate for language and column width -- **Kerning**: Adjust letter spacing where needed (especially headlines) -- **Font loading**: No FOUT/FOIT flashes +- Use semantic tokens and stable color meanings across themes. +- Verify text, control, and focus contrast in every state. +- Keep icon families, stroke/weight, sizing, and optical alignment coherent. +- Prevent image layout shift; use correct aspect ratios, responsive sources, and useful alt text. -### Color & Contrast +### Interaction and state -- **Contrast ratios**: All text meets WCAG standards -- **Consistent token usage**: No hard-coded colors, all use design tokens -- **Theme consistency**: Works in all theme variants -- **Color meaning**: Same colors mean same things throughout -- **Accessible focus**: Focus indicators visible with sufficient contrast -- **Gray on color**: Never put gray text on colored backgrounds; use a shade of that color or transparency +- Every control needs appropriate default, hover, focus, active, disabled, loading, error, and success behavior. +- Preserve visible keyboard focus, logical tab order, labels, and platform-appropriate touch targets. +- Keep motion coherent, interruptible, performant, and reduced-motion aware. Do not add animation merely to make polish visible. +- Validate long, missing, localized, offline, slow, and permission-limited content where the product can encounter it. -### Interaction States +### Content and code -Every interactive element needs all states: +- Keep terminology, capitalization, punctuation, and factual copy consistent. Ask before changing claims. +- Remove debug output, dead code, unused imports, obsolete styles, and polish-created duplication. +- Replace custom implementations with shared components where the system owns the pattern. +- Promote genuinely reusable values to tokens; do not create a system abstraction for one local exception. -- **Default**: Resting state -- **Hover**: Subtle feedback (color, scale, shadow) -- **Focus**: Keyboard focus indicator (never remove without replacement) -- **Active**: Click/tap feedback -- **Disabled**: Clearly non-interactive -- **Loading**: Async action feedback -- **Error**: Validation or error state -- **Success**: Successful completion +## 5. Verify and finish -**Missing states create confusion and broken experiences**. +Walk the complete path again with mouse, keyboard, and touch where applicable. Check: -### Micro-interactions & Transitions +- mobile, intermediate, and wide layouts; +- loading, empty, error, success, disabled, long-content, and missing-content states; +- zoom, contrast, focus, semantics, screen-reader names, and reduced motion; +- console errors, layout shift, interaction latency, image loading, and supported browsers; +- agreement with DESIGN.md, neighboring features, and the user's scope. -- **Smooth transitions**: All state changes animated appropriately (150-300ms) -- **Consistent easing**: Use ease-out-quart/quint/expo for natural deceleration. Never bounce or elastic; they feel dated. -- **No jank**: Smooth animations; use atmospheric blur/filter/mask/shadow effects when they add polish, but bound expensive paint areas and avoid casual layout-property animation -- **Appropriate motion**: Motion serves purpose, not decoration -- **Reduced motion**: Respects `prefers-reduced-motion` +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. -### Content & Copy - -- **Consistent terminology**: Same things called same names throughout -- **Consistent capitalization**: Title Case vs Sentence case applied consistently -- **Grammar & spelling**: No typos -- **Appropriate length**: Not too wordy, not too terse -- **Punctuation consistency**: Periods on sentences, not on labels (unless all labels have them) - -### Icons & Images - -- **Consistent style**: All icons from same family or matching style -- **Appropriate sizing**: Icons sized consistently for context -- **Proper alignment**: Icons align with adjacent text optically -- **Alt text**: All images have descriptive alt text -- **Loading states**: Images don't cause layout shift, proper aspect ratios -- **Retina support**: 2x assets for high-DPI screens - -### Forms & Inputs - -- **Label consistency**: All inputs properly labeled -- **Required indicators**: Clear and consistent -- **Error messages**: Helpful and consistent -- **Tab order**: Logical keyboard navigation -- **Auto-focus**: Appropriate (don't overuse) -- **Validation timing**: Consistent (on blur vs on submit) - -### Edge Cases & Error States - -- **Loading states**: All async actions have loading feedback -- **Empty states**: Helpful empty states, not just blank space -- **Error states**: Clear error messages with recovery paths -- **Success states**: Confirmation of successful actions -- **Long content**: Handles very long names, descriptions, etc. -- **No content**: Handles missing data gracefully -- **Offline**: Appropriate offline handling (if applicable) - -### Responsiveness - -- **All breakpoints**: Test mobile, tablet, desktop -- **Touch targets**: 44x44px minimum on touch devices -- **Readable text**: No text smaller than 14px on mobile -- **No horizontal scroll**: Content fits viewport -- **Appropriate reflow**: Content adapts logically - -### Performance - -- **Fast initial load**: Optimize critical path -- **No layout shift**: Elements don't jump after load (CLS) -- **Smooth interactions**: No lag or jank -- **Optimized images**: Appropriate formats and sizes -- **Lazy loading**: Off-screen content loads lazily - -### Code Quality - -- **Remove console logs**: No debug logging in production -- **Remove commented code**: Clean up dead code -- **Remove unused imports**: Clean up unused dependencies -- **Consistent naming**: Variables and functions follow conventions -- **Type safety**: No TypeScript `any` or ignored errors -- **Accessibility**: Proper ARIA labels and semantic HTML - -## Polish Checklist - -Go through systematically: - -- [ ] Aligned to the design system (drift named and resolved by root cause) -- [ ] Information architecture and flow shape match neighboring features -- [ ] Visual alignment perfect at all breakpoints -- [ ] Spacing uses design tokens consistently -- [ ] Typography hierarchy consistent -- [ ] All interactive states implemented -- [ ] All transitions smooth (60fps) -- [ ] Copy is consistent and polished -- [ ] Icons are consistent and properly sized -- [ ] All forms properly labeled and validated -- [ ] Error states are helpful -- [ ] Loading states are clear -- [ ] Empty states are welcoming -- [ ] Touch targets are 44x44px minimum -- [ ] Contrast ratios meet WCAG AA -- [ ] Keyboard navigation works -- [ ] Focus indicators visible -- [ ] No console errors or warnings -- [ ] No layout shift on load -- [ ] Works in all supported browsers -- [ ] Respects reduced motion preference -- [ ] Code is clean (no TODOs, console.logs, commented code) - -**IMPORTANT**: Polish is about details. Zoom in. Squint at it. Use it yourself. The little things add up. - -Sweat the details. Zoom in until the alignment is right and the spacing reads as deliberate. Then ship. - -**NEVER**: -- Polish before it's functionally complete -- Polish without aligning to the design system; that's decoration on drift -- Guess at design system principles instead of asking when something is ambiguous -- Spend hours on polish if it ships in 30 minutes (triage) -- Introduce bugs while polishing (test thoroughly) -- Ignore systematic issues (if spacing is off everywhere, fix the system, not just one screen) -- Perfect one thing while leaving others rough (consistent quality level) -- Create new one-off components when design system equivalents exist -- Hard-code values that should use design tokens -- Introduce new patterns or flows that diverge from established ones - -## Final Verification - -Before marking as done: - -- **Use it yourself**: Actually interact with the feature. -- **Test on real devices**: Not just browser DevTools. -- **Ask someone else to review**: Fresh eyes catch things. -- **Compare to design**: Match intended design. -- **Check all states**: Don't just test happy path. -- **Treat automation carefully**: Run detector or QA commands when they are available and relevant, fix their defects, but never cite a clean result as proof that the work is polished. - -## Clean Up - -After polishing, ensure code quality: - -- **Replace custom implementations**: If the design system provides a component you reimplemented, switch to the shared version. -- **Remove orphaned code**: Delete unused styles, components, or files made obsolete by polish. -- **Consolidate tokens**: If you introduced new values, check whether they should be tokens. -- **Verify DRYness**: Look for duplication introduced during polishing and consolidate. +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. diff --git a/.cursor/skills/impeccable/reference/quieter.md b/.cursor/skills/impeccable/reference/quieter.md index 55b00b6f3..bb163e40f 100644 --- a/.cursor/skills/impeccable/reference/quieter.md +++ b/.cursor/skills/impeccable/reference/quieter.md @@ -2,7 +2,7 @@ Quiet design is harder than bold design. Subtlety needs precision. Reduce visual --- -## Register +## Visitor mode Persuade + Experience: "quieter" means more restrained palette, more whitespace, more typographic air. Drama is reduced, not eliminated; the POV stays intact. diff --git a/.cursor/skills/impeccable/reference/routing.md b/.cursor/skills/impeccable/reference/routing.md index fef7ca52d..0568954d5 100644 --- a/.cursor/skills/impeccable/reference/routing.md +++ b/.cursor/skills/impeccable/reference/routing.md @@ -11,7 +11,7 @@ Reason over the signals; there is no score to obey: - `critique.latest` with a low `score` or non-zero `p0` / `p1` → `polish` (it reads that snapshot as its backlog), or re-run `critique` if the snapshot looks stale. - `git.changedFiles` pointing at one surface → scope `audit` or `polish` to those files specifically, naming them. - `devServer.running` true → `live` is available for in-browser iteration; if false, don't lead with `live`. **`live` and the bundled `detect.mjs` are web-only.** If `setup.platform` is `ios`, `android`, or `adaptive`, don't lead with either; the browser overlay and the HTML rule engine don't apply to native app code. -- Otherwise group by intent exactly as init's "Recommend starting points" step does (build new / improve what's there / iterate visually), tailored to `setup.register`. +- Otherwise group by intent (build new / improve what's there / iterate visually), tailored to the current surface and `setup.platform`. **If `scan.targets` is non-empty and `setup.platform` is not `ios`/`android`/`adaptive`, run `node .cursor/skills/impeccable/scripts/detect.mjs --json ` once** (the bundled detector over local files: no network, no npx; it reads HTML/CSS, so skip it for native projects). `scan.via` tells you what they are: `git-changes` (the markup/style files in your dirty tree, the most relevant set), `source-dir` (e.g. `src`, `app`), `html`, or `root`. Fold the hits into your picks: many quality / contrast hits → `audit` or `polish`; a specific slop family → the matching command (gradient text or eyebrows → `quieter` / `typeset`, flat or gray palette → `colorize`, and so on). It's a real, current signal that beats guessing. If detect errors or the tree is large and slow, skip it and recommend the user run `audit` themselves; never block the suggestion on it. diff --git a/.cursor/skills/impeccable/reference/shape.md b/.cursor/skills/impeccable/reference/shape.md index 40804f6e4..acbb26e5b 100644 --- a/.cursor/skills/impeccable/reference/shape.md +++ b/.cursor/skills/impeccable/reference/shape.md @@ -1,167 +1,61 @@ -Shape the UX and UI for a feature before any code is written. This command produces a **design brief**: a structured artifact that guides implementation through discovery, not guesswork. +# Shape -**Scope**: Design planning only. This command does NOT write code. It produces the thinking that makes code good. +Discover what should be made and how it should work, then return a confirmed design brief without code. -**Output**: A design brief that can be handed off to /impeccable craft, or directly to /impeccable for freeform implementation. When visual direction probes are used, the images are supporting artifacts, not the primary output. +**Product gate:** when context reports that PRODUCT.md is missing, load and complete [init.md](init.md) before Phase 1. Do not substitute shape questions for the product interview. Once PRODUCT.md exists, return here; product context does not replace task discovery. -## Philosophy +## Phase 1: Discovery interview -Most AI-generated UIs fail not because of bad code, but because of skipped thinking. They jump to "here's a card grid" without asking "what is the user trying to accomplish?" This command inverts that: understand deeply first, so implementation is precise. +Do not write code or choose visual direction yet. -## Phase 1: Discovery Interview +### Cadence -**Do NOT write any code or make any design decisions during this phase.** Your only job is to understand the feature deeply enough to make excellent design decisions later. +- Use the structured question tool when available; otherwise ask and stop. +- Ask two or three related questions per round, then wait. One round is the default; add a second only when the answers expose a material gap. +- Do not dump a questionnaire, repeat settled facts, or turn obvious facts into menus. Assert the likely reading and invite correction. +- A sparse prompt requires at least one answer round. A precise prompt may need only a compact confirmation. -**Unattended runs:** when no user can respond (a one-shot task, an automated run, or an explicit instruction not to ask questions), answer the interview questions yourself from the brief, record the answers as the design brief, and continue without pausing at any confirmation gate in this file. The thinking is still required; only the waiting is waived. +### Round 1: purpose, people, and outcome -This is a required interaction, not optional guidance. Ask these questions in conversation, adapting based on answers. Don't dump them all at once; have a natural dialogue. ask the user directly to clarify what you cannot infer. +Choose the two or three questions that most change the result: -### Interview cadence +- What is this surface or feature for, and what problem must it solve? +- Who specifically reaches it, in what situation and state of mind? +- What is the primary thing they must understand or do? What would success look like? +- What is uniquely true here that a neighboring product or generic template could not claim? -Discovery includes at least one user-answer round unless PRODUCT.md, DESIGN.md, or an already-confirmed brief directly answers the needed inputs. With a sparse prompt, do **not** synthesize a complete brief for confirmation on the first response. +### Round 2: material, behavior, and boundaries -- Use the harness's structured question tool when one exists. Otherwise, ask directly in chat and stop. -- Ask **2-3 questions per round**, then wait for answers. -- Treat PRODUCT.md and DESIGN.md as anchors; they reduce repeated questions but do **not** replace shape for craft. Shape is task-specific. -- One round is the default. Add a second only if the first answers leave material gaps. Don't run a second round just to feel thorough. -- Round 1 should clarify purpose, audience/context, content/scope, and (for brand) visual direction. -- Round 2, when needed, fills in whatever's still genuinely missing. +Run only for material unresolved decisions: -**Assert-then-confirm, not menu-with-escape.** When PRODUCT.md and the user's prompt make one option obvious, name it and ask the user to confirm or override. Don't enumerate "Restrained / Committed / Or something else?" as a real choice; "This reads as Restrained, confirm?" beats a four-option menu when the answer is already clear. +- What real content, evidence, data, and assets must the experience carry? What are realistic minimum, typical, and maximum ranges? +- Which states and transitions matter: first-run, empty, loading, error, success, permissions, overflow, or expert use? +- What is the intended fidelity, breadth, and interactivity: exploration, production-ready screen, full flow, or broader surface? +- What must remain untouched? What would make the result feel wrong even if it looked polished? +- Which platform, framework, performance, accessibility, localization, or delivery constraints are binding? -### Purpose & Context -- What is this feature for? What problem does it solve? -- Who specifically will use it? (Not "users"; be specific: role, context, frequency) -- What does success look like? How will you know this feature is working? -- What's the user's state of mind when they reach this feature? (Rushed? Exploring? Anxious? Focused?) +Never ask for CSS values or canned aesthetic lanes. New-work owns visual-world and concept choices. -### Content & Data -- What content or data does this feature display or collect? -- What are the realistic ranges? (Minimum, typical, maximum, e.g., 0 items, 5 items, 500 items) -- What are the edge cases? (Empty state, error state, first-time use, power user) -- Is any content dynamic? What changes and how often? -- What visual assets are real content here? Note required images, product shots, illustrations, maps, textures, diagrams, generated objects, or existing project assets. +## Phase 2: Resolve the design direction -### Design Direction +For new surfaces, brand expansion, or replacement, follow [new-work.md](new-work.md) through visual authority, any world workshop, and concept choice. Reuse discovery, then return before its contract, persistence, or implementation. Inside an established world, use its concept process only when composition or interaction remains materially open. -Force a visual decision on three fronts. Skip anything PRODUCT.md or DESIGN.md already answers; ask only what's missing. +## Phase 3: Write the brief -- **Color strategy for this surface.** Pick one: Restrained / Committed / Full palette / Drenched. Can override the project default if the surface earns it (e.g. a drenched hero inside an otherwise Restrained product). -- **Theme via scene sentence.** Write one sentence of physical context for this surface: who uses it, where, under what ambient light, in what mood. The sentence forces dark vs light. If it doesn't, add detail until it does. -- **Two or three named anchor references.** Specific products, brands, objects. Not adjectives like "modern" or "clean." +Write the smallest useful brief: -### Scope +1. **Job and audience:** who arrives, their context, need, and visitor mode. +2. **Outcome and proof:** primary task/action, success, real evidence, and product-specific truth. +3. **Selected direction:** visual authority, structural/interaction thesis, sequence, focal moment, and implementation consequence. +4. **Scope and boundaries:** fidelity, breadth, interactivity, named target, what remains untouched, and explicit anti-goals. +5. **States and ranges:** realistic content/data ranges and material states. +6. **Interaction and layout:** hierarchy, topology, responsiveness, affordances, feedback, and transitions; intent, not CSS. +7. **Constraints and open decisions:** platform, delivery, accessibility, localization, reusable components, and choices a builder must not invent. -Always ask. Sketch quality and shipped quality are different outputs; don't guess between them. +Use three to five bullets when the task is settled; use the full structure only for ambiguous, multi-screen, or standalone planning. Do not restate the conversation. -- **Fidelity.** Sketch / mid-fi / high-fi / production-ready? -- **Breadth.** One screen / a flow / a whole surface? -- **Interactivity.** Static visual / interactive prototype / shipped-quality component? -- **Time intent.** Quick exploration, or polish until it ships? +## Confirm and stop -Scope answers are task-scoped. Don't write them to PRODUCT.md or DESIGN.md; carry them through the design brief only. +Present the brief for explicit confirmation or one correction round, then stop: shape never writes code or a direction contract. -### Constraints -- Are there technical constraints? (Framework, performance budget, browser support) -- Are there content constraints? (Localization, dynamic text length, user-generated content) -- Mobile/responsive requirements? -- Accessibility requirements beyond WCAG AA? - -### Anti-Goals -- What should this NOT be? What would be a wrong direction? -- What's the biggest risk of getting this wrong? - -## Phase 1.5: Visual Direction Probe (Capability-Gated) - -After the discovery interview, generate a small set of visual direction probes **before** writing the final brief when all of these are true: - -- The work is **net-new** or directionally ambiguous enough that visual exploration will clarify the brief. -- The requested fidelity is **mid-fi, high-fi, or production-ready**. Skip for sketch-only planning. -- The current harness gives you native image generation (Codex's `image_gen`, an equivalent MCP tool, or similar). Don't ask the user to install APIs or tooling. - -When those conditions are met, this step is mandatory. If image generation isn't natively available, do not ask the user to install APIs or tooling. State in one line that the image step is skipped because the harness lacks native image generation, then proceed. The one-line announcement is required, not optional; it forces a conscious decision instead of letting the step quietly evaporate. - -Use probes to explore visual lanes, not to replace the brief. - -Do not skip probes because the final UI will be semantic, editable, code-native, responsive, or accessible. Those are implementation requirements, not reasons to avoid visual exploration. - -### What to generate - -Generate **2 to 4** distinct direction probes based on the discovery answers, especially: - -- Color strategy -- Theme scene sentence -- Named anchor references -- Scope and fidelity - -The probes should differ in primary visual direction (hierarchy, topology, density, typographic voice, or color strategy), not just palette tweaks. - -### How to use the probes - -- Treat them as **direction tests**, not final designs. -- Use them to pressure-test whether the brief is pointing at the right lane. -- Ask the user which direction feels closest, what feels off, and what should carry forward. -- If the probes reveal a mismatch, revise the brief inputs before finalizing the brief. - -### Important limits - -- Do **not** skip discovery because image generation is available. -- Do **not** treat generated imagery as final UX specification, final copy, or final accessibility behavior. -- Do **not** use this step for minor refinements of existing work. It's for shaping a new surface or clarifying a big directional choice. - -If image generation isn't natively available, announce the skip in one line and proceed to the design brief. - -## Phase 2: Design Brief - -After the interview and any required probes, present a brief and **end your response**. The user must confirm before any implementation runs. Do not present a brief and then continue to code in the same response, even if the brief feels obvious to you. The user's confirmation is the gate. - -**Choose the brief shape based on how clear the answers are:** - -- **Compact form (3-5 bullets)** when discovery was crisp and the original prompt + PRODUCT.md already pinned scope, content, and direction. State what you're building, the visual lane, and end with one or two specific questions or a clear "confirm or override?" prompt. This is the default for typical craft requests with a clear prompt. -- **Full structured form (sections below)** when the task is genuinely ambiguous, multi-screen, or when the user asked for shape as a standalone step. Use this when the discipline of structure earns its weight. - -Don't pad a clear brief into a long one to look thorough. A 70-line brief restating answers the user just gave is noise, not rigor. Equally, don't skip the confirmation pause to look efficient: the pause is the point. - -Present the brief, then **stop and wait for explicit confirmation**. You are not the judge of whether the user already approved. Even when the brief feels obviously right, ask once and wait. The pause is what separates shape from premature implementation. - -### Brief Structure - -**1. Feature Summary** (2-3 sentences) -What this is, who it's for, what it needs to accomplish. - -**2. Primary User Action** -The single most important thing a user should do or understand here. - -**3. Design Direction** -Color strategy (Restrained / Committed / Full palette / Drenched) + the theme scene sentence + 2–3 named anchor references. Reference PRODUCT.md and DESIGN.md where they already answer, and note any per-surface overrides. - -If you ran the Visual Direction Probe step, name which probe direction won and what changed in the brief because of it. - -**4. Scope** -Fidelity, breadth, interactivity, and time intent from the Scope section of the interview. Task-scoped; these don't persist beyond the brief. - -**5. Layout Strategy** -High-level spatial approach: what gets emphasis, what's secondary, how information flows. Describe the visual hierarchy and rhythm, not specific CSS. - -**6. Key States** -List every state the feature needs: default, empty, loading, error, success, edge cases. For each, note what the user needs to see and feel. - -**7. Interaction Model** -How users interact with this feature. What happens on click, hover, scroll? What feedback do they get? What's the flow from entry to completion? - -**8. Content Requirements** -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., 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. - ---- - -ask the user directly to clarify what you cannot infer. Ask for explicit confirmation of the brief before finishing. - -If the user disagrees with any part, revisit the relevant discovery questions. A shape run is incomplete until the user confirms direction. - -Once confirmed, the brief is complete. The user can now hand it to /impeccable, or use it to guide any other implementation approach. (If the user wants the full discovery-then-build flow in one step, they should use /impeccable craft instead, which runs this command internally.) +When no human or structured answer mechanism exists, mark assumptions plainly, return the brief, and stop. diff --git a/.cursor/skills/impeccable/reference/typeset.md b/.cursor/skills/impeccable/reference/typeset.md index 318f33423..1a7c153b8 100644 --- a/.cursor/skills/impeccable/reference/typeset.md +++ b/.cursor/skills/impeccable/reference/typeset.md @@ -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. --- -## Register +## Visitor mode -New identity work: run the font selection procedure in [new-work.md](new-work.md). Fluid `clamp()` scale, ≥1.25 ratio between steps. +- **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, register, **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 .cursor/skills/impeccable/scripts/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 `/impeccable polish` for the final pass. +When the hierarchy holds, hand off to `/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) * )` 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 - -The tactical selection procedure and the reflex-reject list live in [new-work.md](new-work.md) (loaded for new identity work). 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 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. diff --git a/.cursor/skills/impeccable/scripts/command-metadata.json b/.cursor/skills/impeccable/scripts/command-metadata.json index 83796d54b..dad8ef2e0 100644 --- a/.cursor/skills/impeccable/scripts/command-metadata.json +++ b/.cursor/skills/impeccable/scripts/command-metadata.json @@ -1,6 +1,6 @@ { "craft": { - "description": "Full confirmed-brief-then-build flow. Runs multi-round shape discovery first, resolves visual probe and north-star mock gates when available, then builds and visually iterates. Use when building a new feature end-to-end.", + "description": "Deprecated compatibility alias for an ordinary Impeccable new-work request. It adds no behavior; natural build and redesign requests use the same flow.", "argumentHint": "[feature description]" }, "init": { diff --git a/.cursor/skills/impeccable/scripts/concept-seed.mjs b/.cursor/skills/impeccable/scripts/concept-seed.mjs index 1f141a80f..25f20d9ab 100644 --- a/.cursor/skills/impeccable/scripts/concept-seed.mjs +++ b/.cursor/skills/impeccable/scripts/concept-seed.mjs @@ -1,6 +1,7 @@ #!/usr/bin/env node /** - * Concept-seed picker: the dice half of the new-work concept procedure. + * External concept seed: the dice half of new-work's coupled-direction and + * established-world surface procedures. * * The model derives a grounded shortlist of candidate FORMS from the * audience's world and the subject's cultural home (see @@ -12,10 +13,9 @@ * * This script rolls them from outside, the same trick that made the * palette seed work: - * - BUILD INDEX (2-5): which entry of the model's own resonance-ordered - * shortlist to build. The dice never choose an ungrounded ingredient; - * they only refuse the argmax rut. (Index 1 is excluded: that's the - * concept every run would ship anyway.) + * - PROMOTED INDEX: which entry of the model's own resonance-ordered + * shortlist must be taken seriously beside its favorites. The dice never + * choose an ungrounded ingredient; they only refuse the argmax rut. * - CHALLENGERS (3): outside forms from concept-ingredients.json, weighed * against the derived candidates on exactly two axes — audience * identification and product clarity. They win only when they beat the @@ -23,8 +23,8 @@ * material and win over thin categories, which is the intended shape. * * Usage: - * node scripts/concept-seed.mjs # roll at random - * node scripts/concept-seed.mjs --from # deterministic (hash key) + * node scripts/concept-seed.mjs --scope direction + * node scripts/concept-seed.mjs --scope surface --from * * Env vars: * IMPECCABLE_CONCEPT_SEED — same as --from; for reproducible eval runs. @@ -40,6 +40,12 @@ const pool = JSON.parse(readFileSync(join(here, 'concept-ingredients.json'), 'ut const args = process.argv.slice(2); const fromIdx = args.indexOf('--from'); +const scopeIdx = args.indexOf('--scope'); +const scope = scopeIdx !== -1 ? args[scopeIdx + 1] : 'surface'; +if (scope !== 'surface' && scope !== 'direction') { + process.stderr.write('concept-seed: --scope must be direction or surface\n'); + process.exit(1); +} // When no key is supplied, generate one and print it: a user reporting a // bad outcome can hand us the key and we replay the exact roll. const key = fromIdx !== -1 @@ -47,7 +53,7 @@ const key = fromIdx !== -1 : (process.env.IMPECCABLE_CONCEPT_SEED || crypto.randomBytes(4).toString('hex')); function hashUnit(k, salt) { - const h = crypto.createHash('sha256').update(`${salt}:${k}`).digest(); + const h = crypto.createHash('sha256').update(`${scope}:${salt}:${k}`).digest(); return h.readUInt32BE(0) / 0xffffffff; } const unit = (salt) => hashUnit(key, salt); @@ -67,22 +73,48 @@ for (let i = 0; picks.length < 3 && i < 60; i++) { } } -process.stdout.write(`CONCEPT SEED (key: ${key}; rerun with --from ${key} to reproduce this roll) -BUILD INDEX: ${buildIndex} - After ordering your derived candidates by resonance, build the page whose - form comes from candidate number ${buildIndex}, exactly as if it had ranked - first: full commitment. Your top-ranked candidate is what every run in this - category would ship; the assignment exists to refuse that rut, not to - punish it. -CHALLENGERS (weigh against your derived candidates on the same two axes, -audience identification and product clarity; a challenger wins only when -it beats the grounded list on both): +const promotedInstruction = scope === 'direction' + ? `After ordering the grounded coupled directions by product fit, promote + candidate ${buildIndex} into the serious shortlist. Each candidate must join + a durable visual system to a concrete expression for the requested first + surface; select or revise that pair as one decision. It must survive the + current task plus navigation, quiet and dense content, interaction and state, + and a substantially different future surface.` + : `After ordering the task's grounded structural candidates by resonance, + promote candidate ${buildIndex} into the serious shortlist. In an attended + run, present it beside the strongest materially different candidates and + let the user select or revise the surface concept. In a truly unattended + run, use it when it survives audience identification and product clarity.`; + +const challengerInstruction = scope === 'direction' + ? `A challenger enters only when it can supply both reusable identity grammar + and a strong first-surface structure, not a one-page costume or a style pasted + onto an unrelated layout. Weigh audience identification, product clarity, + current-surface force, and cross-surface system breadth.` + : `A challenger wins only when it beats the grounded list on both audience + identification and product clarity. It may change task topology or + interaction, but never the committed visual identity.`; + +const authorityInstruction = scope === 'direction' + ? `PRODUCT.md and explicit incumbent brand commitments constrain every coupled +direction. The seed never chooses exact colors, fonts, tokens, or a user +preference, and it never permits the world and first surface to be selected +independently.` + : `PRODUCT.md and DESIGN.md constrain every surface candidate's identity +vocabulary; they do not cancel task-level composition. The seed never +authorizes a new palette, type system, material world, or unfamiliar control +behavior.`; + +process.stdout.write(`${scope.toUpperCase()} CONCEPT SEED (key: ${key}; rerun with --scope ${scope} --from ${key} to reproduce this roll) +PROMOTED INDEX: ${buildIndex} + ${promotedInstruction} + The promotion exists to refuse the model's ranking rut, not to outrank the + user or the brief. +CHALLENGERS: 1. ${picks[0]} 2. ${picks[1]} 3. ${picks[2]} -If a challenger wins, it replaces the assigned candidate. If the surface is -an existing world whose incumbent carries a deliberate, ownable idea, the -incumbent IS the chosen candidate: intensify its lineage and ignore the -roll entirely. The same override applies when the user, PRODUCT.md, or -DESIGN.md pins a direction: pinned direction beats the roll, always. +${challengerInstruction} +${authorityInstruction} +A user- or brief-pinned decision beats the roll, always. `); diff --git a/.cursor/skills/impeccable/scripts/context-signals.mjs b/.cursor/skills/impeccable/scripts/context-signals.mjs index 2fc27bea7..d296320d7 100644 --- a/.cursor/skills/impeccable/scripts/context-signals.mjs +++ b/.cursor/skills/impeccable/scripts/context-signals.mjs @@ -11,7 +11,7 @@ * output is always valid JSON. * * Signals: - * - setup: PRODUCT.md / DESIGN.md presence, register, whether code exists + * - setup: PRODUCT.md / DESIGN.md presence and whether code exists * - critique: the latest cached critique score (.impeccable/critique) * - git: branch + files changed vs the default branch (a scope hint) * - devServer: whether a local dev server answers on a common port (gates live) @@ -21,7 +21,7 @@ import net from 'node:net'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { execFileSync } from 'node:child_process'; -import { loadContext, extractRegister, extractPlatform } from './context.mjs'; +import { loadContext, extractPlatform } from './context.mjs'; import { getCritiqueDir } from './lib/impeccable-paths.mjs'; /** Is there code here at all, or just context files / an empty repo? */ @@ -196,7 +196,6 @@ export async function gatherSignals(cwd = process.cwd()) { hasDesign: ctx.hasDesign, designPath: ctx.designPath, hasCode: hasCode(cwd), - register: extractRegister(ctx.product), platform: extractPlatform(ctx.product), }, critique: { latest: latestCritique(cwd) }, diff --git a/.cursor/skills/impeccable/scripts/context.mjs b/.cursor/skills/impeccable/scripts/context.mjs index 2ef2d4ce8..080e7ee3a 100644 --- a/.cursor/skills/impeccable/scripts/context.mjs +++ b/.cursor/skills/impeccable/scripts/context.mjs @@ -1,8 +1,10 @@ /** - * Context loader: prints PRODUCT.md (and DESIGN.md if present) as one - * markdown block on stdout, or prints a `NO_PRODUCT_MD:` message when no + * Context loader: prints PRODUCT.md, DESIGN.md when present, the matching + * persisted surface brief when one can be resolved, and native-platform + * guidance selected from PRODUCT.md. It prints a + * `NO_PRODUCT_MD:` message when no * PRODUCT.md is found anywhere. The skill keys off that message to branch: - * from-scratch build commands (init / teach / craft / shape) and clear + * from-scratch build requests (plus init / teach / shape) and clear * build/shape intent divert into the init flow, while scoped commands proceed * using the existing code as context. * @@ -23,10 +25,13 @@ import os from 'node:os'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { parseTargetOptions } from './lib/target-args.mjs'; -import { IMPECCABLE_COMMAND } from './lib/provider.mjs'; +import { IMPECCABLE_COMMAND, IMPECCABLE_PROVIDER_ID } from './lib/provider.mjs'; +import { renderLlmOnlySlopReview } from './lib/slop-review.mjs'; +import { resolveSurfaceBrief } from './lib/surface-briefs.mjs'; const PRODUCT_NAMES = ['PRODUCT.md', 'Product.md', 'product.md']; const DESIGN_NAMES = ['DESIGN.md', 'Design.md', 'design.md']; +const SKILL_REFERENCE_DIR = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', 'reference'); const FALLBACK_DIRS = ['.agents/context', 'docs']; const MONOREPO_MARKER_FILES = ['pnpm-workspace.yaml', 'turbo.json', 'nx.json', 'lerna.json']; const MONOREPO_FALLBACK_PROJECT_DIRS = ['apps', 'packages']; @@ -41,6 +46,8 @@ const WORKSPACE_DISCOVERY_IGNORED_DIRS = new Set([ '.turbo', '.cache', 'coverage', + 'vendor', + 'vendors', ]); const VISUAL_SOURCE_DIRS = ['src', 'app', 'pages', 'components', 'site', 'public', 'styles']; const STYLE_EXTENSIONS = new Set(['.css', '.scss', '.sass', '.less', '.styl']); @@ -73,6 +80,12 @@ export function loadContext(cwd = process.cwd(), options = {}) { const designPath = resolved.designPath; const product = productPath ? safeRead(productPath) : null; const design = designPath ? safeRead(designPath) : null; + const platform = extractPlatform(product); + const surfaceResolution = resolveSurfaceBrief( + resolved.projectRoot, + hasTargetOption(options) ? options.targetPath : null, + ); + const surfaceBrief = surfaceResolution.brief; return { hasProduct: !!product, product, @@ -83,7 +96,18 @@ export function loadContext(cwd = process.cwd(), options = {}) { contextDir: resolved.contextDir, productContextDir: productPath ? path.dirname(productPath) : null, designContextDir: designPath ? path.dirname(designPath) : null, + hasSurfaceBrief: !!surfaceBrief, + surfaceBrief: surfaceBrief?.text ?? null, + surfaceBriefPath: surfaceBrief?.path ? path.relative(absCwd, surfaceBrief.path) : null, + surfaceBriefReason: surfaceResolution.reason, + surfaceBriefCandidates: surfaceResolution.candidates.map((brief) => ({ + slug: brief.slug, + path: path.relative(absCwd, brief.path), + primaryTarget: brief.primaryTarget, + relatedTargets: brief.relatedTargets, + })), hasVisualImplementation: hasVisualImplementation(resolved.projectRoot), + platform, projectRoot: resolved.projectRoot, repoRoot: resolved.repoRoot, isMonorepo: resolved.isMonorepo, @@ -696,6 +720,19 @@ function safeRead(p) { } } +function loadNativePlatformReferences(platform) { + const names = platform === 'adaptive' + ? ['ios', 'android'] + : platform === 'ios' || platform === 'android' + ? [platform] + : []; + return names.flatMap((name) => { + const filePath = path.join(SKILL_REFERENCE_DIR, `${name}.md`); + const content = safeRead(filePath); + return content ? [{ name, filePath, content }] : []; + }); +} + /** * Best-effort evidence that the project already has an incumbent visual * implementation. DESIGN.md is documentation, not the only source of design @@ -719,9 +756,11 @@ export function hasVisualImplementation(projectRoot) { let styledComponents = 0; const inspectFile = (filePath) => { - if (scannedFiles++ >= VISUAL_SCAN_FILE_LIMIT) return false; const ext = path.extname(filePath).toLowerCase(); if (!STYLE_EXTENSIONS.has(ext) && !UI_EXTENSIONS.has(ext)) return false; + const base = path.basename(filePath).toLowerCase(); + if (/\.min\.[a-z]+$/.test(base)) return false; + if (scannedFiles++ >= VISUAL_SCAN_FILE_LIMIT) return false; let body; try { body = fs.readFileSync(filePath, 'utf-8').slice(0, 64 * 1024); @@ -729,18 +768,28 @@ export function hasVisualImplementation(projectRoot) { return false; } - const base = path.basename(filePath).toLowerCase(); + const evidence = body + .replace(/\/\*[\s\S]*?\*\//g, '') + .replace(//g, '') + .replace(/^\s*\/\/.*$/gm, ''); if (STYLE_EXTENSIONS.has(ext)) { - const customProperties = body.match(/--[a-z0-9_-]+\s*:/gi)?.length ?? 0; - const visualDeclarations = body.match(/\b(?:color|background(?:-color)?|border(?:-color)?|font-family)\s*:/gi)?.length ?? 0; - if (/\b(?:tokens?|theme|design-system)\b/.test(base) && body.trim().length > 80) return true; + const customProperties = evidence.match(/--[a-z0-9_-]+\s*:/gi)?.length ?? 0; + const visualDeclarations = evidence.match(/\b(?:color|background(?:-color)?|border(?:-color)?|font-family)\s*:/gi)?.length ?? 0; + if (/\b(?:tokens?|theme|design-system)\b/.test(base) && evidence.trim().length > 80) return true; if (customProperties >= 3 || visualDeclarations >= 5) return true; } - if ((ext === '.html' || ext === '.htm') && body.length > 600 && /]+stylesheet/i.test(body)) { + if ((ext === '.html' || ext === '.htm') && evidence.length > 600 && /]+stylesheet/i.test(evidence)) { return true; } - if (!['.html', '.htm'].includes(ext) && body.length > 300 && /class(?:Name)?\s*=|style\s*=|styled\(|css`/i.test(body)) { + if (!['.html', '.htm'].includes(ext) && evidence.length > 300) { + const embeddedCustomProperties = evidence.match(/--[a-z0-9_-]+\s*:/gi)?.length ?? 0; + const embeddedVisualDeclarations = evidence.match(/\b(?:color|background(?:-color)?|border(?:-color)?|font-family)\s*:/gi)?.length ?? 0; + const classTokens = [...evidence.matchAll(/class(?:Name)?\s*=\s*["'`]([^"'`]+)["'`]/gi)] + .reduce((count, match) => count + match[1].trim().split(/\s+/).length, 0); + if ((embeddedCustomProperties >= 3 && embeddedVisualDeclarations >= 3) || embeddedVisualDeclarations >= 5 || classTokens >= 12) return true; + } + if (!['.html', '.htm'].includes(ext) && evidence.length > 300 && /class(?:Name)?\s*=|style\s*=|styled\(|css`/i.test(evidence)) { styledComponents += 1; if (styledComponents >= 3) return true; } @@ -781,9 +830,9 @@ function escapeRegExp(value) { /** * Read the first non-empty line under a bare `## ` section of - * PRODUCT.md (e.g. `## Register`, `## Platform`). Returns null when the + * PRODUCT.md (for example `## Platform`). Returns null when the * section is absent. The heading match is exact (`\s*$`) so near-miss - * headings like `## Register guidelines` don't shadow the real field. + * near-miss headings don't shadow the real field. */ export function extractSectionValue(product, heading) { if (!product) return null; @@ -802,16 +851,6 @@ export function extractSectionValue(product, heading) { return null; } -/** - * Pull the register (`brand` or `product`) out of PRODUCT.md by looking - * for a `## Register` section and reading the first non-empty line that - * follows it. Returns null when the file is legacy / register-less. - */ -export function extractRegister(product) { - const word = (extractSectionValue(product, 'Register') || '').toLowerCase(); - return word === 'brand' || word === 'product' ? word : null; -} - /** * Pull the platform (`web`, `ios`, `android`, or `adaptive`) out of PRODUCT.md * by looking for a `## Platform` section and reading the first non-empty line @@ -985,30 +1024,35 @@ async function cli() { const parts = ctx.hasVisualImplementation ? [ 'NO_PRODUCT_MD: This project has no PRODUCT.md yet, but it does have an incumbent visual implementation. ' + - 'For `init` or `teach`, load reference/init.md and create PRODUCT.md. For `craft`, `shape`, or other work ' + - 'against the existing product, read its CSS, tokens, components, and assets first and proceed without blocking; ' + - `offer \`${IMPECCABLE_COMMAND} init\` as a follow-up. Only treat the work as greenfield when the user explicitly ` + - 'asks to discard or replace the current identity.', - 'EXISTING_VISUAL_SYSTEM: Code and assets are the incumbent design authority. Missing DESIGN.md is a documentation ' + - 'gap, not permission to invent a replacement identity. Preserve and extend the implementation unless the user ' + - 'explicitly requests a rebrand, or inspection proves the detected files contain no coherent visual decisions.', + 'For `init`, `teach`, `shape`, or any request to create a new surface or replacement visual world, load reference/init.md and create PRODUCT.md with the user first. ' + + 'After init writes PRODUCT.md, reference/new-work.md preserves and documents the incumbent system for an ' + + 'extension or replaces it with the user for a redesign/rebrand. Other ' + + 'narrow refinement commands may read the CSS, tokens, components, and assets and proceed without blocking, then ' + + `offer \`${IMPECCABLE_COMMAND} init\` as a follow-up.`, + 'BUILD_INIT_REQUIRED: Before shape or any new-surface/redesign flow, init must capture PRODUCT.md with the human or structured ' + + 'simulated user. Init writes product truth only; reference/new-work.md owns every visual decision.', + 'SCOPED_EXISTING_ALLOWED: Narrow refinement commands may use the incumbent implementation as authority without ' + + 'blocking on context setup; they must preserve it and offer init afterward.', + 'EXISTING_VISUAL_SYSTEM: For refinement or extension, code and assets are incumbent design authority and missing ' + + 'DESIGN.md is a documentation gap. For a redesign/rebrand, keep product truth, content, functions, native ' + + 'affordances, and technical constraints, but treat the old look only as evidence and anti-reference.', ] : [ 'NO_PRODUCT_MD: This project has no PRODUCT.md yet. ' + - 'For `init`, `teach`, `craft`, `shape`, ' + + 'For `init`, `teach`, `shape`, ' + 'or wording that clearly maps to a from-scratch build/shape flow, load ' + - 'reference/init.md and write PRODUCT.md first, unless no user can ' + - 'respond (a one-shot or automated run, or the user said not to ask): ' + - 'then write a one-paragraph understanding of the product, audience, ' + - 'and the page\'s job from the brief, and continue. For any other ' + + 'reference/init.md, complete its human or structured simulated-user interview, and write PRODUCT.md before ' + + 'designing. If no answer mechanism truly exists, init may infer only from the explicit brief and must label its ' + + 'assumptions. It never writes DESIGN.md. For any other ' + '(scoped) command against existing code, proceed using the code as ' + `context and offer \`${IMPECCABLE_COMMAND} init\` as a suggestion (do not block).`, - 'NEW_WORK: No committed design context was found. If this task produces ' + - 'new design (a build from scratch, or a redesign that discards the ' + - 'current look), you MUST read reference/new-work.md before making any ' + - 'design decision. Scoped fixes to existing code do not need it.', + 'PRODUCT_INIT_REQUIRED: No product context or visual authority was found. New builds and redesigns ' + + 'must finish reference/init.md for PRODUCT.md, then reference/new-work.md establishes the world and surface. Scoped ' + + 'fixes to existing code do not need the new-surface flow.', ]; + appendSurfaceBriefContext(parts, ctx); parts.push(buildResolvedContextDirective(ctx, cliOptions, { targetExists })); + appendHookFallback(parts, ctx); if (shouldWarnMissingTarget(ctx, targetProvided, targetExists)) { parts.push(buildMissingTargetDirective()); } @@ -1020,37 +1064,29 @@ async function cli() { if (ctx.hasDesign) { parts.push(`# DESIGN.md\n\n${ctx.design.trim()}`); } + appendSurfaceBriefContext(parts, ctx); parts.push(buildResolvedContextDirective(ctx, cliOptions, { targetExists })); + appendHookFallback(parts, ctx); if (shouldWarnMissingTarget(ctx, targetProvided, targetExists)) { parts.push(buildMissingTargetDirective()); } - const register = extractRegister(ctx.product); - // The register field survives as a family hint (brand = Persuade/Experience, - // product = Operate/Read); SKILL.md's mode section carries the essentials - // inline, so no register-file read is mandated here. What IS mandated: - // the new-work playbook when no committed design system exists yet. if (!ctx.hasDesign) { parts.push(ctx.hasVisualImplementation - ? 'EXISTING_VISUAL_SYSTEM: PRODUCT.md exists and DESIGN.md is missing, but code contains incumbent visual decisions. ' + - 'Treat CSS, tokens, components, and assets as design authority. Do not route to new identity work merely because the ' + - 'document is absent; preserve and extend the implementation unless the user explicitly asks to discard it.' - : 'NEW_WORK: PRODUCT.md exists but no DESIGN.md or incumbent visual implementation was found. If this task produces ' + - 'new design or a redesign that discards the current look, you MUST read reference/new-work.md before making any ' + - 'design decision. Scoped fixes to existing code do not need it.'); + ? 'INCUMBENT_WORLD_UNDOCUMENTED: PRODUCT.md exists and DESIGN.md is missing, but code contains incumbent visual decisions. ' + + 'For shape or a new-surface/redesign request, load reference/new-work.md: an extension documents and preserves the code-defined world; ' + + 'a redesign replaces it with the user and uses the old look only as evidence and anti-reference. Narrow refinement ' + + 'commands may proceed using the implementation directly.' + : 'WORLD_DISCOVERY_REQUIRED: PRODUCT.md exists but no DESIGN.md or incumbent visual implementation was found. ' + + 'For a new build or redesign, load reference/new-work.md and establish the visual world with the human or structured ' + + 'simulated user before developing the task concept. Scoped fixes to existing code do not need this flow.'); } - if (register) { - parts.push(`REGISTER: \`${register}\` (family hint: brand = Persuade/Experience surfaces, product = Operate/Read). Derive the visitor's mode per SKILL.md.`); - } - const platform = extractPlatform(ctx.product); - const nativeRefs = - platform === 'adaptive' ? ['ios', 'android'] : platform === 'ios' || platform === 'android' ? [platform] : []; - if (nativeRefs.length) { - const refList = nativeRefs.map(p => `\`reference/${p}.md\``).join(' and '); - const label = platform === 'adaptive' ? '`adaptive` (both iOS and Android)' : `\`${platform}\``; + const platformReferences = loadNativePlatformReferences(ctx.platform); + for (const reference of platformReferences) { parts.push( - `NEXT STEP: This project targets ${label}. Also read ${refList} for native conventions, in addition to SKILL.md's mode guidance.`, + `# NATIVE PLATFORM REFERENCE: ${reference.name.toUpperCase()} (reference/${reference.name}.md)\n\n${reference.content.trim()}`, ); - } else if (!platform) { + } + if (!ctx.platform) { // A `## Platform` section that names something we don't recognize (a // toolchain like `flutter`, a typo) would otherwise silently fall back to // web — the wrong default exactly when the user tried to say "native". @@ -1078,6 +1114,72 @@ function pathExistsForTarget(cwd, targetPath) { return fs.existsSync(abs); } +const HOOK_MANIFESTS_BY_PROVIDER = Object.freeze({ + 'claude-code': ['.claude/settings.local.json', '.claude/settings.json'], + codex: ['.codex/hooks.json'], + agents: ['.codex/hooks.json'], + cursor: ['.cursor/hooks.json'], + github: ['.github/hooks/impeccable.json'], +}); + +function truthyEnv(value) { + return typeof value === 'string' && /^(1|true|yes|on)$/i.test(value.trim()); +} + +function valueHasHookMarker(value) { + if (typeof value === 'string') { + return value.includes('skills/impeccable/scripts/hook.mjs') + || value.includes('skills/impeccable/scripts/hook-before-edit.mjs'); + } + if (Array.isArray(value)) return value.some(valueHasHookMarker); + if (value && typeof value === 'object') return Object.values(value).some(valueHasHookMarker); + return false; +} + +function hookEnabledAt(root) { + if (truthyEnv(process.env.IMPECCABLE_HOOK_DISABLED)) return false; + let enabled = true; + for (const name of ['.impeccable/config.json', '.impeccable/config.local.json']) { + const raw = readJson(path.join(root, name)); + if (raw?.hook && Object.prototype.hasOwnProperty.call(raw.hook, 'enabled')) { + enabled = raw.hook.enabled !== false; + } + } + return enabled; +} + +const STOP_REVIEW_PROVIDERS = new Set(['claude-code', 'codex', 'agents']); + +function automaticHookMode(ctx) { + if (ctx.platform === 'ios' || ctx.platform === 'android' || ctx.platform === 'adaptive') { + return 'none'; + } + const activeRoot = path.resolve(ctx.projectRoot || process.cwd()); + if (!hookEnabledAt(activeRoot)) return 'none'; + const manifests = HOOK_MANIFESTS_BY_PROVIDER[IMPECCABLE_PROVIDER_ID] || []; + const roots = [...new Set([process.cwd(), ctx.projectRoot, ctx.repoRoot].filter(Boolean).map((root) => path.resolve(root)))]; + for (const root of roots) { + for (const rel of manifests) { + const raw = readJson(path.join(root, rel)); + if (raw?.hooks && valueHasHookMarker(raw.hooks)) { + return STOP_REVIEW_PROVIDERS.has(IMPECCABLE_PROVIDER_ID) ? 'stop' : 'per-edit'; + } + } + } + return 'none'; +} + +function appendHookFallback(parts, ctx) { + const hookMode = automaticHookMode(ctx); + if (hookMode === 'stop') return; + const native = ctx.platform === 'ios' || ctx.platform === 'android' || ctx.platform === 'adaptive'; + parts.push(renderLlmOnlySlopReview({ + automaticDetector: hookMode === 'per-edit', + manualDetector: hookMode === 'none' && !native, + scriptsPath: path.dirname(fileURLToPath(import.meta.url)), + })); +} + function buildResolvedContextDirective(ctx, options, { targetExists = null } = {}) { const targetPath = hasTargetOption(options) ? options.targetPath : null; return `RESOLVED_CONTEXT:\n${JSON.stringify({ @@ -1087,10 +1189,29 @@ function buildResolvedContextDirective(ctx, options, { targetExists = null } = { repoRoot: ctx.repoRoot, productPath: ctx.productPath, designPath: ctx.designPath, + surfaceBriefPath: ctx.surfaceBriefPath, + surfaceBriefReason: ctx.surfaceBriefReason, + surfaceBriefCandidates: ctx.surfaceBriefCandidates, hasVisualImplementation: ctx.hasVisualImplementation, + platform: ctx.platform, }, null, 2)}`; } +function appendSurfaceBriefContext(parts, ctx) { + if (ctx.hasSurfaceBrief && ctx.surfaceBrief) { + parts.push(`# SURFACE BRIEF (${ctx.surfaceBriefPath})\n\n${ctx.surfaceBrief.trim()}`); + return; + } + if (!ctx.surfaceBriefCandidates?.length) return; + const helper = path.join(path.dirname(fileURLToPath(import.meta.url)), 'surface-brief.mjs'); + parts.push( + 'SURFACE_CONTEXT_AVAILABLE: Persisted surface briefs exist, but none was selected unambiguously for this invocation. ' + + 'Resolve the requested surface to its concrete primary or related source path, then run ' + + `\`node ${helper} read \` once before changing that surface. Candidates:\n` + + JSON.stringify(ctx.surfaceBriefCandidates, null, 2), + ); +} + function shouldWarnMissingTarget(ctx, targetProvided, targetExists = null) { if (ctx.isMonorepo && targetProvided && targetExists === false) return true; return !!( diff --git a/.cursor/skills/impeccable/scripts/critique-storage.mjs b/.cursor/skills/impeccable/scripts/critique-storage.mjs index 6b4d225cf..38674285a 100644 --- a/.cursor/skills/impeccable/scripts/critique-storage.mjs +++ b/.cursor/skills/impeccable/scripts/critique-storage.mjs @@ -29,8 +29,9 @@ import fs from 'node:fs'; import path from 'node:path'; import { fileURLToPath, pathToFileURL } from 'node:url'; import { getCritiqueDir } from './lib/impeccable-paths.mjs'; +import { slugFromTarget } from './lib/target-slug.mjs'; -const SLUG_MAX = 50; +export { slugFromTarget } from './lib/target-slug.mjs'; /** * Mechanically derive a slug from a resolved target. Returns null if the @@ -40,46 +41,6 @@ const SLUG_MAX = 50; * concrete artifact before calling this — we never slug a natural-language * phrase. */ -export function slugFromTarget(resolved, { cwd = process.cwd() } = {}) { - if (!resolved || typeof resolved !== 'string') return null; - const trimmed = resolved.trim(); - if (!trimmed) return null; - - // URL - if (/^https?:\/\//i.test(trimmed)) { - let url; - try { url = new URL(trimmed); } catch { return null; } - const hostPath = `${url.hostname}${url.pathname}`; - return kebab(hostPath); - } - - // File path. Make it project-relative so two devs critiquing the same - // checkout get the same slug regardless of where their repo is cloned. - const abs = path.isAbsolute(trimmed) ? trimmed : path.resolve(cwd, trimmed); - let rel = path.relative(cwd, abs); - // If the target is outside cwd, fall back to the basename so we still - // produce a stable slug (vs the absolute path, which would include - // home dirs / usernames). - if (rel.startsWith('..') || path.isAbsolute(rel)) { - rel = path.basename(abs); - } - if (!rel || rel === '.' || rel === '') return null; - return kebab(rel); -} - -function kebab(s) { - const slug = s - .toLowerCase() - .replace(/[/\\.]+/g, '-') - .replace(/[^a-z0-9-]+/g, '-') - .replace(/-+/g, '-') - .replace(/^-|-$/g, ''); - if (!slug) return null; - // Cap from the tail — the tail (filename) is more identifying than the - // top-level directory. - return slug.length <= SLUG_MAX ? slug : slug.slice(slug.length - SLUG_MAX).replace(/^-/, ''); -} - /** * Filename-safe UTC ISO timestamp: hyphens for separators, trailing Z. * Plain colons aren't allowed on Windows filesystems. diff --git a/.cursor/skills/impeccable/scripts/detector/detect-antipatterns-browser.js b/.cursor/skills/impeccable/scripts/detector/detect-antipatterns-browser.js index 033573e36..76bf4ddeb 100644 --- a/.cursor/skills/impeccable/scripts/detector/detect-antipatterns-browser.js +++ b/.cursor/skills/impeccable/scripts/detector/detect-antipatterns-browser.js @@ -312,17 +312,6 @@ const ANTIPATTERNS = [ skillSection: 'Layout & Space', skillGuideline: 'numbered section markers', }, - { - id: 'numbered-section-markers', - category: 'slop', - scopes: ['layout'], - severity: 'advisory', - name: 'Numbered section markers (01 / 02 / 03)', - description: - 'Numbered display markers as section labels (01, 02, 03) are the AI editorial scaffold one tier deeper than tracked eyebrow chips. If you find yourself reaching for them, choose a different section cadence.', - skillSection: 'Layout & Space', - skillGuideline: 'numbered section markers', - }, { id: 'em-dash-overuse', category: 'slop', diff --git a/.cursor/skills/impeccable/scripts/detector/engines/regex/detect-text.mjs b/.cursor/skills/impeccable/scripts/detector/engines/regex/detect-text.mjs index 81c30ab99..f641d5917 100644 --- a/.cursor/skills/impeccable/scripts/detector/engines/regex/detect-text.mjs +++ b/.cursor/skills/impeccable/scripts/detector/engines/regex/detect-text.mjs @@ -280,22 +280,6 @@ const REGEX_ANALYZERS = [ if (count === 0) return []; return [finding('marketing-buzzword', filePath, `${count} buzzword phrase${count === 1 ? '' : 's'}: "${firstSample}"`)]; }, - // Numbered section markers (01 / 02 / 03 ...) - (content, filePath) => { - const text = stripHtmlToText(content); - const re = /\b(0[1-9]|1[0-2])\b/g; - const seen = new Set(); - let m; - while ((m = re.exec(text)) !== null) seen.add(m[1]); - if (seen.size < 3) return []; - const sorted = [...seen].sort(); - let sequential = 0; - for (let i = 1; i < sorted.length; i++) { - if (parseInt(sorted[i], 10) === parseInt(sorted[i - 1], 10) + 1) sequential++; - } - if (sequential < 2) return []; - return [finding('numbered-section-markers', filePath, `Sequence: ${sorted.slice(0, 6).join(', ')}`)]; - }, // Aphoristic cadence: manufactured-contrast + short-rebuttal (content, filePath) => { const text = stripHtmlToText(content); @@ -430,21 +414,20 @@ function runRegexMatchers(lines, filePath, lineOffset = 0, blockContext = null, } /** Page-level analyzers that scan rendered text content (em-dash use, - * buzzword phrases, numbered section markers, aphoristic cadence). + * buzzword phrases, aphoristic cadence). * These are detector-agnostic — they work on any HTML/text source * and don't need a parsed DOM. Exported so detectHtml can call them * for `.html` files (which otherwise skip the regex engine). */ const TEXT_CONTENT_ANALYZER_IDS = [ 'em-dash-overuse', 'marketing-buzzword', - 'numbered-section-markers', 'aphoristic-cadence', ]; function runTextContentAnalyzers(content, filePath, options = {}) { const profile = options?.profile; if (!shouldRunPageAnalyzers(content, filePath)) return []; - // The 4 text-content analyzers are at indices 3-6 in REGEX_ANALYZERS. + // The 3 text-content analyzers are at indices 3-5 in REGEX_ANALYZERS. const findings = []; for (let i = 0; i < TEXT_CONTENT_ANALYZER_IDS.length; i++) { const analyzer = REGEX_ANALYZERS[3 + i]; @@ -535,7 +518,6 @@ function detectText(content, filePath, options = {}) { 'monotonous-spacing', 'em-dash-overuse', 'marketing-buzzword', - 'numbered-section-markers', 'aphoristic-cadence', 'dark-glow', ]; diff --git a/.cursor/skills/impeccable/scripts/detector/registry/antipatterns.mjs b/.cursor/skills/impeccable/scripts/detector/registry/antipatterns.mjs index b85f7e62f..84c69b0b8 100644 --- a/.cursor/skills/impeccable/scripts/detector/registry/antipatterns.mjs +++ b/.cursor/skills/impeccable/scripts/detector/registry/antipatterns.mjs @@ -210,17 +210,6 @@ const ANTIPATTERNS = [ skillSection: 'Layout & Space', skillGuideline: 'numbered section markers', }, - { - id: 'numbered-section-markers', - category: 'slop', - scopes: ['layout'], - severity: 'advisory', - name: 'Numbered section markers (01 / 02 / 03)', - description: - 'Numbered display markers as section labels (01, 02, 03) are the AI editorial scaffold one tier deeper than tracked eyebrow chips. If you find yourself reaching for them, choose a different section cadence.', - skillSection: 'Layout & Space', - skillGuideline: 'numbered section markers', - }, { id: 'em-dash-overuse', category: 'slop', diff --git a/.cursor/skills/impeccable/scripts/detector/rules/checks.mjs b/.cursor/skills/impeccable/scripts/detector/rules/checks.mjs index 3fdd6b2dd..9bace11b3 100644 --- a/.cursor/skills/impeccable/scripts/detector/rules/checks.mjs +++ b/.cursor/skills/impeccable/scripts/detector/rules/checks.mjs @@ -352,27 +352,57 @@ function isAccentColor(cssColor) { return false; } +function resolveHeroHeadingSizePx(value) { + const input = String(value || '').trim().toLowerCase(); + if (!input) return 0; + + const simpleLengthPx = (token) => { + const match = /^(-?\d*\.?\d+)\s*(px|rem|em|%)?$/.exec(String(token || '').trim()); + if (!match) return null; + const amount = Number(match[1]); + if (!Number.isFinite(amount)) return null; + if (match[2] === 'rem' || match[2] === 'em') return amount * 16; + if (match[2] === '%') return amount * 0.16; + return amount; + }; + + const direct = simpleLengthPx(input); + if (direct !== null) return direct; + + // Static CSS engines cannot resolve viewport units, but clamp's min/max + // bounds still tell us whether the heading can ever reach hero scale. + const clamp = /^clamp\((.*)\)$/.exec(input); + if (clamp) { + const parts = clamp[1].split(','); + if (parts.length === 3) { + const bounds = [simpleLengthPx(parts[0]), simpleLengthPx(parts[2])] + .filter((candidate) => candidate !== null); + if (bounds.length > 0) return Math.max(...bounds); + } + } + + return 0; +} + // Sibling-relationship rule. Anchor on a hero-scale h1, look at the // previousElementSibling, and gate on EITHER the classic tracked- // uppercase eyebrow OR the modern accent-colored bold eyebrow. function checkHeroEyebrow(opts) { const { headingTag, headingText, headingFontSize, + headingInApplicationContext, siblingTag, siblingText, siblingTextTransform, siblingFontSize, siblingLetterSpacing, siblingFontWeight, siblingColor, siblingHasAccentDashPseudo, } = opts; if (headingTag !== 'h1') return []; - // We previously gated on headingFontSize >= 48 to anchor "hero scale". - // But modern hero h1s use clamp() / vw / var(--text-*), none of which - // jsdom can resolve — the computed value comes back as "2em" or - // "var(--text-9xl)" and parseFloat returns 2 or NaN. The gate fails - // on virtually every Tailwind v4 / framework build. The other gates - // (sibling text 2-60 chars, font-size ≤ 14px, accent-bold OR - // tracked-caps) are tight enough to avoid false positives on non- - // hero h1s — a tiny tan label directly above any h1 is the - // antipattern regardless of how big the h1 ends up. + // This is specifically a marketing-hero cliché, not a ban on compact + // context labels in product UI (for example, a station name inside a tab + // panel). Browser-computed sizes are reliable; the static adapter also + // resolves ordinary px/rem/em and clamp() bounds before reaching here. + if (headingInApplicationContext) return []; + if (!(headingFontSize >= 48)) return []; if (!siblingTag) return []; // An h2 above an h1 is a different anti-pattern (heading hierarchy / dual // headings) — never an eyebrow. @@ -1938,6 +1968,7 @@ function checkElementHeroEyebrowDOM(el) { headingTag: tag, headingText: el.textContent || '', headingFontSize: parseFloat(headStyle.fontSize) || 0, + headingInApplicationContext: !!el.closest('[role="tabpanel"], [role="dialog"], [role="application"], dialog'), siblingTag: sibling.tagName.toLowerCase(), siblingText: sibling.textContent || '', siblingTextTransform: sibStyle.textTransform || '', @@ -3367,7 +3398,8 @@ function checkElementHeroEyebrow(el, style, tag, window, customPropMap) { return checkHeroEyebrow({ headingTag: tag, headingText: el.textContent || '', - headingFontSize: parseFloat(headingFontSizeRaw) || 0, + headingFontSize: resolveHeroHeadingSizePx(headingFontSizeRaw), + headingInApplicationContext: !!el.closest?.('[role="tabpanel"], [role="dialog"], [role="application"], dialog'), siblingTag: sibling.tagName.toLowerCase(), siblingText: sibling.textContent || '', siblingTextTransform: sibStyle.textTransform || '', diff --git a/.cursor/skills/impeccable/scripts/hook-lib.mjs b/.cursor/skills/impeccable/scripts/hook-lib.mjs index e61ea692b..50aa2f8fb 100644 --- a/.cursor/skills/impeccable/scripts/hook-lib.mjs +++ b/.cursor/skills/impeccable/scripts/hook-lib.mjs @@ -46,6 +46,7 @@ import path from 'node:path'; import { pathToFileURL, fileURLToPath } from 'node:url'; import { extractPlatform, loadContext } from './context.mjs'; import { IMPECCABLE_COMMAND } from './lib/provider.mjs'; +import { renderStopSlopReview } from './lib/slop-review.mjs'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); @@ -1868,22 +1869,42 @@ export const CONTRACT_MAX_CHARS = 1800; // Cap contract sections per Stop emission so many touched artifacts cannot // stack an unbounded message. export const CONTRACT_AUDIT_MAX_FILES = 3; +export const CONTRACT_EXTS = new Set(['.html', '.htm', '.astro', '.svelte', '.vue', '.jsx', '.tsx']); +export const CONTRACT_REQUIRED_FIELDS = ['UNIQUE', 'NOT-TEMPLATE', 'OWN-WORLD', 'STORY', 'FIRST VIEWPORT', 'FORM']; /** - * Extract the artifact's own direction-contract comment: the first HTML - * comment in the head of the file, when its opening chars identify it as a - * contract/concept block. Returns the trimmed, length-capped body, or null - * when the file carries none (no comment, unclosed comment, marker missing, - * or the comment starts past the head window). + * Extract the artifact's own direction-contract comment from the head of an + * HTML or component file. Supports HTML-family comments and JSX block + * comments so the contract works in the Astro/Svelte/Vue/React scaffolds the + * skill actually builds. Returns the trimmed, length-capped body, or null + * when the file carries no valid contract block. */ export function extractDirectionContract(content) { if (typeof content !== 'string' || !content) return null; const head = content.slice(0, CONTRACT_HEAD_CHARS); - const m = //.exec(head); - if (!m) return null; - const body = m[1].trim(); - if (!body || !/contract|concept/i.test(body.slice(0, CONTRACT_MARKER_CHARS))) return null; - return body.slice(0, CONTRACT_MAX_CHARS); + const candidates = []; + for (const pattern of [//g, /\{\/\*([\s\S]*?)\*\/\}/g]) { + for (const match of head.matchAll(pattern)) { + const index = match.index ?? 0; + const linePrefix = head.slice(head.lastIndexOf('\n', index - 1) + 1, index).trim(); + if (linePrefix.startsWith('//')) continue; + candidates.push({ index, body: match[1].trim() }); + } + } + candidates.sort((a, b) => a.index - b.index); + for (const candidate of candidates) { + if (!candidate.body || !/direction\s+contract|concept\s+contract/i.test(candidate.body.slice(0, CONTRACT_MARKER_CHARS))) continue; + return candidate.body.slice(0, CONTRACT_MAX_CHARS); + } + return null; +} + +export function missingDirectionContractFields(contract) { + const body = typeof contract === 'string' ? contract : ''; + return CONTRACT_REQUIRED_FIELDS.filter((field) => { + const label = field.replace(/[.*+?^${}()|[\]\\]/g, '\\$&').replace(/\s+/g, '\\s+'); + return !new RegExp(`\\b${label}\\s*:`, 'i').test(body); + }); } /** @@ -1899,7 +1920,11 @@ export function renderContractAudit(entries, opts = {}) { const shown = entries.slice(0, CONTRACT_AUDIT_MAX_FILES); const blocks = shown.map(({ filePath, contract }) => { const display = relativize(filePath, cwd); - return `${display} opens with this direction contract, written when the direction was decided:\n\n${contract}`; + const missing = missingDirectionContractFields(contract); + const integrity = missing.length > 0 + ? `\n\nContract integrity defect: missing ${missing.join(', ')}. Repair the contract and the implementation together.` + : ''; + return `${display} opens with this direction contract, written when the direction was decided:\n\n${contract}${integrity}`; }); return [ `${ENVELOPE_PREFIX} Direction-contract audit. Before finishing, audit the rendered page against the contract it opens with, promise by promise.`, @@ -1984,9 +2009,22 @@ export async function runStopHook({ stdinJson, env = {}, cwd = process.cwd(), no return result({ skipped: 'native-platform', platform, durationMs: Date.now() - started }); } + const session = ensureSession(cache, sessionId); + const needsSlopReview = session.llmSlopReviewed !== true; + const det = detector || await loadDetector(); if (!det || typeof det.detectText !== 'function') { - return result({ skipped: 'detector-missing', durationMs: Date.now() - started }); + if (!needsSlopReview) return result({ skipped: 'detector-missing', durationMs: Date.now() - started }); + session.llmSlopReviewed = true; + session.updatedAt = Date.now(); + persistCache(projectCwd, cache); + const text = renderStopSlopReview(); + return { + exitCode: 0, + stdout: payload(text, 'Stop', harness), + emission: { kind: 'stop-llm-slop-review', llmSlopReview: true }, + audit: { ...audit, emitted: true, detectorMissing: true, llmSlopReview: true, chars: text.length, durationMs: Date.now() - started }, + }; } const scanOptions = designSystemOptions(config, det, projectCwd); @@ -2013,10 +2051,11 @@ export async function runStopHook({ stdinJson, env = {}, cwd = process.cwd(), no ? configuredExt.engine === 'html' : (ext === '.html' || ext === '.htm'); - // Direction-contract audit: HTML artifacts only, at most once per file + // Direction-contract audit: HTML and component artifacts, at most once per file // per session. The flag lives on the same session cache entry the // finding dedupe uses, so a second Stop fire stays quiet about it. - if (useHtmlEngine) { + const contractCapable = useHtmlEngine || CONTRACT_EXTS.has(ext); + if (contractCapable) { const fileEntry = ensureFile(cache, sessionId, filePath); if (!fileEntry.contractAudited) { const contract = extractDirectionContract(content); @@ -2046,10 +2085,15 @@ export async function runStopHook({ stdinJson, env = {}, cwd = process.cwd(), no } audit.scannedFiles = scanned; - if (freshGroups.length === 0 && contractEntries.length === 0) { + if (freshGroups.length === 0 && contractEntries.length === 0 && !needsSlopReview) { return result({ emitted: false, skipped: 'stop-clean', durationMs: Date.now() - started }); } + if (needsSlopReview) { + session.llmSlopReviewed = true; + session.updatedAt = Date.now(); + } + // Fresh findings and first-time contract audits earn the cache write; // both mark this batch as surfaced so the next Stop fire is silent // unless new issues appear. @@ -2064,6 +2108,9 @@ export async function runStopHook({ stdinJson, env = {}, cwd = process.cwd(), no if (contractEntries.length > 0) { parts.push(renderContractAudit(contractEntries, { cwd: projectCwd })); } + if (needsSlopReview) { + parts.push(renderStopSlopReview()); + } const text = appendDesignSystemNote(parts.join('\n\n'), scanOptions); return { exitCode: 0, @@ -2074,6 +2121,7 @@ export async function runStopHook({ stdinJson, env = {}, cwd = process.cwd(), no ...(contractEntries.length > 0 ? { contractFiles: contractEntries.map((entry) => entry.filePath) } : {}), + ...(needsSlopReview ? { llmSlopReview: true } : {}), }, audit: { ...audit, @@ -2081,6 +2129,7 @@ export async function runStopHook({ stdinJson, env = {}, cwd = process.cwd(), no freshFiles: freshGroups.length, freshFindings: freshGroups.reduce((sum, group) => sum + group.findings.length, 0), ...(contractEntries.length > 0 ? { contractAudits: contractEntries.length } : {}), + ...(needsSlopReview ? { llmSlopReview: true } : {}), chars: text.length, durationMs: Date.now() - started, }, diff --git a/.cursor/skills/impeccable/scripts/lib/provider.mjs b/.cursor/skills/impeccable/scripts/lib/provider.mjs index 7fa928a9c..ea6fd8e67 100644 --- a/.cursor/skills/impeccable/scripts/lib/provider.mjs +++ b/.cursor/skills/impeccable/scripts/lib/provider.mjs @@ -1,4 +1,5 @@ // Source scripts default to slash commands. The provider build replaces only // this exact declaration, avoiding heuristic rewrites across executable code. export const IMPECCABLE_COMMAND_PREFIX = "/"; +export const IMPECCABLE_PROVIDER_ID = "cursor"; export const IMPECCABLE_COMMAND = `${IMPECCABLE_COMMAND_PREFIX}impeccable`; diff --git a/.cursor/skills/impeccable/scripts/lib/slop-review.mjs b/.cursor/skills/impeccable/scripts/lib/slop-review.mjs new file mode 100644 index 000000000..30f03e34d --- /dev/null +++ b/.cursor/skills/impeccable/scripts/lib/slop-review.mjs @@ -0,0 +1,30 @@ +export const LLM_ONLY_SLOP_ITEMS = Object.freeze([ + 'Monospace used merely to signal “technical” or “developer.”', + 'Light or dark mode chosen by category habit rather than the actual use scene.', + 'Everything wrapped in cards, or identical icon-heading-text cards repeated as the page structure.', + 'Hero-metric scaffolds: one oversized number, a small label, supporting stats, and an accent treatment.', + 'Decorative glassmorphism, meaningless sparklines, or generic rounded rectangles with drop shadows.', + 'A modal chosen by reflex when the task does not require interruption or protected focus.', +]); + +export function renderLlmOnlySlopReview({ manualDetector = false, automaticDetector = false, scriptsPath = null } = {}) { + const lines = [ + automaticDetector + ? 'AI_SLOP_REVIEW_REQUIRED: The automatic detector covers mechanical rules, but this harness has no reliable late review. Before finishing changed UI, inspect the authored result for detector-blind model reflexes:' + : 'AI_SLOP_REVIEW_REQUIRED: The automatic Impeccable design hook is not available for this session. Before finishing changed UI, inspect the authored result for detector-blind model reflexes:', + ...LLM_ONLY_SLOP_ITEMS.map((item) => `- ${item}`), + 'Fix reflexes, not intentional choices required by the brief or established visual authority.', + ]; + if (manualDetector && scriptsPath) { + lines.push(`Then run the mechanical detector once over the changed web UI: \`node ${scriptsPath}/detect.mjs --json \`. Do not run it earlier during concept selection.`); + } + return lines.join('\n'); +} + +export function renderStopSlopReview() { + return [ + '[impeccable@1] Detector-blind AI-slop review. The mechanical pass is complete; now inspect the rendered result for model reflexes it cannot reliably detect:', + ...LLM_ONLY_SLOP_ITEMS.map((item) => `- ${item}`), + 'Fix reflexes, not intentional choices required by the brief or established visual authority. Do not rerun the detector; this is the authored judgment layer.', + ].join('\n'); +} diff --git a/.cursor/skills/impeccable/scripts/lib/surface-briefs.mjs b/.cursor/skills/impeccable/scripts/lib/surface-briefs.mjs new file mode 100644 index 000000000..f83416f69 --- /dev/null +++ b/.cursor/skills/impeccable/scripts/lib/surface-briefs.mjs @@ -0,0 +1,151 @@ +import fs from 'node:fs'; +import path from 'node:path'; +import { slugFromTarget } from './target-slug.mjs'; + +export const SURFACE_BRIEF_VERSION = 1; + +export function getSurfaceBriefDir(projectRoot) { + return path.join(projectRoot, '.impeccable', 'surfaces'); +} + +export function normalizeSurfaceTarget(target, { projectRoot = process.cwd() } = {}) { + if (!target || typeof target !== 'string' || !target.trim()) return null; + const trimmed = target.trim(); + if (/^https?:\/\//i.test(trimmed)) { + try { + const url = new URL(trimmed); + url.hash = ''; + url.search = ''; + return url.toString().replace(/\/$/, '') || url.origin; + } catch { + return null; + } + } + if (/^route:/i.test(trimmed)) { + const route = trimmed.slice(trimmed.indexOf(':') + 1).trim(); + if (!route.startsWith('/') || route.includes('..')) return null; + const normalizedRoute = route.split(/[?#]/, 1)[0].replace(/\/{2,}/g, '/').replace(/\/$/, '') || '/'; + return `route:${normalizedRoute}`; + } + if (trimmed === '/') return 'route:/'; + if (trimmed.startsWith('/')) { + const absolute = path.resolve(trimmed); + const relativeToProject = path.relative(projectRoot, absolute); + const isProjectFile = relativeToProject && !relativeToProject.startsWith('..') && !path.isAbsolute(relativeToProject); + if (!isProjectFile && !fs.existsSync(absolute) && !trimmed.includes('..')) { + const normalizedRoute = trimmed.split(/[?#]/, 1)[0].replace(/\/{2,}/g, '/').replace(/\/$/, '') || '/'; + return `route:${normalizedRoute}`; + } + } + const abs = path.isAbsolute(trimmed) ? trimmed : path.resolve(projectRoot, trimmed); + const rel = path.relative(projectRoot, abs); + if (!rel || rel === '.' || rel.startsWith('..') || path.isAbsolute(rel)) return null; + return rel.split(path.sep).join('/'); +} + +export function surfaceBriefPathForTarget(target, { projectRoot = process.cwd() } = {}) { + const normalized = normalizeSurfaceTarget(target, { projectRoot }); + if (!normalized) return null; + const slugInput = normalized.startsWith('route:') ? `route${normalized.slice('route:'.length)}` : normalized; + const slug = slugFromTarget(slugInput, { cwd: projectRoot }); + return slug ? path.join(getSurfaceBriefDir(projectRoot), `${slug}.md`) : null; +} + +export function parseSurfaceBrief(text, filePath = null) { + const match = String(text || '').match(/^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/); + const meta = {}; + if (match) { + for (const line of match[1].split(/\r?\n/)) { + const colon = line.indexOf(':'); + if (colon < 0) continue; + const key = line.slice(0, colon).trim(); + const raw = line.slice(colon + 1).trim(); + if (!key) continue; + if (/^(?:\[|\{|\")/.test(raw) || /^(?:true|false|null|-?\d+(?:\.\d+)?)$/.test(raw)) { + try { meta[key] = JSON.parse(raw); continue; } catch { /* keep string */ } + } + meta[key] = raw.replace(/^['"]|['"]$/g, ''); + } + } + const primaryTarget = typeof meta.primary_target === 'string' ? meta.primary_target : null; + const relatedTargets = Array.isArray(meta.related_targets) + ? meta.related_targets.filter((value) => typeof value === 'string') + : []; + return { + path: filePath, + text: String(text || ''), + body: match ? String(text || '').slice(match[0].length).trim() : String(text || '').trim(), + meta, + slug: typeof meta.slug === 'string' ? meta.slug : filePath ? path.basename(filePath, '.md') : null, + primaryTarget, + relatedTargets, + targets: [primaryTarget, ...relatedTargets].filter(Boolean), + }; +} + +export function listSurfaceBriefs(projectRoot = process.cwd()) { + const dir = getSurfaceBriefDir(projectRoot); + let names; + try { + names = fs.readdirSync(dir).filter((name) => name.endsWith('.md')).sort(); + } catch { + return []; + } + return names.flatMap((name) => { + const filePath = path.join(dir, name); + try { + return [parseSurfaceBrief(fs.readFileSync(filePath, 'utf-8'), filePath)]; + } catch { + return []; + } + }); +} + +export function resolveSurfaceBrief(projectRoot = process.cwd(), target = null) { + const briefs = listSurfaceBriefs(projectRoot); + if (!target) { + return { + brief: briefs.length === 1 ? briefs[0] : null, + candidates: briefs, + reason: briefs.length === 1 ? 'only-brief' : briefs.length > 1 ? 'ambiguous' : 'none', + }; + } + + const normalized = normalizeSurfaceTarget(target, { projectRoot }); + if (!normalized) return { brief: null, candidates: briefs, reason: 'invalid-target' }; + const exactPath = surfaceBriefPathForTarget(normalized, { projectRoot }); + const exact = briefs.find((brief) => brief.path === exactPath && (!brief.targets.length || brief.targets.includes(normalized))); + if (exact) return { brief: exact, candidates: briefs, reason: 'slug' }; + const mapped = briefs.filter((brief) => brief.targets.includes(normalized)); + return { + brief: mapped.length === 1 ? mapped[0] : null, + candidates: mapped.length > 1 ? mapped : briefs, + reason: mapped.length === 1 ? 'mapping' : mapped.length > 1 ? 'ambiguous-target' : 'not-found', + }; +} + +export function writeSurfaceBrief({ + projectRoot = process.cwd(), + primaryTarget, + relatedTargets = [], + body, +}) { + const normalizedPrimary = normalizeSurfaceTarget(primaryTarget, { projectRoot }); + if (!normalizedPrimary) throw new Error('surface brief requires a concrete project-relative primary target or URL'); + const normalizedRelated = [...new Set(relatedTargets + .map((target) => normalizeSurfaceTarget(target, { projectRoot })) + .filter((target) => target && target !== normalizedPrimary))]; + const slug = slugFromTarget(normalizedPrimary, { cwd: projectRoot }); + const filePath = surfaceBriefPathForTarget(normalizedPrimary, { projectRoot }); + fs.mkdirSync(path.dirname(filePath), { recursive: true }); + const frontmatter = [ + '---', + `version: ${SURFACE_BRIEF_VERSION}`, + `slug: ${JSON.stringify(slug)}`, + `primary_target: ${JSON.stringify(normalizedPrimary)}`, + `related_targets: ${JSON.stringify(normalizedRelated)}`, + '---', + ].join('\n'); + fs.writeFileSync(filePath, `${frontmatter}\n\n${String(body || '').trim()}\n`, 'utf-8'); + return filePath; +} diff --git a/.cursor/skills/impeccable/scripts/lib/target-slug.mjs b/.cursor/skills/impeccable/scripts/lib/target-slug.mjs new file mode 100644 index 000000000..025915ad5 --- /dev/null +++ b/.cursor/skills/impeccable/scripts/lib/target-slug.mjs @@ -0,0 +1,33 @@ +import path from 'node:path'; + +const SLUG_MAX = 50; + +/** Derive one clone-stable slug from a concrete file path or URL. */ +export function slugFromTarget(resolved, { cwd = process.cwd() } = {}) { + if (!resolved || typeof resolved !== 'string') return null; + const trimmed = resolved.trim(); + if (!trimmed) return null; + + if (/^https?:\/\//i.test(trimmed)) { + let url; + try { url = new URL(trimmed); } catch { return null; } + return kebab(`${url.hostname}${url.pathname}`); + } + + const abs = path.isAbsolute(trimmed) ? trimmed : path.resolve(cwd, trimmed); + let rel = path.relative(cwd, abs); + if (rel.startsWith('..') || path.isAbsolute(rel)) rel = path.basename(abs); + if (!rel || rel === '.') return null; + return kebab(rel); +} + +export function kebab(value) { + const slug = String(value || '') + .toLowerCase() + .replace(/[/\\.]+/g, '-') + .replace(/[^a-z0-9-]+/g, '-') + .replace(/-+/g, '-') + .replace(/^-|-$/g, ''); + if (!slug) return null; + return slug.length <= SLUG_MAX ? slug : slug.slice(slug.length - SLUG_MAX).replace(/^-/, ''); +} diff --git a/.cursor/skills/impeccable/scripts/live-browser.js b/.cursor/skills/impeccable/scripts/live-browser.js index 616f1290a..47bbfefbf 100644 --- a/.cursor/skills/impeccable/scripts/live-browser.js +++ b/.cursor/skills/impeccable/scripts/live-browser.js @@ -2592,7 +2592,7 @@ minWidth: '16px', height: '16px', padding: '0 4px', borderRadius: '999px', background: tuneOpen ? C.brand : BP.hairline, - color: tuneOpen ? 'oklch(98% 0 0)' : 'inherit', + color: tuneOpen ? C.ink : 'inherit', fontFamily: MONO, fontSize: '9.5px', fontWeight: '600', lineHeight: '1', boxSizing: 'border-box', @@ -3131,7 +3131,7 @@ position: 'absolute', top: '2px', left: initial ? '18px' : '2px', width: '16px', height: '16px', borderRadius: '50%', - background: 'oklch(98% 0 0)', + background: C.ink, transition: 'left 0.18s ' + EASE, boxShadow: '0 1px 2px oklch(0% 0 0 / 0.2)', }); @@ -3165,7 +3165,7 @@ const b = el('button', { padding: '5px 4px', border: 'none', borderRadius: '3px', background: active ? C.brand : 'transparent', - color: active ? 'oklch(98% 0 0)' : P.text, + color: active ? C.ink : P.text, fontFamily: FONT, fontSize: '10.5px', fontWeight: '500', cursor: 'pointer', whiteSpace: 'nowrap', transition: 'background 0.1s ease, color 0.1s ease', @@ -3178,7 +3178,7 @@ segBtns.forEach(({ btn, val }) => { const on = val === o.value; btn.style.background = on ? C.brand : 'transparent'; - btn.style.color = on ? 'oklch(98% 0 0)' : P.text; + btn.style.color = on ? C.ink : P.text; }); applyParamValue(variantEl, p, o.value); queueCheckpoint('param_changed'); diff --git a/.cursor/skills/impeccable/scripts/surface-brief.mjs b/.cursor/skills/impeccable/scripts/surface-brief.mjs new file mode 100644 index 000000000..723f7c1b4 --- /dev/null +++ b/.cursor/skills/impeccable/scripts/surface-brief.mjs @@ -0,0 +1,74 @@ +#!/usr/bin/env node +import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath, pathToFileURL } from 'node:url'; +import { resolveProjectRoot } from './context.mjs'; +import { + listSurfaceBriefs, + resolveSurfaceBrief, + surfaceBriefPathForTarget, + writeSurfaceBrief, +} from './lib/surface-briefs.mjs'; + +function summary(brief, projectRoot) { + return { + slug: brief.slug, + path: path.relative(projectRoot, brief.path).split(path.sep).join('/'), + primaryTarget: brief.primaryTarget, + relatedTargets: brief.relatedTargets, + }; +} + +function main(argv) { + const [command, target, bodyFile, ...relatedTargets] = argv; + const projectRoot = resolveProjectRoot(process.cwd(), target ? { targetPath: target } : {}); + if (command === 'path') { + const filePath = surfaceBriefPathForTarget(target, { projectRoot }); + if (!filePath) throw new Error('surface brief path requires a concrete target'); + process.stdout.write(`${path.relative(process.cwd(), filePath) || filePath}\n`); + return; + } + if (command === 'list') { + process.stdout.write(`${JSON.stringify(listSurfaceBriefs(projectRoot).map((brief) => summary(brief, projectRoot)), null, 2)}\n`); + return; + } + if (command === 'read') { + const result = resolveSurfaceBrief(projectRoot, target || null); + if (result.brief) { + process.stdout.write(result.brief.text); + return; + } + if (result.candidates.length) process.stderr.write(`${JSON.stringify(result.candidates.map((brief) => summary(brief, projectRoot)), null, 2)}\n`); + process.exit(2); + } + if (command === 'write') { + if (!target || !bodyFile) throw new Error('usage: surface-brief.mjs write '); + const filePath = writeSurfaceBrief({ + projectRoot, + primaryTarget: target, + relatedTargets, + body: fs.readFileSync(bodyFile, 'utf-8'), + }); + process.stdout.write(`${path.relative(process.cwd(), filePath) || filePath}\n`); + return; + } + throw new Error('usage: surface-brief.mjs [target] [body-file] [related-target ...]'); +} + +function isMainModule() { + if (!process.argv[1]) return false; + try { + return fs.realpathSync(fileURLToPath(import.meta.url)) === fs.realpathSync(process.argv[1]); + } catch { + return import.meta.url === pathToFileURL(process.argv[1]).href; + } +} + +if (isMainModule()) { + try { + main(process.argv.slice(2)); + } catch (error) { + process.stderr.write(`${error?.message || error}\n`); + process.exit(1); + } +} diff --git a/.gemini/skills/impeccable/SKILL.md b/.gemini/skills/impeccable/SKILL.md index cb161eabc..117be7e9e 100644 --- a/.gemini/skills/impeccable/SKILL.md +++ b/.gemini/skills/impeccable/SKILL.md @@ -1,61 +1,60 @@ --- name: impeccable description: Use when the user wants to design, redesign, shape, critique, audit, polish, clarify, distill, harden, optimize, adapt, animate, colorize, extract, or otherwise improve a frontend interface. Covers websites, landing pages, dashboards, product UI, app shells, components, forms, settings, onboarding, and empty states. Handles UX review, visual hierarchy, information architecture, cognitive load, accessibility, performance, responsive behavior, theming, anti-patterns, typography, fonts, spacing, layout, alignment, color, motion, micro-interactions, UX copy, error states, edge cases, i18n, and reusable design systems or tokens. Also use for bland designs that need to become bolder or more delightful, loud designs that should become quieter, live browser iteration on UI elements, or ambitious visual effects that should feel technically extraordinary. Not for backend-only or non-UI tasks. -version: 4.0.0-alpha.9 +version: 4.0.0-alpha.10 --- -Designs and iterates production-grade frontend interfaces. Real working code, committed design choices, exceptional craft. +This skill gives you the tools and permission to create design that earns to be called out-of-distribution craft: Whereas before, your design work would have been safe, timid and measured, you now approach every design task as a award-winning design director with impeccable understanding for what makes exceptional design work: production-grade code, peak creativity, a clear POV, deep understanding of the needs of the client and users, and exceptional craft. -Approach every design task as the design lead at a small studio known for giving every client a visual identity that could not be mistaken for anyone else's. The client has already rejected work that felt templated; they are paying for a point of view. Gemini is capable of extraordinary work. Don't hold back. +Core principles: +- Go all out. No hedging, no shortcuts. The deliverable must be complete (except assets the user must provide). +- Dream big and bold. Distinct, beautiful, outstanding and highly inspiring work. +- Iterate with tools available to you (e.g. visual understanding, browser screenshots) until you think this meets the bar. ## Setup -1. Run `node .gemini/skills/impeccable/scripts/context.mjs` once per session (if the runtime shows this skill's loaded base directory, run `node /scripts/context.mjs`; keep cwd at the user's project). It prints the project's context and its directives; follow what it prints. Once its output is in the conversation, never rerun it on a later turn. -2. `craft` and `shape` are build-path exceptions: the new-work gate below owns their flow, and on unattended runs its checkpoints resolve without pausing. For any other invoked sub-command (`audit`, `polish`, `live`, ...), immediately read **`reference/.md`** after `context.mjs` (the `.native` variant from the Commands table when the platform is `ios`/`android`/`adaptive`) and follow it. This read is a hard gate: do not inspect the target, run command-specific scripts, or edit files until the reference is loaded. -3. Read at least one project file (CSS / tokens / theme / a representative component) to learn what world you're in. If PRODUCT.md's `## Platform` is `ios` or `android`, also read `reference/.md` (`adaptive` reads both). +1. Run `node .gemini/skills/impeccable/scripts/context.mjs` once per session (if the runtime shows this skill's loaded base directory, run `node /scripts/context.mjs`; keep cwd at the user's project). Pass a named source file or route as `--target `. It loads PRODUCT.md, DESIGN.md, the matching surface brief, and native-platform guidance when applicable; follow its directives and do not rerun it. +2. Before acting, load the one playbook that owns the request: the Commands table's reference for an explicit or clearly implied sub-command, or [reference/new-work.md](reference/new-work.md) for a new surface or replacement visual world. Then inspect the target and at least one representative source of incumbent visual truth (tokens, theme, CSS, component, or asset) before editing. ## How to design -**The brief wins.** Where the brief pins down a direction (a named aesthetic, an era, a place, a material, a specific font or palette), follow it exactly, including when it asks for a look this skill warns is saturated. Redirecting a pinned direction toward your own taste is a failure, not a save. - -**Existing worlds are sacred.** Most of impeccable's work happens inside a site or app that already exists. When the surface has a committed design system (real tokens, deliberately chosen faces, a palette the brand owns), work inside that world: extend it, sharpen it, leave it unmistakably the same brand, and never degrade a working page's performance. Inventing parallel colors, fonts, or styles on an existing surface is a defect, not creativity. A scoped refinement keeps the named scope's content and media footprint unless the brief explicitly changes them; build the added emphasis by recomposing what is already there through hierarchy, proportion, rhythm, and the system's own motifs. - -**New identity work reads the playbook first.** When nothing committed exists (greenfield, or a codebase with no real tokens or chosen faces), or the user explicitly asks for a redesign that discards the current look, you MUST read [reference/new-work.md](reference/new-work.md) before making any design decision. Missing DESIGN.md alone does not make work greenfield: code, tokens, chosen type, components, and assets are incumbent design authority. Preserve and extend them unless the user asked to replace them. Producing a genuinely new identity without the playbook yields the generic default this skill exists to prevent. Scoped fixes inside an existing world don't need it; the craft floor below governs them. `context.mjs` prints the appropriate authority directive. +- **The brief wins.** Honor pinned aesthetics, eras, materials, fonts, and palettes even when they conflict with a saturated-pattern warning. Redirecting a clear brief toward your taste is failure. +- **Refinement preserves; redesign replaces.** Refinement keeps the incumbent identity, behavior, copy, and everything outside scope. Ask before replacing factual copy or adding claims. Redesign keeps product truth, content, function, native affordances, and constraints, but treats the old look as evidence and anti-reference; choose a replacement world in new-work and replace DESIGN.md. Never split the difference into polish on the discarded look. +- **Visual authority is evidence, not a filename.** Missing DESIGN.md alone does not make a project greenfield; new-work decides whether to preserve, expand, or replace the incumbent world. ## Modes -Name the visitor's mode before designing; the page's grammar follows from it, and most ruined pages are one mode wearing another mode's grammar. **The mode belongs to the surface, not the subject**: a landing page for a dense technical tool is still Persuade, with Persuade's full permission to be striking; a docs page for a fashion house is still Read. Deciding a page can be plain because its subject is workmanlike is the same category error in reverse. The brief and the surface decide the mode; PRODUCT.md's `register` field survives only as a family hint (`brand` covers Persuade and Experience, `product` covers Operate and Read). Depth beyond the paragraphs below: [reference/new-work.md](reference/new-work.md) when inventing identity, [reference/operate.md](reference/operate.md) for substantial Operate and Read work. +Choose the mode from the requested surface, not the product, and persist it only in that surface brief. A tool's landing page is still Persuade; a fashion house's documentation is still Read. See [new-work.md](reference/new-work.md) for new surfaces and [operate.md](reference/operate.md) for deeper Operate/Read guidance. -**Persuade** (the surface exists to win someone over; design IS the product). The deliverable is an impression that stops the scroll, earns the click, converts. Spans every genre; don't collapse them into one look. On new surfaces, briefs that imply imagery must ship real, verified imagery; a colored rectangle where a photo belongs reads as incomplete. New Persuade surfaces take their typeface procedure and reject list from [reference/new-work.md](reference/new-work.md). - -**Operate** (the surface is a tool someone works in; design SERVES the task). A person getting something done: scanability and consistency outrank expressiveness. These surfaces earn trust by feeling native to their platform: system font stacks and workhorse UI faces are legitimate and often correct here (the Persuade reject list does not apply). The brand lives in the details: focus states, empty states, microcopy, one owned accent. The usage scene is part of the spec: an interface read outdoors, in motion, or at a glance must survive its real ambient light, and the theme follows the scene, not the category's habit. - -**Read** (the surface exists to be understood; long-form, reference, guidance). The deliverable is comprehension, and comprehension is earned twice: a structure the reader can hold in their head with nothing standing between them and the answer, and a reading experience good enough to stay in, through typographic quality and whatever visual or interactive support genuinely helps the reader follow. The brand lives in type, spacing, and small accents. - -**Experience** (the surface presents a body of work; the page IS the work). The artifact leads, the interface recedes, and the visitor meets the work itself in the first viewport at every screen size. Boldness here means trusting the work. +- **Persuade:** win someone over; design is the product. Earn attention and action. Ship real imagery when the brief needs it; follow the committed world, not category habit. +- **Operate:** help someone do work. Scanability, consistency, native expectations, and the real usage scene outrank expression. Brand lives in precise details. +- **Read:** make something understood. Structure for comprehension, then make the reading experience worth staying in. +- **Experience:** present a body of work. Let the artifact lead from the first viewport; the interface recedes. ## Craft floor -Build to this floor without announcing it. The design detector (the project hook, `node .gemini/skills/impeccable/scripts/detect.mjs --json `, or `audit`) verifies most of it mechanically; resolve every finding before finalizing. Fix real defects, but use context judgment rather than distorting intentional design to appease a false positive. Classify any intentional exception explicitly and use the hook system's narrowest appropriate waiver when it must persist. +Build to this floor without announcing it. -- Contrast: body text ≥4.5:1 against its background (placeholders too); large text ≥3:1. Gray text on a colored background looks washed out: use a darker shade of the background's own hue, or a transparency of the text color. -- Shadows describe real light: an offset and a soft blur. A zero-offset colored halo is decoration announcing itself. -- Spacing has rhythm: generous separations, tight groupings; cramped padding reads as broken; the space above a heading exceeds the space below it. Verify computed spacing, not intended spacing. -- Type: body line length 65-75ch; display clamp() max ≤6rem; letter-spacing ≥-0.04em; `text-wrap: balance` on headings; modular scale ≥1.25 between steps; light-on-dark adds 0.05-0.1 line-height. Pair faces on a contrast axis, never two similar-but-not-identical ones; one family with committed weight contrast beats a timid pair. Test headings at every breakpoint; overflow means reduce the clamp or rewrite the copy. -- Motion is part of the build: one orchestrated moment beats scattered effects; ease-out exponential curves; reveals enhance an already-visible default (content gated on a class-triggered transition ships blank in hidden tabs and headless renderers). Responsive down to mobile and visible keyboard focus are part of the floor. -- Ship real content (no placeholders, dead links, or fake controls) and cover the interaction states people will actually hit (hover, focus, disabled, loading, error, empty). -- Copy is design material: name things the way the page's own people speak, make every control say what it does, and make every error say what happened and what to do next. -- Before finishing, re-read the brief: every requirement it names must exist on the page, findable in seconds. A beautiful page missing an asked-for feature is unfinished. +- **Contrast:** body and placeholder text ≥4.5:1; large text ≥3:1. On colored surfaces, tint secondary text from that hue or the foreground instead of using gray. +- **Depth:** shadows describe light with offset and soft blur; zero-offset colored halos are decoration. +- **Spacing:** tight groups, generous separation, no cramped containers; space above a heading exceeds space below. Verify computed values. +- **Type:** body measure 65–75ch; display max 6rem and tracking floor -0.04em; balance headings; use clear scale/weight contrast; test overflow at every breakpoint. +- **Motion:** author one coherent moment instead of scattered effects. Use exponential ease-out and an already-visible default. Premium motion is not transform/opacity alone: it may add focus, depth, masks, light, or material change through blur/filter, backdrop-filter, clip-path/masks, or shadow when smooth. Always provide reduced motion. +- **Shipping:** real content, working controls, responsive composition, keyboard focus, and the states users hit: hover, disabled, loading, error, and empty. +- **Copy:** use the product's language; controls name their action, errors name the problem and recovery. +- **Coverage:** every brief requirement must exist and be findable within seconds. -**Gemini-specific defect: hard ban.** Never animate `` elements on hover, including Tailwind `.group:hover` scale/rotate/translate patterns that animate a child image via a parent hover. It adds no information and reads as "AI animated this because it could". If a card needs hover feedback, animate the card's background, border, or shadow. Never the image, never via the image's parent. +Before finishing changed UI, follow the quality guidance supplied by `context.mjs` and hooks. Context requests a manual scan only when no automatic detector is active; never add a second detector pass. + +Never animate `` elements on hover, directly or through a parent. Give the card itself feedback instead. ## Commands | Command | Category | Description | Reference | |---|---|---|---| -| `craft [feature]` | Build | The standard build flow with attended checkpoints | [reference/new-work.md](reference/new-work.md) | +| `craft [feature]` | Build | Deprecated alias for an ordinary new-work request | [reference/craft.md](reference/craft.md) | | `shape [feature]` | Build | Plan UX/UI before writing code | [reference/shape.md](reference/shape.md) | -| `init` | Build | Set up project context: PRODUCT.md, DESIGN.md, live config, next steps | [reference/init.md](reference/init.md) | +| `init` | Build | Capture durable product context in PRODUCT.md | [reference/init.md](reference/init.md) | | `document` | Build | Generate DESIGN.md from existing project code | [reference/document.md](reference/document.md) | | `extract [target]` | Build | Pull reusable tokens and components into design system | [reference/extract.md](reference/extract.md) | | `critique [target]` | Evaluate | UX design review with heuristic scoring | [reference/critique.md](reference/critique.md) | @@ -77,7 +76,14 @@ Build to this floor without announcing it. The design detector (the project hook | `optimize [target]` | Fix | Diagnose and fix UI performance | [reference/optimize.md](reference/optimize.md) | | `live` | Iterate | Visual variant mode: pick elements in the browser, generate alternatives | [reference/live.md](reference/live.md) | -Routing: **no argument** → read [reference/routing.md](reference/routing.md) and present the context-aware menu (never auto-run a command). **First word matches a command** (or `pin` / `unpin` / `hooks`) → load its reference (native variant on native platforms) and follow it; everything after the command name is the target. **Intent clearly maps to one command** ("fix the spacing" → `layout`, "rewrite this error" → `clarify`) → same; if two fit, ask once. **Otherwise** → general design invocation: apply Setup and this file's guidance; builds flow through the new-work gate above, whose playbook carries the direction checkpoint and the finishing pass. `teach` routes to `init`, and `craft` routes to the standard build flow with attended checkpoints. If setup diverted into `init` for a build request, finish init, refresh context, then resume. +Routing: + +- **No argument:** read [routing.md](reference/routing.md) and present its context-aware menu; never auto-run a command. +- **Explicit or clearly implied command:** load its reference (native variant on native platforms) and follow it. Ask once if two commands fit. +- **Otherwise:** treat the request as general design work. Missing PRODUCT.md routes through init; new surfaces and replacement worlds use new-work. +- `teach` aliases `init`. `craft` is a deprecated alias for ordinary new-work and adds nothing. `shape` owns task discovery, then enters new-work only for visual-world and surface-concept decisions. + +After init writes PRODUCT.md, resume without rerunning `context.mjs`. **Pin / Unpin:** `node .gemini/skills/impeccable/scripts/pin.mjs ` creates or removes a standalone `/` shortcut. Report the script's result concisely; relay stderr verbatim on error. diff --git a/.gemini/skills/impeccable/reference/android.md b/.gemini/skills/impeccable/reference/android.md index 3575c31fb..6337b9018 100644 --- a/.gemini/skills/impeccable/reference/android.md +++ b/.gemini/skills/impeccable/reference/android.md @@ -2,7 +2,7 @@ For native Android apps: Jetpack Compose, Android Views, React Native, Expo, Flutter shipping to Android hardware. -On native, register narrows. Material Design 3 governs structure, navigation, and interaction whatever the register; brand expresses through Material's theming (color roles, type scale, shape, motion). A Material-everywhere cross-platform app that also ships to iPhone still owes iOS its OS guarantees on that hardware: safe-area insets, Reduce Motion, edge-swipe back. +On native, the visitor mode narrows what expression may override. Material Design 3 governs structure, navigation, and interaction in every mode; brand expresses through Material's theming (color roles, type scale, shape, motion). A Material-everywhere cross-platform app that also ships to iPhone still owes iOS its OS guarantees on that hardware: safe-area insets, Reduce Motion, edge-swipe back. ## The Android slop test diff --git a/.gemini/skills/impeccable/reference/animate.md b/.gemini/skills/impeccable/reference/animate.md index 1a1098a5b..d51bb6d63 100644 --- a/.gemini/skills/impeccable/reference/animate.md +++ b/.gemini/skills/impeccable/reference/animate.md @@ -1,203 +1,88 @@ > **Additional context needed**: performance constraints. -Add motion that conveys state, gives feedback, and clarifies hierarchy. Cut motion that exists only for decoration. Animation fatigue is a real cost; spend the budget on the moments that need it. +Use motion to explain state, relationship, and hierarchy, or to create one authored moment the surface has earned. Decoration without purpose is animation debt. --- -## Register +## Visitor mode -Persuade + Experience: 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. +- **Persuade + Experience:** motion may carry the voice. Prefer one rehearsed focal sequence to repeated section reveals. +- **Operate + Read:** motion serves feedback, state, and continuity. Keep routine transitions fast and do not make users wait through page-load choreography. +- **Native (`ios` / `android` / `adaptive`):** follow the Motion section of [ios.md](ios.md) or [android.md](android.md), including the platform's Reduce Motion behavior. Do not apply the web tooling below. -Operate + Read: 150–250 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. +## Find the job -Native (`ios` / `android` / `adaptive`): implementation follows the Motion section of [ios.md](ios.md) / [android.md](android.md) (read it first if Setup hasn't already): system transitions and OS Reduce Motion, never the web tooling below. +Inspect the existing motion language, interaction states, target devices, and performance budget. Find only the places where motion would: ---- +- acknowledge an action; +- make a state change or spatial relationship legible; +- preserve continuity through navigation or layout change; +- direct attention at a meaningful moment; +- embody the selected visual world. -## Assess Animation Opportunities +Ask only when a material constraint cannot be inferred. Do not animate a static area merely because it exists. -Analyze where motion would improve the experience: +## Set the motion thesis -1. **Identify static areas**: - - **Missing feedback**: Actions without visual acknowledgment (button clicks, form submission, etc.) - - **Jarring transitions**: Instant state changes that feel abrupt (show/hide, page loads, route changes) - - **Unclear relationships**: Spatial or hierarchical relationships that aren't obvious - - **Lack of delight**: Functional but joyless interactions - - **Missed guidance**: Opportunities to direct attention or explain behavior +Write a short plan before implementation: -2. **Understand the context**: - - What's the personality? (Playful vs serious, energetic vs calm) - - What's the performance budget? (Mobile-first? Complex page?) - - Who's the audience? (Motion-sensitive users? Power users who want speed?) - - What matters most? (One hero animation vs many micro-interactions?) +- **Focal moment:** the one sequence or interaction that deserves authorship, if any. +- **Continuity:** the state, layout, or navigation changes that need explanation. +- **Feedback:** the controls and outcomes that need acknowledgment. +- **Budget:** which effects may be expensive, how often they run, and the reduced-motion equivalent. -If any of these are unclear from the codebase, ask the user directly to clarify what you cannot infer. +The focal moment must come from this product and surface concept. A generic fade-and-rise, hover lift, parallax layer, or scroll reveal is not a thesis. -**CRITICAL**: Respect `prefers-reduced-motion`. Always provide non-animated alternatives for users who need them. +## Choose material by meaning -## Plan Animation Strategy +Transform and opacity are reliable foundations, not the entire palette. Choose properties for what the transition communicates: -Create a purposeful animation plan: +- **Continuity and relationship:** shared-element motion, FLIP-style transforms, view transitions, or deliberate spatial movement. +- **Focus and depth:** bounded blur, filter, backdrop, light, or shadow changes. +- **Reveal and composition:** masks, clip paths, cropping, or controlled occlusion. +- **Material and energy:** color, gradient position, texture, distortion, or shader effects when the world and runtime support them. +- **State and feedback:** the smallest change that makes cause and result unmistakable. -- **Feedback layer**: Which interactions need acknowledgment? -- **Transition layer**: Which state changes need smoothing? -- **Entrance moment**: The ONE entrance worth rehearsing, where the mode invites it. Not every surface wants one. -- **Delight layer**: Where can we surprise and delight? +Do not stack techniques for spectacle. One strong material idea, carried through the focal sequence and quiet supporting states, is usually enough. -**IMPORTANT**: One well-orchestrated experience beats scattered animations everywhere. Focus on high-impact moments. +Sibling stagger is appropriate when a list appears as a list. Cap the total delay, and never reinterpret every scrolled section as a staggered list. -## Implement Animations +## Timing and easing -Add motion systematically across these categories: +Timing should express distance and consequence: -### Micro-interactions -- **Button feedback**: - - Hover: Subtle scale (1.02-1.05), color shift, shadow increase - - Click: Quick scale down then up (0.95 → 1), ripple effect - - Loading: Spinner or pulse state -- **Form interactions**: - - Input focus: Border color transition, slight scale or glow - - Validation: Shake on error, check mark on success, smooth color transitions -- **Toggle switches**: Smooth slide + color transition (200-300ms) -- **Checkboxes/radio**: Check mark animation, ripple effect -- **Like/favorite**: Scale + rotation, particle effects, color transition +| Duration | Typical use | +|---|---| +| 100–150 ms | immediate feedback | +| 150–300 ms | routine state change | +| 300–500 ms | layout, overlay, or view transition | +| 500–800 ms | a deliberately authored focal entrance | -### State Transitions -- **Show/hide**: Fade + slide (not instant), appropriate timing (200-300ms) -- **Expand/collapse**: Height transition with overflow handling, icon rotation -- **Loading states**: Skeleton screen fades, spinner animations, progress bars -- **Success/error**: Color transitions, icon animations, gentle scale pulse -- **Enable/disable**: Opacity transitions, cursor changes +Exit faster than entrance. Use natural deceleration such as `cubic-bezier(0.16, 1, 0.3, 1)` for confident arrivals; do not use bounce or elastic curves by reflex. Long feedback feels like latency. -### Entrance Animations -- **The one entrance moment** (where the mode invites it): a committed entrance for primary content (scale, parallax, or a creative effect) -- **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. +## Implement to the runtime - 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. +- Use CSS transitions and keyframes for declarative state and bounded sequences. +- Use Web Animations API or the project's existing motion library for interruption, sequencing, and dynamic values. +- Use View Transitions or shared-element techniques when continuity across states is the point. +- Use scroll-driven motion only when the scroll relationship itself carries meaning, with a robust fallback. +- Do not add a dependency for an effect the existing stack can express cleanly. -### Navigation & Flow -- **Page transitions**: Crossfade between routes, shared element transitions -- **Tab switching**: Slide indicator, content fade/slide -- **Carousel/slider**: Smooth transforms, snap points, momentum -- **Scroll effects**: Parallax layers, sticky headers with state changes, scroll progress indicators +Keep content visible in the default state so failed scripts do not hide the page. Avoid casually animating layout-driving properties such as `width`, `height`, `top`, `left`, and margins; use FLIP, transforms, or grid techniques when appropriate. Bound blur, filter, shadow, canvas, and shader work to isolated regions. Apply `will-change` only during known animation. Measure on target viewports and devices rather than assuming transform means fast. -### Feedback & Guidance -- **Hover hints**: Tooltip fade-ins, cursor changes, element highlights -- **Drag & drop**: Lift effect (shadow + scale), drop zone highlights, smooth repositioning -- **Copy/paste**: Brief highlight flash on paste, "copied" confirmation -- **Focus flow**: Highlight path through form or workflow +## Accessibility and control -### Delight Moments -- **Empty states**: Subtle floating animations on illustrations -- **Completed actions**: Confetti, check mark flourish, success celebrations -- **Easter eggs**: Hidden interactions for discovery -- **Contextual animation**: Weather effects, time-of-day themes, seasonal touches +Honor `prefers-reduced-motion` with an intentional alternative. Preserve state change and hierarchy while removing travel, parallax, flashing, or prolonged sequences. Do not replace every animation globally with `0.01ms` if that destroys useful feedback. Motion must never block focus, interaction, reading, or task completion. -## Technical Implementation +Respect autoplay and sound preferences. Any nonessential loop must stop when offscreen or hidden. -Use appropriate techniques for each animation: +## Verify -### Timing & Easing +- The focal motion is specific to the selected world and surface. +- Every supporting animation explains feedback, state, or relationship. +- Interruption and repeated use behave correctly. +- Desktop, mobile, keyboard, and reduced-motion paths remain usable. +- Expensive effects stay smooth on the target device. +- Removing an animation would lose meaning or authored character, not merely decoration. -**Duration: the 100/300/500 rule.** Timing matters more than easing for "feels right": - -| Duration | Use Case | Examples | -|----------|----------|----------| -| **100–150ms** | Instant feedback | Button press, toggle, color change | -| **200–300ms** | State changes | Menu open, tooltip, hover state | -| **300–500ms** | Layout changes | Accordion, modal, drawer | -| **500–800ms** | Entrance animations | Page load, hero reveal | - -**Easing curves (use these, not CSS defaults):** -```css -/* Recommended: natural deceleration */ ---ease-out-quart: cubic-bezier(0.25, 1, 0.5, 1); /* Smooth */ ---ease-out-quint: cubic-bezier(0.22, 1, 0.36, 1); /* Slightly snappier */ ---ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1); /* Confident, decisive */ - -/* AVOID: feel dated and tacky */ -/* bounce: cubic-bezier(0.34, 1.56, 0.64, 1); */ -/* elastic: cubic-bezier(0.68, -0.6, 0.32, 1.6); */ -``` - -**Exit animations are faster than entrances.** Use ~75% of enter duration. - -### CSS Animations -```css -/* Prefer for simple, declarative animations */ -- transitions for state changes -- @keyframes for complex sequences -- transform and opacity for reliable movement -- blur, filters, masks, clip paths, shadows, and color shifts for premium atmospheric effects when verified smooth -``` - -### JavaScript Animation -```javascript -/* Use for complex, interactive animations */ -- Web Animations API for programmatic control -- Framer Motion for React -- 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 -- **Layout safety**: Avoid casual animation of layout-driving properties (`width`, `height`, `top`, `left`, margins) -- **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) { - * { - animation-duration: 0.01ms !important; - animation-iteration-count: 1 !important; - transition-duration: 0.01ms !important; - } -} -``` - -**NEVER**: -- Use bounce or elastic easing curves; they feel dated and draw attention to the animation itself -- Animate layout properties casually (`width`, `height`, `top`, `left`, margins) when transform, FLIP, or grid-based techniques would work -- Use durations over 500ms for feedback (it feels laggy) -- Animate without purpose (every animation needs a reason) -- Ignore `prefers-reduced-motion` (this is an accessibility violation) -- Animate everything (animation fatigue makes interfaces feel exhausting) -- Block interaction during animations unless intentional - -## Verify Quality - -Test animations thoroughly: - -- **Smooth at 60fps**: No jank on target devices -- **Feels natural**: Easing curves feel organic, not robotic -- **Appropriate timing**: Not too fast (jarring) or too slow (laggy) -- **Reduced motion works**: Animations disabled or simplified appropriately -- **Doesn't block**: Users can interact during/after animations -- **Adds value**: Makes interface clearer or more delightful - -When the motion clarifies state instead of decorating it, hand off to `/impeccable polish` for the final pass. +When motion earns its place, hand off to `/impeccable polish` for the final pass. diff --git a/.gemini/skills/impeccable/reference/audit.md b/.gemini/skills/impeccable/reference/audit.md index 72815af69..70d80771a 100644 --- a/.gemini/skills/impeccable/reference/audit.md +++ b/.gemini/skills/impeccable/reference/audit.md @@ -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 - diff --git a/.gemini/skills/impeccable/reference/audit.native.md b/.gemini/skills/impeccable/reference/audit.native.md index 737d50e4f..0126fa157 100644 --- a/.gemini/skills/impeccable/reference/audit.native.md +++ b/.gemini/skills/impeccable/reference/audit.native.md @@ -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) diff --git a/.gemini/skills/impeccable/reference/bolder.md b/.gemini/skills/impeccable/reference/bolder.md index 6b44f0e38..78f5e4811 100644 --- a/.gemini/skills/impeccable/reference/bolder.md +++ b/.gemini/skills/impeccable/reference/bolder.md @@ -8,12 +8,12 @@ ## Why it reads flat -A section usually reads flat for reasons its neighbors have already solved. Look at what the rest of the page does that this section does not: the display type at full strength, the structural devices that carry meaning, the signature motif, the density and pacing, the specificity of the copy. A flat section is typically one that quietly opts out of the system's own strongest moves. The most reliable bolder pass brings the target up to the expressive level its neighbors already reach, in the system's own vocabulary rather than a new one. +A section usually reads flat for reasons its neighbors have already solved. Look at what the rest of the page does that this section does not: the display type at full strength, the structural devices that carry meaning, the signature motif, the density and pacing. A flat section is typically one that quietly opts out of the system's own strongest moves. The most reliable bolder pass brings the target up to the expressive level its neighbors already reach, in the system's own vocabulary rather than a new one. ## The amplification - **Amplify what the system already owns.** Reuse its motif and its type scale at full strength, turned up for this section rather than invented for it. The bolder version should look more like the same brand, not less. -- **Let the content carry the weight.** Flat copy makes a flat design, and no amount of size fixes a generic claim. Make every word specific enough to earn its place, and let the section's real evidence do the work that a decorative addition would otherwise fake. +- **Keep content true.** Existing claims are part of the scope: preserve them unless the user supplies replacements. If real evidence is essential to the direction but absent, ask for it. - **Commit, then clarify.** Half-measures read as noise. Make the one decisive move completely, then quiet everything around it so the move is legible. If every element got louder, the section got flatter. - **Give it its own rhythm.** The target should read as a peak in the scroll, a shift in density or pace from what surrounds it, not simply more of the same. diff --git a/.gemini/skills/impeccable/reference/clarify.md b/.gemini/skills/impeccable/reference/clarify.md index 00ee978c3..3047a9d82 100644 --- a/.gemini/skills/impeccable/reference/clarify.md +++ b/.gemini/skills/impeccable/reference/clarify.md @@ -1,288 +1,94 @@ -> **Additional context needed**: audience technical level and users' mental state in context. +> **Additional context needed**: audience knowledge and emotional state. -Find the unclear, confusing, or poorly written interface text and rewrite it. Vague copy creates support tickets and abandonment; specific copy gets users through the task. +Rewrite unclear interface text so users understand what happened, what matters, and what to do next. Preserve factual meaning, product terminology, and brand voice. +## Audit the language ---- +Read the entire interaction path, not isolated strings. Identify: -## Assess Current Copy +- ambiguous nouns, verbs, and actions; +- internal jargon or assumed knowledge; +- vague labels, outcomes, and system states; +- missing consequences, recovery, or timing; +- inconsistent terminology and capitalization; +- redundant headings, intros, helper text, and confirmations; +- text that breaks at realistic widths or in translation; +- tone that ignores stress, risk, success, or urgency. -Identify what makes the text unclear or ineffective: +Infer audience and task from product context and surrounding UI. Ask before changing factual claims, legal meaning, or a term that may be domain-specific. -1. **Find clarity problems**: - - **Jargon**: Technical terms users won't understand - - **Ambiguity**: Multiple interpretations possible - - **Passive voice**: "Your file has been uploaded" vs "We uploaded your file" - - **Length**: Too wordy or too terse - - **Assumptions**: Assuming user knowledge they don't have - - **Missing context**: Users don't know what to do or why - - **Tone mismatch**: Too formal, too casual, or inappropriate for situation +## Set the message hierarchy -2. **Understand the context**: - - Who's the audience? (Technical? General? First-time users?) - - What's the user's mental state? (Stressed during error? Confident during success?) - - What's the action? (What do we want users to do?) - - What's the constraint? (Character limits? Space limitations?) +For each state, decide: -**CRITICAL**: Clear copy helps users succeed. Unclear copy creates frustration, errors, and support tickets. +1. the one fact the user needs now; +2. the action available next; +3. supporting context that changes the decision; +4. the appropriate tone for this moment. -## Plan Copy Improvements +Say each idea once. If the heading already explains the state, the introduction should add new information or disappear. -Create a strategy for clearer communication: +## Rewrite by function -- **Primary message**: What's the ONE thing users need to know? -- **Action needed**: What should users do next (if anything)? -- **Tone**: How should this feel? (Helpful? Apologetic? Encouraging?) -- **Constraints**: Length limits, brand voice, localization considerations +### Actions and navigation -**IMPORTANT**: Good UX writing is invisible. Users should understand immediately without noticing the words. +Use a specific verb and object when the outcome is not already obvious. Labels should describe what will happen, not the gesture used to trigger it. Keep the same noun and verb for the same concept throughout the product. -## Improve Copy Systematically +For destructive actions, name the object and consequence. Prefer undo over confirmation when recovery is safe. When confirmation is necessary, name the action on both the message and button instead of using `Yes`, `No`, `OK`, or `Submit`. -Refine text across these common areas: +### Forms -### Error Messages -**Bad**: "Error 403: Forbidden" -**Good**: "You don't have permission to view this page. Contact your admin for access." +Use persistent labels; placeholders are examples, not labels. Put format and eligibility requirements before submission. Explain why information is requested only when it is not obvious. Required and optional treatment should be consistent. -**Bad**: "Invalid input" -**Good**: "Email addresses need an @ symbol. Try: name@example.com" +Validation says what needs attention and how to correct it without blaming the user. Keep related instructions near the field and announce errors accessibly. -**Principles**: -- Explain what went wrong in plain language -- Suggest how to fix it -- Don't blame the user -- Include examples when helpful -- Link to help/support if applicable - -### Form Labels & Instructions -**Bad**: "DOB (MM/DD/YYYY)" -**Good**: "Date of birth" (with placeholder showing format) - -**Bad**: "Enter value here" -**Good**: "Your email address" or "Company name" - -**Principles**: -- Use clear, specific labels (not generic placeholders) -- Show format expectations with examples -- Explain why you're asking (when not obvious) -- Put instructions before the field, not after -- Keep required field indicators clear - -### Button & CTA Text -**Bad**: "Click here" | "Submit" | "OK" -**Good**: "Create account" | "Save changes" | "Got it, thanks" +### Errors and permissions -**Principles**: -- Describe the action specifically -- Use active voice (verb + noun) -- Match user's mental model -- Be specific ("Save" is better than "OK") +An actionable error answers: -### Help Text & Tooltips -**Bad**: "This is the username field" -**Good**: "Choose a username. You can change this later in Settings." +1. what failed; +2. why, when known and useful; +3. how to recover or what alternative remains. -**Principles**: -- Add value (don't just repeat the label) -- Answer the implicit question ("What is this?" or "Why do you need this?") -- Keep it brief but complete -- Link to detailed docs if needed +Do not expose internal codes as the primary message. Do not promise a cause or resolution the system cannot know. Treat privacy, payment, deletion, access loss, and blocked work seriously; warmth is welcome, jokes are not. -### Empty States -**Bad**: "No items" -**Good**: "No projects yet. Create your first project to get started." +### Loading, empty, and success states -**Principles**: -- Explain why it's empty (if not obvious) -- Show next action clearly -- Make it welcoming, not dead-end +Loading text names the real operation and sets an honest expectation when the wait is meaningful. Show determinate progress when available; never invent progress. -### Success Messages -**Bad**: "Success" -**Good**: "Settings saved! Your changes will take effect immediately." +An empty state distinguishes first use, no results, filters, permissions, and failure. Explain the state and provide the next useful action. -**Principles**: -- Confirm what happened -- Explain what happens next (if relevant) -- Be brief but complete -- Match the user's emotional moment (celebrate big wins) - -### Loading States -**Bad**: "Loading..." (for 30+ seconds) -**Good**: "Analyzing your data... this usually takes 30-60 seconds" - -**Principles**: -- Set expectations (how long?) -- Explain what's happening (when it's not obvious) -- Show progress when possible -- Offer escape hatch if appropriate ("Cancel") +Success confirms the completed outcome and mentions the next consequence only when it changes what the user should do. Routine success should be brief. -### Confirmation Dialogs -**Bad**: "Are you sure?" -**Good**: "Delete 'Project Alpha'? This can't be undone." +### Help and instructional text -**Principles**: -- State the specific action -- Explain consequences (especially for destructive actions) -- Use clear button labels ("Delete project" not "Yes") -- Don't overuse confirmations (only for risky actions) +Helper text answers an implicit question instead of restating the control. Use progressive disclosure for uncommon detail. Link text must make sense out of context; icon-only controls need accessible names. -### Navigation & Wayfinding -**Bad**: Generic labels like "Items" | "Things" | "Stuff" -**Good**: Specific labels like "Your projects" | "Team members" | "Settings" +## Voice, accessibility, and localization -**Principles**: -- Be specific and descriptive -- Use language users understand (not internal jargon) -- Make hierarchy clear -- Consider information scent (breadcrumbs, current location) +Voice stays consistent; tone adapts to the moment. Use plain language without flattening terminology the audience genuinely knows. -## Apply Clarity Principles +- Write complete translatable messages rather than concatenated fragments. +- Keep variables and numbers structured so translators can reorder them. +- Allow expansion instead of abbreviating prematurely. +- Make alt text convey the image's information; use empty alt for decoration. +- Keep screen-reader names aligned with visible labels and outcomes. +- Do not rely on punctuation, color, or iconography to carry the message alone. -Every piece of copy should follow these rules: +Maintain a short terminology glossary when inconsistency spans the product. Do not vary words for literary effect in an interface. -1. **Be specific**: "Enter email" not "Enter value" -2. **Be concise**: Cut unnecessary words (but don't sacrifice clarity) -3. **Be active**: "Save changes" not "Changes will be saved" -4. **Be human**: "Oops, something went wrong" not "System error encountered" -5. **Tell users what to do**, not just what happened -6. **Be consistent**: Use same terms throughout (don't vary for variety) +## Verify -**NEVER**: -- Use jargon without explanation -- Blame users ("You made an error" → "This field is required") -- Be vague ("Something went wrong" without explanation) -- Use passive voice unnecessarily -- Write overly long explanations (be concise) -- Use humor for errors (be empathetic instead) -- Assume technical knowledge -- Vary terminology (pick one term and stick with it) -- Repeat information (headers restating intros, redundant explanations) -- Use placeholders as the only labels (they disappear when users type) +Read the flow in context and test: -## Verify Improvements +- comprehension without hidden product knowledge; +- actionability at errors, empty states, and decision points; +- factual accuracy and consistent terminology; +- scanability at target widths and 200% zoom; +- long names, localization expansion, pluralization, and dynamic values; +- accessible names and announced state changes; +- tone appropriate to consequence and emotional context. -Test that copy improvements work: +The final copy is as short as it can be without removing meaning or recovery. -- **Comprehension**: Can users understand without context? -- **Actionability**: Do users know what to do next? -- **Brevity**: Is it as short as possible while remaining clear? -- **Consistency**: Does it match terminology elsewhere? -- **Tone**: Is it appropriate for the situation? - -When the copy reads cleanly, hand off to `/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. +When the language reads cleanly, hand off to `/impeccable polish` for the final pass. diff --git a/.gemini/skills/impeccable/reference/codex.md b/.gemini/skills/impeccable/reference/codex.md index 18a924a91..6c550f1dc 100644 --- a/.gemini/skills/impeccable/reference/codex.md +++ b/.gemini/skills/impeccable/reference/codex.md @@ -1,105 +1,38 @@ -# Codex: Visual Direction & Asset Production +# Codex: Surface Probes & Asset Production -This file is loaded by `/impeccable craft` when the harness has native image generation (currently Codex via `image_gen`). Other harnesses skip it. It covers the two craft steps that depend on real image generation: landing the visual direction, and producing the raster assets the implementation will compose. +Load this from [new-work.md](new-work.md) only when the harness has native image generation and a substantial, high-fidelity surface would benefit from seeing the shortlisted concept before code. PRODUCT.md and DESIGN.md are preconditions. New-work has already resolved the visual world; this file must not reopen it. -Read this *before* generating any images. The order matters, and the per-step user pauses are what keep generated imagery from drifting away from the brief. +The purpose of a probe is to test composition, narrative, hierarchy, density, focal moment, signature use, and image requirements. It is not a second identity workshop. Keep DESIGN.md's palette, typography direction, material language, component character, imagery stance, and motion grammar fixed. -### Four stop points before code +## Generate the smallest useful probe set -Steps A through D each end with the user. Do not advance past any of them on your own read of the situation. +Generate one to three high-fidelity north-star comps using the native image-generation capability. Base them on the real content and the surface concepts already developed with the user. -1. **STOP after Step A questions.** Wait for answers. -2. **STOP after Step B palette generation.** Wait for "confirm palette." -3. **STOP after Step C mocks.** Wait for direction approval or delegation. -4. **Only after Step D approves a direction** do you return to the build (new-work.md's plan, then code). +- When the user shortlisted multiple concepts, show one clear expression of each. +- When one concept is already selected, vary only the structural uncertainty that the image can resolve: topology, sequence, density, hierarchy, focal composition, or interaction framing. +- Show enough beyond the opening moment to prove the concept can govern the whole requested surface. +- Do not generate a palette artifact, ask new atmosphere questions, introduce a different type voice, or invent a new motif. If the committed world cannot support the concept, return to the concept shortlist rather than changing the world. -Prior shape approval does **not** satisfy any of these. Shape's "confirm or override" advances you into Step A; it is not a substitute for it. +Treat each comp as a direction test, not a screenshot specification. Core UI text, responsive behavior, accessibility, semantics, and interaction states remain implementation responsibilities. -## Step A: Explore Directions with the User +## One approval point -Before generating anything, run a brief direction conversation grounded in the shape brief. +Show the probes together and ask what should carry forward, what feels false to the world, and whether the selected surface concept should be approved, combined, revised, or rejected. Then stop and wait. A structured simulated user counts as attended and receives the same question. -**Step A is required even when shape just produced a confirmed brief.** The shape questions and Step A questions cover different ground: shape pins purpose, content, scope; Step A pins palette, atmosphere, and named visual references for the comps you're about to generate. The only time you can skip Step A is when the user has already answered these exact palette/atmosphere/reference questions in the same session. +Do not begin code until the user approves a direction or explicitly delegates the choice. If they delegate, choose using the task brief, PRODUCT.md, and DESIGN.md, and state the evidence. Approval refines the task concept; it does not modify DESIGN.md. -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: +After approval, summarize the composition and the parts of the comp that must not be literalized. Return to new-work.md and write the direction contract from the approved surface concept before code. -- "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.)" +## Inventory implementation fidelity -**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. +Before building, inventory the approved comp's major visible ingredients and choose an implementation medium for each: semantic HTML/CSS/SVG, existing project asset, generated raster, sourced raster, icon library, canvas/WebGL, or accepted omission. -## Step B: Generate the Brand Palette First +Pay special attention to the dominant composition, signature use, image-native content, second-fold system, and any interaction the still image only implies. If the concept depends on a photograph, architectural scene, product object, portrait, or other raster-native material, do not silently replace it with generic CSS scenery. -Generate **one** palette artifact before any mocks. This is a small, focused image: typography pairing on the chosen background, primary + accent color swatches, one signature ornament or motif. Single image, single pass. +Treat the comp as a north star, not something to trace. Do not rasterize core UI text or controls. Do not substitute a different visual driver after approval without asking. -Why palette first: mocks generated against a vague color sense produce noise that drowns out the structural decisions. A confirmed palette is the first concrete contract for everything downstream. +## Produce only the assets the build needs -Show the palette to the user. Ask one question: "This is the palette I'm locking in for the mocks. Confirm, or call out what to shift?" +When clean raster ingredients are required and a scoped subagent is available and authorized, use `impeccable_asset_producer`. Give it the approved comp, output paths, required dimensions and formats, transparency needs, crop notes, and what must remain semantic code. Otherwise produce the minimum required assets with the native image-generation capability in the current thread. -**STOP and wait for confirmation.** Do not generate mocks against an unconfirmed palette. "Probably good enough" is the wrong call here; the palette is the contract for everything downstream. - -## Step C: Generate 1-3 Visual Mocks Against the Palette - -Once the palette is confirmed, generate **1 to 3** high-fidelity north-star comps. Each mock must use the confirmed palette and typography. Mocks differ in *structural* direction (hierarchy, topology, density, composition), not in color or motif. - -- Brand work: push visual identity, composition, mood, and signature motifs. -- Product work: push hierarchy, topology, density, tone, grounded in realistic product structure. -- Landing pages and long-form brand surfaces: show enough of the second fold to establish the system beyond the hero. - -Use the `image_gen` tool directly (or via the imagegen skill when available). Don't ask the user to install anything. - -## Step D: Approval Loop - -Show the comps. Ask what carries forward. Iterate until **one direction is approved** or the user explicitly delegates. - -**STOP and wait for the approval or the delegation.** Do not begin Step E or return to the build until a single direction is named. If the user delegates, pick the strongest direction and explain it from the brief, not personal taste. - -Before moving to assets, summarize what to carry into code and what *not* to literalize from the mock. This is the handoff between visual exploration and semantic implementation. - -## Step E: Mock Fidelity Inventory - -Inventory the approved mock's major visible ingredients. For each, decide implementation: semantic HTML/CSS/SVG, generated raster, sourced raster, icon library, canvas/WebGL, or accepted omission. - -Common ingredients to inventory: - -- Hero silhouette and dominant composition -- Signature motifs (planets, devices, portraits, charts, route lines, insets, badges, etc.) -- Nav and primary-action treatment (when the surface has one) -- Section sequence, especially the second fold -- Image-native content the concept depends on -- Typography, density, color/material treatment, motion cues - -Treat the mock as a north star, not a screenshot to trace. Don't rasterize core UI text. But if the live result lacks the mock's major ingredients, the implementation is wrong. - -If a photographic, architectural, product, or place-led mock becomes generic CSS scenery, decorative diagrams, bullets, or copy, stop and fix it. That's a broken implementation, not a harmless interpretation. - -Don't substitute a different hero composition or visual driver post-approval without user sign-off. - -## Step F: Asset Slicing via the Asset Producer - -Raster ingredients identified in Step E need clean production assets. Use the bundled `impeccable_asset_producer` subagent rather than producing inline. - -Spawn it as a scoped subagent. If you do not have explicit permission to use agents, stop and ask: - -```text -Asset production will work better as a scoped subagent job. Should I spawn the Impeccable asset producer subagent for this step? -``` - -Pass to the agent: - -- Approved mock path or screenshot reference -- Crop paths or a contact sheet with crop ids -- Output directory -- Required dimensions, format, transparency needs -- Avoid list -- Notes on what should remain semantic HTML/CSS/SVG instead of raster - -Attach image generation capability to the spawned agent when the harness supports it. Do **not** load image-generation reference material into the parent thread. - -Inline asset production is allowed only if the user declines subagents, the harness cannot spawn the authorized agent, or the user explicitly asks for single-thread mode. - -Prefer HTML/CSS/SVG/canvas when they can credibly reproduce an ingredient; reach for real, generated, or stock imagery when the mock or subject matter calls for actual visual content. - -## After This File - -Once Steps A through F are complete, return to the build: implement per new-work.md and SKILL.md's craft floor. The implementation builds against the confirmed palette, approved mock, and the assets the producer wrote. +Return to [new-work.md](new-work.md) for the direction contract, implementation, and finishing pass. diff --git a/.gemini/skills/impeccable/reference/colorize.md b/.gemini/skills/impeccable/reference/colorize.md index abee1acf8..dc45f88c5 100644 --- a/.gemini/skills/impeccable/reference/colorize.md +++ b/.gemini/skills/impeccable/reference/colorize.md @@ -1,257 +1,86 @@ > **Additional context needed**: existing brand colors. -Replace timid grayscale or single-accent designs with a strategic palette: pick a color strategy, choose a hue family that fits the brand, then apply color with intent. More color ≠ better. Strategic color beats rainbow vomit. +Introduce color as hierarchy, meaning, and atmosphere. Preserve confirmed brand and semantic conventions; do not replace a visual world under the guise of colorizing it. --- -## Register +## Visitor mode -Persuade + Experience: palette IS voice. Pick a color strategy first per SKILL.md (Restrained / Committed / Full palette / Drenched) and follow its dosage. Committed, Full palette, and Drenched deliberately exceed the ≤10% rule; that rule is Restrained only. Unexpected combinations are allowed; a dominant color can own the page when the chosen strategy calls for it. +- **Persuade + Experience:** color may carry the voice and own large regions when the selected world calls for it. +- **Operate + Read:** color primarily encodes action, selection, status, wayfinding, and reading hierarchy. Rarity gives an accent force. -Operate + Read: semantic-first and almost always Restrained. Accent color is reserved for primary action, current selection, and state indicators. Not decoration. Every color has a consistent meaning across every screen. +## Audit before choosing ---- +Read DESIGN.md, tokens, assets, current themes, and representative states. Identify: -## Assess Color Opportunity +- which colors are confirmed brand commitments; +- current surface, text, action, and semantic roles; +- places where grayscale obscures hierarchy or state; +- contrast failures and color-only communication; +- light/dark or data-visualization requirements; +- whether the task asks for more color or a new identity. -Analyze the current state and identify opportunities: +If a new identity is required, use [new-work.md](new-work.md). Ask only when a binding brand decision cannot be inferred. -1. **Understand current state**: - - **Color absence**: Pure grayscale? Limited neutrals? One timid accent? - - **Missed opportunities**: Where could color add meaning, hierarchy, or delight? - - **Context**: What's appropriate for this domain and audience? - - **Brand**: Are there existing brand colors we should use? +## Choose a strategy -2. **Identify where color adds value**: - - **Semantic meaning**: Success (green), error (red), warning (yellow/orange), info (blue) - - **Hierarchy**: Drawing attention to important elements - - **Categorization**: Different sections, types, or states - - **Emotional tone**: Warmth, energy, trust, creativity - - **Wayfinding**: Helping users navigate and understand structure - - **Delight**: Moments of visual interest and personality +Name the intended emotional temperature, dominant relationship, contrast range, and color dosage before editing. The strategy may be restrained or immersive; it must follow the brief and selected world rather than a fixed percentage rule. -If any of these are unclear from the codebase, ask the user directly to clarify what you cannot infer. +Build roles, not a bag of swatches: -**CRITICAL**: More color ≠ better. Strategic color beats rainbow vomit every time. Every color should have a purpose. +- canvas and elevated surfaces; +- primary and secondary text; +- action, focus, and selection; +- borders and separators; +- success, warning, error, and information; +- data categories or scales when needed. -## Plan Color Strategy +Use the project's existing color space. For a new web palette, prefer OKLCH because lightness and chroma can be adjusted predictably. Choose hue from product meaning and visual direction, never from a default category association. -Create a purposeful color introduction plan: +## Apply at system scale -- **Color palette**: What colors match the brand/context? (Choose 2-4 colors max beyond neutrals) -- **Dominant color**: Which color owns 60% of colored elements? -- **Accent colors**: Which colors provide contrast and highlights? (30% and 10%) -- **Application strategy**: Where does each color appear and why? +- Let the strongest color own a deliberate region or role instead of scattering tiny accents. +- Keep the primary action easy to find; do not spend its color on decoration. +- Tint neutrals only when the brand hue genuinely creates cohesion. Neutral gray is valid when it serves the world. +- On colored surfaces, derive secondary text from the foreground or surface hue rather than using washed-out generic gray. +- Keep semantic meanings consistent, but respect platform and domain conventions instead of assuming fixed hues. +- For data, use distinct lightness, chroma, shape, label, or pattern so color is not the only code. +- 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. -**IMPORTANT**: Color should enhance hierarchy and meaning, not create chaos. Less is more when it matters more. +Decoration without a relationship to hierarchy, state, content, or the visual world is not a color strategy. -## Introduce Color Strategically +## Contrast and perception -Add color systematically across these dimensions: +Verify computed foreground/background pairs: -### Semantic Color -- **State indicators**: - - Success: Green tones (emerald, forest, mint) - - Error: Red/pink tones (rose, crimson, coral) - - Warning: Orange/amber tones - - Info: Blue tones (sky, ocean, indigo) - - Neutral: Gray/slate for inactive states +| Content | WCAG AA minimum | +|---|---| +| body text | 4.5:1 | +| large text | 3:1 | +| controls, icons, focus indicators | 3:1 | -- **Status badges**: Colored backgrounds or borders for states (active, pending, completed, etc.) -- **Progress indicators**: Colored bars, rings, or charts showing completion or health +Do not rely on eyesight alone. Check interactive states, overlays, text on images, disabled content, and both themes. Simulate common vision deficiencies. Information conveyed by color also needs text, shape, iconography, or position. -### Accent Color Application -- **Primary actions**: Color the most important buttons/CTAs -- **Links**: Add color to clickable text (maintain accessibility) -- **Icons**: Colorize key icons for recognition and personality -- **Headers/titles**: Add color to section headers or key labels -- **Hover states**: Introduce color on interaction +When deriving OKLCH ramps, vary lightness and reduce chroma near white and black. Do not keep high chroma at extreme lightness merely to make the math uniform. Prefer explicit colors over chains of translucent overlays when alpha would make contrast context-dependent. -### Background & Surfaces -- **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 toward the brand, not "for warmth" by reflex +## Verify -**Use OKLCH for color**: It's perceptually uniform, meaning equal steps in lightness *look* equal. Great for generating harmonious scales. - -### Data Visualization -- **Charts & graphs**: Use color to encode categories or values -- **Heatmaps**: Color intensity shows density or importance -- **Comparison**: Color coding for different datasets or timeframes - -### Borders & Accents -- **Hairline borders**: 1px colored borders on full perimeter (not side-stripes; see the absolute ban on `border-left/right > 1px`) -- **Underlines**: Color underlines for emphasis or active states -- **Dividers**: Subtle colored dividers instead of gray lines -- **Focus rings**: Colored focus indicators matching brand -- **Surface tints**: A 4-8% background wash of the accent color instead of a stripe - -**NEVER**: `border-left` or `border-right` greater than 1px as a colored accent stripe. This is one of the three absolute bans in the parent skill. If you want to mark a card as "active" or "warning", use a full hairline border, a background tint, a leading glyph, or a numbered prefix. Not a side stripe. - -### Typography Color -- **Colored headings**: Use brand colors for section headings (maintain contrast) -- **Highlight text**: Color for emphasis or categories -- **Labels & tags**: Small colored labels for metadata or categories - -### Decorative Elements -- **Illustrations**: Add colored illustrations or icons -- **Shapes**: Geometric shapes in brand colors as background elements -- **Gradients**: Colorful gradient overlays or mesh backgrounds -- **Blobs/organic shapes**: Soft colored shapes for visual interest - -## Balance & Refinement - -Ensure color addition improves rather than overwhelms: - -### Maintain Hierarchy -- **Dominant color** (60%): Primary brand color or most used accent -- **Secondary color** (30%): Supporting color for variety -- **Accent color** (10%): High contrast for key moments -- **Neutrals** (remaining): Gray/black/white for structure - -### Accessibility -- **Contrast ratios**: Ensure WCAG compliance (4.5:1 for text, 3:1 for UI components) -- **Don't rely on color alone**: Use icons, labels, or patterns alongside color -- **Test for color blindness**: Verify red/green combinations work for all users - -### Cohesion -- **Consistent palette**: Use colors from defined palette, not arbitrary choices -- **Systematic application**: Same color meanings throughout (green always = success) -- **Temperature consistency**: Warm palette stays warm, cool stays cool - -**NEVER**: -- 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 -- Violate WCAG contrast requirements -- Use color as the only indicator (accessibility issue) -- Make everything colorful (defeats the purpose) -- Default to purple-blue gradients (AI slop aesthetic) - -## Verify Color Addition - -Test that colorization improves the experience: - -- **Better hierarchy**: Does color guide attention appropriately? -- **Clearer meaning**: Does color help users understand states/categories? -- **More engaging**: Does the interface feel warmer and more inviting? -- **Still accessible**: Do all color combinations meet WCAG standards? -- **Not overwhelming**: Is color balanced and purposeful? +- Every color has a stable role or a world-specific atmospheric purpose. +- Attention lands on the intended action, content, or state. +- The palette works across quiet, dense, interactive, error, and empty states. +- Light and dark themes are each composed, not mechanically inverted. +- Contrast and non-color cues pass in all relevant states. +- The result is recognizably this product, not a generic “colorful” treatment. When the palette earns its place, hand off to `/impeccable polish` for the final pass. ## Live-mode signature params -When invoked from live mode, each variant MUST declare a `color-amount` param so the user can dial between a restrained accent and a drenched surface without regeneration. Author the variant's CSS against `var(--p-color-amount, 0.5)`, typically as the alpha multiplier on backgrounds, or as a scaling factor on the chroma axis in an OKLCH expression. 0 = neutral/monochrome, 1 = full saturation / dominant coverage. +When invoked from live mode, every variant declares a `color-amount` parameter. Author CSS against `var(--p-color-amount, 0.5)` so the user can move from neutral to the variant's full color strategy without regeneration. ```json {"id":"color-amount","kind":"range","min":0,"max":1,"step":0.05,"default":0.5,"label":"Color amount"} ``` -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). +Add at most two variant-specific parameters, such as palette, temperature, or tint behavior. Follow [live.md](live.md)'s parameter contract. diff --git a/.gemini/skills/impeccable/reference/craft.md b/.gemini/skills/impeccable/reference/craft.md index cc7f98d37..dbbc9402c 100644 --- a/.gemini/skills/impeccable/reference/craft.md +++ b/.gemini/skills/impeccable/reference/craft.md @@ -1,3 +1,5 @@ # Craft (deprecated alias) -`craft` is a deprecated alias for the standard build flow. Builds route through SKILL.md's new-work gate; [new-work.md](new-work.md) carries the direction checkpoint ("Decide, then build"), the identity playbook, and the finishing pass. Invoking `craft` explicitly forces the attended checkpoints: pause at the stated direction for confirmation, and follow [codex.md](codex.md)'s mock flow when the harness generates images. Nothing else differs from a plain build request. +`craft` is a deprecated alias for an ordinary request to make new visual work. It adds no setup, interview, checkpoint, tool, or quality behavior. Apply SKILL.md's normal routing: create missing PRODUCT.md through [init.md](init.md), then follow [new-work.md](new-work.md) for visual authority, world and surface decisions, implementation, and finish. + +Do not tell users they need to invoke `craft`. Natural requests such as “build this feature,” “make a landing page,” or “redesign this screen” use the same flow. diff --git a/.gemini/skills/impeccable/reference/critique.md b/.gemini/skills/impeccable/reference/critique.md index bf292586f..a0fccf698 100644 --- a/.gemini/skills/impeccable/reference/critique.md +++ b/.gemini/skills/impeccable/reference/critique.md @@ -43,13 +43,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 @@ -115,11 +115,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. @@ -176,7 +176,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. 2. **Pass the structured metadata** through `IMPECCABLE_CRITIQUE_META` (JSON), then run the write command: ```bash diff --git a/.gemini/skills/impeccable/reference/delight.md b/.gemini/skills/impeccable/reference/delight.md index 596cff09e..40aaddc34 100644 --- a/.gemini/skills/impeccable/reference/delight.md +++ b/.gemini/skills/impeccable/reference/delight.md @@ -1,302 +1,70 @@ -> **Additional context needed**: what's appropriate for the domain (playful vs professional vs quirky vs elegant). +> **Additional context needed**: the brand's emotional range. -Find the moments where personality and unexpected polish would turn a functional interface into one users remember and tell other people about. Add only where the moment earns it; delight everywhere reads as noise. +Make the experience memorable at moments that earn it. Delight is not a layer of generic whimsy; it is product character revealed through a useful interaction, a humane response, or an unexpectedly considered detail. --- -## Register +## Visitor mode -Persuade + Experience: delight can be distributed across copy voice, section transitions, discovery rewards, seasonal touches, personality across the whole surface. +- **Persuade + Experience:** personality may run through voice, composition, motion, and discovery, provided the artifact remains the focus. +- **Operate + Read:** concentrate delight at meaningful moments such as first use, completion, recovery, or mastery. Reliability carries everything else. -Operate + Read: delight at specific moments, not pages. Completion, first-time actions, error recovery, milestone crossings. Reliability and consistency carry the rest of the experience; delight pushed everywhere reads as noise. +## Find the opportunity ---- +Inspect the target, DESIGN.md, product voice, repeated-use frequency, and emotional context. Look for: -## Assess Delight Opportunities +- effort worth acknowledging; +- waiting that can become informative; +- an empty or first-use state that can orient; +- an error or recovery moment that needs empathy; +- an interaction whose physical or verbal response could express the brand; +- a useful capability people might enjoy discovering. -Identify where delight would enhance (not distract from) the experience: +Do not manufacture a celebration for an ordinary click. Ask only when the brand's emotional range or the stakes cannot be inferred. -1. **Find natural delight moments**: - - **Success states**: Completed actions (save, send, publish) - - **Empty states**: First-time experiences, onboarding - - **Loading states**: Waiting periods that could be entertaining - - **Achievements**: Milestones, streaks, completions - - **Interactions**: Hover states, clicks, drags - - **Errors**: Softening frustrating moments - - **Easter eggs**: Hidden discoveries for curious users +## Define one delight thesis -2. **Understand the context**: - - What's the brand personality? (Playful? Professional? Quirky? Elegant?) - - Who's the audience? (Tech-savvy? Creative? Corporate?) - - What's the emotional context? (Accomplishment? Exploration? Frustration?) - - What's appropriate? (Banking app ≠ gaming app) +State in one sentence what the user should feel and why that feeling belongs to this product. Then choose the smallest system that can deliver it: -3. **Define delight strategy**: - - **Subtle sophistication**: Refined micro-interactions (luxury brands) - - **Playful personality**: Whimsical illustrations and copy (consumer apps) - - **Helpful surprises**: Anticipating needs before users ask (productivity tools) - - **Sensory richness**: Satisfying sounds, smooth animations (creative tools) +- a distinctive response to a meaningful action; +- product-specific language that clarifies while carrying voice; +- an interaction or transition with a recognizable material behavior; +- an illustration, sound, haptic, or environmental detail grounded in the product world; +- a discovery reward that reveals real utility. -If any of these are unclear from the codebase, ask the user directly to clarify what you cannot infer. +Derive the treatment from product mechanism and visual world, not a stock catalog. -**CRITICAL**: Delight should enhance usability, never obscure it. If users notice the delight more than accomplishing their goal, you've gone too far. +## Build for the emotional moment -## Delight Principles +- **Success:** match the response to the effort and consequence. Major milestones can expand; routine saves should simply feel certain. +- **Waiting:** show truthful progress, useful context, or product-specific activity. Never fake work or delay completion to stage a flourish. +- **Empty and first use:** make the next action clear before adding personality. +- **Error and recovery:** lead with the problem and recovery. Warmth may reduce stress; jokes must not trivialize loss, money, privacy, or blocked work. +- **Repeated interaction:** keep the response satisfying after the hundredth use. Variation is useful only when it remains coherent and predictable enough to trust. +- **Discovery:** reward curiosity without hiding required functionality. -Follow these guidelines: +Copy must use the product's language. Generic whimsy is worse than neutral clarity. -### Delight Amplifies, Never Blocks -- Delight moments should be quick (< 1 second) -- Never delay core functionality for delight -- Make delight skippable or subtle -- Respect user's time and task focus +## Protect the experience -### Surprise and Discovery -- Hide delightful details for users to discover -- Reward exploration and curiosity -- Don't announce every delight moment -- Let users share discoveries with others +Delight must not: -### Appropriate to Context -- Match delight to emotional moment (celebrate success, empathize with errors) -- Respect the user's state (don't be playful during critical errors) -- Match brand personality and audience expectations -- Cultural sensitivity (what's delightful varies by culture) +- delay, block, or obscure the primary task; +- override platform conventions or accessibility; +- add unrequested factual claims; +- play sound without consent or ignore mute settings; +- become mandatory, unskippable, or exhausting on repeat; +- add a dependency or asset cost disproportionate to the moment. -### Compound Over Time -- Delight should remain fresh with repeated use -- Vary responses (not same animation every time) -- Reveal deeper layers with continued use -- Build anticipation through patterns +For authored motion, load [animate.md](animate.md). Respect reduced motion, screen readers, keyboard use, touch, localization, and cultural context. Nonessential loops stop when hidden. Make celebration intensity proportional to frequency and consequence. -## Delight Techniques +## Verify -Add personality and joy through these methods: +- The moment is specific enough that a neighboring product could not use it unchanged. +- It improves comprehension, confidence, motivation, or emotional recovery. +- The interface remains fast and obvious without the flourish. +- Repetition does not turn charm into friction. +- Reduced-motion, muted, keyboard, touch, and localized paths work. +- The result feels like the selected world, not a generic “delight” treatment. -### Micro-interactions & Animation - -**Button delight**: -```css -/* Satisfying button press */ -.button { - transition: transform 0.1s, box-shadow 0.1s; -} -.button:active { - transform: translateY(2px); - box-shadow: 0 2px 4px rgba(0,0,0,0.2); -} - -/* Ripple effect on click */ -/* Smooth lift on hover */ -.button:hover { - transform: translateY(-2px); - transition: transform 0.2s cubic-bezier(0.25, 1, 0.5, 1); /* ease-out-quart */ -} -``` - -**Loading delight**: -- Playful loading animations (not just spinners) -- Personality in loading messages (write product-specific ones, not generic AI filler) -- Progress indication with encouraging messages -- Skeleton screens with subtle animations - -**Success animations**: -- Checkmark draw animation -- Confetti burst for major achievements -- Gentle scale + fade for confirmation -- Satisfying sound effects (subtle) - -**Hover surprises**: -- Icons that animate on hover -- Color shifts or glow effects -- Tooltip reveals with personality -- Cursor changes (custom cursors for branded experiences) - -### Personality in Copy - -**Playful error messages**: -``` -"Error 404" -"This page is playing hide and seek. (And winning)" - -"Connection failed" -"Looks like the internet took a coffee break. Want to retry?" -``` - -**Encouraging empty states**: -``` -"No projects" -"Your canvas awaits. Create something amazing." - -"No saved articles" -"Nothing on the nightstand yet. Save a story for later." -``` - -**Playful labels & tooltips**: -``` -"Delete" -"Send to void" (for playful brand) - -"Help" -"Rescue me" (tooltip) -``` - -**IMPORTANT**: Match copy personality to brand. Banks shouldn't be wacky, but they can be warm. - -### Illustrations & Visual Personality - -**Custom illustrations**: -- Empty state illustrations (not stock icons) -- Error state illustrations (friendly monsters, quirky characters) -- Loading state illustrations (animated characters) -- Success state illustrations (celebrations) - -**Icon personality**: -- Custom icon set matching brand personality -- Animated icons (subtle motion on hover/click) -- Illustrative icons (more detailed than generic) -- Consistent style across all icons - -**Background effects**: -- Subtle particle effects -- Gradient mesh backgrounds -- Geometric patterns -- Parallax depth -- Time-of-day themes (morning vs night) - -### Satisfying Interactions - -**Drag and drop delight**: -- Lift effect on drag (shadow, scale) -- Snap animation when dropped -- Satisfying placement sound -- Undo toast ("Dropped in wrong place? [Undo]") - -**Toggle switches**: -- Smooth slide with spring physics -- Color transition -- Haptic feedback on mobile -- Optional sound effect - -**Progress & achievements** (Operate surfaces with recurring tasks only; gamification on editorial, portfolio, or one-visit surfaces reads as noise): -- Streak counters with celebratory milestones -- Progress bars that "celebrate" at 100% -- Badge unlocks with animation -- Playful stats ("You're on fire! 5 days in a row") - -**Form interactions**: -- Input fields that animate on focus -- Checkboxes with a satisfying scale pulse when checked -- Success state that celebrates valid input -- Auto-grow textareas - -### Sound Design - -**Subtle audio cues** (when appropriate): -- Notification sounds (distinctive but not annoying) -- Success sounds (satisfying "ding") -- Error sounds (empathetic, not harsh) -- Typing sounds for chat/messaging -- Ambient background audio (very subtle) - -**IMPORTANT**: -- Respect system sound settings -- Provide mute option -- Keep volumes quiet (subtle cues, not alarms) -- Don't play on every interaction (sound fatigue is real) - -### Easter Eggs & Hidden Delights - -**Discovery rewards**: -- Konami code unlocks special theme -- Hidden keyboard shortcuts (Cmd+K for special features) -- Hover reveals on logos or illustrations -- Alt text jokes on images (for screen reader users too!) -- A view-source or console colophon (a note on the typefaces, a thank-you, a hiring line) - -**Seasonal touches**: -- Holiday themes (subtle, tasteful) -- Seasonal color shifts -- Weather-based variations -- Time-based changes (dark at night, light during day) - -**Contextual personality**: -- Different messages based on time of day -- Responses to specific user actions -- Randomized variations (not same every time) -- Progressive reveals with continued use - -### Loading & Waiting States - -**Make waiting engaging**: -- Interesting loading messages that rotate -- Progress bars with personality -- Mini-games during long loads -- Fun facts or tips while waiting -- Countdown with encouraging messages - -``` -Loading messages: write ones specific to your product, not generic AI filler: -- "Syncing with your team's changes..." -- "Fetching this week's issue..." -- "Developing your photos..." -- "Checking tomorrow's tide tables..." -``` - -**WARNING**: Avoid cliched loading messages like "Herding pixels", "Teaching robots to dance", "Consulting the magic 8-ball", "Counting backwards from infinity". These are AI-slop copy, instantly recognizable as machine-generated. Write messages that are specific to what your product actually does. - -### Celebration Moments - -**Success celebrations**: -- Confetti for major milestones -- Animated checkmarks for completions -- Progress bar celebrations at 100% -- "Achievement unlocked" style notifications -- Personalized messages ("You published your 10th article!") - -**Milestone recognition** (same scoping as Progress & achievements: Operate surfaces with recurring tasks): -- First-time actions get special treatment -- Streak tracking and celebration -- Progress toward goals -- Anniversary celebrations - -## Implementation Patterns - -**Animation libraries**: -- Framer Motion (React) -- GSAP (universal) -- Lottie (After Effects animations) -- Canvas confetti (party effects) - -**Sound libraries**: -- Howler.js (audio management) -- Use-sound (React hook) - -**Physics libraries**: -- React Spring (spring physics) -- Popmotion (animation primitives) - -**IMPORTANT**: File size matters. Compress images, optimize animations, lazy load delight features. - -**NEVER**: -- Delay core functionality for delight -- Force users through delightful moments (make skippable) -- Use delight to hide poor UX -- Overdo it (less is more) -- Ignore accessibility (animate responsibly, provide alternatives) -- Make every interaction delightful (special moments should be special) -- Sacrifice performance for delight -- Be inappropriate for context (read the room) - -## Verify Delight Quality - -Test that delight actually delights: - -- **User reactions**: Do users smile? Share screenshots? -- **Doesn't annoy**: Still pleasant after 100th time? -- **Doesn't block**: Can users opt out or skip? -- **Performant**: No jank, no slowdown -- **Appropriate**: Matches brand and context -- **Accessible**: Works with reduced motion, screen readers - -When the moments feel earned, hand off to `/impeccable polish` for the final pass. +When the personality feels earned, hand off to `/impeccable polish` for the final pass. diff --git a/.gemini/skills/impeccable/reference/document.md b/.gemini/skills/impeccable/reference/document.md index 0d38eb568..121a00ab1 100644 --- a/.gemini/skills/impeccable/reference/document.md +++ b/.gemini/skills/impeccable/reference/document.md @@ -1,6 +1,6 @@ Generate a `DESIGN.md` file at the project root that captures the current visual design system, so AI agents generating new screens stay on-brand. -DESIGN.md follows the [official DESIGN.md format spec](https://raw.githubusercontent.com/google-labs-code/design.md/main/docs/spec.md): YAML frontmatter carrying machine-readable design tokens, followed by a markdown body with exactly six sections in a fixed order. **Tokens are normative; prose provides context for how to apply them.** Sections may be omitted when not relevant, but **do not reorder them and do not rename them**. Section headers must match the spec character-for-character so the file stays parseable by other DESIGN.md-aware tools (Stitch itself, awesome-design-md, skill-rest, etc.). +DESIGN.md follows the [official DESIGN.md format spec](https://raw.githubusercontent.com/google-labs-code/design.md/main/docs/spec.md): optional YAML frontmatter carrying machine-readable design tokens, followed by up to eight markdown sections in a fixed order. **Tokens are normative; prose provides context for how to apply them.** Sections may be omitted when not relevant, but those present stay in the specified order. Use the canonical headings below so the file remains portable across DESIGN.md-aware tools. ## The frontmatter: token schema @@ -43,26 +43,28 @@ components: 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. +- **Colors accept any valid CSS color string.** Hex is the recommended default for portability, but preserve an incumbent `rgb()`, `hsl()`, `oklch()`, wide-gamut, or mixed-color value when it is the project's normative source. 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 (`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) +## The markdown body: eight sections (canonical order) 1. `## Overview` 2. `## Colors` 3. `## Typography` -4. `## Elevation` -5. `## Components` -6. `## Do's and Don'ts` +4. `## Layout` +5. `## Elevation & Depth` +6. `## Shapes` +7. `## Components` +8. `## Do's and Don'ts` -Optional evocative subtitles are allowed in the form `## 2. Colors: The [Name] Palette` (Stitch's own outputs do this), but the literal word in each header (Overview, Colors, Typography, Elevation, Components, Do's and Don'ts) must be present. Do NOT add extra top-level sections (Layout Principles, Responsive Behavior, Motion, Agent Prompt Guide). Fold that content into the six spec sections where it naturally belongs. +Omit irrelevant sections rather than filling them with invented rules. Put responsive layout in Layout, depth in Elevation & Depth, radius and form language in Shapes, and per-component behavior in Components. Unknown sections are preserved by the format, but new visual guidance should use the canonical structure whenever it fits. ## When to run -- 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. +- New-work found a coherent incumbent visual system but no `DESIGN.md`. +- The first implementation of a new world is complete and its provisional decisions need to be carbonized. - An existing `DESIGN.md` is stale (the design has drifted). - Before a large redesign, to capture the current state as a reference. @@ -71,9 +73,9 @@ 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 init, nothing built yet). Interview for five high-level answers, write a minimal DESIGN.md marked ``. Re-run in scan mode once there's code. +- **Seed mode**: the project is pre-implementation. Ensure PRODUCT.md exists, then reuse new-work's visual-world workshop and write its directional DESIGN.md 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. +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` requests new-work's world workshop, but it does not authorize replacing coherent code: when an incumbent system exists, offer scan mode or route an explicit identity-replacement request through new-work. ## Scan mode (approach C: auto-extract, then confirm descriptive language) @@ -97,7 +99,8 @@ Build a structured draft from the discovered tokens. For each token class: - **Typography**: Map observed sizes and weights to the Material hierarchy (display / headline / title / body / label). Note font-family stacks and the scale ratio. - **Elevation**: Catalogue the shadow vocabulary. If the project is flat and uses tonal layering instead, that's a valid answer; state it explicitly. - **Components**: For each common component (button, card, input, chip, list item, tooltip, nav), extract shape (radius), color assignment, hover/focus treatment, internal padding. -- **Spacing + layout**: Fold into Overview or relevant Components. The spec does NOT have a Layout section. +- **Layout + spacing**: Extract grid, container, breakpoint, rhythm, and density behavior into Layout. +- **Shapes**: Extract radius, corner, border, clipping, and recurring form behavior into Shapes. ### Step 2b: Stage the frontmatter @@ -112,19 +115,19 @@ Skip anything the project doesn't have. Empty scale keys or fabricated tokens po ### Step 3: Ask the user for qualitative language -The following require creative input that cannot be auto-extracted. Group them into one `AskUserQuestion` interaction: +The following require creative input that cannot be auto-extracted. Ask them in two structured rounds of no more than three questions each (or the harness's lower limit), waiting between rounds: - **Creative North Star**: a single named metaphor for the whole system ("The Editorial Sanctuary", "The Golden State Curator", "The Lab Notebook"). Offer 2-3 options that honor PRODUCT.md's brand personality. -- **Overview voice**: mood adjectives, aesthetic philosophy in 2-3 sentences, anti-references (what the system should not feel like). +- **Overview voice**: mood adjectives, aesthetic philosophy in 2-3 sentences, and any confirmed visual anti-reference. - **Color character** (for auto-extracted colors): descriptive names ("Deep Muted Teal-Navy", not "blue-800"). Suggest 2-3 options per key color based on hue/saturation. - **Elevation philosophy**: flat/layered/lifted. If shadows exist, is their role ambient or structural? - **Component philosophy**: the feel of buttons, cards, inputs in one phrase ("tactile and confident" vs. "refined and restrained"). -Quote a line from PRODUCT.md when possible so the user sees their own strategic language carry forward. +Carry a line from PRODUCT.md only when it is a durable brand commitment that actually constrains the visual system. Page strategy and surface concepts do not belong here. ### Step 4: Write DESIGN.md -The file opens with the YAML frontmatter staged in Step 2b (schema documented at the top of this reference), then the markdown body using the structure below. Headers must match character-for-character. Optional evocative subtitles (e.g. `## 2. Colors: The Coastal Palette`) are allowed. +The file opens with the YAML frontmatter staged in Step 2b (schema documented at the top of this reference), then the markdown body using the canonical structure below. ```markdown --- @@ -136,13 +139,13 @@ colors: # Design System: [Project Title] -## 1. Overview +## Overview **Creative North Star: "[Named metaphor in quotes]"** -[2-3 paragraph holistic description: personality, density, aesthetic philosophy. Start from the North Star and work outward. State what this system explicitly rejects (pulled from PRODUCT.md's anti-references). End with a short **Key Characteristics:** bullet list.] +[2-3 paragraph holistic description: personality, density, and aesthetic philosophy. Start from the North Star and work outward. State only confirmed visual rejections. End with a short **Key Characteristics:** bullet list.] -## 2. Colors +## Colors [Describe the palette character in one sentence.] @@ -162,7 +165,7 @@ colors: ### Named Rules (optional, powerful) **The [Rule Name] Rule.** [Short, forceful prohibition or doctrine, e.g. "The One Voice Rule. The primary accent is used on ≤10% of any given screen. Its rarity is the point."] -## 3. Typography +## Typography **Display Font:** [Family] (with [fallback]) **Body Font:** [Family] (with [fallback]) @@ -180,7 +183,11 @@ colors: ### Named Rules (optional) **The [Rule Name] Rule.** [Short doctrine about type use.] -## 4. Elevation +## Layout + +[Describe the grid or spatial model, container behavior, density, responsive changes, and the spacing rhythm. Include exact values only when observed.] + +## Elevation & Depth [One paragraph: does this system use shadows, tonal layering, or a hybrid? If "no shadows", say so explicitly and describe how depth is conveyed instead.] @@ -191,7 +198,11 @@ colors: ### Named Rules (optional) **The [Rule Name] Rule.** [e.g. "The Flat-By-Default Rule. Surfaces are flat at rest. Shadows appear only as a response to state (hover, elevation, focus)."] -## 5. Components +## Shapes + +[Describe the form language: corner/radius strategy, borders, clipping, and any recurring silhouette or geometry.] + +## Components For each component, lead with a short character line, then specify shape, color assignment, states, and any distinctive behavior. @@ -223,16 +234,16 @@ For each component, lead with a short character line, then specify shape, color ### [Signature Component] (optional; if the project has a distinctive custom component worth documenting) [Description.] -## 6. Do's and Don'ts +## Do's and Don'ts -Concrete, forceful guardrails. Lead each with "Do" or "Don't". Be specific: include exact colors, pixel values, and named anti-patterns the user mentioned in PRODUCT.md. **Every anti-reference in PRODUCT.md should show up here as a "Don't" with the same language**, so the visual spec carries the strategic line through. Quote PRODUCT.md directly where possible: if PRODUCT.md says *"avoid dark mode with purple gradients, neon accents, glassmorphism"*, the Don'ts here should repeat that by name. +Concrete visual guardrails grounded in the incumbent implementation or the user's chosen world. Lead each with "Do" or "Don't" and include exact values only when established. Do not turn a task-specific concept or surface strategy into a system-wide prohibition. ### Do: - **Do** [specific prescription with exact values / named rule]. - **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** [...] ``` @@ -307,7 +318,7 @@ The `html` and `css` fields must be **self-contained, drop-in snippets** that re Aim for a tight set of **5-10 components** that best represent the visual system: - **Canonical primitives (always include if the project has them):** button (each variant as a separate component entry), input/text field, navigation, chip/tag, card. -- **Signature components (include if distinctive):** hero CTA, featured card, filter pill, a distinctive table or list-row treatment, any custom pattern the user mentioned as important in PRODUCT.md. +- **Signature components (include if distinctive):** the recurring custom patterns that actually define the implemented system. - **Skip the rest.** Utility components, form building blocks, wrapper layouts: not worth documenting unless visually distinctive. If the project has **no component library yet** (bare landing page, new project), synthesize canonical primitives from the tokens using best-practice defaults consistent with the DESIGN.md's rules. Every `.impeccable/design.json` has *something* to render, even on day zero. @@ -338,64 +349,40 @@ Your own write is the freshest source; subsequent commands in this session don't ## Seed mode -For projects with no visual system to extract yet. Produces a minimal scaffold, not a full spec. +For projects with no visual system to extract yet. Produces a user-chosen visual-world scaffold, not a fabricated token spec. -### Step 1: Confirm seed mode +### Step 1: Route through new-work's workshop -Before interviewing: "There's no existing visual system to scan. I'll ask five quick questions to seed a starter DESIGN.md. You can re-run `/impeccable document` once there's code, to capture the real tokens and components. OK?" +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 the user prefers to skip, stop. No file. +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. -### Step 2: Five questions +If new-work already completed the workshop in this session, use its chosen direction directly. Do not ask again. -Group into one `AskUserQuestion` interaction. Options must be concrete. +### Step 2: Write seed DESIGN.md -1. **Color strategy.** Pick one: - - Restrained: tinted neutrals + one accent ≤10% - - Committed: one saturated color carries 30–60% of the surface - - Full palette: 3–4 named color roles, each deliberate - - Drenched: the surface IS the color - - Then: one hue family or anchor reference ("deep teal", "mustard", "Klim #ff4500 orange"). - -2. **Typography direction.** Pick one (specific fonts come later): - - Serif display + sans body - - Single sans (warm / technical / geometric / humanist; pick a feel) - - Display + mono - - Mono-forward - - Editorial script + sans - -3. **Motion energy.** Pick one: - - Restrained: state changes only - - Responsive: feedback + transitions, no choreography - - Choreographed: orchestrated entrances, scroll-driven sequences - -4. **Three named references.** Brands, products, printed objects. Not adjectives. - -5. **One anti-reference.** What it should NOT feel like. Also named. - -### Step 3: Write seed DESIGN.md - -Use the six-section spec from Scan mode. Populate what the interview answers; leave the rest as honest placeholders. The seed is a scaffold, not a fabricated spec. +Use the canonical section order from Scan mode. Populate the selected workshop direction and leave unresolved implementation facts as honest placeholders. The seed commits a world and its invariants; it does not pretend implementation tokens already exist. Lead the file with: ```markdown - + ``` Per-section guidance in seed mode: -- **Overview**: Creative North Star and philosophy phrased from the answers (color strategy + motion energy + references). Reference the user's anti-reference directly. -- **Colors**: Color strategy as a Named Rule (e.g. *"The Drenched Rule. The surface IS the color."*). Hue family or anchor reference. No hex values; mark as `[to be resolved during implementation]`. -- **Typography**: the direction the user picked (e.g. "Serif display + sans body"). No font names yet: `[font pairing to be chosen at implementation]`. -- **Elevation**: inferred from motion energy. Restrained/Responsive → flat by default; Choreographed → layered. One sentence. +- **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. +- **Elevation & Depth**: the selected material and depth behavior, stated as an invariant rather than inferred from a generic preset. +- **Shapes**: the selected form and corner language. - **Components**: omit entirely; no components exist yet. -- **Do's and Don'ts**: carry PRODUCT.md's anti-references directly plus the anti-reference named in Q5. +- **Do's and Don'ts**: record the durable guardrails confirmed during the world choice, not task-local refusals. 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 +### Step 3: 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." @@ -405,15 +392,15 @@ Your own write is the freshest source; no reload needed. ## Style guidelines - **Frontmatter first, prose second.** Tokens go in the YAML frontmatter; prose contextualizes them. Don't redefine a token value in two places; the frontmatter is normative. -- **Cite PRODUCT.md anti-references by name** in the Do's and Don'ts section. If PRODUCT.md lists "SaaS landing-page clichés" or "generic AI tool marketing" as anti-references, the DESIGN.md Don'ts should repeat those phrases verbatim so the visual spec enforces the strategic line. -- **Match the spec, don't invent new sections.** The six section names are fixed. If you have Layout/Motion/Responsive content to document, fold it into Overview (philosophy-level rules) or Components (per-component behavior). +- **Carry only durable product constraints.** A binding logo, identity asset, accessibility need, or brand commitment from PRODUCT.md may constrain DESIGN.md. Surface strategy stays in its surface brief. +- **Match the spec.** Use its eight canonical sections in order and omit any that are irrelevant. Put motion guidance with the world or component it affects rather than creating a token group the schema does not support. - **Descriptive > technical**: "Gently curved edges (8px radius)" > "rounded-lg". Include the technical value in parens, lead with the description. - **Functional > decorative**: for each token, explain WHERE and WHY it's used, not just WHAT it is. - **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 forceful**. The voice of a design director. "Prohibited", "forbidden", "never", "always", not "consider", "might", "prefer". Match PRODUCT.md's tone. -- **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. -- **Reference PRODUCT.md**. The anti-references section of PRODUCT.md should directly inform the Do's and Don'ts section here. Quote or paraphrase. +- **Be decisive where evidence is decisive.** Use hard language for actual invariants and softer language for provisional guidance. +- **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. ## Pitfalls @@ -423,7 +410,7 @@ Your own write is the freshest source; no reload needed. - Don't invent components that don't exist. If the project only has buttons and cards, only document those. - Don't overwrite an existing DESIGN.md without asking. - Don't duplicate content from PRODUCT.md. DESIGN.md is strictly visual. -- Don't add a "Layout Principles" or "Motion" or "Responsive Behavior" top-level section. The spec has six, not nine. Fold that content where it belongs. +- Don't replace canonical sections with near-synonyms. Put layout and responsive behavior in `Layout`; put motion with the affected world or component. - Don't rename sections even slightly. "Colors" not "Color Palette & Roles". "Typography" not "Typography Rules". Tooling parsing depends on exact headers. - Don't duplicate token values between frontmatter and prose. If a color is in `colors.primary` as hex, the prose can name it and describe its role but should not reassert a different hex. The frontmatter is normative. - Don't invent frontmatter token groups outside Stitch's schema (no `motion:`, `breakpoints:`, `shadows:` at the top level). Stitch's Zod schema only accepts `colors`, `typography`, `rounded`, `spacing`, `components`. Anything else belongs in the sidecar's `extensions`. diff --git a/.gemini/skills/impeccable/reference/hooks.md b/.gemini/skills/impeccable/reference/hooks.md index cf1f1a44c..da9bd32e6 100644 --- a/.gemini/skills/impeccable/reference/hooks.md +++ b/.gemini/skills/impeccable/reference/hooks.md @@ -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. diff --git a/.gemini/skills/impeccable/reference/init.md b/.gemini/skills/impeccable/reference/init.md index ce4a3a6d5..d3e3a1fbd 100644 --- a/.gemini/skills/impeccable/reference/init.md +++ b/.gemini/skills/impeccable/reference/init.md @@ -1,224 +1,114 @@ -# Init Flow +# Init flow -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 [DESIGN.md format spec](https://raw.githubusercontent.com/google-labs-code/design.md/main/docs/spec.md). Answers "how it looks". -- **`.impeccable/live/config.json`** (live mode): pre-configured so `/impeccable live` boots straight into variant mode with no first-time detour. - -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. +`init` captures durable product truth in PRODUCT.md. It does not invent a visual world and does not write DESIGN.md; [new-work.md](new-work.md) creates or expands one, and [document.md](document.md) records an incumbent one. Existing runnable web projects may also receive `.impeccable/live/config.json`. ## Step 1: Load current state -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). +Use the PRODUCT.md path resolved by context.mjs. Update it instead of creating a competing authority. In a child app inheriting root context, confirm shared versus app-specific scope before writing. -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. -- **PRODUCT.md exists, DESIGN.md missing**: skip to Step 5 and offer to run `/impeccable document` for DESIGN.md. -- **PRODUCT.md exists but has no `## Register` section (legacy)**: add it. Infer a hypothesis from the codebase (see Step 2), confirm with the user, write the field. -- **PRODUCT.md exists but has no `## Platform` section (legacy)**: add it the same way, but only when the project is native (`ios` / `android` / `adaptive`) or the user wants it explicit; a missing field already means `web`. -- **Both exist**: ask the user directly to clarify what you cannot infer. Ask which file to refresh. Skip the one the user doesn't want changed. -- **Just DESIGN.md exists (unusual)**: do Steps 2-4 to produce PRODUCT.md. +- **No PRODUCT.md:** explore, interview, and write it. +- **PRODUCT.md exists:** ask what product knowledge is stale or missing; do not reopen confirmed fields without a reason. +- **Legacy PRODUCT.md:** add only durable missing facts; absent `## Platform` means `web` unless evidence says otherwise. +- **Only DESIGN.md exists:** leave it untouched and create PRODUCT.md. +- **Redesign/rebrand request:** preserve confirmed product truth unless the user changes it. Visual replacement happens later in new-work, not here. -Never silently overwrite an existing file. Always confirm first. +Never silently overwrite an existing file or offer DESIGN.md during init. If another request invoked init, finish PRODUCT.md and resume it. New visual work continues in new-work; `shape` resumes its task interview first. -If init was invoked as a setup blocker by another command, such as `/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 project -## Step 2: Explore the codebase +Before asking, scan enough to avoid making the user repeat known facts: product docs and copy; package/config and app boundaries; features, workflows, routes, and roles; names, logos, legal/proof assets, and brand commitments; platform/accessibility signals; and the dev command/entry when live mode applies. -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: +Treat repository evidence as a hypothesis, not user approval. Note visual maturity without documenting, extending, or replacing the world. -- **README and docs**: Project purpose, target audience, any stated goals -- **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 -- **Any style guides or brand documentation** +Form a platform hypothesis: `web`, `ios`, `android`, or `adaptive` (one product that genuinely adapts its design language per OS). Mobile web remains `web`; a native wrapper around a website does not make its design language native. -Also form a **register hypothesis** from what you find: +## Step 3: Interview for product truth -- Brand signals: `/`, `/about`, `/pricing`, `/blog/*`, `/docs/*`, hero sections, big typography, scroll-driven sections, landing-page-shaped content. -- Product signals: `/app/*`, `/dashboard`, `/settings`, `/(auth)`, forms, data tables, side/top nav, app-shell components. +ask the user directly to clarify what you cannot infer. Ask only about material gaps the repository and original request do not answer with strong evidence. -Register is a hypothesis at this point, not a decision; Step 3 confirms it. +Use the structured question tool when available; otherwise ask and wait. Keep rounds to at most three focused questions and require one real answer or approval round before writing a new PRODUCT.md. Confirm inferences. -Also form a **platform hypothesis**: +Start with the unknowns that most change future product decisions: -- Native signals: React Native / Expo (`react-native`, `expo`), Flutter (`pubspec.yaml`, `flutter`), SwiftUI / UIKit (`.swift`, `.xcodeproj`, an `ios/` app target), Jetpack Compose / Android (`build.gradle`, an `android/` app module, `AndroidManifest.xml`). An `ios/` and/or `android/` directory that is a real app target, not just a Capacitor/Cordova wrapper around a website. -- Web signals (the default): a web framework (Vite, Next, Nuxt, SvelteKit, Astro), an HTML entry, a CSS/Tailwind setup, no native app target. +1. Who is the primary user, in what situation, and what job are they doing? +2. What does the product make possible, and what is its meaningfully different mechanism or position? +3. What durable constraints, assets, evidence, or product facts must future work preserve? -Values: `web` / `ios` / `android` / `adaptive` (one codebase, ships both, adapts per OS). Mobile web is still `web`. Like register, this is a hypothesis; Step 3 confirms it. +Confirm ambiguous platform separately. Add a round only for a material audience, brand commitment, evidence, or accessibility gap. Record undecided facts instead of inventing them. -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. +Do not ask for an aesthetic direction, emotional feel, visual references, colors, typography, or style during init. If the user volunteers a binding visual constraint, record it without expanding it. -## Step 3: Ask strategic questions (for PRODUCT.md) +### What belongs here -ask the user directly to clarify what you cannot infer. Ask about anything the codebase doesn't answer with strong, explicit evidence. +- users, jobs, workflows, purpose, success, positioning, and operating context; +- capabilities, constraints, terminology, evidence, platform, and accessibility; +- confirmed voice, assets, and brand commitments. -### Interview mode, not confirmation mode +### What does not belong here -If the repo is empty or the user's brief is sparse, run a short interview before proposing PRODUCT.md. Do **not** turn a one-sentence request into a complete inferred PRODUCT.md and ask for blanket confirmation. - -- Use the harness's structured question tool when one exists. Otherwise, ask directly in chat and stop: one question at a time, with lettered options where the crawl suggests likely answers, waiting for each answer before the next. -- Keep skill vocabulary (register, belief ladder, anti-references) out of question text; ask for the thing in words the user would use. For the brand register, ask like a magazine editor profiling the brand: curious and narrative, drawing out the story, the feel, and what a visitor should come to believe. -- Ask in focused rounds and wait for answers between them. Keep **one topic per question**; add rounds rather than fold several topics into one either-or choice. Options obey the same rule: an option answers only the question asked; never write a compound option that bundles a feeling with a business outcome or names an additional audience. -- Use inferred answers as hypotheses or options, not as finished facts. -- Complete at least one real user-answer round before drafting PRODUCT.md, unless every required answer is directly discoverable from repo docs. -- Round 1 should establish register, platform, users, purpose, positioning, and desired outcome. -- Round 2 should establish brand personality or references, anti-references, and accessibility needs, plus conversion & proof for Persuade surfaces. - -### Minimum viable interview - -Ask enough to complete PRODUCT.md. At minimum, cover register confirmation, **platform confirmation** (`web` / `ios` / `android` / `adaptive`), users, purpose, positioning, brand personality, anti-references, and accessibility needs (plus conversion & proof for Persuade surfaces) unless each answer is directly discoverable from repo context. Never let the template's default `web` stand unconfirmed for a native or cross-platform repo. After at least one interview round, you may propose inferred answers, but the user must confirm them before you write PRODUCT.md. Never synthesize PRODUCT.md from the original task prompt alone. - -### Register (ask first; it shapes everything below) - -Every design task serves one of four visitor modes (see SKILL.md's Registers): **Persuade** (marketing, landing, campaigns), **Experience** (portfolios, albums, bodies of work), **Operate** (app UI, admin, dashboards, tools), and **Read** (documentation, editorial, long-form). PRODUCT.md stores the two-value family that covers them: `brand` covers Persuade and Experience, `product` covers Operate and Read. The brief decides the exact mode per task; the stored register only sets the default family. - -If Step 2 produced a clear hypothesis, lead with it: *"From the codebase, this looks like a [brand / product] surface. Does that match your intent, or should we treat it differently?"* - -If the signal is genuinely split (e.g. a product with a big marketing landing), ask the user directly to clarify what you cannot infer. Ask which register describes the **primary** surface. The register can be overridden per task later, but PRODUCT.md carries one default. Settle the default before drafting any register-dependent questions; never batch Persuade-only questions (Conversion & proof) into the same round as the question that decides the register. - -### Platform (ask right after register) - -Every project targets **web** (includes responsive mobile web), **ios**, **android**, or **adaptive** (one codebase, ships both, adapts per OS: Flutter, React Native, KMP). Platform picks the native rulebook: HIG for `ios`, Material 3 for `android`, both for `adaptive`, none for `web`. - -If Step 2 produced a clear hypothesis, lead with it: *"From the codebase, this looks like a [web / ios / android / adaptive] project. Does that match?"* For cross-platform apps, decide by the **design language the app renders**, not the toolchain: one look on both platforms (Flutter's Material-everywhere default) takes that platform's value; genuine per-OS adaptation (Cupertino on iOS, Material on Android) is `adaptive`. When in doubt, `web`. - -A monorepo shipping both a website and a native app gets a PRODUCT.md per app, each with its own `## Platform`; the root PRODUCT.md carries the primary surface's platform. - -### Users & Purpose -- Who uses this? What's their context when using it? -- What job are they trying to get done? -- What is this for? A purpose stated in README or docs is a hypothesis, not strong evidence; confirm it, don't transcribe it. -- What does success look like? -- If more than one kind of user is plausible, confirm a primary and secondary audience; don't manufacture a split that isn't there. An audience implied by another answer (a success metric, a CTA) is still unconfirmed; ask before writing it as secondary. -- If the surface speaks to a different audience than the people who use the product, ask the user to name both. -- For brand: what emotions should the interface evoke? (confidence, delight, calm, urgency) Ask this standalone; don't fold emotions into the success question. -- For product: what workflow are they in? What's the primary task on any given screen? - -### Positioning -- In one line, what does this do that nothing else does? The single strategic claim every screen reinforces. - -### 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? - - 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? - -### Conversion & proof (Persuade surfaces only) - -Ask these only when the primary surface is a Persuade one (marketing, landing, campaigns). Experience and Read surfaces (portfolios, albums, long-form writing, documentation) get no CTA, belief-ladder, or proof questions even when the stored register is `brand`; skip this section and its PRODUCT.md counterpart. - -- What's the primary CTA? -- What's the secondary fallback, for visitors not ready for the primary? -- The one line a visitor should remember after 10 seconds. -- What must the visitor believe, in order, before taking the primary CTA? (The template's belief ladder.) -- What proof is on hand? Ask the user to hand over any testimonials, case studies, press, or client/partner logos they already have. If you can receive files directly, collect them; otherwise create `.impeccable/assets/proof/` and ask the user to add files there. Reference supplied files by path; record text proof inline. - -### Accessibility & Inclusion -- Specific accessibility requirements? (WCAG level, known user needs) -- Considerations for reduced motion, color blindness, or other accommodations? - -Skip questions where the answer is already clear. **Do NOT ask about colors, fonts, radii, or visual styling here.** Those belong in DESIGN.md, not PRODUCT.md. +- visual worlds, palettes, typography, components, or page concepts; +- visitor mode, narrative, CTA/proof sequence, or other surface strategy; +- invented testimonials, customers, benchmarks, pricing, licensing, or deployment claims; +- a requirement to decide every optional field. ## Step 4: Write PRODUCT.md -Write PRODUCT.md only after the user has confirmed the strategic answers from Step 3. If an inferred answer is uncertain or unconfirmed, ask before writing. Confirmed means what the user actually said yes to; do not pad a confirmed answer with extras they never picked (additional anti-references, audiences, roadmap claims, a WCAG level), whether drawn from the crawl, another answer, or your own option text. If an extra belongs in the doc, ask about it first. - -Synthesize into a strategic document: +Write only confirmed facts and explicitly marked open decisions. Omit irrelevant sections rather than filling them with generic prose. ```markdown # Product -## Register - -product - ## Platform web ## Users -[Who they are, their context, the job to be done. Primary audience; a secondary audience or a surface-vs-user split only when they apply.] +[Primary users, their situation, and job. Add other audiences only when confirmed.] ## Product Purpose -[What this product does, why it exists, what success looks like] +[What the product does, why it exists, and what success means.] ## Positioning -[The single strategic claim every screen reinforces. Not a visual rule, not an anti-reference.] +[The product mechanism or claim a neighboring product could not truthfully copy.] -## Conversion & proof -[Persuade surfaces only (marketing, landing, campaigns). Omit this section entirely, heading included, for the product register and for Experience or Read surfaces such as portfolios, albums, or long-form writing.] -- Primary and secondary CTA: [...] -- The line a visitor remembers after 10 seconds: [...] -- Belief ladder: [...] -- Proof on hand: [testimonials, case studies, press, or logos, referenced by path] +## Operating Context +[Workflows, environments, tools, documents, materials, and rituals that are factual parts of using or evaluating the product.] -## Brand Personality -[Voice, tone, 3-word personality, emotional goals] +## Capabilities and Constraints +[Confirmed functionality, technical constraints, terminology, and explicitly undecided product facts.] -## Anti-references -[What this should NOT look like. Specific bad-example sites or patterns to avoid.] +## Brand Commitments +[Existing name, voice, assets, personality, identity constraints, and references the user explicitly made binding. Omit when none exist.] -## Design Principles -[3-5 strategic principles derived from the conversation. Principles like "practice what you preach", "show, don't tell", "expert confidence". NOT visual rules like "use OKLCH" or "magenta accent".] +## Evidence on Hand +[Real content, data, demonstrations, testimonials, case studies, press, or assets, with paths where applicable. State absences that future work must not fabricate.] + +## Product Principles +[Three to five durable strategic principles derived from confirmed answers; no visual recipes.] ## Accessibility & Inclusion -[WCAG level, known user needs, considerations] +[Known user needs or required standard. Omit when no product-specific requirement was established.] ``` -Register is either `brand` or `product` as a bare value. No prose, no commentary. Platform is `web`, `ios`, `android`, or `adaptive`, also a bare value; omit the section only on legacy files you're leaving untouched, otherwise write `web` explicitly. +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. -Write fields as prose, and use bold sparingly: only where a word carries a decision, never as a label lead-in on every line. +### Completion gate -Write to `PROJECT_ROOT/PRODUCT.md`. If `.impeccable.md` existed, the loader already renamed it; merge into that content rather than starting from scratch. +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: Decide on DESIGN.md +## Step 5: Configure live mode when useful -Offer `/impeccable document` either way. Two paths: +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. -- **Code exists** (CSS tokens, components, a running site): "I can generate a DESIGN.md that captures your visual system (colors, typography, components) so variants stay on-brand. Want to do that now?" -- **Pre-implementation** (empty project): "I can seed a starter DESIGN.md from five quick questions about color strategy, type direction, motion energy, and references. You can re-run once there's code, to capture the real tokens. Want to do that now?" +## Step 6: Wrap up or resume -If the user agrees, delegate to `/impeccable document` (it auto-detects scan vs seed). Load its reference and follow that flow. +Summarize captured and deliberately undecided facts. Do not offer DESIGN.md merely because it is missing. -If the user prefers to skip, mention they can run `/impeccable document` any time later. +Recommend the next action from the actual project state: -## Step 6: Configure live mode (when code exists) +- Empty or early project: ask naturally for the surface to be built, or use `/impeccable shape ` when the user wants a confirmed brief without implementation. New-work will establish a visual world only when the requested work needs one. +- Existing coherent interface without DESIGN.md: `/impeccable document` if the user wants the incumbent system recorded independently of a new build. +- Existing surface needing work: name the most relevant scoped command. +- Web project ready for visual iteration: `/impeccable live` when configured. -**Skip this step when the platform is native** (`ios` / `android` / `adaptive`): live mode drives a browser overlay. A hybrid wrapper or Expo web target serving HTML doesn't change that. - -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 .gemini/skills/impeccable/scripts/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) and platform captured (web / ios / android / adaptive) -- 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 or live config is pending, one line on how to set it up later - -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 platform**, offer the 2-4 most relevant (not a menu dump), and give the exact command to type. Group by intent: - -- **Build something new**: `/impeccable craft ` (shape, then build end-to-end) or `/impeccable shape ` (plan first). Lead with this for empty or early-stage projects. -- **Improve what's there**: name the specific surface. `/impeccable critique ` for a scored UX review; `/impeccable audit ` for a11y / perf / responsive checks; `/impeccable polish ` 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** (web only): `/impeccable live` (configured in Step 6) to pick elements in the browser and generate variants in place. **Skip this group for native platforms.** - -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 the user directly to clarify what you cannot infer. Ask whether they'd like a brief summary of PRODUCT.md appended to GEMINI.md for easier agent reference. If yes, append a short **Design Context** pointer section there. +If init was invoked by another request, resume without rerunning context.mjs; new-work owns later visual decisions. diff --git a/.gemini/skills/impeccable/reference/ios.md b/.gemini/skills/impeccable/reference/ios.md index ef10ee0d2..ccef5d2c4 100644 --- a/.gemini/skills/impeccable/reference/ios.md +++ b/.gemini/skills/impeccable/reference/ios.md @@ -2,7 +2,7 @@ For native iOS / iPadOS apps: SwiftUI, UIKit, React Native, Expo, Flutter shipping to Apple hardware. -On native, register narrows. HIG conformance governs structure, navigation, and interaction whatever the register; brand expresses through the expressive layer the platform provides (tint, type, motion, content). Calm, Duolingo, and Spotify carry strong identity entirely inside HIG conventions. +On native, the visitor mode narrows what expression may override. HIG conformance governs structure, navigation, and interaction in every mode; brand expresses through the layer the platform leaves open (tint, type, motion, content). ## The iOS slop test diff --git a/.gemini/skills/impeccable/reference/layout.md b/.gemini/skills/impeccable/reference/layout.md index 5a884ac20..b9eeddc13 100644 --- a/.gemini/skills/impeccable/reference/layout.md +++ b/.gemini/skills/impeccable/reference/layout.md @@ -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. --- -## Register +## 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, register, 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 .gemini/skills/impeccable/scripts/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 `/impeccable polish` for the final pass. +When the structure holds, hand off to `/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) * )`: 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. diff --git a/.gemini/skills/impeccable/reference/live.md b/.gemini/skills/impeccable/reference/live.md index c405f2833..db4432797 100644 --- a/.gemini/skills/impeccable/reference/live.md +++ b/.gemini/skills/impeccable/reference/live.md @@ -33,7 +33,7 @@ Chat is overhead. No recap, no tutorial output, no pasting PRODUCT / DESIGN bodi node .gemini/skills/impeccable/scripts/live.mjs ``` -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. +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, DESIGN.md, and any surface brief already loaded by Setup in mind for variant generation: **DESIGN.md wins on visual decisions; PRODUCT.md wins on durable product and voice decisions; the surface brief wins on this surface's strategy.** 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 requires the user's explicit redesign/replacement intent. `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). @@ -187,7 +187,7 @@ All three carry `fallback: "agent-driven"`. Follow **Handle fallback** below. ### 3. Load the action's reference -If `event.action` is `impeccable` (the default freeform action), use SKILL.md's shared laws plus the loaded register reference (`brand.md` or `product.md`). Do not load a sub-command reference. **Freeform is not a pass to skip parameters:** you still follow the composition budget and the freeform bias in **§7 Parameters** below. Sub-command files list MUST-have signature knobs; freeform has no such file, so sizing knobs from surface weight and primary axes is entirely on you. +If `event.action` is `impeccable` (the default freeform action), use SKILL.md's shared laws and decide the visitor mode from the selected surface. Do not load a sub-command reference. **Freeform is not a pass to skip parameters:** you still follow the composition budget and the freeform bias in **§7 Parameters** below. Sub-command files list MUST-have signature knobs; freeform has no such file, so sizing knobs from surface weight and primary axes is entirely on you. Any other `event.action` (`bolder`, `quieter`, `distill`, `polish`, `typeset`, `colorize`, `layout`, `adapt`, `animate`, `delight`, `overdrive`): Read `reference/.md` before planning. Each sub-command encodes a specific discipline; skipping its reference produces generic output. Those files may require specific params; layer them on top of the §7 budget, not instead of it. @@ -216,7 +216,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. @@ -224,10 +224,7 @@ This sentence is the **identity lock**. Every variant must be readable as the sa **Default mode**: the existing identity is preserved. Variants vary expression axes within it. *This is the right mode for ~90% of live sessions.* The user picked an element on a real product they're shipping; they expect variants of *their* hero, not three different brands' heroes. -**Departure mode**: the existing identity is rejected. Variants propose alternatives consistent with PRODUCT.md voice. Trigger only when at least one is true: - -- PRODUCT.md anti-references explicitly call out the current surface ("the current `index.html` is itself an example"; "diffuse away from this"; "the page on screen is the failure"). Generic anti-references that describe what to avoid in general do **not** trigger departure mode; only ones that point at *this* surface specifically. -- The user's freeform prompt explicitly asks for departure ("rebuild this from scratch", "what if it weren't editorial at all", "show me something completely different"). +**Departure mode**: the existing identity is rejected. Variants propose alternatives consistent with durable product and brand truth. Trigger only when the user explicitly asks for departure in the current request or freeform prompt ("redesign this", "rebuild this from scratch", "what if it weren't editorial at all", "show me something completely different"). A stale page critique or an old task note is not replacement authorization. If you're unsure, you're in default mode. The cost of being wrong about default is "three on-brand variants with similar feel": recoverable, the user picks none. The cost of being wrong about departure is "three off-brand variants": unrecoverable, the user is annoyed. @@ -246,13 +243,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 the brand's stated voice and register in PRODUCT.md. Do NOT pick from a fixed catalog of lane categories. The right three directions for this brand are not the same as the right three for another brand, 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. @@ -262,7 +259,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. @@ -289,7 +286,7 @@ In **default mode**, the prompt narrows the axes you choose, not the identity. * In **departure mode**, the prompt narrows the lanes you draw from, not the families. *"Make it feel like a newspaper front page"* would itself be a departure-mode prompt; honor it but pick three meaningfully different newspaper-adjacent lanes (broadsheet vs. tabloid vs. trade journal), and run the family pass to confirm they don't collapse into one. -When the prompt and PRODUCT.md anti-references conflict (the prompt asks for X, the anti-references ban X), the anti-references win; they describe the brand's standing position, the prompt is one moment. +When the prompt conflicts with a confirmed binding brand commitment or DESIGN.md invariant, preserve the invariant unless the user explicitly revokes or replaces it. Task-local strategy from the matching surface brief may change when the user changes that surface's goal. ### 6. Write all variants in a single edit diff --git a/.gemini/skills/impeccable/reference/new-work.md b/.gemini/skills/impeccable/reference/new-work.md index 4efb4f8b7..028b059d4 100644 --- a/.gemini/skills/impeccable/reference/new-work.md +++ b/.gemini/skills/impeccable/reference/new-work.md @@ -1,47 +1,178 @@ -# New identity work +# New visual work -Read this only when the project has no committed visual identity, or the user explicitly asked to replace the current one. A missing DESIGN.md is not enough: code, tokens, chosen type, components, and assets are incumbent design authority. If they exist, preserve and extend them unless the brief says to discard them. +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. -This playbook is task-scoped. PRODUCT.md owns durable product facts and DESIGN.md owns durable visual invariants; the direction for one page or feature belongs in the current task, not in either document. +## 1. Name the intent -## Choose the authority +- **Greenfield:** no coherent visual implementation. +- **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. -Classify the work before inventing anything: +A plain “redesign this page/site” authorizes replacement. “Redesign this within the current brand/system” means extension or refinement. Ask once only when the wording is genuinely ambiguous. -- **Extend**: an incumbent system exists and the task adds or improves a surface. Preserve its lineage, semantics, and affordances. -- **Surface redesign**: the current identity remains, but this surface may change its topology, hierarchy, and expression. -- **New identity**: nothing committed exists, or the user explicitly authorized a rebrand. +## 2. Resolve visual authority -When the evidence is mixed, preserve. Novelty is not permission to erase authored decisions. +Read DESIGN.md and representative code, tokens, components, and assets. Choose one path: -## Find the concept +### A. Explicit redesign -Name the subject, audience, page job, and visitor mode from SKILL.md. Then identify the product's unique mechanism: what it does, proves, or enables that a neighboring product cannot truthfully claim. +The old DESIGN.md and implementation are not authority. Keep only unrevoked product facts, content, function, native expectations, constraints, and brand commitments. Establish a replacement world. -Derive the concept from that mechanism through one primary transformation: information topology, evidence, temporal behavior, or spatial mapping. The transformation must change how the surface is organized or behaves, not merely how it is decorated. This produces stranger and more defensible work than imitating a culturally specific document or object. Borrowing a literal cultural form is allowed only when it is genuinely native to the product and audience, and never as costume. +### B. DESIGN.md covers this kind of surface -For **Operate** and **Read**, start with the task and information structure. Native controls remain native controls; expression comes from hierarchy, density, rhythm, state, and the system around them. For **Persuade** and **Experience** creating a new identity, explore more freely, but keep the product legible and the primary action obvious. +Use its invariants and normative tokens. Skip world-building and discover the surface. -Run `node .gemini/skills/impeccable/scripts/concept-seed.mjs` only for Persuade or Experience new-identity work when two or more directions remain equally credible. It is an entropy source, not an authority: never let the draw overrule the strongest fit. When color is genuinely unconstrained, `node .gemini/skills/impeccable/scripts/palette.mjs` may break a reflex palette; subject evidence and incumbent color still win. +### C. A coherent implementation exists but DESIGN.md does not -## State the direction +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. -Before code, write a task-scoped direction contract of at most 120 words in your reasoning: +### D. The brand exists, but a whole-surface family is unresolved -- the thesis this surface owns; -- the structural or behavioral transformation; -- what authority is preserved; -- the first viewport's composition and primary action; -- the signature element the surface will be remembered by. +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. -Do not embed this contract as a comment in the production artifact or turn it into a second design system. In an attended substantial build, confirm it once. In an unattended run, record it and continue. +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. -## Build once, commit fully +### E. No confirmed visual authority exists -Plan one compact token system and one layout source: the concept or the content's real structure. Build the strongest coherent direction once. Commitment means the transformation governs the whole surface; it does not mean every control must be rebuilt as a metaphor. +Establish a world. Scaffolds, framework defaults, and stray utilities are not identity. -Make the first viewport demonstrate the product's mechanism. Pace long surfaces through contrast in density and scale, and cut sections that only repeat claims. Briefs that depend on imagery ship real, verified imagery. Preserve semantic HTML, familiar interaction behavior, accessibility, and the project's technical conventions. +## 3. Discover the requested surface -## Finish proportionally +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. -Inspect desktop and mobile, write one honest critique against the brief and direction, patch material defects, then run the detector once. Repeat only when a real defect remains. A separate reviewer is optional when the harness already provides one and the extra cost is earned by the risk; it is not a default step and its presence is not evidence of quality. +Ask one attended round of at most three material questions without repeating durable facts. CTA hierarchy, proof sequence, content gaps, and interaction outcomes belong here, not PRODUCT.md. For a fully specified narrow request, state the interpretation and invite correction. + +When `shape` has already completed its discovery interview, reuse those confirmed answers and do not ask this round again. + +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. Each candidate joins a durable visual system (identity thesis, information/layout grammar, material and type behavior, color strategy, imagery, motion, reusable signature) to a concrete expression for this surface (topology, sequence, focal moment, and implementation consequence). Do not rank yet. +3. **Break the ranking rut once.** Run `node .gemini/skills/impeccable/scripts/concept-seed.mjs --scope direction`. Promote the assigned grounded pair and weigh its challengers only when they can become both a coherent system and a strong solution to this task. +4. **Test coupling and breadth.** Reject a pair if its surface could swap into another candidate unchanged, or if its world is merely the surface motif repeated. Test each survivor on the requested surface plus navigation, quiet and dense content, interaction/state, and one unlike future surface. Compare it with both the category habit and predictable contrarian response. +5. **Offer coupled choices.** Present two or three materially different pairs without recommendation cues. For each, show the world rules, this surface's 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. 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 .gemini/skills/impeccable/scripts/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 promoted candidate when a roll ran; otherwise use the strongest grounded structure. It must survive both tests. + +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). + +### Write or update DESIGN.md + +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: + +`` + +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 .gemini/skills/impeccable/scripts/surface-brief.mjs read ` + +Exit 0: preserve still-valid decisions and change only what the user changed. For redesign, retain valid product strategy, content, function, and open decisions; replace visual direction and contract. Exit 2: no brief. After the contract, write with `node .gemini/skills/impeccable/scripts/surface-brief.mjs write [related-target ...]`. + +The body is concise and contains: + +```markdown +# Surface brief: [name] + +## Scope +[Primary/related route or artifact, visitor mode, and what this surface owns.] + +## Product strategy +[Surface-specific audience and job, desired outcome, primary/secondary action or task, content and proof sequence, factual constraints, and explicitly open decisions.] + +## Selected direction +[Reference to the applicable DESIGN.md world or expression range, selected surface concept, focal moment, narrative/interaction sequence, and implementation consequence.] + +## Direction contract +[The six contract blocks below.] + +## Open decisions +[Only unresolved items that later work must not silently invent. Omit when empty.] +``` + +Commit `.impeccable/surfaces/.md` as stable later-work context. Exclude global truth, exact tokens, transient notes, and work logs. + +## 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. + +Before code, write a direction contract of at most 150 words in an opening HTML or framework comment. The first 200 characters must name `DIRECTION CONTRACT`. + +- `UNIQUE`: the task thesis tied to the product mechanism; +- `NOT-TEMPLATE`: the category-default arrangement this structure refuses; +- `OWN-WORLD`: the confirmed DESIGN.md invariants, tokens, and materials it uses; +- `STORY`: what the visitor understands, believes, and does; +- `FIRST VIEWPORT`: exact composition, hierarchy, action, and where the concept exceeds competent convention; +- `FORM`: chosen structure or behavior, signature, implementation consequence, and seed key. + +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. + +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. + +## 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. + +Build the strongest coherent direction once. Its grammar governs navigation, actions, controls, content, and transitions without disguising affordances. Give the focal form the scale that gives it force; do not trap it inside a standard hero panel. + +**Make the opening a thesis.** Demonstrate the mechanism immediately; leave an idea, interaction, or evidence, not merely mood. + +**Commit before correcting.** Land the hard move at full strength before refining it. In unattended work, safety is the known risk. + +**Commit at page scale.** Let color, material, image, or type own a region when the world calls for it. Scattered signature decoration is not commitment. + +**Pace the whole surface.** Vary density, scale, image, motion, and quiet inside one grammar. Cut repeated claims; prove the mechanism with real artifact, interaction, data, transformation, or content. + +**Author motion as material.** Motion expresses the world and task. Premium moments go beyond repeated transform/opacity through earned focus, depth, masks, continuity, light, or material change. Bound expensive effects, test in-browser, keep content visible by default, and design reduced motion. + +Preserve semantics, affordances, accessibility, performance, responsiveness, and project conventions. Operate/Read express through topology, hierarchy, density, rhythm, and state; Persuade/Experience may earn drama. + +## 8. Solidify the visual record + +After first implementation of a new/replacement world or approved expansion, refresh DESIGN.md from the build: + +- replace provisional direction with the exact type, color roles, tokens, spacing/radii, components, states, and motion that survived; +- add normative YAML tokens only for values the implementation actually uses; +- remove the directional-seed status once the record and implementation agree; +- preserve broader world invariants and expression ranges; +- do not promote the task's story, hero composition, or one-off motif into a global rule unless it is intentionally reusable. + +Ordinary extension does not rewrite DESIGN.md; only approved durable changes do. + +## 9. Finish like a 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. diff --git a/.gemini/skills/impeccable/reference/operate.md b/.gemini/skills/impeccable/reference/operate.md index 04ee9d7d2..f420cbaa2 100644 --- a/.gemini/skills/impeccable/reference/operate.md +++ b/.gemini/skills/impeccable/reference/operate.md @@ -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. - No orchestrated page-load sequences. Product loads into a task; users don't want to watch it load. -## Product bans (on top of the shared absolute bans) +## Product constraints - Decorative motion that doesn't convey state. - Inconsistent component vocabulary across screens. If the "save" button looks different in two places, one is wrong. @@ -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. diff --git a/.gemini/skills/impeccable/reference/polish.md b/.gemini/skills/impeccable/reference/polish.md index 6af417659..f36a8152d 100644 --- a/.gemini/skills/impeccable/reference/polish.md +++ b/.gemini/skills/impeccable/reference/polish.md @@ -1,241 +1,98 @@ -> **Additional context needed**: quality bar (MVP vs flagship). +> **Additional context needed**: quality bar and shipping constraints. -Perform a meticulous final pass to catch all the small details that separate good work from great work. The difference between shipped and polished. +Polish is refinement, never concealed redesign. Preserve the incumbent visual world, content, behavior, and everything outside scope. If the concept itself is wrong, say so and recommend redesign or `bolder` instead of smuggling in a replacement. -Detector and automated QA output are defect evidence only. A clean script result is never proof that the design is strong; gather browser evidence and inspect the real interaction path. +A detector result is defect evidence, not proof of quality. Inspect the rendered experience and real interaction path. -## Design System Discovery +## 1. Establish the system -Aligning the feature to the design system is **not optional**. Polish without alignment is decoration on top of drift, and it makes the next person's job harder. Discovery comes before any other polish work. +Read DESIGN.md and representative tokens, shared components, patterns, and neighboring flows. If no formal system exists, use coherent project conventions. -1. **Find the design system**: Search for design system documentation, component libraries, style guides, or token definitions. Study the core patterns: design principles, target audience, color tokens, spacing scale, typography styles, component API, motion conventions. -2. **Note the conventions**: How are shared components imported? What spacing scale is used? Which colors come from tokens vs hard-coded values? What motion and interaction patterns are established? What flow shapes are used for comparable actions (modal vs full-page, inline vs route, save-on-blur vs explicit submit)? -3. **Identify drift, then name the root cause**: For every deviation, classify it as a **missing token** (the value should exist in the system but doesn't), a **one-off implementation** (a shared component already exists but wasn't used), or a **conceptual misalignment** (the feature's flow, IA, or hierarchy doesn't match neighboring features). The fix differs by category: patch the value, swap to the shared component, or rework the flow. Fixing the symptom without naming the cause is how drift compounds. +Classify each drift before fixing it: -If a design system exists, polish **must** align the feature with it. If none exists, polish against the conventions visible in the codebase. **If anything about the system is ambiguous, ask. Never guess at design system principles.** +- **missing token:** the system needs a reusable value; +- **one-off implementation:** an existing shared component or pattern should replace it; +- **conceptual mismatch:** the flow, information architecture, or hierarchy differs from comparable product areas; +- **local defect:** the implementation is simply incomplete or inconsistent. -## Pre-Polish Assessment +Fix the cause at the narrowest correct level. Ask when a binding system principle cannot be inferred. -Understand the current state and goals before touching anything: +## 2. Gather the evidence -1. **Review completeness**: - - Is it functionally complete? - - Are there known issues to preserve (mark with TODOs)? - - What's the quality bar? (MVP vs flagship feature?) - - When does it ship? (How much time for polish?) +Use the feature yourself at representative desktop and mobile sizes. Determine: -2. **Think experience-first**: Who actually uses this, and what's the best possible experience for them? Effective design beats decorative polish; a feature that looks beautiful but fights the user's flow is not polished. Walk the path from their perspective before opening DevTools. +- whether the path is functionally complete; +- the intended quality bar and time available; +- known constraints or deliberately unfinished work; +- the states, content lengths, roles, and input methods users will actually encounter. -3. **Identify polish areas**: - - Visual inconsistencies - - Spacing and alignment issues - - Interaction state gaps - - Copy inconsistencies - - Edge cases and error states - - Loading and transition smoothness - - Information architecture and flow drift (does this feature reveal complexity the way neighboring features do?) +If a prior critique exists, use it as one input: -4. **Pull in any prior critique** (optional signal): If `/impeccable critique` has been run on the same target, its priority issues are a useful prior for what to address first. Resolve the target to a file path or URL, then: - ```bash - slug=$(node .gemini/skills/impeccable/scripts/critique-storage.mjs slug "") - node .gemini/skills/impeccable/scripts/critique-storage.mjs latest "$slug" - ``` - Exit 0 with body = found; fold the P0/P1 items into your polish list and mention the snapshot path so the user sees what you read. Exit 2 = no snapshot, continue without it. The critique is one input among many. Do your own pass either way. +```bash +slug=$(node .gemini/skills/impeccable/scripts/critique-storage.mjs slug "") +node .gemini/skills/impeccable/scripts/critique-storage.mjs latest "$slug" +``` -5. **Triage cosmetic vs functional**: Classify each issue as **cosmetic** (looks off, doesn't impede the user) or **functional** (breaks, blocks, or confuses the experience). When polish time is tight, functional issues ship first; cosmetic ones can land in a follow-up. Quality should be consistent; never perfect one corner while leaving another rough. +Exit 0 returns the latest snapshot; incorporate relevant P0/P1 findings and name the snapshot read. Exit 2 means none exists. Perform an independent pass either way. -**CRITICAL**: Polish is the last step, not the first. Don't polish work that's not functionally complete. +## 3. Triage -## Polish Systematically +Separate functional defects from cosmetic ones and fix in this order: -Work through these dimensions methodically: +1. broken or blocked tasks, data loss, misleading state, and inaccessible paths; +2. missing loading, empty, error, success, disabled, and permission states; +3. flow, hierarchy, responsive, and design-system drift; +4. visual and motion inconsistencies; +5. code and asset cleanup. -### Visual Alignment & Spacing +Do not perfect one corner while leaving the rest below the same quality bar. -- **Pixel-perfect alignment**: Everything lines up to grid -- **Consistent spacing**: All gaps use spacing scale (no random 13px gaps) -- **Optical alignment**: Adjust for visual weight (icons may need offset for optical centering) -- **Responsive consistency**: Spacing and alignment work at all breakpoints -- **Grid adherence**: Elements snap to baseline grid +## 4. Polish the whole path -**Check**: -- Enable grid overlay and verify alignment -- Check spacing with browser inspector -- Test at multiple viewport sizes -- Look for elements that "feel" off +### Flow and hierarchy -### Information Architecture & Flow +- Match neighboring mental models, terminology, disclosure, routing, save behavior, and optimistic or pessimistic patterns. +- Make the primary task and current state obvious without flattening every element to equal weight. +- Ensure arrival, transition, empty, and recovery paths connect instead of behaving as isolated screens. -Visual polish on a misshapen flow is wasted work. Match the *shape* of the experience to the system, not just the surface. +### Layout and type -- **Progressive disclosure**: Match how much is revealed when, compared to neighboring features. A settings page exposing 40 fields when the rest of the app reveals 5 at a time is drift, even if every field is perfectly styled. -- **Established user flows**: Multi-step actions follow the same shape as comparable flows elsewhere: modal vs full-page, inline edit vs separate route, save-on-blur vs explicit submit, optimistic vs pessimistic updates. -- **Hierarchy & complexity**: The same conceptual weight gets the same visual weight throughout. Primary actions don't become tertiary in one corner of the product, and tertiary actions don't shout. -- **Empty, loading, and arrival transitions**: How content arrives, updates, and leaves matches how it does in adjacent features. -- **Naming and mental model**: The feature uses the same nouns and verbs as the rest of the system. A "Workspace" here shouldn't be a "Project" three screens away. +- Align to the project's grid and spacing scale; fix optical as well as mathematical alignment. +- Group related content tightly and separate distinct groups generously. +- Keep same-role typography consistent; test measure, wrapping, localization expansion, zoom, and font loading. +- Verify every supported viewport rather than correcting only the current screenshot. -### Typography Refinement +### Color, imagery, and icons -- **Hierarchy consistency**: Same elements use same sizes/weights throughout -- **Line length**: 45-75 characters for body text -- **Line height**: Appropriate for font size and context -- **Widows & orphans**: No single words on last line -- **Hyphenation**: Appropriate for language and column width -- **Kerning**: Adjust letter spacing where needed (especially headlines) -- **Font loading**: No FOUT/FOIT flashes +- Use semantic tokens and stable color meanings across themes. +- Verify text, control, and focus contrast in every state. +- Keep icon families, stroke/weight, sizing, and optical alignment coherent. +- Prevent image layout shift; use correct aspect ratios, responsive sources, and useful alt text. -### Color & Contrast +### Interaction and state -- **Contrast ratios**: All text meets WCAG standards -- **Consistent token usage**: No hard-coded colors, all use design tokens -- **Theme consistency**: Works in all theme variants -- **Color meaning**: Same colors mean same things throughout -- **Accessible focus**: Focus indicators visible with sufficient contrast -- **Gray on color**: Never put gray text on colored backgrounds; use a shade of that color or transparency +- Every control needs appropriate default, hover, focus, active, disabled, loading, error, and success behavior. +- Preserve visible keyboard focus, logical tab order, labels, and platform-appropriate touch targets. +- Keep motion coherent, interruptible, performant, and reduced-motion aware. Do not add animation merely to make polish visible. +- Validate long, missing, localized, offline, slow, and permission-limited content where the product can encounter it. -### Interaction States +### Content and code -Every interactive element needs all states: +- Keep terminology, capitalization, punctuation, and factual copy consistent. Ask before changing claims. +- Remove debug output, dead code, unused imports, obsolete styles, and polish-created duplication. +- Replace custom implementations with shared components where the system owns the pattern. +- Promote genuinely reusable values to tokens; do not create a system abstraction for one local exception. -- **Default**: Resting state -- **Hover**: Subtle feedback (color, scale, shadow) -- **Focus**: Keyboard focus indicator (never remove without replacement) -- **Active**: Click/tap feedback -- **Disabled**: Clearly non-interactive -- **Loading**: Async action feedback -- **Error**: Validation or error state -- **Success**: Successful completion +## 5. Verify and finish -**Missing states create confusion and broken experiences**. +Walk the complete path again with mouse, keyboard, and touch where applicable. Check: -### Micro-interactions & Transitions +- mobile, intermediate, and wide layouts; +- loading, empty, error, success, disabled, long-content, and missing-content states; +- zoom, contrast, focus, semantics, screen-reader names, and reduced motion; +- console errors, layout shift, interaction latency, image loading, and supported browsers; +- agreement with DESIGN.md, neighboring features, and the user's scope. -- **Smooth transitions**: All state changes animated appropriately (150-300ms) -- **Consistent easing**: Use ease-out-quart/quint/expo for natural deceleration. Never bounce or elastic; they feel dated. -- **No jank**: Smooth animations; use atmospheric blur/filter/mask/shadow effects when they add polish, but bound expensive paint areas and avoid casual layout-property animation -- **Appropriate motion**: Motion serves purpose, not decoration -- **Reduced motion**: Respects `prefers-reduced-motion` +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. -### Content & Copy - -- **Consistent terminology**: Same things called same names throughout -- **Consistent capitalization**: Title Case vs Sentence case applied consistently -- **Grammar & spelling**: No typos -- **Appropriate length**: Not too wordy, not too terse -- **Punctuation consistency**: Periods on sentences, not on labels (unless all labels have them) - -### Icons & Images - -- **Consistent style**: All icons from same family or matching style -- **Appropriate sizing**: Icons sized consistently for context -- **Proper alignment**: Icons align with adjacent text optically -- **Alt text**: All images have descriptive alt text -- **Loading states**: Images don't cause layout shift, proper aspect ratios -- **Retina support**: 2x assets for high-DPI screens - -### Forms & Inputs - -- **Label consistency**: All inputs properly labeled -- **Required indicators**: Clear and consistent -- **Error messages**: Helpful and consistent -- **Tab order**: Logical keyboard navigation -- **Auto-focus**: Appropriate (don't overuse) -- **Validation timing**: Consistent (on blur vs on submit) - -### Edge Cases & Error States - -- **Loading states**: All async actions have loading feedback -- **Empty states**: Helpful empty states, not just blank space -- **Error states**: Clear error messages with recovery paths -- **Success states**: Confirmation of successful actions -- **Long content**: Handles very long names, descriptions, etc. -- **No content**: Handles missing data gracefully -- **Offline**: Appropriate offline handling (if applicable) - -### Responsiveness - -- **All breakpoints**: Test mobile, tablet, desktop -- **Touch targets**: 44x44px minimum on touch devices -- **Readable text**: No text smaller than 14px on mobile -- **No horizontal scroll**: Content fits viewport -- **Appropriate reflow**: Content adapts logically - -### Performance - -- **Fast initial load**: Optimize critical path -- **No layout shift**: Elements don't jump after load (CLS) -- **Smooth interactions**: No lag or jank -- **Optimized images**: Appropriate formats and sizes -- **Lazy loading**: Off-screen content loads lazily - -### Code Quality - -- **Remove console logs**: No debug logging in production -- **Remove commented code**: Clean up dead code -- **Remove unused imports**: Clean up unused dependencies -- **Consistent naming**: Variables and functions follow conventions -- **Type safety**: No TypeScript `any` or ignored errors -- **Accessibility**: Proper ARIA labels and semantic HTML - -## Polish Checklist - -Go through systematically: - -- [ ] Aligned to the design system (drift named and resolved by root cause) -- [ ] Information architecture and flow shape match neighboring features -- [ ] Visual alignment perfect at all breakpoints -- [ ] Spacing uses design tokens consistently -- [ ] Typography hierarchy consistent -- [ ] All interactive states implemented -- [ ] All transitions smooth (60fps) -- [ ] Copy is consistent and polished -- [ ] Icons are consistent and properly sized -- [ ] All forms properly labeled and validated -- [ ] Error states are helpful -- [ ] Loading states are clear -- [ ] Empty states are welcoming -- [ ] Touch targets are 44x44px minimum -- [ ] Contrast ratios meet WCAG AA -- [ ] Keyboard navigation works -- [ ] Focus indicators visible -- [ ] No console errors or warnings -- [ ] No layout shift on load -- [ ] Works in all supported browsers -- [ ] Respects reduced motion preference -- [ ] Code is clean (no TODOs, console.logs, commented code) - -**IMPORTANT**: Polish is about details. Zoom in. Squint at it. Use it yourself. The little things add up. - -Sweat the details. Zoom in until the alignment is right and the spacing reads as deliberate. Then ship. - -**NEVER**: -- Polish before it's functionally complete -- Polish without aligning to the design system; that's decoration on drift -- Guess at design system principles instead of asking when something is ambiguous -- Spend hours on polish if it ships in 30 minutes (triage) -- Introduce bugs while polishing (test thoroughly) -- Ignore systematic issues (if spacing is off everywhere, fix the system, not just one screen) -- Perfect one thing while leaving others rough (consistent quality level) -- Create new one-off components when design system equivalents exist -- Hard-code values that should use design tokens -- Introduce new patterns or flows that diverge from established ones - -## Final Verification - -Before marking as done: - -- **Use it yourself**: Actually interact with the feature. -- **Test on real devices**: Not just browser DevTools. -- **Ask someone else to review**: Fresh eyes catch things. -- **Compare to design**: Match intended design. -- **Check all states**: Don't just test happy path. -- **Treat automation carefully**: Run detector or QA commands when they are available and relevant, fix their defects, but never cite a clean result as proof that the work is polished. - -## Clean Up - -After polishing, ensure code quality: - -- **Replace custom implementations**: If the design system provides a component you reimplemented, switch to the shared version. -- **Remove orphaned code**: Delete unused styles, components, or files made obsolete by polish. -- **Consolidate tokens**: If you introduced new values, check whether they should be tokens. -- **Verify DRYness**: Look for duplication introduced during polishing and consolidate. +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. diff --git a/.gemini/skills/impeccable/reference/quieter.md b/.gemini/skills/impeccable/reference/quieter.md index 55b00b6f3..bb163e40f 100644 --- a/.gemini/skills/impeccable/reference/quieter.md +++ b/.gemini/skills/impeccable/reference/quieter.md @@ -2,7 +2,7 @@ Quiet design is harder than bold design. Subtlety needs precision. Reduce visual --- -## Register +## Visitor mode Persuade + Experience: "quieter" means more restrained palette, more whitespace, more typographic air. Drama is reduced, not eliminated; the POV stays intact. diff --git a/.gemini/skills/impeccable/reference/routing.md b/.gemini/skills/impeccable/reference/routing.md index 1b6138457..a09c24de9 100644 --- a/.gemini/skills/impeccable/reference/routing.md +++ b/.gemini/skills/impeccable/reference/routing.md @@ -11,7 +11,7 @@ Reason over the signals; there is no score to obey: - `critique.latest` with a low `score` or non-zero `p0` / `p1` → `polish` (it reads that snapshot as its backlog), or re-run `critique` if the snapshot looks stale. - `git.changedFiles` pointing at one surface → scope `audit` or `polish` to those files specifically, naming them. - `devServer.running` true → `live` is available for in-browser iteration; if false, don't lead with `live`. **`live` and the bundled `detect.mjs` are web-only.** If `setup.platform` is `ios`, `android`, or `adaptive`, don't lead with either; the browser overlay and the HTML rule engine don't apply to native app code. -- Otherwise group by intent exactly as init's "Recommend starting points" step does (build new / improve what's there / iterate visually), tailored to `setup.register`. +- Otherwise group by intent (build new / improve what's there / iterate visually), tailored to the current surface and `setup.platform`. **If `scan.targets` is non-empty and `setup.platform` is not `ios`/`android`/`adaptive`, run `node .gemini/skills/impeccable/scripts/detect.mjs --json ` once** (the bundled detector over local files: no network, no npx; it reads HTML/CSS, so skip it for native projects). `scan.via` tells you what they are: `git-changes` (the markup/style files in your dirty tree, the most relevant set), `source-dir` (e.g. `src`, `app`), `html`, or `root`. Fold the hits into your picks: many quality / contrast hits → `audit` or `polish`; a specific slop family → the matching command (gradient text or eyebrows → `quieter` / `typeset`, flat or gray palette → `colorize`, and so on). It's a real, current signal that beats guessing. If detect errors or the tree is large and slow, skip it and recommend the user run `audit` themselves; never block the suggestion on it. diff --git a/.gemini/skills/impeccable/reference/shape.md b/.gemini/skills/impeccable/reference/shape.md index 40804f6e4..acbb26e5b 100644 --- a/.gemini/skills/impeccable/reference/shape.md +++ b/.gemini/skills/impeccable/reference/shape.md @@ -1,167 +1,61 @@ -Shape the UX and UI for a feature before any code is written. This command produces a **design brief**: a structured artifact that guides implementation through discovery, not guesswork. +# Shape -**Scope**: Design planning only. This command does NOT write code. It produces the thinking that makes code good. +Discover what should be made and how it should work, then return a confirmed design brief without code. -**Output**: A design brief that can be handed off to /impeccable craft, or directly to /impeccable for freeform implementation. When visual direction probes are used, the images are supporting artifacts, not the primary output. +**Product gate:** when context reports that PRODUCT.md is missing, load and complete [init.md](init.md) before Phase 1. Do not substitute shape questions for the product interview. Once PRODUCT.md exists, return here; product context does not replace task discovery. -## Philosophy +## Phase 1: Discovery interview -Most AI-generated UIs fail not because of bad code, but because of skipped thinking. They jump to "here's a card grid" without asking "what is the user trying to accomplish?" This command inverts that: understand deeply first, so implementation is precise. +Do not write code or choose visual direction yet. -## Phase 1: Discovery Interview +### Cadence -**Do NOT write any code or make any design decisions during this phase.** Your only job is to understand the feature deeply enough to make excellent design decisions later. +- Use the structured question tool when available; otherwise ask and stop. +- Ask two or three related questions per round, then wait. One round is the default; add a second only when the answers expose a material gap. +- Do not dump a questionnaire, repeat settled facts, or turn obvious facts into menus. Assert the likely reading and invite correction. +- A sparse prompt requires at least one answer round. A precise prompt may need only a compact confirmation. -**Unattended runs:** when no user can respond (a one-shot task, an automated run, or an explicit instruction not to ask questions), answer the interview questions yourself from the brief, record the answers as the design brief, and continue without pausing at any confirmation gate in this file. The thinking is still required; only the waiting is waived. +### Round 1: purpose, people, and outcome -This is a required interaction, not optional guidance. Ask these questions in conversation, adapting based on answers. Don't dump them all at once; have a natural dialogue. ask the user directly to clarify what you cannot infer. +Choose the two or three questions that most change the result: -### Interview cadence +- What is this surface or feature for, and what problem must it solve? +- Who specifically reaches it, in what situation and state of mind? +- What is the primary thing they must understand or do? What would success look like? +- What is uniquely true here that a neighboring product or generic template could not claim? -Discovery includes at least one user-answer round unless PRODUCT.md, DESIGN.md, or an already-confirmed brief directly answers the needed inputs. With a sparse prompt, do **not** synthesize a complete brief for confirmation on the first response. +### Round 2: material, behavior, and boundaries -- Use the harness's structured question tool when one exists. Otherwise, ask directly in chat and stop. -- Ask **2-3 questions per round**, then wait for answers. -- Treat PRODUCT.md and DESIGN.md as anchors; they reduce repeated questions but do **not** replace shape for craft. Shape is task-specific. -- One round is the default. Add a second only if the first answers leave material gaps. Don't run a second round just to feel thorough. -- Round 1 should clarify purpose, audience/context, content/scope, and (for brand) visual direction. -- Round 2, when needed, fills in whatever's still genuinely missing. +Run only for material unresolved decisions: -**Assert-then-confirm, not menu-with-escape.** When PRODUCT.md and the user's prompt make one option obvious, name it and ask the user to confirm or override. Don't enumerate "Restrained / Committed / Or something else?" as a real choice; "This reads as Restrained, confirm?" beats a four-option menu when the answer is already clear. +- What real content, evidence, data, and assets must the experience carry? What are realistic minimum, typical, and maximum ranges? +- Which states and transitions matter: first-run, empty, loading, error, success, permissions, overflow, or expert use? +- What is the intended fidelity, breadth, and interactivity: exploration, production-ready screen, full flow, or broader surface? +- What must remain untouched? What would make the result feel wrong even if it looked polished? +- Which platform, framework, performance, accessibility, localization, or delivery constraints are binding? -### Purpose & Context -- What is this feature for? What problem does it solve? -- Who specifically will use it? (Not "users"; be specific: role, context, frequency) -- What does success look like? How will you know this feature is working? -- What's the user's state of mind when they reach this feature? (Rushed? Exploring? Anxious? Focused?) +Never ask for CSS values or canned aesthetic lanes. New-work owns visual-world and concept choices. -### Content & Data -- What content or data does this feature display or collect? -- What are the realistic ranges? (Minimum, typical, maximum, e.g., 0 items, 5 items, 500 items) -- What are the edge cases? (Empty state, error state, first-time use, power user) -- Is any content dynamic? What changes and how often? -- What visual assets are real content here? Note required images, product shots, illustrations, maps, textures, diagrams, generated objects, or existing project assets. +## Phase 2: Resolve the design direction -### Design Direction +For new surfaces, brand expansion, or replacement, follow [new-work.md](new-work.md) through visual authority, any world workshop, and concept choice. Reuse discovery, then return before its contract, persistence, or implementation. Inside an established world, use its concept process only when composition or interaction remains materially open. -Force a visual decision on three fronts. Skip anything PRODUCT.md or DESIGN.md already answers; ask only what's missing. +## Phase 3: Write the brief -- **Color strategy for this surface.** Pick one: Restrained / Committed / Full palette / Drenched. Can override the project default if the surface earns it (e.g. a drenched hero inside an otherwise Restrained product). -- **Theme via scene sentence.** Write one sentence of physical context for this surface: who uses it, where, under what ambient light, in what mood. The sentence forces dark vs light. If it doesn't, add detail until it does. -- **Two or three named anchor references.** Specific products, brands, objects. Not adjectives like "modern" or "clean." +Write the smallest useful brief: -### Scope +1. **Job and audience:** who arrives, their context, need, and visitor mode. +2. **Outcome and proof:** primary task/action, success, real evidence, and product-specific truth. +3. **Selected direction:** visual authority, structural/interaction thesis, sequence, focal moment, and implementation consequence. +4. **Scope and boundaries:** fidelity, breadth, interactivity, named target, what remains untouched, and explicit anti-goals. +5. **States and ranges:** realistic content/data ranges and material states. +6. **Interaction and layout:** hierarchy, topology, responsiveness, affordances, feedback, and transitions; intent, not CSS. +7. **Constraints and open decisions:** platform, delivery, accessibility, localization, reusable components, and choices a builder must not invent. -Always ask. Sketch quality and shipped quality are different outputs; don't guess between them. +Use three to five bullets when the task is settled; use the full structure only for ambiguous, multi-screen, or standalone planning. Do not restate the conversation. -- **Fidelity.** Sketch / mid-fi / high-fi / production-ready? -- **Breadth.** One screen / a flow / a whole surface? -- **Interactivity.** Static visual / interactive prototype / shipped-quality component? -- **Time intent.** Quick exploration, or polish until it ships? +## Confirm and stop -Scope answers are task-scoped. Don't write them to PRODUCT.md or DESIGN.md; carry them through the design brief only. +Present the brief for explicit confirmation or one correction round, then stop: shape never writes code or a direction contract. -### Constraints -- Are there technical constraints? (Framework, performance budget, browser support) -- Are there content constraints? (Localization, dynamic text length, user-generated content) -- Mobile/responsive requirements? -- Accessibility requirements beyond WCAG AA? - -### Anti-Goals -- What should this NOT be? What would be a wrong direction? -- What's the biggest risk of getting this wrong? - -## Phase 1.5: Visual Direction Probe (Capability-Gated) - -After the discovery interview, generate a small set of visual direction probes **before** writing the final brief when all of these are true: - -- The work is **net-new** or directionally ambiguous enough that visual exploration will clarify the brief. -- The requested fidelity is **mid-fi, high-fi, or production-ready**. Skip for sketch-only planning. -- The current harness gives you native image generation (Codex's `image_gen`, an equivalent MCP tool, or similar). Don't ask the user to install APIs or tooling. - -When those conditions are met, this step is mandatory. If image generation isn't natively available, do not ask the user to install APIs or tooling. State in one line that the image step is skipped because the harness lacks native image generation, then proceed. The one-line announcement is required, not optional; it forces a conscious decision instead of letting the step quietly evaporate. - -Use probes to explore visual lanes, not to replace the brief. - -Do not skip probes because the final UI will be semantic, editable, code-native, responsive, or accessible. Those are implementation requirements, not reasons to avoid visual exploration. - -### What to generate - -Generate **2 to 4** distinct direction probes based on the discovery answers, especially: - -- Color strategy -- Theme scene sentence -- Named anchor references -- Scope and fidelity - -The probes should differ in primary visual direction (hierarchy, topology, density, typographic voice, or color strategy), not just palette tweaks. - -### How to use the probes - -- Treat them as **direction tests**, not final designs. -- Use them to pressure-test whether the brief is pointing at the right lane. -- Ask the user which direction feels closest, what feels off, and what should carry forward. -- If the probes reveal a mismatch, revise the brief inputs before finalizing the brief. - -### Important limits - -- Do **not** skip discovery because image generation is available. -- Do **not** treat generated imagery as final UX specification, final copy, or final accessibility behavior. -- Do **not** use this step for minor refinements of existing work. It's for shaping a new surface or clarifying a big directional choice. - -If image generation isn't natively available, announce the skip in one line and proceed to the design brief. - -## Phase 2: Design Brief - -After the interview and any required probes, present a brief and **end your response**. The user must confirm before any implementation runs. Do not present a brief and then continue to code in the same response, even if the brief feels obvious to you. The user's confirmation is the gate. - -**Choose the brief shape based on how clear the answers are:** - -- **Compact form (3-5 bullets)** when discovery was crisp and the original prompt + PRODUCT.md already pinned scope, content, and direction. State what you're building, the visual lane, and end with one or two specific questions or a clear "confirm or override?" prompt. This is the default for typical craft requests with a clear prompt. -- **Full structured form (sections below)** when the task is genuinely ambiguous, multi-screen, or when the user asked for shape as a standalone step. Use this when the discipline of structure earns its weight. - -Don't pad a clear brief into a long one to look thorough. A 70-line brief restating answers the user just gave is noise, not rigor. Equally, don't skip the confirmation pause to look efficient: the pause is the point. - -Present the brief, then **stop and wait for explicit confirmation**. You are not the judge of whether the user already approved. Even when the brief feels obviously right, ask once and wait. The pause is what separates shape from premature implementation. - -### Brief Structure - -**1. Feature Summary** (2-3 sentences) -What this is, who it's for, what it needs to accomplish. - -**2. Primary User Action** -The single most important thing a user should do or understand here. - -**3. Design Direction** -Color strategy (Restrained / Committed / Full palette / Drenched) + the theme scene sentence + 2–3 named anchor references. Reference PRODUCT.md and DESIGN.md where they already answer, and note any per-surface overrides. - -If you ran the Visual Direction Probe step, name which probe direction won and what changed in the brief because of it. - -**4. Scope** -Fidelity, breadth, interactivity, and time intent from the Scope section of the interview. Task-scoped; these don't persist beyond the brief. - -**5. Layout Strategy** -High-level spatial approach: what gets emphasis, what's secondary, how information flows. Describe the visual hierarchy and rhythm, not specific CSS. - -**6. Key States** -List every state the feature needs: default, empty, loading, error, success, edge cases. For each, note what the user needs to see and feel. - -**7. Interaction Model** -How users interact with this feature. What happens on click, hover, scroll? What feedback do they get? What's the flow from entry to completion? - -**8. Content Requirements** -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., 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. - ---- - -ask the user directly to clarify what you cannot infer. Ask for explicit confirmation of the brief before finishing. - -If the user disagrees with any part, revisit the relevant discovery questions. A shape run is incomplete until the user confirms direction. - -Once confirmed, the brief is complete. The user can now hand it to /impeccable, or use it to guide any other implementation approach. (If the user wants the full discovery-then-build flow in one step, they should use /impeccable craft instead, which runs this command internally.) +When no human or structured answer mechanism exists, mark assumptions plainly, return the brief, and stop. diff --git a/.gemini/skills/impeccable/reference/typeset.md b/.gemini/skills/impeccable/reference/typeset.md index 5f94430c5..9f238e952 100644 --- a/.gemini/skills/impeccable/reference/typeset.md +++ b/.gemini/skills/impeccable/reference/typeset.md @@ -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. --- -## Register +## Visitor mode -New identity work: run the font selection procedure in [new-work.md](new-work.md). Fluid `clamp()` scale, ≥1.25 ratio between steps. +- **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, register, **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 .gemini/skills/impeccable/scripts/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 `/impeccable polish` for the final pass. +When the hierarchy holds, hand off to `/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) * )` 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 - -The tactical selection procedure and the reflex-reject list live in [new-work.md](new-work.md) (loaded for new identity work). 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 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. diff --git a/.gemini/skills/impeccable/scripts/command-metadata.json b/.gemini/skills/impeccable/scripts/command-metadata.json index 83796d54b..dad8ef2e0 100644 --- a/.gemini/skills/impeccable/scripts/command-metadata.json +++ b/.gemini/skills/impeccable/scripts/command-metadata.json @@ -1,6 +1,6 @@ { "craft": { - "description": "Full confirmed-brief-then-build flow. Runs multi-round shape discovery first, resolves visual probe and north-star mock gates when available, then builds and visually iterates. Use when building a new feature end-to-end.", + "description": "Deprecated compatibility alias for an ordinary Impeccable new-work request. It adds no behavior; natural build and redesign requests use the same flow.", "argumentHint": "[feature description]" }, "init": { diff --git a/.gemini/skills/impeccable/scripts/concept-seed.mjs b/.gemini/skills/impeccable/scripts/concept-seed.mjs index 1f141a80f..25f20d9ab 100644 --- a/.gemini/skills/impeccable/scripts/concept-seed.mjs +++ b/.gemini/skills/impeccable/scripts/concept-seed.mjs @@ -1,6 +1,7 @@ #!/usr/bin/env node /** - * Concept-seed picker: the dice half of the new-work concept procedure. + * External concept seed: the dice half of new-work's coupled-direction and + * established-world surface procedures. * * The model derives a grounded shortlist of candidate FORMS from the * audience's world and the subject's cultural home (see @@ -12,10 +13,9 @@ * * This script rolls them from outside, the same trick that made the * palette seed work: - * - BUILD INDEX (2-5): which entry of the model's own resonance-ordered - * shortlist to build. The dice never choose an ungrounded ingredient; - * they only refuse the argmax rut. (Index 1 is excluded: that's the - * concept every run would ship anyway.) + * - PROMOTED INDEX: which entry of the model's own resonance-ordered + * shortlist must be taken seriously beside its favorites. The dice never + * choose an ungrounded ingredient; they only refuse the argmax rut. * - CHALLENGERS (3): outside forms from concept-ingredients.json, weighed * against the derived candidates on exactly two axes — audience * identification and product clarity. They win only when they beat the @@ -23,8 +23,8 @@ * material and win over thin categories, which is the intended shape. * * Usage: - * node scripts/concept-seed.mjs # roll at random - * node scripts/concept-seed.mjs --from # deterministic (hash key) + * node scripts/concept-seed.mjs --scope direction + * node scripts/concept-seed.mjs --scope surface --from * * Env vars: * IMPECCABLE_CONCEPT_SEED — same as --from; for reproducible eval runs. @@ -40,6 +40,12 @@ const pool = JSON.parse(readFileSync(join(here, 'concept-ingredients.json'), 'ut const args = process.argv.slice(2); const fromIdx = args.indexOf('--from'); +const scopeIdx = args.indexOf('--scope'); +const scope = scopeIdx !== -1 ? args[scopeIdx + 1] : 'surface'; +if (scope !== 'surface' && scope !== 'direction') { + process.stderr.write('concept-seed: --scope must be direction or surface\n'); + process.exit(1); +} // When no key is supplied, generate one and print it: a user reporting a // bad outcome can hand us the key and we replay the exact roll. const key = fromIdx !== -1 @@ -47,7 +53,7 @@ const key = fromIdx !== -1 : (process.env.IMPECCABLE_CONCEPT_SEED || crypto.randomBytes(4).toString('hex')); function hashUnit(k, salt) { - const h = crypto.createHash('sha256').update(`${salt}:${k}`).digest(); + const h = crypto.createHash('sha256').update(`${scope}:${salt}:${k}`).digest(); return h.readUInt32BE(0) / 0xffffffff; } const unit = (salt) => hashUnit(key, salt); @@ -67,22 +73,48 @@ for (let i = 0; picks.length < 3 && i < 60; i++) { } } -process.stdout.write(`CONCEPT SEED (key: ${key}; rerun with --from ${key} to reproduce this roll) -BUILD INDEX: ${buildIndex} - After ordering your derived candidates by resonance, build the page whose - form comes from candidate number ${buildIndex}, exactly as if it had ranked - first: full commitment. Your top-ranked candidate is what every run in this - category would ship; the assignment exists to refuse that rut, not to - punish it. -CHALLENGERS (weigh against your derived candidates on the same two axes, -audience identification and product clarity; a challenger wins only when -it beats the grounded list on both): +const promotedInstruction = scope === 'direction' + ? `After ordering the grounded coupled directions by product fit, promote + candidate ${buildIndex} into the serious shortlist. Each candidate must join + a durable visual system to a concrete expression for the requested first + surface; select or revise that pair as one decision. It must survive the + current task plus navigation, quiet and dense content, interaction and state, + and a substantially different future surface.` + : `After ordering the task's grounded structural candidates by resonance, + promote candidate ${buildIndex} into the serious shortlist. In an attended + run, present it beside the strongest materially different candidates and + let the user select or revise the surface concept. In a truly unattended + run, use it when it survives audience identification and product clarity.`; + +const challengerInstruction = scope === 'direction' + ? `A challenger enters only when it can supply both reusable identity grammar + and a strong first-surface structure, not a one-page costume or a style pasted + onto an unrelated layout. Weigh audience identification, product clarity, + current-surface force, and cross-surface system breadth.` + : `A challenger wins only when it beats the grounded list on both audience + identification and product clarity. It may change task topology or + interaction, but never the committed visual identity.`; + +const authorityInstruction = scope === 'direction' + ? `PRODUCT.md and explicit incumbent brand commitments constrain every coupled +direction. The seed never chooses exact colors, fonts, tokens, or a user +preference, and it never permits the world and first surface to be selected +independently.` + : `PRODUCT.md and DESIGN.md constrain every surface candidate's identity +vocabulary; they do not cancel task-level composition. The seed never +authorizes a new palette, type system, material world, or unfamiliar control +behavior.`; + +process.stdout.write(`${scope.toUpperCase()} CONCEPT SEED (key: ${key}; rerun with --scope ${scope} --from ${key} to reproduce this roll) +PROMOTED INDEX: ${buildIndex} + ${promotedInstruction} + The promotion exists to refuse the model's ranking rut, not to outrank the + user or the brief. +CHALLENGERS: 1. ${picks[0]} 2. ${picks[1]} 3. ${picks[2]} -If a challenger wins, it replaces the assigned candidate. If the surface is -an existing world whose incumbent carries a deliberate, ownable idea, the -incumbent IS the chosen candidate: intensify its lineage and ignore the -roll entirely. The same override applies when the user, PRODUCT.md, or -DESIGN.md pins a direction: pinned direction beats the roll, always. +${challengerInstruction} +${authorityInstruction} +A user- or brief-pinned decision beats the roll, always. `); diff --git a/.gemini/skills/impeccable/scripts/context-signals.mjs b/.gemini/skills/impeccable/scripts/context-signals.mjs index 2fc27bea7..d296320d7 100644 --- a/.gemini/skills/impeccable/scripts/context-signals.mjs +++ b/.gemini/skills/impeccable/scripts/context-signals.mjs @@ -11,7 +11,7 @@ * output is always valid JSON. * * Signals: - * - setup: PRODUCT.md / DESIGN.md presence, register, whether code exists + * - setup: PRODUCT.md / DESIGN.md presence and whether code exists * - critique: the latest cached critique score (.impeccable/critique) * - git: branch + files changed vs the default branch (a scope hint) * - devServer: whether a local dev server answers on a common port (gates live) @@ -21,7 +21,7 @@ import net from 'node:net'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { execFileSync } from 'node:child_process'; -import { loadContext, extractRegister, extractPlatform } from './context.mjs'; +import { loadContext, extractPlatform } from './context.mjs'; import { getCritiqueDir } from './lib/impeccable-paths.mjs'; /** Is there code here at all, or just context files / an empty repo? */ @@ -196,7 +196,6 @@ export async function gatherSignals(cwd = process.cwd()) { hasDesign: ctx.hasDesign, designPath: ctx.designPath, hasCode: hasCode(cwd), - register: extractRegister(ctx.product), platform: extractPlatform(ctx.product), }, critique: { latest: latestCritique(cwd) }, diff --git a/.gemini/skills/impeccable/scripts/context.mjs b/.gemini/skills/impeccable/scripts/context.mjs index 2ef2d4ce8..080e7ee3a 100644 --- a/.gemini/skills/impeccable/scripts/context.mjs +++ b/.gemini/skills/impeccable/scripts/context.mjs @@ -1,8 +1,10 @@ /** - * Context loader: prints PRODUCT.md (and DESIGN.md if present) as one - * markdown block on stdout, or prints a `NO_PRODUCT_MD:` message when no + * Context loader: prints PRODUCT.md, DESIGN.md when present, the matching + * persisted surface brief when one can be resolved, and native-platform + * guidance selected from PRODUCT.md. It prints a + * `NO_PRODUCT_MD:` message when no * PRODUCT.md is found anywhere. The skill keys off that message to branch: - * from-scratch build commands (init / teach / craft / shape) and clear + * from-scratch build requests (plus init / teach / shape) and clear * build/shape intent divert into the init flow, while scoped commands proceed * using the existing code as context. * @@ -23,10 +25,13 @@ import os from 'node:os'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { parseTargetOptions } from './lib/target-args.mjs'; -import { IMPECCABLE_COMMAND } from './lib/provider.mjs'; +import { IMPECCABLE_COMMAND, IMPECCABLE_PROVIDER_ID } from './lib/provider.mjs'; +import { renderLlmOnlySlopReview } from './lib/slop-review.mjs'; +import { resolveSurfaceBrief } from './lib/surface-briefs.mjs'; const PRODUCT_NAMES = ['PRODUCT.md', 'Product.md', 'product.md']; const DESIGN_NAMES = ['DESIGN.md', 'Design.md', 'design.md']; +const SKILL_REFERENCE_DIR = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', 'reference'); const FALLBACK_DIRS = ['.agents/context', 'docs']; const MONOREPO_MARKER_FILES = ['pnpm-workspace.yaml', 'turbo.json', 'nx.json', 'lerna.json']; const MONOREPO_FALLBACK_PROJECT_DIRS = ['apps', 'packages']; @@ -41,6 +46,8 @@ const WORKSPACE_DISCOVERY_IGNORED_DIRS = new Set([ '.turbo', '.cache', 'coverage', + 'vendor', + 'vendors', ]); const VISUAL_SOURCE_DIRS = ['src', 'app', 'pages', 'components', 'site', 'public', 'styles']; const STYLE_EXTENSIONS = new Set(['.css', '.scss', '.sass', '.less', '.styl']); @@ -73,6 +80,12 @@ export function loadContext(cwd = process.cwd(), options = {}) { const designPath = resolved.designPath; const product = productPath ? safeRead(productPath) : null; const design = designPath ? safeRead(designPath) : null; + const platform = extractPlatform(product); + const surfaceResolution = resolveSurfaceBrief( + resolved.projectRoot, + hasTargetOption(options) ? options.targetPath : null, + ); + const surfaceBrief = surfaceResolution.brief; return { hasProduct: !!product, product, @@ -83,7 +96,18 @@ export function loadContext(cwd = process.cwd(), options = {}) { contextDir: resolved.contextDir, productContextDir: productPath ? path.dirname(productPath) : null, designContextDir: designPath ? path.dirname(designPath) : null, + hasSurfaceBrief: !!surfaceBrief, + surfaceBrief: surfaceBrief?.text ?? null, + surfaceBriefPath: surfaceBrief?.path ? path.relative(absCwd, surfaceBrief.path) : null, + surfaceBriefReason: surfaceResolution.reason, + surfaceBriefCandidates: surfaceResolution.candidates.map((brief) => ({ + slug: brief.slug, + path: path.relative(absCwd, brief.path), + primaryTarget: brief.primaryTarget, + relatedTargets: brief.relatedTargets, + })), hasVisualImplementation: hasVisualImplementation(resolved.projectRoot), + platform, projectRoot: resolved.projectRoot, repoRoot: resolved.repoRoot, isMonorepo: resolved.isMonorepo, @@ -696,6 +720,19 @@ function safeRead(p) { } } +function loadNativePlatformReferences(platform) { + const names = platform === 'adaptive' + ? ['ios', 'android'] + : platform === 'ios' || platform === 'android' + ? [platform] + : []; + return names.flatMap((name) => { + const filePath = path.join(SKILL_REFERENCE_DIR, `${name}.md`); + const content = safeRead(filePath); + return content ? [{ name, filePath, content }] : []; + }); +} + /** * Best-effort evidence that the project already has an incumbent visual * implementation. DESIGN.md is documentation, not the only source of design @@ -719,9 +756,11 @@ export function hasVisualImplementation(projectRoot) { let styledComponents = 0; const inspectFile = (filePath) => { - if (scannedFiles++ >= VISUAL_SCAN_FILE_LIMIT) return false; const ext = path.extname(filePath).toLowerCase(); if (!STYLE_EXTENSIONS.has(ext) && !UI_EXTENSIONS.has(ext)) return false; + const base = path.basename(filePath).toLowerCase(); + if (/\.min\.[a-z]+$/.test(base)) return false; + if (scannedFiles++ >= VISUAL_SCAN_FILE_LIMIT) return false; let body; try { body = fs.readFileSync(filePath, 'utf-8').slice(0, 64 * 1024); @@ -729,18 +768,28 @@ export function hasVisualImplementation(projectRoot) { return false; } - const base = path.basename(filePath).toLowerCase(); + const evidence = body + .replace(/\/\*[\s\S]*?\*\//g, '') + .replace(//g, '') + .replace(/^\s*\/\/.*$/gm, ''); if (STYLE_EXTENSIONS.has(ext)) { - const customProperties = body.match(/--[a-z0-9_-]+\s*:/gi)?.length ?? 0; - const visualDeclarations = body.match(/\b(?:color|background(?:-color)?|border(?:-color)?|font-family)\s*:/gi)?.length ?? 0; - if (/\b(?:tokens?|theme|design-system)\b/.test(base) && body.trim().length > 80) return true; + const customProperties = evidence.match(/--[a-z0-9_-]+\s*:/gi)?.length ?? 0; + const visualDeclarations = evidence.match(/\b(?:color|background(?:-color)?|border(?:-color)?|font-family)\s*:/gi)?.length ?? 0; + if (/\b(?:tokens?|theme|design-system)\b/.test(base) && evidence.trim().length > 80) return true; if (customProperties >= 3 || visualDeclarations >= 5) return true; } - if ((ext === '.html' || ext === '.htm') && body.length > 600 && /]+stylesheet/i.test(body)) { + if ((ext === '.html' || ext === '.htm') && evidence.length > 600 && /]+stylesheet/i.test(evidence)) { return true; } - if (!['.html', '.htm'].includes(ext) && body.length > 300 && /class(?:Name)?\s*=|style\s*=|styled\(|css`/i.test(body)) { + if (!['.html', '.htm'].includes(ext) && evidence.length > 300) { + const embeddedCustomProperties = evidence.match(/--[a-z0-9_-]+\s*:/gi)?.length ?? 0; + const embeddedVisualDeclarations = evidence.match(/\b(?:color|background(?:-color)?|border(?:-color)?|font-family)\s*:/gi)?.length ?? 0; + const classTokens = [...evidence.matchAll(/class(?:Name)?\s*=\s*["'`]([^"'`]+)["'`]/gi)] + .reduce((count, match) => count + match[1].trim().split(/\s+/).length, 0); + if ((embeddedCustomProperties >= 3 && embeddedVisualDeclarations >= 3) || embeddedVisualDeclarations >= 5 || classTokens >= 12) return true; + } + if (!['.html', '.htm'].includes(ext) && evidence.length > 300 && /class(?:Name)?\s*=|style\s*=|styled\(|css`/i.test(evidence)) { styledComponents += 1; if (styledComponents >= 3) return true; } @@ -781,9 +830,9 @@ function escapeRegExp(value) { /** * Read the first non-empty line under a bare `## ` section of - * PRODUCT.md (e.g. `## Register`, `## Platform`). Returns null when the + * PRODUCT.md (for example `## Platform`). Returns null when the * section is absent. The heading match is exact (`\s*$`) so near-miss - * headings like `## Register guidelines` don't shadow the real field. + * near-miss headings don't shadow the real field. */ export function extractSectionValue(product, heading) { if (!product) return null; @@ -802,16 +851,6 @@ export function extractSectionValue(product, heading) { return null; } -/** - * Pull the register (`brand` or `product`) out of PRODUCT.md by looking - * for a `## Register` section and reading the first non-empty line that - * follows it. Returns null when the file is legacy / register-less. - */ -export function extractRegister(product) { - const word = (extractSectionValue(product, 'Register') || '').toLowerCase(); - return word === 'brand' || word === 'product' ? word : null; -} - /** * Pull the platform (`web`, `ios`, `android`, or `adaptive`) out of PRODUCT.md * by looking for a `## Platform` section and reading the first non-empty line @@ -985,30 +1024,35 @@ async function cli() { const parts = ctx.hasVisualImplementation ? [ 'NO_PRODUCT_MD: This project has no PRODUCT.md yet, but it does have an incumbent visual implementation. ' + - 'For `init` or `teach`, load reference/init.md and create PRODUCT.md. For `craft`, `shape`, or other work ' + - 'against the existing product, read its CSS, tokens, components, and assets first and proceed without blocking; ' + - `offer \`${IMPECCABLE_COMMAND} init\` as a follow-up. Only treat the work as greenfield when the user explicitly ` + - 'asks to discard or replace the current identity.', - 'EXISTING_VISUAL_SYSTEM: Code and assets are the incumbent design authority. Missing DESIGN.md is a documentation ' + - 'gap, not permission to invent a replacement identity. Preserve and extend the implementation unless the user ' + - 'explicitly requests a rebrand, or inspection proves the detected files contain no coherent visual decisions.', + 'For `init`, `teach`, `shape`, or any request to create a new surface or replacement visual world, load reference/init.md and create PRODUCT.md with the user first. ' + + 'After init writes PRODUCT.md, reference/new-work.md preserves and documents the incumbent system for an ' + + 'extension or replaces it with the user for a redesign/rebrand. Other ' + + 'narrow refinement commands may read the CSS, tokens, components, and assets and proceed without blocking, then ' + + `offer \`${IMPECCABLE_COMMAND} init\` as a follow-up.`, + 'BUILD_INIT_REQUIRED: Before shape or any new-surface/redesign flow, init must capture PRODUCT.md with the human or structured ' + + 'simulated user. Init writes product truth only; reference/new-work.md owns every visual decision.', + 'SCOPED_EXISTING_ALLOWED: Narrow refinement commands may use the incumbent implementation as authority without ' + + 'blocking on context setup; they must preserve it and offer init afterward.', + 'EXISTING_VISUAL_SYSTEM: For refinement or extension, code and assets are incumbent design authority and missing ' + + 'DESIGN.md is a documentation gap. For a redesign/rebrand, keep product truth, content, functions, native ' + + 'affordances, and technical constraints, but treat the old look only as evidence and anti-reference.', ] : [ 'NO_PRODUCT_MD: This project has no PRODUCT.md yet. ' + - 'For `init`, `teach`, `craft`, `shape`, ' + + 'For `init`, `teach`, `shape`, ' + 'or wording that clearly maps to a from-scratch build/shape flow, load ' + - 'reference/init.md and write PRODUCT.md first, unless no user can ' + - 'respond (a one-shot or automated run, or the user said not to ask): ' + - 'then write a one-paragraph understanding of the product, audience, ' + - 'and the page\'s job from the brief, and continue. For any other ' + + 'reference/init.md, complete its human or structured simulated-user interview, and write PRODUCT.md before ' + + 'designing. If no answer mechanism truly exists, init may infer only from the explicit brief and must label its ' + + 'assumptions. It never writes DESIGN.md. For any other ' + '(scoped) command against existing code, proceed using the code as ' + `context and offer \`${IMPECCABLE_COMMAND} init\` as a suggestion (do not block).`, - 'NEW_WORK: No committed design context was found. If this task produces ' + - 'new design (a build from scratch, or a redesign that discards the ' + - 'current look), you MUST read reference/new-work.md before making any ' + - 'design decision. Scoped fixes to existing code do not need it.', + 'PRODUCT_INIT_REQUIRED: No product context or visual authority was found. New builds and redesigns ' + + 'must finish reference/init.md for PRODUCT.md, then reference/new-work.md establishes the world and surface. Scoped ' + + 'fixes to existing code do not need the new-surface flow.', ]; + appendSurfaceBriefContext(parts, ctx); parts.push(buildResolvedContextDirective(ctx, cliOptions, { targetExists })); + appendHookFallback(parts, ctx); if (shouldWarnMissingTarget(ctx, targetProvided, targetExists)) { parts.push(buildMissingTargetDirective()); } @@ -1020,37 +1064,29 @@ async function cli() { if (ctx.hasDesign) { parts.push(`# DESIGN.md\n\n${ctx.design.trim()}`); } + appendSurfaceBriefContext(parts, ctx); parts.push(buildResolvedContextDirective(ctx, cliOptions, { targetExists })); + appendHookFallback(parts, ctx); if (shouldWarnMissingTarget(ctx, targetProvided, targetExists)) { parts.push(buildMissingTargetDirective()); } - const register = extractRegister(ctx.product); - // The register field survives as a family hint (brand = Persuade/Experience, - // product = Operate/Read); SKILL.md's mode section carries the essentials - // inline, so no register-file read is mandated here. What IS mandated: - // the new-work playbook when no committed design system exists yet. if (!ctx.hasDesign) { parts.push(ctx.hasVisualImplementation - ? 'EXISTING_VISUAL_SYSTEM: PRODUCT.md exists and DESIGN.md is missing, but code contains incumbent visual decisions. ' + - 'Treat CSS, tokens, components, and assets as design authority. Do not route to new identity work merely because the ' + - 'document is absent; preserve and extend the implementation unless the user explicitly asks to discard it.' - : 'NEW_WORK: PRODUCT.md exists but no DESIGN.md or incumbent visual implementation was found. If this task produces ' + - 'new design or a redesign that discards the current look, you MUST read reference/new-work.md before making any ' + - 'design decision. Scoped fixes to existing code do not need it.'); + ? 'INCUMBENT_WORLD_UNDOCUMENTED: PRODUCT.md exists and DESIGN.md is missing, but code contains incumbent visual decisions. ' + + 'For shape or a new-surface/redesign request, load reference/new-work.md: an extension documents and preserves the code-defined world; ' + + 'a redesign replaces it with the user and uses the old look only as evidence and anti-reference. Narrow refinement ' + + 'commands may proceed using the implementation directly.' + : 'WORLD_DISCOVERY_REQUIRED: PRODUCT.md exists but no DESIGN.md or incumbent visual implementation was found. ' + + 'For a new build or redesign, load reference/new-work.md and establish the visual world with the human or structured ' + + 'simulated user before developing the task concept. Scoped fixes to existing code do not need this flow.'); } - if (register) { - parts.push(`REGISTER: \`${register}\` (family hint: brand = Persuade/Experience surfaces, product = Operate/Read). Derive the visitor's mode per SKILL.md.`); - } - const platform = extractPlatform(ctx.product); - const nativeRefs = - platform === 'adaptive' ? ['ios', 'android'] : platform === 'ios' || platform === 'android' ? [platform] : []; - if (nativeRefs.length) { - const refList = nativeRefs.map(p => `\`reference/${p}.md\``).join(' and '); - const label = platform === 'adaptive' ? '`adaptive` (both iOS and Android)' : `\`${platform}\``; + const platformReferences = loadNativePlatformReferences(ctx.platform); + for (const reference of platformReferences) { parts.push( - `NEXT STEP: This project targets ${label}. Also read ${refList} for native conventions, in addition to SKILL.md's mode guidance.`, + `# NATIVE PLATFORM REFERENCE: ${reference.name.toUpperCase()} (reference/${reference.name}.md)\n\n${reference.content.trim()}`, ); - } else if (!platform) { + } + if (!ctx.platform) { // A `## Platform` section that names something we don't recognize (a // toolchain like `flutter`, a typo) would otherwise silently fall back to // web — the wrong default exactly when the user tried to say "native". @@ -1078,6 +1114,72 @@ function pathExistsForTarget(cwd, targetPath) { return fs.existsSync(abs); } +const HOOK_MANIFESTS_BY_PROVIDER = Object.freeze({ + 'claude-code': ['.claude/settings.local.json', '.claude/settings.json'], + codex: ['.codex/hooks.json'], + agents: ['.codex/hooks.json'], + cursor: ['.cursor/hooks.json'], + github: ['.github/hooks/impeccable.json'], +}); + +function truthyEnv(value) { + return typeof value === 'string' && /^(1|true|yes|on)$/i.test(value.trim()); +} + +function valueHasHookMarker(value) { + if (typeof value === 'string') { + return value.includes('skills/impeccable/scripts/hook.mjs') + || value.includes('skills/impeccable/scripts/hook-before-edit.mjs'); + } + if (Array.isArray(value)) return value.some(valueHasHookMarker); + if (value && typeof value === 'object') return Object.values(value).some(valueHasHookMarker); + return false; +} + +function hookEnabledAt(root) { + if (truthyEnv(process.env.IMPECCABLE_HOOK_DISABLED)) return false; + let enabled = true; + for (const name of ['.impeccable/config.json', '.impeccable/config.local.json']) { + const raw = readJson(path.join(root, name)); + if (raw?.hook && Object.prototype.hasOwnProperty.call(raw.hook, 'enabled')) { + enabled = raw.hook.enabled !== false; + } + } + return enabled; +} + +const STOP_REVIEW_PROVIDERS = new Set(['claude-code', 'codex', 'agents']); + +function automaticHookMode(ctx) { + if (ctx.platform === 'ios' || ctx.platform === 'android' || ctx.platform === 'adaptive') { + return 'none'; + } + const activeRoot = path.resolve(ctx.projectRoot || process.cwd()); + if (!hookEnabledAt(activeRoot)) return 'none'; + const manifests = HOOK_MANIFESTS_BY_PROVIDER[IMPECCABLE_PROVIDER_ID] || []; + const roots = [...new Set([process.cwd(), ctx.projectRoot, ctx.repoRoot].filter(Boolean).map((root) => path.resolve(root)))]; + for (const root of roots) { + for (const rel of manifests) { + const raw = readJson(path.join(root, rel)); + if (raw?.hooks && valueHasHookMarker(raw.hooks)) { + return STOP_REVIEW_PROVIDERS.has(IMPECCABLE_PROVIDER_ID) ? 'stop' : 'per-edit'; + } + } + } + return 'none'; +} + +function appendHookFallback(parts, ctx) { + const hookMode = automaticHookMode(ctx); + if (hookMode === 'stop') return; + const native = ctx.platform === 'ios' || ctx.platform === 'android' || ctx.platform === 'adaptive'; + parts.push(renderLlmOnlySlopReview({ + automaticDetector: hookMode === 'per-edit', + manualDetector: hookMode === 'none' && !native, + scriptsPath: path.dirname(fileURLToPath(import.meta.url)), + })); +} + function buildResolvedContextDirective(ctx, options, { targetExists = null } = {}) { const targetPath = hasTargetOption(options) ? options.targetPath : null; return `RESOLVED_CONTEXT:\n${JSON.stringify({ @@ -1087,10 +1189,29 @@ function buildResolvedContextDirective(ctx, options, { targetExists = null } = { repoRoot: ctx.repoRoot, productPath: ctx.productPath, designPath: ctx.designPath, + surfaceBriefPath: ctx.surfaceBriefPath, + surfaceBriefReason: ctx.surfaceBriefReason, + surfaceBriefCandidates: ctx.surfaceBriefCandidates, hasVisualImplementation: ctx.hasVisualImplementation, + platform: ctx.platform, }, null, 2)}`; } +function appendSurfaceBriefContext(parts, ctx) { + if (ctx.hasSurfaceBrief && ctx.surfaceBrief) { + parts.push(`# SURFACE BRIEF (${ctx.surfaceBriefPath})\n\n${ctx.surfaceBrief.trim()}`); + return; + } + if (!ctx.surfaceBriefCandidates?.length) return; + const helper = path.join(path.dirname(fileURLToPath(import.meta.url)), 'surface-brief.mjs'); + parts.push( + 'SURFACE_CONTEXT_AVAILABLE: Persisted surface briefs exist, but none was selected unambiguously for this invocation. ' + + 'Resolve the requested surface to its concrete primary or related source path, then run ' + + `\`node ${helper} read \` once before changing that surface. Candidates:\n` + + JSON.stringify(ctx.surfaceBriefCandidates, null, 2), + ); +} + function shouldWarnMissingTarget(ctx, targetProvided, targetExists = null) { if (ctx.isMonorepo && targetProvided && targetExists === false) return true; return !!( diff --git a/.gemini/skills/impeccable/scripts/critique-storage.mjs b/.gemini/skills/impeccable/scripts/critique-storage.mjs index 6b4d225cf..38674285a 100644 --- a/.gemini/skills/impeccable/scripts/critique-storage.mjs +++ b/.gemini/skills/impeccable/scripts/critique-storage.mjs @@ -29,8 +29,9 @@ import fs from 'node:fs'; import path from 'node:path'; import { fileURLToPath, pathToFileURL } from 'node:url'; import { getCritiqueDir } from './lib/impeccable-paths.mjs'; +import { slugFromTarget } from './lib/target-slug.mjs'; -const SLUG_MAX = 50; +export { slugFromTarget } from './lib/target-slug.mjs'; /** * Mechanically derive a slug from a resolved target. Returns null if the @@ -40,46 +41,6 @@ const SLUG_MAX = 50; * concrete artifact before calling this — we never slug a natural-language * phrase. */ -export function slugFromTarget(resolved, { cwd = process.cwd() } = {}) { - if (!resolved || typeof resolved !== 'string') return null; - const trimmed = resolved.trim(); - if (!trimmed) return null; - - // URL - if (/^https?:\/\//i.test(trimmed)) { - let url; - try { url = new URL(trimmed); } catch { return null; } - const hostPath = `${url.hostname}${url.pathname}`; - return kebab(hostPath); - } - - // File path. Make it project-relative so two devs critiquing the same - // checkout get the same slug regardless of where their repo is cloned. - const abs = path.isAbsolute(trimmed) ? trimmed : path.resolve(cwd, trimmed); - let rel = path.relative(cwd, abs); - // If the target is outside cwd, fall back to the basename so we still - // produce a stable slug (vs the absolute path, which would include - // home dirs / usernames). - if (rel.startsWith('..') || path.isAbsolute(rel)) { - rel = path.basename(abs); - } - if (!rel || rel === '.' || rel === '') return null; - return kebab(rel); -} - -function kebab(s) { - const slug = s - .toLowerCase() - .replace(/[/\\.]+/g, '-') - .replace(/[^a-z0-9-]+/g, '-') - .replace(/-+/g, '-') - .replace(/^-|-$/g, ''); - if (!slug) return null; - // Cap from the tail — the tail (filename) is more identifying than the - // top-level directory. - return slug.length <= SLUG_MAX ? slug : slug.slice(slug.length - SLUG_MAX).replace(/^-/, ''); -} - /** * Filename-safe UTC ISO timestamp: hyphens for separators, trailing Z. * Plain colons aren't allowed on Windows filesystems. diff --git a/.gemini/skills/impeccable/scripts/detector/detect-antipatterns-browser.js b/.gemini/skills/impeccable/scripts/detector/detect-antipatterns-browser.js index 033573e36..76bf4ddeb 100644 --- a/.gemini/skills/impeccable/scripts/detector/detect-antipatterns-browser.js +++ b/.gemini/skills/impeccable/scripts/detector/detect-antipatterns-browser.js @@ -312,17 +312,6 @@ const ANTIPATTERNS = [ skillSection: 'Layout & Space', skillGuideline: 'numbered section markers', }, - { - id: 'numbered-section-markers', - category: 'slop', - scopes: ['layout'], - severity: 'advisory', - name: 'Numbered section markers (01 / 02 / 03)', - description: - 'Numbered display markers as section labels (01, 02, 03) are the AI editorial scaffold one tier deeper than tracked eyebrow chips. If you find yourself reaching for them, choose a different section cadence.', - skillSection: 'Layout & Space', - skillGuideline: 'numbered section markers', - }, { id: 'em-dash-overuse', category: 'slop', diff --git a/.gemini/skills/impeccable/scripts/detector/engines/regex/detect-text.mjs b/.gemini/skills/impeccable/scripts/detector/engines/regex/detect-text.mjs index 81c30ab99..f641d5917 100644 --- a/.gemini/skills/impeccable/scripts/detector/engines/regex/detect-text.mjs +++ b/.gemini/skills/impeccable/scripts/detector/engines/regex/detect-text.mjs @@ -280,22 +280,6 @@ const REGEX_ANALYZERS = [ if (count === 0) return []; return [finding('marketing-buzzword', filePath, `${count} buzzword phrase${count === 1 ? '' : 's'}: "${firstSample}"`)]; }, - // Numbered section markers (01 / 02 / 03 ...) - (content, filePath) => { - const text = stripHtmlToText(content); - const re = /\b(0[1-9]|1[0-2])\b/g; - const seen = new Set(); - let m; - while ((m = re.exec(text)) !== null) seen.add(m[1]); - if (seen.size < 3) return []; - const sorted = [...seen].sort(); - let sequential = 0; - for (let i = 1; i < sorted.length; i++) { - if (parseInt(sorted[i], 10) === parseInt(sorted[i - 1], 10) + 1) sequential++; - } - if (sequential < 2) return []; - return [finding('numbered-section-markers', filePath, `Sequence: ${sorted.slice(0, 6).join(', ')}`)]; - }, // Aphoristic cadence: manufactured-contrast + short-rebuttal (content, filePath) => { const text = stripHtmlToText(content); @@ -430,21 +414,20 @@ function runRegexMatchers(lines, filePath, lineOffset = 0, blockContext = null, } /** Page-level analyzers that scan rendered text content (em-dash use, - * buzzword phrases, numbered section markers, aphoristic cadence). + * buzzword phrases, aphoristic cadence). * These are detector-agnostic — they work on any HTML/text source * and don't need a parsed DOM. Exported so detectHtml can call them * for `.html` files (which otherwise skip the regex engine). */ const TEXT_CONTENT_ANALYZER_IDS = [ 'em-dash-overuse', 'marketing-buzzword', - 'numbered-section-markers', 'aphoristic-cadence', ]; function runTextContentAnalyzers(content, filePath, options = {}) { const profile = options?.profile; if (!shouldRunPageAnalyzers(content, filePath)) return []; - // The 4 text-content analyzers are at indices 3-6 in REGEX_ANALYZERS. + // The 3 text-content analyzers are at indices 3-5 in REGEX_ANALYZERS. const findings = []; for (let i = 0; i < TEXT_CONTENT_ANALYZER_IDS.length; i++) { const analyzer = REGEX_ANALYZERS[3 + i]; @@ -535,7 +518,6 @@ function detectText(content, filePath, options = {}) { 'monotonous-spacing', 'em-dash-overuse', 'marketing-buzzword', - 'numbered-section-markers', 'aphoristic-cadence', 'dark-glow', ]; diff --git a/.gemini/skills/impeccable/scripts/detector/registry/antipatterns.mjs b/.gemini/skills/impeccable/scripts/detector/registry/antipatterns.mjs index b85f7e62f..84c69b0b8 100644 --- a/.gemini/skills/impeccable/scripts/detector/registry/antipatterns.mjs +++ b/.gemini/skills/impeccable/scripts/detector/registry/antipatterns.mjs @@ -210,17 +210,6 @@ const ANTIPATTERNS = [ skillSection: 'Layout & Space', skillGuideline: 'numbered section markers', }, - { - id: 'numbered-section-markers', - category: 'slop', - scopes: ['layout'], - severity: 'advisory', - name: 'Numbered section markers (01 / 02 / 03)', - description: - 'Numbered display markers as section labels (01, 02, 03) are the AI editorial scaffold one tier deeper than tracked eyebrow chips. If you find yourself reaching for them, choose a different section cadence.', - skillSection: 'Layout & Space', - skillGuideline: 'numbered section markers', - }, { id: 'em-dash-overuse', category: 'slop', diff --git a/.gemini/skills/impeccable/scripts/detector/rules/checks.mjs b/.gemini/skills/impeccable/scripts/detector/rules/checks.mjs index 3fdd6b2dd..9bace11b3 100644 --- a/.gemini/skills/impeccable/scripts/detector/rules/checks.mjs +++ b/.gemini/skills/impeccable/scripts/detector/rules/checks.mjs @@ -352,27 +352,57 @@ function isAccentColor(cssColor) { return false; } +function resolveHeroHeadingSizePx(value) { + const input = String(value || '').trim().toLowerCase(); + if (!input) return 0; + + const simpleLengthPx = (token) => { + const match = /^(-?\d*\.?\d+)\s*(px|rem|em|%)?$/.exec(String(token || '').trim()); + if (!match) return null; + const amount = Number(match[1]); + if (!Number.isFinite(amount)) return null; + if (match[2] === 'rem' || match[2] === 'em') return amount * 16; + if (match[2] === '%') return amount * 0.16; + return amount; + }; + + const direct = simpleLengthPx(input); + if (direct !== null) return direct; + + // Static CSS engines cannot resolve viewport units, but clamp's min/max + // bounds still tell us whether the heading can ever reach hero scale. + const clamp = /^clamp\((.*)\)$/.exec(input); + if (clamp) { + const parts = clamp[1].split(','); + if (parts.length === 3) { + const bounds = [simpleLengthPx(parts[0]), simpleLengthPx(parts[2])] + .filter((candidate) => candidate !== null); + if (bounds.length > 0) return Math.max(...bounds); + } + } + + return 0; +} + // Sibling-relationship rule. Anchor on a hero-scale h1, look at the // previousElementSibling, and gate on EITHER the classic tracked- // uppercase eyebrow OR the modern accent-colored bold eyebrow. function checkHeroEyebrow(opts) { const { headingTag, headingText, headingFontSize, + headingInApplicationContext, siblingTag, siblingText, siblingTextTransform, siblingFontSize, siblingLetterSpacing, siblingFontWeight, siblingColor, siblingHasAccentDashPseudo, } = opts; if (headingTag !== 'h1') return []; - // We previously gated on headingFontSize >= 48 to anchor "hero scale". - // But modern hero h1s use clamp() / vw / var(--text-*), none of which - // jsdom can resolve — the computed value comes back as "2em" or - // "var(--text-9xl)" and parseFloat returns 2 or NaN. The gate fails - // on virtually every Tailwind v4 / framework build. The other gates - // (sibling text 2-60 chars, font-size ≤ 14px, accent-bold OR - // tracked-caps) are tight enough to avoid false positives on non- - // hero h1s — a tiny tan label directly above any h1 is the - // antipattern regardless of how big the h1 ends up. + // This is specifically a marketing-hero cliché, not a ban on compact + // context labels in product UI (for example, a station name inside a tab + // panel). Browser-computed sizes are reliable; the static adapter also + // resolves ordinary px/rem/em and clamp() bounds before reaching here. + if (headingInApplicationContext) return []; + if (!(headingFontSize >= 48)) return []; if (!siblingTag) return []; // An h2 above an h1 is a different anti-pattern (heading hierarchy / dual // headings) — never an eyebrow. @@ -1938,6 +1968,7 @@ function checkElementHeroEyebrowDOM(el) { headingTag: tag, headingText: el.textContent || '', headingFontSize: parseFloat(headStyle.fontSize) || 0, + headingInApplicationContext: !!el.closest('[role="tabpanel"], [role="dialog"], [role="application"], dialog'), siblingTag: sibling.tagName.toLowerCase(), siblingText: sibling.textContent || '', siblingTextTransform: sibStyle.textTransform || '', @@ -3367,7 +3398,8 @@ function checkElementHeroEyebrow(el, style, tag, window, customPropMap) { return checkHeroEyebrow({ headingTag: tag, headingText: el.textContent || '', - headingFontSize: parseFloat(headingFontSizeRaw) || 0, + headingFontSize: resolveHeroHeadingSizePx(headingFontSizeRaw), + headingInApplicationContext: !!el.closest?.('[role="tabpanel"], [role="dialog"], [role="application"], dialog'), siblingTag: sibling.tagName.toLowerCase(), siblingText: sibling.textContent || '', siblingTextTransform: sibStyle.textTransform || '', diff --git a/.gemini/skills/impeccable/scripts/hook-lib.mjs b/.gemini/skills/impeccable/scripts/hook-lib.mjs index e61ea692b..50aa2f8fb 100644 --- a/.gemini/skills/impeccable/scripts/hook-lib.mjs +++ b/.gemini/skills/impeccable/scripts/hook-lib.mjs @@ -46,6 +46,7 @@ import path from 'node:path'; import { pathToFileURL, fileURLToPath } from 'node:url'; import { extractPlatform, loadContext } from './context.mjs'; import { IMPECCABLE_COMMAND } from './lib/provider.mjs'; +import { renderStopSlopReview } from './lib/slop-review.mjs'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); @@ -1868,22 +1869,42 @@ export const CONTRACT_MAX_CHARS = 1800; // Cap contract sections per Stop emission so many touched artifacts cannot // stack an unbounded message. export const CONTRACT_AUDIT_MAX_FILES = 3; +export const CONTRACT_EXTS = new Set(['.html', '.htm', '.astro', '.svelte', '.vue', '.jsx', '.tsx']); +export const CONTRACT_REQUIRED_FIELDS = ['UNIQUE', 'NOT-TEMPLATE', 'OWN-WORLD', 'STORY', 'FIRST VIEWPORT', 'FORM']; /** - * Extract the artifact's own direction-contract comment: the first HTML - * comment in the head of the file, when its opening chars identify it as a - * contract/concept block. Returns the trimmed, length-capped body, or null - * when the file carries none (no comment, unclosed comment, marker missing, - * or the comment starts past the head window). + * Extract the artifact's own direction-contract comment from the head of an + * HTML or component file. Supports HTML-family comments and JSX block + * comments so the contract works in the Astro/Svelte/Vue/React scaffolds the + * skill actually builds. Returns the trimmed, length-capped body, or null + * when the file carries no valid contract block. */ export function extractDirectionContract(content) { if (typeof content !== 'string' || !content) return null; const head = content.slice(0, CONTRACT_HEAD_CHARS); - const m = //.exec(head); - if (!m) return null; - const body = m[1].trim(); - if (!body || !/contract|concept/i.test(body.slice(0, CONTRACT_MARKER_CHARS))) return null; - return body.slice(0, CONTRACT_MAX_CHARS); + const candidates = []; + for (const pattern of [//g, /\{\/\*([\s\S]*?)\*\/\}/g]) { + for (const match of head.matchAll(pattern)) { + const index = match.index ?? 0; + const linePrefix = head.slice(head.lastIndexOf('\n', index - 1) + 1, index).trim(); + if (linePrefix.startsWith('//')) continue; + candidates.push({ index, body: match[1].trim() }); + } + } + candidates.sort((a, b) => a.index - b.index); + for (const candidate of candidates) { + if (!candidate.body || !/direction\s+contract|concept\s+contract/i.test(candidate.body.slice(0, CONTRACT_MARKER_CHARS))) continue; + return candidate.body.slice(0, CONTRACT_MAX_CHARS); + } + return null; +} + +export function missingDirectionContractFields(contract) { + const body = typeof contract === 'string' ? contract : ''; + return CONTRACT_REQUIRED_FIELDS.filter((field) => { + const label = field.replace(/[.*+?^${}()|[\]\\]/g, '\\$&').replace(/\s+/g, '\\s+'); + return !new RegExp(`\\b${label}\\s*:`, 'i').test(body); + }); } /** @@ -1899,7 +1920,11 @@ export function renderContractAudit(entries, opts = {}) { const shown = entries.slice(0, CONTRACT_AUDIT_MAX_FILES); const blocks = shown.map(({ filePath, contract }) => { const display = relativize(filePath, cwd); - return `${display} opens with this direction contract, written when the direction was decided:\n\n${contract}`; + const missing = missingDirectionContractFields(contract); + const integrity = missing.length > 0 + ? `\n\nContract integrity defect: missing ${missing.join(', ')}. Repair the contract and the implementation together.` + : ''; + return `${display} opens with this direction contract, written when the direction was decided:\n\n${contract}${integrity}`; }); return [ `${ENVELOPE_PREFIX} Direction-contract audit. Before finishing, audit the rendered page against the contract it opens with, promise by promise.`, @@ -1984,9 +2009,22 @@ export async function runStopHook({ stdinJson, env = {}, cwd = process.cwd(), no return result({ skipped: 'native-platform', platform, durationMs: Date.now() - started }); } + const session = ensureSession(cache, sessionId); + const needsSlopReview = session.llmSlopReviewed !== true; + const det = detector || await loadDetector(); if (!det || typeof det.detectText !== 'function') { - return result({ skipped: 'detector-missing', durationMs: Date.now() - started }); + if (!needsSlopReview) return result({ skipped: 'detector-missing', durationMs: Date.now() - started }); + session.llmSlopReviewed = true; + session.updatedAt = Date.now(); + persistCache(projectCwd, cache); + const text = renderStopSlopReview(); + return { + exitCode: 0, + stdout: payload(text, 'Stop', harness), + emission: { kind: 'stop-llm-slop-review', llmSlopReview: true }, + audit: { ...audit, emitted: true, detectorMissing: true, llmSlopReview: true, chars: text.length, durationMs: Date.now() - started }, + }; } const scanOptions = designSystemOptions(config, det, projectCwd); @@ -2013,10 +2051,11 @@ export async function runStopHook({ stdinJson, env = {}, cwd = process.cwd(), no ? configuredExt.engine === 'html' : (ext === '.html' || ext === '.htm'); - // Direction-contract audit: HTML artifacts only, at most once per file + // Direction-contract audit: HTML and component artifacts, at most once per file // per session. The flag lives on the same session cache entry the // finding dedupe uses, so a second Stop fire stays quiet about it. - if (useHtmlEngine) { + const contractCapable = useHtmlEngine || CONTRACT_EXTS.has(ext); + if (contractCapable) { const fileEntry = ensureFile(cache, sessionId, filePath); if (!fileEntry.contractAudited) { const contract = extractDirectionContract(content); @@ -2046,10 +2085,15 @@ export async function runStopHook({ stdinJson, env = {}, cwd = process.cwd(), no } audit.scannedFiles = scanned; - if (freshGroups.length === 0 && contractEntries.length === 0) { + if (freshGroups.length === 0 && contractEntries.length === 0 && !needsSlopReview) { return result({ emitted: false, skipped: 'stop-clean', durationMs: Date.now() - started }); } + if (needsSlopReview) { + session.llmSlopReviewed = true; + session.updatedAt = Date.now(); + } + // Fresh findings and first-time contract audits earn the cache write; // both mark this batch as surfaced so the next Stop fire is silent // unless new issues appear. @@ -2064,6 +2108,9 @@ export async function runStopHook({ stdinJson, env = {}, cwd = process.cwd(), no if (contractEntries.length > 0) { parts.push(renderContractAudit(contractEntries, { cwd: projectCwd })); } + if (needsSlopReview) { + parts.push(renderStopSlopReview()); + } const text = appendDesignSystemNote(parts.join('\n\n'), scanOptions); return { exitCode: 0, @@ -2074,6 +2121,7 @@ export async function runStopHook({ stdinJson, env = {}, cwd = process.cwd(), no ...(contractEntries.length > 0 ? { contractFiles: contractEntries.map((entry) => entry.filePath) } : {}), + ...(needsSlopReview ? { llmSlopReview: true } : {}), }, audit: { ...audit, @@ -2081,6 +2129,7 @@ export async function runStopHook({ stdinJson, env = {}, cwd = process.cwd(), no freshFiles: freshGroups.length, freshFindings: freshGroups.reduce((sum, group) => sum + group.findings.length, 0), ...(contractEntries.length > 0 ? { contractAudits: contractEntries.length } : {}), + ...(needsSlopReview ? { llmSlopReview: true } : {}), chars: text.length, durationMs: Date.now() - started, }, diff --git a/.gemini/skills/impeccable/scripts/lib/provider.mjs b/.gemini/skills/impeccable/scripts/lib/provider.mjs index 7fa928a9c..ec807e971 100644 --- a/.gemini/skills/impeccable/scripts/lib/provider.mjs +++ b/.gemini/skills/impeccable/scripts/lib/provider.mjs @@ -1,4 +1,5 @@ // Source scripts default to slash commands. The provider build replaces only // this exact declaration, avoiding heuristic rewrites across executable code. export const IMPECCABLE_COMMAND_PREFIX = "/"; +export const IMPECCABLE_PROVIDER_ID = "gemini"; export const IMPECCABLE_COMMAND = `${IMPECCABLE_COMMAND_PREFIX}impeccable`; diff --git a/.gemini/skills/impeccable/scripts/lib/slop-review.mjs b/.gemini/skills/impeccable/scripts/lib/slop-review.mjs new file mode 100644 index 000000000..30f03e34d --- /dev/null +++ b/.gemini/skills/impeccable/scripts/lib/slop-review.mjs @@ -0,0 +1,30 @@ +export const LLM_ONLY_SLOP_ITEMS = Object.freeze([ + 'Monospace used merely to signal “technical” or “developer.”', + 'Light or dark mode chosen by category habit rather than the actual use scene.', + 'Everything wrapped in cards, or identical icon-heading-text cards repeated as the page structure.', + 'Hero-metric scaffolds: one oversized number, a small label, supporting stats, and an accent treatment.', + 'Decorative glassmorphism, meaningless sparklines, or generic rounded rectangles with drop shadows.', + 'A modal chosen by reflex when the task does not require interruption or protected focus.', +]); + +export function renderLlmOnlySlopReview({ manualDetector = false, automaticDetector = false, scriptsPath = null } = {}) { + const lines = [ + automaticDetector + ? 'AI_SLOP_REVIEW_REQUIRED: The automatic detector covers mechanical rules, but this harness has no reliable late review. Before finishing changed UI, inspect the authored result for detector-blind model reflexes:' + : 'AI_SLOP_REVIEW_REQUIRED: The automatic Impeccable design hook is not available for this session. Before finishing changed UI, inspect the authored result for detector-blind model reflexes:', + ...LLM_ONLY_SLOP_ITEMS.map((item) => `- ${item}`), + 'Fix reflexes, not intentional choices required by the brief or established visual authority.', + ]; + if (manualDetector && scriptsPath) { + lines.push(`Then run the mechanical detector once over the changed web UI: \`node ${scriptsPath}/detect.mjs --json \`. Do not run it earlier during concept selection.`); + } + return lines.join('\n'); +} + +export function renderStopSlopReview() { + return [ + '[impeccable@1] Detector-blind AI-slop review. The mechanical pass is complete; now inspect the rendered result for model reflexes it cannot reliably detect:', + ...LLM_ONLY_SLOP_ITEMS.map((item) => `- ${item}`), + 'Fix reflexes, not intentional choices required by the brief or established visual authority. Do not rerun the detector; this is the authored judgment layer.', + ].join('\n'); +} diff --git a/.gemini/skills/impeccable/scripts/lib/surface-briefs.mjs b/.gemini/skills/impeccable/scripts/lib/surface-briefs.mjs new file mode 100644 index 000000000..f83416f69 --- /dev/null +++ b/.gemini/skills/impeccable/scripts/lib/surface-briefs.mjs @@ -0,0 +1,151 @@ +import fs from 'node:fs'; +import path from 'node:path'; +import { slugFromTarget } from './target-slug.mjs'; + +export const SURFACE_BRIEF_VERSION = 1; + +export function getSurfaceBriefDir(projectRoot) { + return path.join(projectRoot, '.impeccable', 'surfaces'); +} + +export function normalizeSurfaceTarget(target, { projectRoot = process.cwd() } = {}) { + if (!target || typeof target !== 'string' || !target.trim()) return null; + const trimmed = target.trim(); + if (/^https?:\/\//i.test(trimmed)) { + try { + const url = new URL(trimmed); + url.hash = ''; + url.search = ''; + return url.toString().replace(/\/$/, '') || url.origin; + } catch { + return null; + } + } + if (/^route:/i.test(trimmed)) { + const route = trimmed.slice(trimmed.indexOf(':') + 1).trim(); + if (!route.startsWith('/') || route.includes('..')) return null; + const normalizedRoute = route.split(/[?#]/, 1)[0].replace(/\/{2,}/g, '/').replace(/\/$/, '') || '/'; + return `route:${normalizedRoute}`; + } + if (trimmed === '/') return 'route:/'; + if (trimmed.startsWith('/')) { + const absolute = path.resolve(trimmed); + const relativeToProject = path.relative(projectRoot, absolute); + const isProjectFile = relativeToProject && !relativeToProject.startsWith('..') && !path.isAbsolute(relativeToProject); + if (!isProjectFile && !fs.existsSync(absolute) && !trimmed.includes('..')) { + const normalizedRoute = trimmed.split(/[?#]/, 1)[0].replace(/\/{2,}/g, '/').replace(/\/$/, '') || '/'; + return `route:${normalizedRoute}`; + } + } + const abs = path.isAbsolute(trimmed) ? trimmed : path.resolve(projectRoot, trimmed); + const rel = path.relative(projectRoot, abs); + if (!rel || rel === '.' || rel.startsWith('..') || path.isAbsolute(rel)) return null; + return rel.split(path.sep).join('/'); +} + +export function surfaceBriefPathForTarget(target, { projectRoot = process.cwd() } = {}) { + const normalized = normalizeSurfaceTarget(target, { projectRoot }); + if (!normalized) return null; + const slugInput = normalized.startsWith('route:') ? `route${normalized.slice('route:'.length)}` : normalized; + const slug = slugFromTarget(slugInput, { cwd: projectRoot }); + return slug ? path.join(getSurfaceBriefDir(projectRoot), `${slug}.md`) : null; +} + +export function parseSurfaceBrief(text, filePath = null) { + const match = String(text || '').match(/^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/); + const meta = {}; + if (match) { + for (const line of match[1].split(/\r?\n/)) { + const colon = line.indexOf(':'); + if (colon < 0) continue; + const key = line.slice(0, colon).trim(); + const raw = line.slice(colon + 1).trim(); + if (!key) continue; + if (/^(?:\[|\{|\")/.test(raw) || /^(?:true|false|null|-?\d+(?:\.\d+)?)$/.test(raw)) { + try { meta[key] = JSON.parse(raw); continue; } catch { /* keep string */ } + } + meta[key] = raw.replace(/^['"]|['"]$/g, ''); + } + } + const primaryTarget = typeof meta.primary_target === 'string' ? meta.primary_target : null; + const relatedTargets = Array.isArray(meta.related_targets) + ? meta.related_targets.filter((value) => typeof value === 'string') + : []; + return { + path: filePath, + text: String(text || ''), + body: match ? String(text || '').slice(match[0].length).trim() : String(text || '').trim(), + meta, + slug: typeof meta.slug === 'string' ? meta.slug : filePath ? path.basename(filePath, '.md') : null, + primaryTarget, + relatedTargets, + targets: [primaryTarget, ...relatedTargets].filter(Boolean), + }; +} + +export function listSurfaceBriefs(projectRoot = process.cwd()) { + const dir = getSurfaceBriefDir(projectRoot); + let names; + try { + names = fs.readdirSync(dir).filter((name) => name.endsWith('.md')).sort(); + } catch { + return []; + } + return names.flatMap((name) => { + const filePath = path.join(dir, name); + try { + return [parseSurfaceBrief(fs.readFileSync(filePath, 'utf-8'), filePath)]; + } catch { + return []; + } + }); +} + +export function resolveSurfaceBrief(projectRoot = process.cwd(), target = null) { + const briefs = listSurfaceBriefs(projectRoot); + if (!target) { + return { + brief: briefs.length === 1 ? briefs[0] : null, + candidates: briefs, + reason: briefs.length === 1 ? 'only-brief' : briefs.length > 1 ? 'ambiguous' : 'none', + }; + } + + const normalized = normalizeSurfaceTarget(target, { projectRoot }); + if (!normalized) return { brief: null, candidates: briefs, reason: 'invalid-target' }; + const exactPath = surfaceBriefPathForTarget(normalized, { projectRoot }); + const exact = briefs.find((brief) => brief.path === exactPath && (!brief.targets.length || brief.targets.includes(normalized))); + if (exact) return { brief: exact, candidates: briefs, reason: 'slug' }; + const mapped = briefs.filter((brief) => brief.targets.includes(normalized)); + return { + brief: mapped.length === 1 ? mapped[0] : null, + candidates: mapped.length > 1 ? mapped : briefs, + reason: mapped.length === 1 ? 'mapping' : mapped.length > 1 ? 'ambiguous-target' : 'not-found', + }; +} + +export function writeSurfaceBrief({ + projectRoot = process.cwd(), + primaryTarget, + relatedTargets = [], + body, +}) { + const normalizedPrimary = normalizeSurfaceTarget(primaryTarget, { projectRoot }); + if (!normalizedPrimary) throw new Error('surface brief requires a concrete project-relative primary target or URL'); + const normalizedRelated = [...new Set(relatedTargets + .map((target) => normalizeSurfaceTarget(target, { projectRoot })) + .filter((target) => target && target !== normalizedPrimary))]; + const slug = slugFromTarget(normalizedPrimary, { cwd: projectRoot }); + const filePath = surfaceBriefPathForTarget(normalizedPrimary, { projectRoot }); + fs.mkdirSync(path.dirname(filePath), { recursive: true }); + const frontmatter = [ + '---', + `version: ${SURFACE_BRIEF_VERSION}`, + `slug: ${JSON.stringify(slug)}`, + `primary_target: ${JSON.stringify(normalizedPrimary)}`, + `related_targets: ${JSON.stringify(normalizedRelated)}`, + '---', + ].join('\n'); + fs.writeFileSync(filePath, `${frontmatter}\n\n${String(body || '').trim()}\n`, 'utf-8'); + return filePath; +} diff --git a/.gemini/skills/impeccable/scripts/lib/target-slug.mjs b/.gemini/skills/impeccable/scripts/lib/target-slug.mjs new file mode 100644 index 000000000..025915ad5 --- /dev/null +++ b/.gemini/skills/impeccable/scripts/lib/target-slug.mjs @@ -0,0 +1,33 @@ +import path from 'node:path'; + +const SLUG_MAX = 50; + +/** Derive one clone-stable slug from a concrete file path or URL. */ +export function slugFromTarget(resolved, { cwd = process.cwd() } = {}) { + if (!resolved || typeof resolved !== 'string') return null; + const trimmed = resolved.trim(); + if (!trimmed) return null; + + if (/^https?:\/\//i.test(trimmed)) { + let url; + try { url = new URL(trimmed); } catch { return null; } + return kebab(`${url.hostname}${url.pathname}`); + } + + const abs = path.isAbsolute(trimmed) ? trimmed : path.resolve(cwd, trimmed); + let rel = path.relative(cwd, abs); + if (rel.startsWith('..') || path.isAbsolute(rel)) rel = path.basename(abs); + if (!rel || rel === '.') return null; + return kebab(rel); +} + +export function kebab(value) { + const slug = String(value || '') + .toLowerCase() + .replace(/[/\\.]+/g, '-') + .replace(/[^a-z0-9-]+/g, '-') + .replace(/-+/g, '-') + .replace(/^-|-$/g, ''); + if (!slug) return null; + return slug.length <= SLUG_MAX ? slug : slug.slice(slug.length - SLUG_MAX).replace(/^-/, ''); +} diff --git a/.gemini/skills/impeccable/scripts/live-browser.js b/.gemini/skills/impeccable/scripts/live-browser.js index 616f1290a..47bbfefbf 100644 --- a/.gemini/skills/impeccable/scripts/live-browser.js +++ b/.gemini/skills/impeccable/scripts/live-browser.js @@ -2592,7 +2592,7 @@ minWidth: '16px', height: '16px', padding: '0 4px', borderRadius: '999px', background: tuneOpen ? C.brand : BP.hairline, - color: tuneOpen ? 'oklch(98% 0 0)' : 'inherit', + color: tuneOpen ? C.ink : 'inherit', fontFamily: MONO, fontSize: '9.5px', fontWeight: '600', lineHeight: '1', boxSizing: 'border-box', @@ -3131,7 +3131,7 @@ position: 'absolute', top: '2px', left: initial ? '18px' : '2px', width: '16px', height: '16px', borderRadius: '50%', - background: 'oklch(98% 0 0)', + background: C.ink, transition: 'left 0.18s ' + EASE, boxShadow: '0 1px 2px oklch(0% 0 0 / 0.2)', }); @@ -3165,7 +3165,7 @@ const b = el('button', { padding: '5px 4px', border: 'none', borderRadius: '3px', background: active ? C.brand : 'transparent', - color: active ? 'oklch(98% 0 0)' : P.text, + color: active ? C.ink : P.text, fontFamily: FONT, fontSize: '10.5px', fontWeight: '500', cursor: 'pointer', whiteSpace: 'nowrap', transition: 'background 0.1s ease, color 0.1s ease', @@ -3178,7 +3178,7 @@ segBtns.forEach(({ btn, val }) => { const on = val === o.value; btn.style.background = on ? C.brand : 'transparent'; - btn.style.color = on ? 'oklch(98% 0 0)' : P.text; + btn.style.color = on ? C.ink : P.text; }); applyParamValue(variantEl, p, o.value); queueCheckpoint('param_changed'); diff --git a/.gemini/skills/impeccable/scripts/surface-brief.mjs b/.gemini/skills/impeccable/scripts/surface-brief.mjs new file mode 100644 index 000000000..723f7c1b4 --- /dev/null +++ b/.gemini/skills/impeccable/scripts/surface-brief.mjs @@ -0,0 +1,74 @@ +#!/usr/bin/env node +import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath, pathToFileURL } from 'node:url'; +import { resolveProjectRoot } from './context.mjs'; +import { + listSurfaceBriefs, + resolveSurfaceBrief, + surfaceBriefPathForTarget, + writeSurfaceBrief, +} from './lib/surface-briefs.mjs'; + +function summary(brief, projectRoot) { + return { + slug: brief.slug, + path: path.relative(projectRoot, brief.path).split(path.sep).join('/'), + primaryTarget: brief.primaryTarget, + relatedTargets: brief.relatedTargets, + }; +} + +function main(argv) { + const [command, target, bodyFile, ...relatedTargets] = argv; + const projectRoot = resolveProjectRoot(process.cwd(), target ? { targetPath: target } : {}); + if (command === 'path') { + const filePath = surfaceBriefPathForTarget(target, { projectRoot }); + if (!filePath) throw new Error('surface brief path requires a concrete target'); + process.stdout.write(`${path.relative(process.cwd(), filePath) || filePath}\n`); + return; + } + if (command === 'list') { + process.stdout.write(`${JSON.stringify(listSurfaceBriefs(projectRoot).map((brief) => summary(brief, projectRoot)), null, 2)}\n`); + return; + } + if (command === 'read') { + const result = resolveSurfaceBrief(projectRoot, target || null); + if (result.brief) { + process.stdout.write(result.brief.text); + return; + } + if (result.candidates.length) process.stderr.write(`${JSON.stringify(result.candidates.map((brief) => summary(brief, projectRoot)), null, 2)}\n`); + process.exit(2); + } + if (command === 'write') { + if (!target || !bodyFile) throw new Error('usage: surface-brief.mjs write '); + const filePath = writeSurfaceBrief({ + projectRoot, + primaryTarget: target, + relatedTargets, + body: fs.readFileSync(bodyFile, 'utf-8'), + }); + process.stdout.write(`${path.relative(process.cwd(), filePath) || filePath}\n`); + return; + } + throw new Error('usage: surface-brief.mjs [target] [body-file] [related-target ...]'); +} + +function isMainModule() { + if (!process.argv[1]) return false; + try { + return fs.realpathSync(fileURLToPath(import.meta.url)) === fs.realpathSync(process.argv[1]); + } catch { + return import.meta.url === pathToFileURL(process.argv[1]).href; + } +} + +if (isMainModule()) { + try { + main(process.argv.slice(2)); + } catch (error) { + process.stderr.write(`${error?.message || error}\n`); + process.exit(1); + } +} diff --git a/.github/skills/impeccable/SKILL.md b/.github/skills/impeccable/SKILL.md index af55a53a2..30af87ea8 100644 --- a/.github/skills/impeccable/SKILL.md +++ b/.github/skills/impeccable/SKILL.md @@ -1,62 +1,61 @@ --- name: impeccable description: Use when the user wants to design, redesign, shape, critique, audit, polish, clarify, distill, harden, optimize, adapt, animate, colorize, extract, or otherwise improve a frontend interface. Covers websites, landing pages, dashboards, product UI, app shells, components, forms, settings, onboarding, and empty states. Handles UX review, visual hierarchy, information architecture, cognitive load, accessibility, performance, responsive behavior, theming, anti-patterns, typography, fonts, spacing, layout, alignment, color, motion, micro-interactions, UX copy, error states, edge cases, i18n, and reusable design systems or tokens. Also use for bland designs that need to become bolder or more delightful, loud designs that should become quieter, live browser iteration on UI elements, or ambitious visual effects that should feel technically extraordinary. Not for backend-only or non-UI tasks. -version: 4.0.0-alpha.9 +version: 4.0.0-alpha.10 user-invocable: true argument-hint: "[craft|shape · audit|critique · animate|bolder|colorize|delight|layout|overdrive|quieter|typeset · adapt|clarify|distill · harden|onboard|optimize|polish · init|document|extract|live] [target]" license: Apache 2.0 --- -Designs and iterates production-grade frontend interfaces. Real working code, committed design choices, exceptional craft. +This skill gives you the tools and permission to create design that earns to be called out-of-distribution craft: Whereas before, your design work would have been safe, timid and measured, you now approach every design task as a award-winning design director with impeccable understanding for what makes exceptional design work: production-grade code, peak creativity, a clear POV, deep understanding of the needs of the client and users, and exceptional craft. -Approach every design task as the design lead at a small studio known for giving every client a visual identity that could not be mistaken for anyone else's. The client has already rejected work that felt templated; they are paying for a point of view. the model is capable of extraordinary work. Don't hold back. +Core principles: +- Go all out. No hedging, no shortcuts. The deliverable must be complete (except assets the user must provide). +- Dream big and bold. Distinct, beautiful, outstanding and highly inspiring work. +- Iterate with tools available to you (e.g. visual understanding, browser screenshots) until you think this meets the bar. ## Setup -1. Run `node .github/skills/impeccable/scripts/context.mjs` once per session (if the runtime shows this skill's loaded base directory, run `node /scripts/context.mjs`; keep cwd at the user's project). It prints the project's context and its directives; follow what it prints. Once its output is in the conversation, never rerun it on a later turn. -2. `craft` and `shape` are build-path exceptions: the new-work gate below owns their flow, and on unattended runs its checkpoints resolve without pausing. For any other invoked sub-command (`audit`, `polish`, `live`, ...), immediately read **`reference/.md`** after `context.mjs` (the `.native` variant from the Commands table when the platform is `ios`/`android`/`adaptive`) and follow it. This read is a hard gate: do not inspect the target, run command-specific scripts, or edit files until the reference is loaded. -3. Read at least one project file (CSS / tokens / theme / a representative component) to learn what world you're in. If PRODUCT.md's `## Platform` is `ios` or `android`, also read `reference/.md` (`adaptive` reads both). +1. Run `node .github/skills/impeccable/scripts/context.mjs` once per session (if the runtime shows this skill's loaded base directory, run `node /scripts/context.mjs`; keep cwd at the user's project). Pass a named source file or route as `--target `. It loads PRODUCT.md, DESIGN.md, the matching surface brief, and native-platform guidance when applicable; follow its directives and do not rerun it. +2. Before acting, load the one playbook that owns the request: the Commands table's reference for an explicit or clearly implied sub-command, or [reference/new-work.md](reference/new-work.md) for a new surface or replacement visual world. Then inspect the target and at least one representative source of incumbent visual truth (tokens, theme, CSS, component, or asset) before editing. ## How to design -**The brief wins.** Where the brief pins down a direction (a named aesthetic, an era, a place, a material, a specific font or palette), follow it exactly, including when it asks for a look this skill warns is saturated. Redirecting a pinned direction toward your own taste is a failure, not a save. - -**Existing worlds are sacred.** Most of impeccable's work happens inside a site or app that already exists. When the surface has a committed design system (real tokens, deliberately chosen faces, a palette the brand owns), work inside that world: extend it, sharpen it, leave it unmistakably the same brand, and never degrade a working page's performance. Inventing parallel colors, fonts, or styles on an existing surface is a defect, not creativity. A scoped refinement keeps the named scope's content and media footprint unless the brief explicitly changes them; build the added emphasis by recomposing what is already there through hierarchy, proportion, rhythm, and the system's own motifs. - -**New identity work reads the playbook first.** When nothing committed exists (greenfield, or a codebase with no real tokens or chosen faces), or the user explicitly asks for a redesign that discards the current look, you MUST read [reference/new-work.md](reference/new-work.md) before making any design decision. Missing DESIGN.md alone does not make work greenfield: code, tokens, chosen type, components, and assets are incumbent design authority. Preserve and extend them unless the user asked to replace them. Producing a genuinely new identity without the playbook yields the generic default this skill exists to prevent. Scoped fixes inside an existing world don't need it; the craft floor below governs them. `context.mjs` prints the appropriate authority directive. +- **The brief wins.** Honor pinned aesthetics, eras, materials, fonts, and palettes even when they conflict with a saturated-pattern warning. Redirecting a clear brief toward your taste is failure. +- **Refinement preserves; redesign replaces.** Refinement keeps the incumbent identity, behavior, copy, and everything outside scope. Ask before replacing factual copy or adding claims. Redesign keeps product truth, content, function, native affordances, and constraints, but treats the old look as evidence and anti-reference; choose a replacement world in new-work and replace DESIGN.md. Never split the difference into polish on the discarded look. +- **Visual authority is evidence, not a filename.** Missing DESIGN.md alone does not make a project greenfield; new-work decides whether to preserve, expand, or replace the incumbent world. ## Modes -Name the visitor's mode before designing; the page's grammar follows from it, and most ruined pages are one mode wearing another mode's grammar. **The mode belongs to the surface, not the subject**: a landing page for a dense technical tool is still Persuade, with Persuade's full permission to be striking; a docs page for a fashion house is still Read. Deciding a page can be plain because its subject is workmanlike is the same category error in reverse. The brief and the surface decide the mode; PRODUCT.md's `register` field survives only as a family hint (`brand` covers Persuade and Experience, `product` covers Operate and Read). Depth beyond the paragraphs below: [reference/new-work.md](reference/new-work.md) when inventing identity, [reference/operate.md](reference/operate.md) for substantial Operate and Read work. +Choose the mode from the requested surface, not the product, and persist it only in that surface brief. A tool's landing page is still Persuade; a fashion house's documentation is still Read. See [new-work.md](reference/new-work.md) for new surfaces and [operate.md](reference/operate.md) for deeper Operate/Read guidance. -**Persuade** (the surface exists to win someone over; design IS the product). The deliverable is an impression that stops the scroll, earns the click, converts. Spans every genre; don't collapse them into one look. On new surfaces, briefs that imply imagery must ship real, verified imagery; a colored rectangle where a photo belongs reads as incomplete. New Persuade surfaces take their typeface procedure and reject list from [reference/new-work.md](reference/new-work.md). - -**Operate** (the surface is a tool someone works in; design SERVES the task). A person getting something done: scanability and consistency outrank expressiveness. These surfaces earn trust by feeling native to their platform: system font stacks and workhorse UI faces are legitimate and often correct here (the Persuade reject list does not apply). The brand lives in the details: focus states, empty states, microcopy, one owned accent. The usage scene is part of the spec: an interface read outdoors, in motion, or at a glance must survive its real ambient light, and the theme follows the scene, not the category's habit. - -**Read** (the surface exists to be understood; long-form, reference, guidance). The deliverable is comprehension, and comprehension is earned twice: a structure the reader can hold in their head with nothing standing between them and the answer, and a reading experience good enough to stay in, through typographic quality and whatever visual or interactive support genuinely helps the reader follow. The brand lives in type, spacing, and small accents. - -**Experience** (the surface presents a body of work; the page IS the work). The artifact leads, the interface recedes, and the visitor meets the work itself in the first viewport at every screen size. Boldness here means trusting the work. +- **Persuade:** win someone over; design is the product. Earn attention and action. Ship real imagery when the brief needs it; follow the committed world, not category habit. +- **Operate:** help someone do work. Scanability, consistency, native expectations, and the real usage scene outrank expression. Brand lives in precise details. +- **Read:** make something understood. Structure for comprehension, then make the reading experience worth staying in. +- **Experience:** present a body of work. Let the artifact lead from the first viewport; the interface recedes. ## Craft floor -Build to this floor without announcing it. The design detector (the project hook, `node .github/skills/impeccable/scripts/detect.mjs --json `, or `audit`) verifies most of it mechanically; resolve every finding before finalizing. Fix real defects, but use context judgment rather than distorting intentional design to appease a false positive. Classify any intentional exception explicitly and use the hook system's narrowest appropriate waiver when it must persist. +Build to this floor without announcing it. -- Contrast: body text ≥4.5:1 against its background (placeholders too); large text ≥3:1. Gray text on a colored background looks washed out: use a darker shade of the background's own hue, or a transparency of the text color. -- Shadows describe real light: an offset and a soft blur. A zero-offset colored halo is decoration announcing itself. -- Spacing has rhythm: generous separations, tight groupings; cramped padding reads as broken; the space above a heading exceeds the space below it. Verify computed spacing, not intended spacing. -- Type: body line length 65-75ch; display clamp() max ≤6rem; letter-spacing ≥-0.04em; `text-wrap: balance` on headings; modular scale ≥1.25 between steps; light-on-dark adds 0.05-0.1 line-height. Pair faces on a contrast axis, never two similar-but-not-identical ones; one family with committed weight contrast beats a timid pair. Test headings at every breakpoint; overflow means reduce the clamp or rewrite the copy. -- Motion is part of the build: one orchestrated moment beats scattered effects; ease-out exponential curves; reveals enhance an already-visible default (content gated on a class-triggered transition ships blank in hidden tabs and headless renderers). Responsive down to mobile and visible keyboard focus are part of the floor. -- Ship real content (no placeholders, dead links, or fake controls) and cover the interaction states people will actually hit (hover, focus, disabled, loading, error, empty). -- Copy is design material: name things the way the page's own people speak, make every control say what it does, and make every error say what happened and what to do next. -- Before finishing, re-read the brief: every requirement it names must exist on the page, findable in seconds. A beautiful page missing an asked-for feature is unfinished. +- **Contrast:** body and placeholder text ≥4.5:1; large text ≥3:1. On colored surfaces, tint secondary text from that hue or the foreground instead of using gray. +- **Depth:** shadows describe light with offset and soft blur; zero-offset colored halos are decoration. +- **Spacing:** tight groups, generous separation, no cramped containers; space above a heading exceeds space below. Verify computed values. +- **Type:** body measure 65–75ch; display max 6rem and tracking floor -0.04em; balance headings; use clear scale/weight contrast; test overflow at every breakpoint. +- **Motion:** author one coherent moment instead of scattered effects. Use exponential ease-out and an already-visible default. Premium motion is not transform/opacity alone: it may add focus, depth, masks, light, or material change through blur/filter, backdrop-filter, clip-path/masks, or shadow when smooth. Always provide reduced motion. +- **Shipping:** real content, working controls, responsive composition, keyboard focus, and the states users hit: hover, disabled, loading, error, and empty. +- **Copy:** use the product's language; controls name their action, errors name the problem and recovery. +- **Coverage:** every brief requirement must exist and be findable within seconds. + +Before finishing changed UI, follow the quality guidance supplied by `context.mjs` and hooks. Context requests a manual scan only when no automatic detector is active; never add a second detector pass. ## Commands | Command | Category | Description | Reference | |---|---|---|---| -| `craft [feature]` | Build | The standard build flow with attended checkpoints | [reference/new-work.md](reference/new-work.md) | +| `craft [feature]` | Build | Deprecated alias for an ordinary new-work request | [reference/craft.md](reference/craft.md) | | `shape [feature]` | Build | Plan UX/UI before writing code | [reference/shape.md](reference/shape.md) | -| `init` | Build | Set up project context: PRODUCT.md, DESIGN.md, live config, next steps | [reference/init.md](reference/init.md) | +| `init` | Build | Capture durable product context in PRODUCT.md | [reference/init.md](reference/init.md) | | `document` | Build | Generate DESIGN.md from existing project code | [reference/document.md](reference/document.md) | | `extract [target]` | Build | Pull reusable tokens and components into design system | [reference/extract.md](reference/extract.md) | | `critique [target]` | Evaluate | UX design review with heuristic scoring | [reference/critique.md](reference/critique.md) | @@ -78,7 +77,14 @@ Build to this floor without announcing it. The design detector (the project hook | `optimize [target]` | Fix | Diagnose and fix UI performance | [reference/optimize.md](reference/optimize.md) | | `live` | Iterate | Visual variant mode: pick elements in the browser, generate alternatives | [reference/live.md](reference/live.md) | -Routing: **no argument** → read [reference/routing.md](reference/routing.md) and present the context-aware menu (never auto-run a command). **First word matches a command** (or `pin` / `unpin` / `hooks`) → load its reference (native variant on native platforms) and follow it; everything after the command name is the target. **Intent clearly maps to one command** ("fix the spacing" → `layout`, "rewrite this error" → `clarify`) → same; if two fit, ask once. **Otherwise** → general design invocation: apply Setup and this file's guidance; builds flow through the new-work gate above, whose playbook carries the direction checkpoint and the finishing pass. `teach` routes to `init`, and `craft` routes to the standard build flow with attended checkpoints. If setup diverted into `init` for a build request, finish init, refresh context, then resume. +Routing: + +- **No argument:** read [routing.md](reference/routing.md) and present its context-aware menu; never auto-run a command. +- **Explicit or clearly implied command:** load its reference (native variant on native platforms) and follow it. Ask once if two commands fit. +- **Otherwise:** treat the request as general design work. Missing PRODUCT.md routes through init; new surfaces and replacement worlds use new-work. +- `teach` aliases `init`. `craft` is a deprecated alias for ordinary new-work and adds nothing. `shape` owns task discovery, then enters new-work only for visual-world and surface-concept decisions. + +After init writes PRODUCT.md, resume without rerunning `context.mjs`. **Pin / Unpin:** `node .github/skills/impeccable/scripts/pin.mjs ` creates or removes a standalone `/` shortcut. Report the script's result concisely; relay stderr verbatim on error. diff --git a/.github/skills/impeccable/reference/android.md b/.github/skills/impeccable/reference/android.md index 3575c31fb..6337b9018 100644 --- a/.github/skills/impeccable/reference/android.md +++ b/.github/skills/impeccable/reference/android.md @@ -2,7 +2,7 @@ For native Android apps: Jetpack Compose, Android Views, React Native, Expo, Flutter shipping to Android hardware. -On native, register narrows. Material Design 3 governs structure, navigation, and interaction whatever the register; brand expresses through Material's theming (color roles, type scale, shape, motion). A Material-everywhere cross-platform app that also ships to iPhone still owes iOS its OS guarantees on that hardware: safe-area insets, Reduce Motion, edge-swipe back. +On native, the visitor mode narrows what expression may override. Material Design 3 governs structure, navigation, and interaction in every mode; brand expresses through Material's theming (color roles, type scale, shape, motion). A Material-everywhere cross-platform app that also ships to iPhone still owes iOS its OS guarantees on that hardware: safe-area insets, Reduce Motion, edge-swipe back. ## The Android slop test diff --git a/.github/skills/impeccable/reference/animate.md b/.github/skills/impeccable/reference/animate.md index 1a1098a5b..d51bb6d63 100644 --- a/.github/skills/impeccable/reference/animate.md +++ b/.github/skills/impeccable/reference/animate.md @@ -1,203 +1,88 @@ > **Additional context needed**: performance constraints. -Add motion that conveys state, gives feedback, and clarifies hierarchy. Cut motion that exists only for decoration. Animation fatigue is a real cost; spend the budget on the moments that need it. +Use motion to explain state, relationship, and hierarchy, or to create one authored moment the surface has earned. Decoration without purpose is animation debt. --- -## Register +## Visitor mode -Persuade + Experience: 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. +- **Persuade + Experience:** motion may carry the voice. Prefer one rehearsed focal sequence to repeated section reveals. +- **Operate + Read:** motion serves feedback, state, and continuity. Keep routine transitions fast and do not make users wait through page-load choreography. +- **Native (`ios` / `android` / `adaptive`):** follow the Motion section of [ios.md](ios.md) or [android.md](android.md), including the platform's Reduce Motion behavior. Do not apply the web tooling below. -Operate + Read: 150–250 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. +## Find the job -Native (`ios` / `android` / `adaptive`): implementation follows the Motion section of [ios.md](ios.md) / [android.md](android.md) (read it first if Setup hasn't already): system transitions and OS Reduce Motion, never the web tooling below. +Inspect the existing motion language, interaction states, target devices, and performance budget. Find only the places where motion would: ---- +- acknowledge an action; +- make a state change or spatial relationship legible; +- preserve continuity through navigation or layout change; +- direct attention at a meaningful moment; +- embody the selected visual world. -## Assess Animation Opportunities +Ask only when a material constraint cannot be inferred. Do not animate a static area merely because it exists. -Analyze where motion would improve the experience: +## Set the motion thesis -1. **Identify static areas**: - - **Missing feedback**: Actions without visual acknowledgment (button clicks, form submission, etc.) - - **Jarring transitions**: Instant state changes that feel abrupt (show/hide, page loads, route changes) - - **Unclear relationships**: Spatial or hierarchical relationships that aren't obvious - - **Lack of delight**: Functional but joyless interactions - - **Missed guidance**: Opportunities to direct attention or explain behavior +Write a short plan before implementation: -2. **Understand the context**: - - What's the personality? (Playful vs serious, energetic vs calm) - - What's the performance budget? (Mobile-first? Complex page?) - - Who's the audience? (Motion-sensitive users? Power users who want speed?) - - What matters most? (One hero animation vs many micro-interactions?) +- **Focal moment:** the one sequence or interaction that deserves authorship, if any. +- **Continuity:** the state, layout, or navigation changes that need explanation. +- **Feedback:** the controls and outcomes that need acknowledgment. +- **Budget:** which effects may be expensive, how often they run, and the reduced-motion equivalent. -If any of these are unclear from the codebase, ask the user directly to clarify what you cannot infer. +The focal moment must come from this product and surface concept. A generic fade-and-rise, hover lift, parallax layer, or scroll reveal is not a thesis. -**CRITICAL**: Respect `prefers-reduced-motion`. Always provide non-animated alternatives for users who need them. +## Choose material by meaning -## Plan Animation Strategy +Transform and opacity are reliable foundations, not the entire palette. Choose properties for what the transition communicates: -Create a purposeful animation plan: +- **Continuity and relationship:** shared-element motion, FLIP-style transforms, view transitions, or deliberate spatial movement. +- **Focus and depth:** bounded blur, filter, backdrop, light, or shadow changes. +- **Reveal and composition:** masks, clip paths, cropping, or controlled occlusion. +- **Material and energy:** color, gradient position, texture, distortion, or shader effects when the world and runtime support them. +- **State and feedback:** the smallest change that makes cause and result unmistakable. -- **Feedback layer**: Which interactions need acknowledgment? -- **Transition layer**: Which state changes need smoothing? -- **Entrance moment**: The ONE entrance worth rehearsing, where the mode invites it. Not every surface wants one. -- **Delight layer**: Where can we surprise and delight? +Do not stack techniques for spectacle. One strong material idea, carried through the focal sequence and quiet supporting states, is usually enough. -**IMPORTANT**: One well-orchestrated experience beats scattered animations everywhere. Focus on high-impact moments. +Sibling stagger is appropriate when a list appears as a list. Cap the total delay, and never reinterpret every scrolled section as a staggered list. -## Implement Animations +## Timing and easing -Add motion systematically across these categories: +Timing should express distance and consequence: -### Micro-interactions -- **Button feedback**: - - Hover: Subtle scale (1.02-1.05), color shift, shadow increase - - Click: Quick scale down then up (0.95 → 1), ripple effect - - Loading: Spinner or pulse state -- **Form interactions**: - - Input focus: Border color transition, slight scale or glow - - Validation: Shake on error, check mark on success, smooth color transitions -- **Toggle switches**: Smooth slide + color transition (200-300ms) -- **Checkboxes/radio**: Check mark animation, ripple effect -- **Like/favorite**: Scale + rotation, particle effects, color transition +| Duration | Typical use | +|---|---| +| 100–150 ms | immediate feedback | +| 150–300 ms | routine state change | +| 300–500 ms | layout, overlay, or view transition | +| 500–800 ms | a deliberately authored focal entrance | -### State Transitions -- **Show/hide**: Fade + slide (not instant), appropriate timing (200-300ms) -- **Expand/collapse**: Height transition with overflow handling, icon rotation -- **Loading states**: Skeleton screen fades, spinner animations, progress bars -- **Success/error**: Color transitions, icon animations, gentle scale pulse -- **Enable/disable**: Opacity transitions, cursor changes +Exit faster than entrance. Use natural deceleration such as `cubic-bezier(0.16, 1, 0.3, 1)` for confident arrivals; do not use bounce or elastic curves by reflex. Long feedback feels like latency. -### Entrance Animations -- **The one entrance moment** (where the mode invites it): a committed entrance for primary content (scale, parallax, or a creative effect) -- **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. +## Implement to the runtime - 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. +- Use CSS transitions and keyframes for declarative state and bounded sequences. +- Use Web Animations API or the project's existing motion library for interruption, sequencing, and dynamic values. +- Use View Transitions or shared-element techniques when continuity across states is the point. +- Use scroll-driven motion only when the scroll relationship itself carries meaning, with a robust fallback. +- Do not add a dependency for an effect the existing stack can express cleanly. -### Navigation & Flow -- **Page transitions**: Crossfade between routes, shared element transitions -- **Tab switching**: Slide indicator, content fade/slide -- **Carousel/slider**: Smooth transforms, snap points, momentum -- **Scroll effects**: Parallax layers, sticky headers with state changes, scroll progress indicators +Keep content visible in the default state so failed scripts do not hide the page. Avoid casually animating layout-driving properties such as `width`, `height`, `top`, `left`, and margins; use FLIP, transforms, or grid techniques when appropriate. Bound blur, filter, shadow, canvas, and shader work to isolated regions. Apply `will-change` only during known animation. Measure on target viewports and devices rather than assuming transform means fast. -### Feedback & Guidance -- **Hover hints**: Tooltip fade-ins, cursor changes, element highlights -- **Drag & drop**: Lift effect (shadow + scale), drop zone highlights, smooth repositioning -- **Copy/paste**: Brief highlight flash on paste, "copied" confirmation -- **Focus flow**: Highlight path through form or workflow +## Accessibility and control -### Delight Moments -- **Empty states**: Subtle floating animations on illustrations -- **Completed actions**: Confetti, check mark flourish, success celebrations -- **Easter eggs**: Hidden interactions for discovery -- **Contextual animation**: Weather effects, time-of-day themes, seasonal touches +Honor `prefers-reduced-motion` with an intentional alternative. Preserve state change and hierarchy while removing travel, parallax, flashing, or prolonged sequences. Do not replace every animation globally with `0.01ms` if that destroys useful feedback. Motion must never block focus, interaction, reading, or task completion. -## Technical Implementation +Respect autoplay and sound preferences. Any nonessential loop must stop when offscreen or hidden. -Use appropriate techniques for each animation: +## Verify -### Timing & Easing +- The focal motion is specific to the selected world and surface. +- Every supporting animation explains feedback, state, or relationship. +- Interruption and repeated use behave correctly. +- Desktop, mobile, keyboard, and reduced-motion paths remain usable. +- Expensive effects stay smooth on the target device. +- Removing an animation would lose meaning or authored character, not merely decoration. -**Duration: the 100/300/500 rule.** Timing matters more than easing for "feels right": - -| Duration | Use Case | Examples | -|----------|----------|----------| -| **100–150ms** | Instant feedback | Button press, toggle, color change | -| **200–300ms** | State changes | Menu open, tooltip, hover state | -| **300–500ms** | Layout changes | Accordion, modal, drawer | -| **500–800ms** | Entrance animations | Page load, hero reveal | - -**Easing curves (use these, not CSS defaults):** -```css -/* Recommended: natural deceleration */ ---ease-out-quart: cubic-bezier(0.25, 1, 0.5, 1); /* Smooth */ ---ease-out-quint: cubic-bezier(0.22, 1, 0.36, 1); /* Slightly snappier */ ---ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1); /* Confident, decisive */ - -/* AVOID: feel dated and tacky */ -/* bounce: cubic-bezier(0.34, 1.56, 0.64, 1); */ -/* elastic: cubic-bezier(0.68, -0.6, 0.32, 1.6); */ -``` - -**Exit animations are faster than entrances.** Use ~75% of enter duration. - -### CSS Animations -```css -/* Prefer for simple, declarative animations */ -- transitions for state changes -- @keyframes for complex sequences -- transform and opacity for reliable movement -- blur, filters, masks, clip paths, shadows, and color shifts for premium atmospheric effects when verified smooth -``` - -### JavaScript Animation -```javascript -/* Use for complex, interactive animations */ -- Web Animations API for programmatic control -- Framer Motion for React -- 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 -- **Layout safety**: Avoid casual animation of layout-driving properties (`width`, `height`, `top`, `left`, margins) -- **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) { - * { - animation-duration: 0.01ms !important; - animation-iteration-count: 1 !important; - transition-duration: 0.01ms !important; - } -} -``` - -**NEVER**: -- Use bounce or elastic easing curves; they feel dated and draw attention to the animation itself -- Animate layout properties casually (`width`, `height`, `top`, `left`, margins) when transform, FLIP, or grid-based techniques would work -- Use durations over 500ms for feedback (it feels laggy) -- Animate without purpose (every animation needs a reason) -- Ignore `prefers-reduced-motion` (this is an accessibility violation) -- Animate everything (animation fatigue makes interfaces feel exhausting) -- Block interaction during animations unless intentional - -## Verify Quality - -Test animations thoroughly: - -- **Smooth at 60fps**: No jank on target devices -- **Feels natural**: Easing curves feel organic, not robotic -- **Appropriate timing**: Not too fast (jarring) or too slow (laggy) -- **Reduced motion works**: Animations disabled or simplified appropriately -- **Doesn't block**: Users can interact during/after animations -- **Adds value**: Makes interface clearer or more delightful - -When the motion clarifies state instead of decorating it, hand off to `/impeccable polish` for the final pass. +When motion earns its place, hand off to `/impeccable polish` for the final pass. diff --git a/.github/skills/impeccable/reference/audit.md b/.github/skills/impeccable/reference/audit.md index 72815af69..70d80771a 100644 --- a/.github/skills/impeccable/reference/audit.md +++ b/.github/skills/impeccable/reference/audit.md @@ -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 - diff --git a/.github/skills/impeccable/reference/audit.native.md b/.github/skills/impeccable/reference/audit.native.md index 737d50e4f..0126fa157 100644 --- a/.github/skills/impeccable/reference/audit.native.md +++ b/.github/skills/impeccable/reference/audit.native.md @@ -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) diff --git a/.github/skills/impeccable/reference/bolder.md b/.github/skills/impeccable/reference/bolder.md index 6b44f0e38..78f5e4811 100644 --- a/.github/skills/impeccable/reference/bolder.md +++ b/.github/skills/impeccable/reference/bolder.md @@ -8,12 +8,12 @@ ## Why it reads flat -A section usually reads flat for reasons its neighbors have already solved. Look at what the rest of the page does that this section does not: the display type at full strength, the structural devices that carry meaning, the signature motif, the density and pacing, the specificity of the copy. A flat section is typically one that quietly opts out of the system's own strongest moves. The most reliable bolder pass brings the target up to the expressive level its neighbors already reach, in the system's own vocabulary rather than a new one. +A section usually reads flat for reasons its neighbors have already solved. Look at what the rest of the page does that this section does not: the display type at full strength, the structural devices that carry meaning, the signature motif, the density and pacing. A flat section is typically one that quietly opts out of the system's own strongest moves. The most reliable bolder pass brings the target up to the expressive level its neighbors already reach, in the system's own vocabulary rather than a new one. ## The amplification - **Amplify what the system already owns.** Reuse its motif and its type scale at full strength, turned up for this section rather than invented for it. The bolder version should look more like the same brand, not less. -- **Let the content carry the weight.** Flat copy makes a flat design, and no amount of size fixes a generic claim. Make every word specific enough to earn its place, and let the section's real evidence do the work that a decorative addition would otherwise fake. +- **Keep content true.** Existing claims are part of the scope: preserve them unless the user supplies replacements. If real evidence is essential to the direction but absent, ask for it. - **Commit, then clarify.** Half-measures read as noise. Make the one decisive move completely, then quiet everything around it so the move is legible. If every element got louder, the section got flatter. - **Give it its own rhythm.** The target should read as a peak in the scroll, a shift in density or pace from what surrounds it, not simply more of the same. diff --git a/.github/skills/impeccable/reference/clarify.md b/.github/skills/impeccable/reference/clarify.md index 00ee978c3..3047a9d82 100644 --- a/.github/skills/impeccable/reference/clarify.md +++ b/.github/skills/impeccable/reference/clarify.md @@ -1,288 +1,94 @@ -> **Additional context needed**: audience technical level and users' mental state in context. +> **Additional context needed**: audience knowledge and emotional state. -Find the unclear, confusing, or poorly written interface text and rewrite it. Vague copy creates support tickets and abandonment; specific copy gets users through the task. +Rewrite unclear interface text so users understand what happened, what matters, and what to do next. Preserve factual meaning, product terminology, and brand voice. +## Audit the language ---- +Read the entire interaction path, not isolated strings. Identify: -## Assess Current Copy +- ambiguous nouns, verbs, and actions; +- internal jargon or assumed knowledge; +- vague labels, outcomes, and system states; +- missing consequences, recovery, or timing; +- inconsistent terminology and capitalization; +- redundant headings, intros, helper text, and confirmations; +- text that breaks at realistic widths or in translation; +- tone that ignores stress, risk, success, or urgency. -Identify what makes the text unclear or ineffective: +Infer audience and task from product context and surrounding UI. Ask before changing factual claims, legal meaning, or a term that may be domain-specific. -1. **Find clarity problems**: - - **Jargon**: Technical terms users won't understand - - **Ambiguity**: Multiple interpretations possible - - **Passive voice**: "Your file has been uploaded" vs "We uploaded your file" - - **Length**: Too wordy or too terse - - **Assumptions**: Assuming user knowledge they don't have - - **Missing context**: Users don't know what to do or why - - **Tone mismatch**: Too formal, too casual, or inappropriate for situation +## Set the message hierarchy -2. **Understand the context**: - - Who's the audience? (Technical? General? First-time users?) - - What's the user's mental state? (Stressed during error? Confident during success?) - - What's the action? (What do we want users to do?) - - What's the constraint? (Character limits? Space limitations?) +For each state, decide: -**CRITICAL**: Clear copy helps users succeed. Unclear copy creates frustration, errors, and support tickets. +1. the one fact the user needs now; +2. the action available next; +3. supporting context that changes the decision; +4. the appropriate tone for this moment. -## Plan Copy Improvements +Say each idea once. If the heading already explains the state, the introduction should add new information or disappear. -Create a strategy for clearer communication: +## Rewrite by function -- **Primary message**: What's the ONE thing users need to know? -- **Action needed**: What should users do next (if anything)? -- **Tone**: How should this feel? (Helpful? Apologetic? Encouraging?) -- **Constraints**: Length limits, brand voice, localization considerations +### Actions and navigation -**IMPORTANT**: Good UX writing is invisible. Users should understand immediately without noticing the words. +Use a specific verb and object when the outcome is not already obvious. Labels should describe what will happen, not the gesture used to trigger it. Keep the same noun and verb for the same concept throughout the product. -## Improve Copy Systematically +For destructive actions, name the object and consequence. Prefer undo over confirmation when recovery is safe. When confirmation is necessary, name the action on both the message and button instead of using `Yes`, `No`, `OK`, or `Submit`. -Refine text across these common areas: +### Forms -### Error Messages -**Bad**: "Error 403: Forbidden" -**Good**: "You don't have permission to view this page. Contact your admin for access." +Use persistent labels; placeholders are examples, not labels. Put format and eligibility requirements before submission. Explain why information is requested only when it is not obvious. Required and optional treatment should be consistent. -**Bad**: "Invalid input" -**Good**: "Email addresses need an @ symbol. Try: name@example.com" +Validation says what needs attention and how to correct it without blaming the user. Keep related instructions near the field and announce errors accessibly. -**Principles**: -- Explain what went wrong in plain language -- Suggest how to fix it -- Don't blame the user -- Include examples when helpful -- Link to help/support if applicable - -### Form Labels & Instructions -**Bad**: "DOB (MM/DD/YYYY)" -**Good**: "Date of birth" (with placeholder showing format) - -**Bad**: "Enter value here" -**Good**: "Your email address" or "Company name" - -**Principles**: -- Use clear, specific labels (not generic placeholders) -- Show format expectations with examples -- Explain why you're asking (when not obvious) -- Put instructions before the field, not after -- Keep required field indicators clear - -### Button & CTA Text -**Bad**: "Click here" | "Submit" | "OK" -**Good**: "Create account" | "Save changes" | "Got it, thanks" +### Errors and permissions -**Principles**: -- Describe the action specifically -- Use active voice (verb + noun) -- Match user's mental model -- Be specific ("Save" is better than "OK") +An actionable error answers: -### Help Text & Tooltips -**Bad**: "This is the username field" -**Good**: "Choose a username. You can change this later in Settings." +1. what failed; +2. why, when known and useful; +3. how to recover or what alternative remains. -**Principles**: -- Add value (don't just repeat the label) -- Answer the implicit question ("What is this?" or "Why do you need this?") -- Keep it brief but complete -- Link to detailed docs if needed +Do not expose internal codes as the primary message. Do not promise a cause or resolution the system cannot know. Treat privacy, payment, deletion, access loss, and blocked work seriously; warmth is welcome, jokes are not. -### Empty States -**Bad**: "No items" -**Good**: "No projects yet. Create your first project to get started." +### Loading, empty, and success states -**Principles**: -- Explain why it's empty (if not obvious) -- Show next action clearly -- Make it welcoming, not dead-end +Loading text names the real operation and sets an honest expectation when the wait is meaningful. Show determinate progress when available; never invent progress. -### Success Messages -**Bad**: "Success" -**Good**: "Settings saved! Your changes will take effect immediately." +An empty state distinguishes first use, no results, filters, permissions, and failure. Explain the state and provide the next useful action. -**Principles**: -- Confirm what happened -- Explain what happens next (if relevant) -- Be brief but complete -- Match the user's emotional moment (celebrate big wins) - -### Loading States -**Bad**: "Loading..." (for 30+ seconds) -**Good**: "Analyzing your data... this usually takes 30-60 seconds" - -**Principles**: -- Set expectations (how long?) -- Explain what's happening (when it's not obvious) -- Show progress when possible -- Offer escape hatch if appropriate ("Cancel") +Success confirms the completed outcome and mentions the next consequence only when it changes what the user should do. Routine success should be brief. -### Confirmation Dialogs -**Bad**: "Are you sure?" -**Good**: "Delete 'Project Alpha'? This can't be undone." +### Help and instructional text -**Principles**: -- State the specific action -- Explain consequences (especially for destructive actions) -- Use clear button labels ("Delete project" not "Yes") -- Don't overuse confirmations (only for risky actions) +Helper text answers an implicit question instead of restating the control. Use progressive disclosure for uncommon detail. Link text must make sense out of context; icon-only controls need accessible names. -### Navigation & Wayfinding -**Bad**: Generic labels like "Items" | "Things" | "Stuff" -**Good**: Specific labels like "Your projects" | "Team members" | "Settings" +## Voice, accessibility, and localization -**Principles**: -- Be specific and descriptive -- Use language users understand (not internal jargon) -- Make hierarchy clear -- Consider information scent (breadcrumbs, current location) +Voice stays consistent; tone adapts to the moment. Use plain language without flattening terminology the audience genuinely knows. -## Apply Clarity Principles +- Write complete translatable messages rather than concatenated fragments. +- Keep variables and numbers structured so translators can reorder them. +- Allow expansion instead of abbreviating prematurely. +- Make alt text convey the image's information; use empty alt for decoration. +- Keep screen-reader names aligned with visible labels and outcomes. +- Do not rely on punctuation, color, or iconography to carry the message alone. -Every piece of copy should follow these rules: +Maintain a short terminology glossary when inconsistency spans the product. Do not vary words for literary effect in an interface. -1. **Be specific**: "Enter email" not "Enter value" -2. **Be concise**: Cut unnecessary words (but don't sacrifice clarity) -3. **Be active**: "Save changes" not "Changes will be saved" -4. **Be human**: "Oops, something went wrong" not "System error encountered" -5. **Tell users what to do**, not just what happened -6. **Be consistent**: Use same terms throughout (don't vary for variety) +## Verify -**NEVER**: -- Use jargon without explanation -- Blame users ("You made an error" → "This field is required") -- Be vague ("Something went wrong" without explanation) -- Use passive voice unnecessarily -- Write overly long explanations (be concise) -- Use humor for errors (be empathetic instead) -- Assume technical knowledge -- Vary terminology (pick one term and stick with it) -- Repeat information (headers restating intros, redundant explanations) -- Use placeholders as the only labels (they disappear when users type) +Read the flow in context and test: -## Verify Improvements +- comprehension without hidden product knowledge; +- actionability at errors, empty states, and decision points; +- factual accuracy and consistent terminology; +- scanability at target widths and 200% zoom; +- long names, localization expansion, pluralization, and dynamic values; +- accessible names and announced state changes; +- tone appropriate to consequence and emotional context. -Test that copy improvements work: +The final copy is as short as it can be without removing meaning or recovery. -- **Comprehension**: Can users understand without context? -- **Actionability**: Do users know what to do next? -- **Brevity**: Is it as short as possible while remaining clear? -- **Consistency**: Does it match terminology elsewhere? -- **Tone**: Is it appropriate for the situation? - -When the copy reads cleanly, hand off to `/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. +When the language reads cleanly, hand off to `/impeccable polish` for the final pass. diff --git a/.github/skills/impeccable/reference/codex.md b/.github/skills/impeccable/reference/codex.md index 18a924a91..6c550f1dc 100644 --- a/.github/skills/impeccable/reference/codex.md +++ b/.github/skills/impeccable/reference/codex.md @@ -1,105 +1,38 @@ -# Codex: Visual Direction & Asset Production +# Codex: Surface Probes & Asset Production -This file is loaded by `/impeccable craft` when the harness has native image generation (currently Codex via `image_gen`). Other harnesses skip it. It covers the two craft steps that depend on real image generation: landing the visual direction, and producing the raster assets the implementation will compose. +Load this from [new-work.md](new-work.md) only when the harness has native image generation and a substantial, high-fidelity surface would benefit from seeing the shortlisted concept before code. PRODUCT.md and DESIGN.md are preconditions. New-work has already resolved the visual world; this file must not reopen it. -Read this *before* generating any images. The order matters, and the per-step user pauses are what keep generated imagery from drifting away from the brief. +The purpose of a probe is to test composition, narrative, hierarchy, density, focal moment, signature use, and image requirements. It is not a second identity workshop. Keep DESIGN.md's palette, typography direction, material language, component character, imagery stance, and motion grammar fixed. -### Four stop points before code +## Generate the smallest useful probe set -Steps A through D each end with the user. Do not advance past any of them on your own read of the situation. +Generate one to three high-fidelity north-star comps using the native image-generation capability. Base them on the real content and the surface concepts already developed with the user. -1. **STOP after Step A questions.** Wait for answers. -2. **STOP after Step B palette generation.** Wait for "confirm palette." -3. **STOP after Step C mocks.** Wait for direction approval or delegation. -4. **Only after Step D approves a direction** do you return to the build (new-work.md's plan, then code). +- When the user shortlisted multiple concepts, show one clear expression of each. +- When one concept is already selected, vary only the structural uncertainty that the image can resolve: topology, sequence, density, hierarchy, focal composition, or interaction framing. +- Show enough beyond the opening moment to prove the concept can govern the whole requested surface. +- Do not generate a palette artifact, ask new atmosphere questions, introduce a different type voice, or invent a new motif. If the committed world cannot support the concept, return to the concept shortlist rather than changing the world. -Prior shape approval does **not** satisfy any of these. Shape's "confirm or override" advances you into Step A; it is not a substitute for it. +Treat each comp as a direction test, not a screenshot specification. Core UI text, responsive behavior, accessibility, semantics, and interaction states remain implementation responsibilities. -## Step A: Explore Directions with the User +## One approval point -Before generating anything, run a brief direction conversation grounded in the shape brief. +Show the probes together and ask what should carry forward, what feels false to the world, and whether the selected surface concept should be approved, combined, revised, or rejected. Then stop and wait. A structured simulated user counts as attended and receives the same question. -**Step A is required even when shape just produced a confirmed brief.** The shape questions and Step A questions cover different ground: shape pins purpose, content, scope; Step A pins palette, atmosphere, and named visual references for the comps you're about to generate. The only time you can skip Step A is when the user has already answered these exact palette/atmosphere/reference questions in the same session. +Do not begin code until the user approves a direction or explicitly delegates the choice. If they delegate, choose using the task brief, PRODUCT.md, and DESIGN.md, and state the evidence. Approval refines the task concept; it does not modify DESIGN.md. -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: +After approval, summarize the composition and the parts of the comp that must not be literalized. Return to new-work.md and write the direction contract from the approved surface concept before code. -- "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.)" +## Inventory implementation fidelity -**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. +Before building, inventory the approved comp's major visible ingredients and choose an implementation medium for each: semantic HTML/CSS/SVG, existing project asset, generated raster, sourced raster, icon library, canvas/WebGL, or accepted omission. -## Step B: Generate the Brand Palette First +Pay special attention to the dominant composition, signature use, image-native content, second-fold system, and any interaction the still image only implies. If the concept depends on a photograph, architectural scene, product object, portrait, or other raster-native material, do not silently replace it with generic CSS scenery. -Generate **one** palette artifact before any mocks. This is a small, focused image: typography pairing on the chosen background, primary + accent color swatches, one signature ornament or motif. Single image, single pass. +Treat the comp as a north star, not something to trace. Do not rasterize core UI text or controls. Do not substitute a different visual driver after approval without asking. -Why palette first: mocks generated against a vague color sense produce noise that drowns out the structural decisions. A confirmed palette is the first concrete contract for everything downstream. +## Produce only the assets the build needs -Show the palette to the user. Ask one question: "This is the palette I'm locking in for the mocks. Confirm, or call out what to shift?" +When clean raster ingredients are required and a scoped subagent is available and authorized, use `impeccable_asset_producer`. Give it the approved comp, output paths, required dimensions and formats, transparency needs, crop notes, and what must remain semantic code. Otherwise produce the minimum required assets with the native image-generation capability in the current thread. -**STOP and wait for confirmation.** Do not generate mocks against an unconfirmed palette. "Probably good enough" is the wrong call here; the palette is the contract for everything downstream. - -## Step C: Generate 1-3 Visual Mocks Against the Palette - -Once the palette is confirmed, generate **1 to 3** high-fidelity north-star comps. Each mock must use the confirmed palette and typography. Mocks differ in *structural* direction (hierarchy, topology, density, composition), not in color or motif. - -- Brand work: push visual identity, composition, mood, and signature motifs. -- Product work: push hierarchy, topology, density, tone, grounded in realistic product structure. -- Landing pages and long-form brand surfaces: show enough of the second fold to establish the system beyond the hero. - -Use the `image_gen` tool directly (or via the imagegen skill when available). Don't ask the user to install anything. - -## Step D: Approval Loop - -Show the comps. Ask what carries forward. Iterate until **one direction is approved** or the user explicitly delegates. - -**STOP and wait for the approval or the delegation.** Do not begin Step E or return to the build until a single direction is named. If the user delegates, pick the strongest direction and explain it from the brief, not personal taste. - -Before moving to assets, summarize what to carry into code and what *not* to literalize from the mock. This is the handoff between visual exploration and semantic implementation. - -## Step E: Mock Fidelity Inventory - -Inventory the approved mock's major visible ingredients. For each, decide implementation: semantic HTML/CSS/SVG, generated raster, sourced raster, icon library, canvas/WebGL, or accepted omission. - -Common ingredients to inventory: - -- Hero silhouette and dominant composition -- Signature motifs (planets, devices, portraits, charts, route lines, insets, badges, etc.) -- Nav and primary-action treatment (when the surface has one) -- Section sequence, especially the second fold -- Image-native content the concept depends on -- Typography, density, color/material treatment, motion cues - -Treat the mock as a north star, not a screenshot to trace. Don't rasterize core UI text. But if the live result lacks the mock's major ingredients, the implementation is wrong. - -If a photographic, architectural, product, or place-led mock becomes generic CSS scenery, decorative diagrams, bullets, or copy, stop and fix it. That's a broken implementation, not a harmless interpretation. - -Don't substitute a different hero composition or visual driver post-approval without user sign-off. - -## Step F: Asset Slicing via the Asset Producer - -Raster ingredients identified in Step E need clean production assets. Use the bundled `impeccable_asset_producer` subagent rather than producing inline. - -Spawn it as a scoped subagent. If you do not have explicit permission to use agents, stop and ask: - -```text -Asset production will work better as a scoped subagent job. Should I spawn the Impeccable asset producer subagent for this step? -``` - -Pass to the agent: - -- Approved mock path or screenshot reference -- Crop paths or a contact sheet with crop ids -- Output directory -- Required dimensions, format, transparency needs -- Avoid list -- Notes on what should remain semantic HTML/CSS/SVG instead of raster - -Attach image generation capability to the spawned agent when the harness supports it. Do **not** load image-generation reference material into the parent thread. - -Inline asset production is allowed only if the user declines subagents, the harness cannot spawn the authorized agent, or the user explicitly asks for single-thread mode. - -Prefer HTML/CSS/SVG/canvas when they can credibly reproduce an ingredient; reach for real, generated, or stock imagery when the mock or subject matter calls for actual visual content. - -## After This File - -Once Steps A through F are complete, return to the build: implement per new-work.md and SKILL.md's craft floor. The implementation builds against the confirmed palette, approved mock, and the assets the producer wrote. +Return to [new-work.md](new-work.md) for the direction contract, implementation, and finishing pass. diff --git a/.github/skills/impeccable/reference/colorize.md b/.github/skills/impeccable/reference/colorize.md index abee1acf8..dc45f88c5 100644 --- a/.github/skills/impeccable/reference/colorize.md +++ b/.github/skills/impeccable/reference/colorize.md @@ -1,257 +1,86 @@ > **Additional context needed**: existing brand colors. -Replace timid grayscale or single-accent designs with a strategic palette: pick a color strategy, choose a hue family that fits the brand, then apply color with intent. More color ≠ better. Strategic color beats rainbow vomit. +Introduce color as hierarchy, meaning, and atmosphere. Preserve confirmed brand and semantic conventions; do not replace a visual world under the guise of colorizing it. --- -## Register +## Visitor mode -Persuade + Experience: palette IS voice. Pick a color strategy first per SKILL.md (Restrained / Committed / Full palette / Drenched) and follow its dosage. Committed, Full palette, and Drenched deliberately exceed the ≤10% rule; that rule is Restrained only. Unexpected combinations are allowed; a dominant color can own the page when the chosen strategy calls for it. +- **Persuade + Experience:** color may carry the voice and own large regions when the selected world calls for it. +- **Operate + Read:** color primarily encodes action, selection, status, wayfinding, and reading hierarchy. Rarity gives an accent force. -Operate + Read: semantic-first and almost always Restrained. Accent color is reserved for primary action, current selection, and state indicators. Not decoration. Every color has a consistent meaning across every screen. +## Audit before choosing ---- +Read DESIGN.md, tokens, assets, current themes, and representative states. Identify: -## Assess Color Opportunity +- which colors are confirmed brand commitments; +- current surface, text, action, and semantic roles; +- places where grayscale obscures hierarchy or state; +- contrast failures and color-only communication; +- light/dark or data-visualization requirements; +- whether the task asks for more color or a new identity. -Analyze the current state and identify opportunities: +If a new identity is required, use [new-work.md](new-work.md). Ask only when a binding brand decision cannot be inferred. -1. **Understand current state**: - - **Color absence**: Pure grayscale? Limited neutrals? One timid accent? - - **Missed opportunities**: Where could color add meaning, hierarchy, or delight? - - **Context**: What's appropriate for this domain and audience? - - **Brand**: Are there existing brand colors we should use? +## Choose a strategy -2. **Identify where color adds value**: - - **Semantic meaning**: Success (green), error (red), warning (yellow/orange), info (blue) - - **Hierarchy**: Drawing attention to important elements - - **Categorization**: Different sections, types, or states - - **Emotional tone**: Warmth, energy, trust, creativity - - **Wayfinding**: Helping users navigate and understand structure - - **Delight**: Moments of visual interest and personality +Name the intended emotional temperature, dominant relationship, contrast range, and color dosage before editing. The strategy may be restrained or immersive; it must follow the brief and selected world rather than a fixed percentage rule. -If any of these are unclear from the codebase, ask the user directly to clarify what you cannot infer. +Build roles, not a bag of swatches: -**CRITICAL**: More color ≠ better. Strategic color beats rainbow vomit every time. Every color should have a purpose. +- canvas and elevated surfaces; +- primary and secondary text; +- action, focus, and selection; +- borders and separators; +- success, warning, error, and information; +- data categories or scales when needed. -## Plan Color Strategy +Use the project's existing color space. For a new web palette, prefer OKLCH because lightness and chroma can be adjusted predictably. Choose hue from product meaning and visual direction, never from a default category association. -Create a purposeful color introduction plan: +## Apply at system scale -- **Color palette**: What colors match the brand/context? (Choose 2-4 colors max beyond neutrals) -- **Dominant color**: Which color owns 60% of colored elements? -- **Accent colors**: Which colors provide contrast and highlights? (30% and 10%) -- **Application strategy**: Where does each color appear and why? +- Let the strongest color own a deliberate region or role instead of scattering tiny accents. +- Keep the primary action easy to find; do not spend its color on decoration. +- Tint neutrals only when the brand hue genuinely creates cohesion. Neutral gray is valid when it serves the world. +- On colored surfaces, derive secondary text from the foreground or surface hue rather than using washed-out generic gray. +- Keep semantic meanings consistent, but respect platform and domain conventions instead of assuming fixed hues. +- For data, use distinct lightness, chroma, shape, label, or pattern so color is not the only code. +- 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. -**IMPORTANT**: Color should enhance hierarchy and meaning, not create chaos. Less is more when it matters more. +Decoration without a relationship to hierarchy, state, content, or the visual world is not a color strategy. -## Introduce Color Strategically +## Contrast and perception -Add color systematically across these dimensions: +Verify computed foreground/background pairs: -### Semantic Color -- **State indicators**: - - Success: Green tones (emerald, forest, mint) - - Error: Red/pink tones (rose, crimson, coral) - - Warning: Orange/amber tones - - Info: Blue tones (sky, ocean, indigo) - - Neutral: Gray/slate for inactive states +| Content | WCAG AA minimum | +|---|---| +| body text | 4.5:1 | +| large text | 3:1 | +| controls, icons, focus indicators | 3:1 | -- **Status badges**: Colored backgrounds or borders for states (active, pending, completed, etc.) -- **Progress indicators**: Colored bars, rings, or charts showing completion or health +Do not rely on eyesight alone. Check interactive states, overlays, text on images, disabled content, and both themes. Simulate common vision deficiencies. Information conveyed by color also needs text, shape, iconography, or position. -### Accent Color Application -- **Primary actions**: Color the most important buttons/CTAs -- **Links**: Add color to clickable text (maintain accessibility) -- **Icons**: Colorize key icons for recognition and personality -- **Headers/titles**: Add color to section headers or key labels -- **Hover states**: Introduce color on interaction +When deriving OKLCH ramps, vary lightness and reduce chroma near white and black. Do not keep high chroma at extreme lightness merely to make the math uniform. Prefer explicit colors over chains of translucent overlays when alpha would make contrast context-dependent. -### Background & Surfaces -- **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 toward the brand, not "for warmth" by reflex +## Verify -**Use OKLCH for color**: It's perceptually uniform, meaning equal steps in lightness *look* equal. Great for generating harmonious scales. - -### Data Visualization -- **Charts & graphs**: Use color to encode categories or values -- **Heatmaps**: Color intensity shows density or importance -- **Comparison**: Color coding for different datasets or timeframes - -### Borders & Accents -- **Hairline borders**: 1px colored borders on full perimeter (not side-stripes; see the absolute ban on `border-left/right > 1px`) -- **Underlines**: Color underlines for emphasis or active states -- **Dividers**: Subtle colored dividers instead of gray lines -- **Focus rings**: Colored focus indicators matching brand -- **Surface tints**: A 4-8% background wash of the accent color instead of a stripe - -**NEVER**: `border-left` or `border-right` greater than 1px as a colored accent stripe. This is one of the three absolute bans in the parent skill. If you want to mark a card as "active" or "warning", use a full hairline border, a background tint, a leading glyph, or a numbered prefix. Not a side stripe. - -### Typography Color -- **Colored headings**: Use brand colors for section headings (maintain contrast) -- **Highlight text**: Color for emphasis or categories -- **Labels & tags**: Small colored labels for metadata or categories - -### Decorative Elements -- **Illustrations**: Add colored illustrations or icons -- **Shapes**: Geometric shapes in brand colors as background elements -- **Gradients**: Colorful gradient overlays or mesh backgrounds -- **Blobs/organic shapes**: Soft colored shapes for visual interest - -## Balance & Refinement - -Ensure color addition improves rather than overwhelms: - -### Maintain Hierarchy -- **Dominant color** (60%): Primary brand color or most used accent -- **Secondary color** (30%): Supporting color for variety -- **Accent color** (10%): High contrast for key moments -- **Neutrals** (remaining): Gray/black/white for structure - -### Accessibility -- **Contrast ratios**: Ensure WCAG compliance (4.5:1 for text, 3:1 for UI components) -- **Don't rely on color alone**: Use icons, labels, or patterns alongside color -- **Test for color blindness**: Verify red/green combinations work for all users - -### Cohesion -- **Consistent palette**: Use colors from defined palette, not arbitrary choices -- **Systematic application**: Same color meanings throughout (green always = success) -- **Temperature consistency**: Warm palette stays warm, cool stays cool - -**NEVER**: -- 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 -- Violate WCAG contrast requirements -- Use color as the only indicator (accessibility issue) -- Make everything colorful (defeats the purpose) -- Default to purple-blue gradients (AI slop aesthetic) - -## Verify Color Addition - -Test that colorization improves the experience: - -- **Better hierarchy**: Does color guide attention appropriately? -- **Clearer meaning**: Does color help users understand states/categories? -- **More engaging**: Does the interface feel warmer and more inviting? -- **Still accessible**: Do all color combinations meet WCAG standards? -- **Not overwhelming**: Is color balanced and purposeful? +- Every color has a stable role or a world-specific atmospheric purpose. +- Attention lands on the intended action, content, or state. +- The palette works across quiet, dense, interactive, error, and empty states. +- Light and dark themes are each composed, not mechanically inverted. +- Contrast and non-color cues pass in all relevant states. +- The result is recognizably this product, not a generic “colorful” treatment. When the palette earns its place, hand off to `/impeccable polish` for the final pass. ## Live-mode signature params -When invoked from live mode, each variant MUST declare a `color-amount` param so the user can dial between a restrained accent and a drenched surface without regeneration. Author the variant's CSS against `var(--p-color-amount, 0.5)`, typically as the alpha multiplier on backgrounds, or as a scaling factor on the chroma axis in an OKLCH expression. 0 = neutral/monochrome, 1 = full saturation / dominant coverage. +When invoked from live mode, every variant declares a `color-amount` parameter. Author CSS against `var(--p-color-amount, 0.5)` so the user can move from neutral to the variant's full color strategy without regeneration. ```json {"id":"color-amount","kind":"range","min":0,"max":1,"step":0.05,"default":0.5,"label":"Color amount"} ``` -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). +Add at most two variant-specific parameters, such as palette, temperature, or tint behavior. Follow [live.md](live.md)'s parameter contract. diff --git a/.github/skills/impeccable/reference/craft.md b/.github/skills/impeccable/reference/craft.md index cc7f98d37..dbbc9402c 100644 --- a/.github/skills/impeccable/reference/craft.md +++ b/.github/skills/impeccable/reference/craft.md @@ -1,3 +1,5 @@ # Craft (deprecated alias) -`craft` is a deprecated alias for the standard build flow. Builds route through SKILL.md's new-work gate; [new-work.md](new-work.md) carries the direction checkpoint ("Decide, then build"), the identity playbook, and the finishing pass. Invoking `craft` explicitly forces the attended checkpoints: pause at the stated direction for confirmation, and follow [codex.md](codex.md)'s mock flow when the harness generates images. Nothing else differs from a plain build request. +`craft` is a deprecated alias for an ordinary request to make new visual work. It adds no setup, interview, checkpoint, tool, or quality behavior. Apply SKILL.md's normal routing: create missing PRODUCT.md through [init.md](init.md), then follow [new-work.md](new-work.md) for visual authority, world and surface decisions, implementation, and finish. + +Do not tell users they need to invoke `craft`. Natural requests such as “build this feature,” “make a landing page,” or “redesign this screen” use the same flow. diff --git a/.github/skills/impeccable/reference/critique.md b/.github/skills/impeccable/reference/critique.md index 74de97307..6ca231cee 100644 --- a/.github/skills/impeccable/reference/critique.md +++ b/.github/skills/impeccable/reference/critique.md @@ -43,13 +43,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 @@ -115,11 +115,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. @@ -176,7 +176,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. 2. **Pass the structured metadata** through `IMPECCABLE_CRITIQUE_META` (JSON), then run the write command: ```bash diff --git a/.github/skills/impeccable/reference/delight.md b/.github/skills/impeccable/reference/delight.md index 596cff09e..40aaddc34 100644 --- a/.github/skills/impeccable/reference/delight.md +++ b/.github/skills/impeccable/reference/delight.md @@ -1,302 +1,70 @@ -> **Additional context needed**: what's appropriate for the domain (playful vs professional vs quirky vs elegant). +> **Additional context needed**: the brand's emotional range. -Find the moments where personality and unexpected polish would turn a functional interface into one users remember and tell other people about. Add only where the moment earns it; delight everywhere reads as noise. +Make the experience memorable at moments that earn it. Delight is not a layer of generic whimsy; it is product character revealed through a useful interaction, a humane response, or an unexpectedly considered detail. --- -## Register +## Visitor mode -Persuade + Experience: delight can be distributed across copy voice, section transitions, discovery rewards, seasonal touches, personality across the whole surface. +- **Persuade + Experience:** personality may run through voice, composition, motion, and discovery, provided the artifact remains the focus. +- **Operate + Read:** concentrate delight at meaningful moments such as first use, completion, recovery, or mastery. Reliability carries everything else. -Operate + Read: delight at specific moments, not pages. Completion, first-time actions, error recovery, milestone crossings. Reliability and consistency carry the rest of the experience; delight pushed everywhere reads as noise. +## Find the opportunity ---- +Inspect the target, DESIGN.md, product voice, repeated-use frequency, and emotional context. Look for: -## Assess Delight Opportunities +- effort worth acknowledging; +- waiting that can become informative; +- an empty or first-use state that can orient; +- an error or recovery moment that needs empathy; +- an interaction whose physical or verbal response could express the brand; +- a useful capability people might enjoy discovering. -Identify where delight would enhance (not distract from) the experience: +Do not manufacture a celebration for an ordinary click. Ask only when the brand's emotional range or the stakes cannot be inferred. -1. **Find natural delight moments**: - - **Success states**: Completed actions (save, send, publish) - - **Empty states**: First-time experiences, onboarding - - **Loading states**: Waiting periods that could be entertaining - - **Achievements**: Milestones, streaks, completions - - **Interactions**: Hover states, clicks, drags - - **Errors**: Softening frustrating moments - - **Easter eggs**: Hidden discoveries for curious users +## Define one delight thesis -2. **Understand the context**: - - What's the brand personality? (Playful? Professional? Quirky? Elegant?) - - Who's the audience? (Tech-savvy? Creative? Corporate?) - - What's the emotional context? (Accomplishment? Exploration? Frustration?) - - What's appropriate? (Banking app ≠ gaming app) +State in one sentence what the user should feel and why that feeling belongs to this product. Then choose the smallest system that can deliver it: -3. **Define delight strategy**: - - **Subtle sophistication**: Refined micro-interactions (luxury brands) - - **Playful personality**: Whimsical illustrations and copy (consumer apps) - - **Helpful surprises**: Anticipating needs before users ask (productivity tools) - - **Sensory richness**: Satisfying sounds, smooth animations (creative tools) +- a distinctive response to a meaningful action; +- product-specific language that clarifies while carrying voice; +- an interaction or transition with a recognizable material behavior; +- an illustration, sound, haptic, or environmental detail grounded in the product world; +- a discovery reward that reveals real utility. -If any of these are unclear from the codebase, ask the user directly to clarify what you cannot infer. +Derive the treatment from product mechanism and visual world, not a stock catalog. -**CRITICAL**: Delight should enhance usability, never obscure it. If users notice the delight more than accomplishing their goal, you've gone too far. +## Build for the emotional moment -## Delight Principles +- **Success:** match the response to the effort and consequence. Major milestones can expand; routine saves should simply feel certain. +- **Waiting:** show truthful progress, useful context, or product-specific activity. Never fake work or delay completion to stage a flourish. +- **Empty and first use:** make the next action clear before adding personality. +- **Error and recovery:** lead with the problem and recovery. Warmth may reduce stress; jokes must not trivialize loss, money, privacy, or blocked work. +- **Repeated interaction:** keep the response satisfying after the hundredth use. Variation is useful only when it remains coherent and predictable enough to trust. +- **Discovery:** reward curiosity without hiding required functionality. -Follow these guidelines: +Copy must use the product's language. Generic whimsy is worse than neutral clarity. -### Delight Amplifies, Never Blocks -- Delight moments should be quick (< 1 second) -- Never delay core functionality for delight -- Make delight skippable or subtle -- Respect user's time and task focus +## Protect the experience -### Surprise and Discovery -- Hide delightful details for users to discover -- Reward exploration and curiosity -- Don't announce every delight moment -- Let users share discoveries with others +Delight must not: -### Appropriate to Context -- Match delight to emotional moment (celebrate success, empathize with errors) -- Respect the user's state (don't be playful during critical errors) -- Match brand personality and audience expectations -- Cultural sensitivity (what's delightful varies by culture) +- delay, block, or obscure the primary task; +- override platform conventions or accessibility; +- add unrequested factual claims; +- play sound without consent or ignore mute settings; +- become mandatory, unskippable, or exhausting on repeat; +- add a dependency or asset cost disproportionate to the moment. -### Compound Over Time -- Delight should remain fresh with repeated use -- Vary responses (not same animation every time) -- Reveal deeper layers with continued use -- Build anticipation through patterns +For authored motion, load [animate.md](animate.md). Respect reduced motion, screen readers, keyboard use, touch, localization, and cultural context. Nonessential loops stop when hidden. Make celebration intensity proportional to frequency and consequence. -## Delight Techniques +## Verify -Add personality and joy through these methods: +- The moment is specific enough that a neighboring product could not use it unchanged. +- It improves comprehension, confidence, motivation, or emotional recovery. +- The interface remains fast and obvious without the flourish. +- Repetition does not turn charm into friction. +- Reduced-motion, muted, keyboard, touch, and localized paths work. +- The result feels like the selected world, not a generic “delight” treatment. -### Micro-interactions & Animation - -**Button delight**: -```css -/* Satisfying button press */ -.button { - transition: transform 0.1s, box-shadow 0.1s; -} -.button:active { - transform: translateY(2px); - box-shadow: 0 2px 4px rgba(0,0,0,0.2); -} - -/* Ripple effect on click */ -/* Smooth lift on hover */ -.button:hover { - transform: translateY(-2px); - transition: transform 0.2s cubic-bezier(0.25, 1, 0.5, 1); /* ease-out-quart */ -} -``` - -**Loading delight**: -- Playful loading animations (not just spinners) -- Personality in loading messages (write product-specific ones, not generic AI filler) -- Progress indication with encouraging messages -- Skeleton screens with subtle animations - -**Success animations**: -- Checkmark draw animation -- Confetti burst for major achievements -- Gentle scale + fade for confirmation -- Satisfying sound effects (subtle) - -**Hover surprises**: -- Icons that animate on hover -- Color shifts or glow effects -- Tooltip reveals with personality -- Cursor changes (custom cursors for branded experiences) - -### Personality in Copy - -**Playful error messages**: -``` -"Error 404" -"This page is playing hide and seek. (And winning)" - -"Connection failed" -"Looks like the internet took a coffee break. Want to retry?" -``` - -**Encouraging empty states**: -``` -"No projects" -"Your canvas awaits. Create something amazing." - -"No saved articles" -"Nothing on the nightstand yet. Save a story for later." -``` - -**Playful labels & tooltips**: -``` -"Delete" -"Send to void" (for playful brand) - -"Help" -"Rescue me" (tooltip) -``` - -**IMPORTANT**: Match copy personality to brand. Banks shouldn't be wacky, but they can be warm. - -### Illustrations & Visual Personality - -**Custom illustrations**: -- Empty state illustrations (not stock icons) -- Error state illustrations (friendly monsters, quirky characters) -- Loading state illustrations (animated characters) -- Success state illustrations (celebrations) - -**Icon personality**: -- Custom icon set matching brand personality -- Animated icons (subtle motion on hover/click) -- Illustrative icons (more detailed than generic) -- Consistent style across all icons - -**Background effects**: -- Subtle particle effects -- Gradient mesh backgrounds -- Geometric patterns -- Parallax depth -- Time-of-day themes (morning vs night) - -### Satisfying Interactions - -**Drag and drop delight**: -- Lift effect on drag (shadow, scale) -- Snap animation when dropped -- Satisfying placement sound -- Undo toast ("Dropped in wrong place? [Undo]") - -**Toggle switches**: -- Smooth slide with spring physics -- Color transition -- Haptic feedback on mobile -- Optional sound effect - -**Progress & achievements** (Operate surfaces with recurring tasks only; gamification on editorial, portfolio, or one-visit surfaces reads as noise): -- Streak counters with celebratory milestones -- Progress bars that "celebrate" at 100% -- Badge unlocks with animation -- Playful stats ("You're on fire! 5 days in a row") - -**Form interactions**: -- Input fields that animate on focus -- Checkboxes with a satisfying scale pulse when checked -- Success state that celebrates valid input -- Auto-grow textareas - -### Sound Design - -**Subtle audio cues** (when appropriate): -- Notification sounds (distinctive but not annoying) -- Success sounds (satisfying "ding") -- Error sounds (empathetic, not harsh) -- Typing sounds for chat/messaging -- Ambient background audio (very subtle) - -**IMPORTANT**: -- Respect system sound settings -- Provide mute option -- Keep volumes quiet (subtle cues, not alarms) -- Don't play on every interaction (sound fatigue is real) - -### Easter Eggs & Hidden Delights - -**Discovery rewards**: -- Konami code unlocks special theme -- Hidden keyboard shortcuts (Cmd+K for special features) -- Hover reveals on logos or illustrations -- Alt text jokes on images (for screen reader users too!) -- A view-source or console colophon (a note on the typefaces, a thank-you, a hiring line) - -**Seasonal touches**: -- Holiday themes (subtle, tasteful) -- Seasonal color shifts -- Weather-based variations -- Time-based changes (dark at night, light during day) - -**Contextual personality**: -- Different messages based on time of day -- Responses to specific user actions -- Randomized variations (not same every time) -- Progressive reveals with continued use - -### Loading & Waiting States - -**Make waiting engaging**: -- Interesting loading messages that rotate -- Progress bars with personality -- Mini-games during long loads -- Fun facts or tips while waiting -- Countdown with encouraging messages - -``` -Loading messages: write ones specific to your product, not generic AI filler: -- "Syncing with your team's changes..." -- "Fetching this week's issue..." -- "Developing your photos..." -- "Checking tomorrow's tide tables..." -``` - -**WARNING**: Avoid cliched loading messages like "Herding pixels", "Teaching robots to dance", "Consulting the magic 8-ball", "Counting backwards from infinity". These are AI-slop copy, instantly recognizable as machine-generated. Write messages that are specific to what your product actually does. - -### Celebration Moments - -**Success celebrations**: -- Confetti for major milestones -- Animated checkmarks for completions -- Progress bar celebrations at 100% -- "Achievement unlocked" style notifications -- Personalized messages ("You published your 10th article!") - -**Milestone recognition** (same scoping as Progress & achievements: Operate surfaces with recurring tasks): -- First-time actions get special treatment -- Streak tracking and celebration -- Progress toward goals -- Anniversary celebrations - -## Implementation Patterns - -**Animation libraries**: -- Framer Motion (React) -- GSAP (universal) -- Lottie (After Effects animations) -- Canvas confetti (party effects) - -**Sound libraries**: -- Howler.js (audio management) -- Use-sound (React hook) - -**Physics libraries**: -- React Spring (spring physics) -- Popmotion (animation primitives) - -**IMPORTANT**: File size matters. Compress images, optimize animations, lazy load delight features. - -**NEVER**: -- Delay core functionality for delight -- Force users through delightful moments (make skippable) -- Use delight to hide poor UX -- Overdo it (less is more) -- Ignore accessibility (animate responsibly, provide alternatives) -- Make every interaction delightful (special moments should be special) -- Sacrifice performance for delight -- Be inappropriate for context (read the room) - -## Verify Delight Quality - -Test that delight actually delights: - -- **User reactions**: Do users smile? Share screenshots? -- **Doesn't annoy**: Still pleasant after 100th time? -- **Doesn't block**: Can users opt out or skip? -- **Performant**: No jank, no slowdown -- **Appropriate**: Matches brand and context -- **Accessible**: Works with reduced motion, screen readers - -When the moments feel earned, hand off to `/impeccable polish` for the final pass. +When the personality feels earned, hand off to `/impeccable polish` for the final pass. diff --git a/.github/skills/impeccable/reference/document.md b/.github/skills/impeccable/reference/document.md index 0d38eb568..121a00ab1 100644 --- a/.github/skills/impeccable/reference/document.md +++ b/.github/skills/impeccable/reference/document.md @@ -1,6 +1,6 @@ Generate a `DESIGN.md` file at the project root that captures the current visual design system, so AI agents generating new screens stay on-brand. -DESIGN.md follows the [official DESIGN.md format spec](https://raw.githubusercontent.com/google-labs-code/design.md/main/docs/spec.md): YAML frontmatter carrying machine-readable design tokens, followed by a markdown body with exactly six sections in a fixed order. **Tokens are normative; prose provides context for how to apply them.** Sections may be omitted when not relevant, but **do not reorder them and do not rename them**. Section headers must match the spec character-for-character so the file stays parseable by other DESIGN.md-aware tools (Stitch itself, awesome-design-md, skill-rest, etc.). +DESIGN.md follows the [official DESIGN.md format spec](https://raw.githubusercontent.com/google-labs-code/design.md/main/docs/spec.md): optional YAML frontmatter carrying machine-readable design tokens, followed by up to eight markdown sections in a fixed order. **Tokens are normative; prose provides context for how to apply them.** Sections may be omitted when not relevant, but those present stay in the specified order. Use the canonical headings below so the file remains portable across DESIGN.md-aware tools. ## The frontmatter: token schema @@ -43,26 +43,28 @@ components: 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. +- **Colors accept any valid CSS color string.** Hex is the recommended default for portability, but preserve an incumbent `rgb()`, `hsl()`, `oklch()`, wide-gamut, or mixed-color value when it is the project's normative source. 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 (`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) +## The markdown body: eight sections (canonical order) 1. `## Overview` 2. `## Colors` 3. `## Typography` -4. `## Elevation` -5. `## Components` -6. `## Do's and Don'ts` +4. `## Layout` +5. `## Elevation & Depth` +6. `## Shapes` +7. `## Components` +8. `## Do's and Don'ts` -Optional evocative subtitles are allowed in the form `## 2. Colors: The [Name] Palette` (Stitch's own outputs do this), but the literal word in each header (Overview, Colors, Typography, Elevation, Components, Do's and Don'ts) must be present. Do NOT add extra top-level sections (Layout Principles, Responsive Behavior, Motion, Agent Prompt Guide). Fold that content into the six spec sections where it naturally belongs. +Omit irrelevant sections rather than filling them with invented rules. Put responsive layout in Layout, depth in Elevation & Depth, radius and form language in Shapes, and per-component behavior in Components. Unknown sections are preserved by the format, but new visual guidance should use the canonical structure whenever it fits. ## When to run -- 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. +- New-work found a coherent incumbent visual system but no `DESIGN.md`. +- The first implementation of a new world is complete and its provisional decisions need to be carbonized. - An existing `DESIGN.md` is stale (the design has drifted). - Before a large redesign, to capture the current state as a reference. @@ -71,9 +73,9 @@ 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 init, nothing built yet). Interview for five high-level answers, write a minimal DESIGN.md marked ``. Re-run in scan mode once there's code. +- **Seed mode**: the project is pre-implementation. Ensure PRODUCT.md exists, then reuse new-work's visual-world workshop and write its directional DESIGN.md 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. +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` requests new-work's world workshop, but it does not authorize replacing coherent code: when an incumbent system exists, offer scan mode or route an explicit identity-replacement request through new-work. ## Scan mode (approach C: auto-extract, then confirm descriptive language) @@ -97,7 +99,8 @@ Build a structured draft from the discovered tokens. For each token class: - **Typography**: Map observed sizes and weights to the Material hierarchy (display / headline / title / body / label). Note font-family stacks and the scale ratio. - **Elevation**: Catalogue the shadow vocabulary. If the project is flat and uses tonal layering instead, that's a valid answer; state it explicitly. - **Components**: For each common component (button, card, input, chip, list item, tooltip, nav), extract shape (radius), color assignment, hover/focus treatment, internal padding. -- **Spacing + layout**: Fold into Overview or relevant Components. The spec does NOT have a Layout section. +- **Layout + spacing**: Extract grid, container, breakpoint, rhythm, and density behavior into Layout. +- **Shapes**: Extract radius, corner, border, clipping, and recurring form behavior into Shapes. ### Step 2b: Stage the frontmatter @@ -112,19 +115,19 @@ Skip anything the project doesn't have. Empty scale keys or fabricated tokens po ### Step 3: Ask the user for qualitative language -The following require creative input that cannot be auto-extracted. Group them into one `AskUserQuestion` interaction: +The following require creative input that cannot be auto-extracted. Ask them in two structured rounds of no more than three questions each (or the harness's lower limit), waiting between rounds: - **Creative North Star**: a single named metaphor for the whole system ("The Editorial Sanctuary", "The Golden State Curator", "The Lab Notebook"). Offer 2-3 options that honor PRODUCT.md's brand personality. -- **Overview voice**: mood adjectives, aesthetic philosophy in 2-3 sentences, anti-references (what the system should not feel like). +- **Overview voice**: mood adjectives, aesthetic philosophy in 2-3 sentences, and any confirmed visual anti-reference. - **Color character** (for auto-extracted colors): descriptive names ("Deep Muted Teal-Navy", not "blue-800"). Suggest 2-3 options per key color based on hue/saturation. - **Elevation philosophy**: flat/layered/lifted. If shadows exist, is their role ambient or structural? - **Component philosophy**: the feel of buttons, cards, inputs in one phrase ("tactile and confident" vs. "refined and restrained"). -Quote a line from PRODUCT.md when possible so the user sees their own strategic language carry forward. +Carry a line from PRODUCT.md only when it is a durable brand commitment that actually constrains the visual system. Page strategy and surface concepts do not belong here. ### Step 4: Write DESIGN.md -The file opens with the YAML frontmatter staged in Step 2b (schema documented at the top of this reference), then the markdown body using the structure below. Headers must match character-for-character. Optional evocative subtitles (e.g. `## 2. Colors: The Coastal Palette`) are allowed. +The file opens with the YAML frontmatter staged in Step 2b (schema documented at the top of this reference), then the markdown body using the canonical structure below. ```markdown --- @@ -136,13 +139,13 @@ colors: # Design System: [Project Title] -## 1. Overview +## Overview **Creative North Star: "[Named metaphor in quotes]"** -[2-3 paragraph holistic description: personality, density, aesthetic philosophy. Start from the North Star and work outward. State what this system explicitly rejects (pulled from PRODUCT.md's anti-references). End with a short **Key Characteristics:** bullet list.] +[2-3 paragraph holistic description: personality, density, and aesthetic philosophy. Start from the North Star and work outward. State only confirmed visual rejections. End with a short **Key Characteristics:** bullet list.] -## 2. Colors +## Colors [Describe the palette character in one sentence.] @@ -162,7 +165,7 @@ colors: ### Named Rules (optional, powerful) **The [Rule Name] Rule.** [Short, forceful prohibition or doctrine, e.g. "The One Voice Rule. The primary accent is used on ≤10% of any given screen. Its rarity is the point."] -## 3. Typography +## Typography **Display Font:** [Family] (with [fallback]) **Body Font:** [Family] (with [fallback]) @@ -180,7 +183,11 @@ colors: ### Named Rules (optional) **The [Rule Name] Rule.** [Short doctrine about type use.] -## 4. Elevation +## Layout + +[Describe the grid or spatial model, container behavior, density, responsive changes, and the spacing rhythm. Include exact values only when observed.] + +## Elevation & Depth [One paragraph: does this system use shadows, tonal layering, or a hybrid? If "no shadows", say so explicitly and describe how depth is conveyed instead.] @@ -191,7 +198,11 @@ colors: ### Named Rules (optional) **The [Rule Name] Rule.** [e.g. "The Flat-By-Default Rule. Surfaces are flat at rest. Shadows appear only as a response to state (hover, elevation, focus)."] -## 5. Components +## Shapes + +[Describe the form language: corner/radius strategy, borders, clipping, and any recurring silhouette or geometry.] + +## Components For each component, lead with a short character line, then specify shape, color assignment, states, and any distinctive behavior. @@ -223,16 +234,16 @@ For each component, lead with a short character line, then specify shape, color ### [Signature Component] (optional; if the project has a distinctive custom component worth documenting) [Description.] -## 6. Do's and Don'ts +## Do's and Don'ts -Concrete, forceful guardrails. Lead each with "Do" or "Don't". Be specific: include exact colors, pixel values, and named anti-patterns the user mentioned in PRODUCT.md. **Every anti-reference in PRODUCT.md should show up here as a "Don't" with the same language**, so the visual spec carries the strategic line through. Quote PRODUCT.md directly where possible: if PRODUCT.md says *"avoid dark mode with purple gradients, neon accents, glassmorphism"*, the Don'ts here should repeat that by name. +Concrete visual guardrails grounded in the incumbent implementation or the user's chosen world. Lead each with "Do" or "Don't" and include exact values only when established. Do not turn a task-specific concept or surface strategy into a system-wide prohibition. ### Do: - **Do** [specific prescription with exact values / named rule]. - **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** [...] ``` @@ -307,7 +318,7 @@ The `html` and `css` fields must be **self-contained, drop-in snippets** that re Aim for a tight set of **5-10 components** that best represent the visual system: - **Canonical primitives (always include if the project has them):** button (each variant as a separate component entry), input/text field, navigation, chip/tag, card. -- **Signature components (include if distinctive):** hero CTA, featured card, filter pill, a distinctive table or list-row treatment, any custom pattern the user mentioned as important in PRODUCT.md. +- **Signature components (include if distinctive):** the recurring custom patterns that actually define the implemented system. - **Skip the rest.** Utility components, form building blocks, wrapper layouts: not worth documenting unless visually distinctive. If the project has **no component library yet** (bare landing page, new project), synthesize canonical primitives from the tokens using best-practice defaults consistent with the DESIGN.md's rules. Every `.impeccable/design.json` has *something* to render, even on day zero. @@ -338,64 +349,40 @@ Your own write is the freshest source; subsequent commands in this session don't ## Seed mode -For projects with no visual system to extract yet. Produces a minimal scaffold, not a full spec. +For projects with no visual system to extract yet. Produces a user-chosen visual-world scaffold, not a fabricated token spec. -### Step 1: Confirm seed mode +### Step 1: Route through new-work's workshop -Before interviewing: "There's no existing visual system to scan. I'll ask five quick questions to seed a starter DESIGN.md. You can re-run `/impeccable document` once there's code, to capture the real tokens and components. OK?" +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 the user prefers to skip, stop. No file. +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. -### Step 2: Five questions +If new-work already completed the workshop in this session, use its chosen direction directly. Do not ask again. -Group into one `AskUserQuestion` interaction. Options must be concrete. +### Step 2: Write seed DESIGN.md -1. **Color strategy.** Pick one: - - Restrained: tinted neutrals + one accent ≤10% - - Committed: one saturated color carries 30–60% of the surface - - Full palette: 3–4 named color roles, each deliberate - - Drenched: the surface IS the color - - Then: one hue family or anchor reference ("deep teal", "mustard", "Klim #ff4500 orange"). - -2. **Typography direction.** Pick one (specific fonts come later): - - Serif display + sans body - - Single sans (warm / technical / geometric / humanist; pick a feel) - - Display + mono - - Mono-forward - - Editorial script + sans - -3. **Motion energy.** Pick one: - - Restrained: state changes only - - Responsive: feedback + transitions, no choreography - - Choreographed: orchestrated entrances, scroll-driven sequences - -4. **Three named references.** Brands, products, printed objects. Not adjectives. - -5. **One anti-reference.** What it should NOT feel like. Also named. - -### Step 3: Write seed DESIGN.md - -Use the six-section spec from Scan mode. Populate what the interview answers; leave the rest as honest placeholders. The seed is a scaffold, not a fabricated spec. +Use the canonical section order from Scan mode. Populate the selected workshop direction and leave unresolved implementation facts as honest placeholders. The seed commits a world and its invariants; it does not pretend implementation tokens already exist. Lead the file with: ```markdown - + ``` Per-section guidance in seed mode: -- **Overview**: Creative North Star and philosophy phrased from the answers (color strategy + motion energy + references). Reference the user's anti-reference directly. -- **Colors**: Color strategy as a Named Rule (e.g. *"The Drenched Rule. The surface IS the color."*). Hue family or anchor reference. No hex values; mark as `[to be resolved during implementation]`. -- **Typography**: the direction the user picked (e.g. "Serif display + sans body"). No font names yet: `[font pairing to be chosen at implementation]`. -- **Elevation**: inferred from motion energy. Restrained/Responsive → flat by default; Choreographed → layered. One sentence. +- **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. +- **Elevation & Depth**: the selected material and depth behavior, stated as an invariant rather than inferred from a generic preset. +- **Shapes**: the selected form and corner language. - **Components**: omit entirely; no components exist yet. -- **Do's and Don'ts**: carry PRODUCT.md's anti-references directly plus the anti-reference named in Q5. +- **Do's and Don'ts**: record the durable guardrails confirmed during the world choice, not task-local refusals. 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 +### Step 3: 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." @@ -405,15 +392,15 @@ Your own write is the freshest source; no reload needed. ## Style guidelines - **Frontmatter first, prose second.** Tokens go in the YAML frontmatter; prose contextualizes them. Don't redefine a token value in two places; the frontmatter is normative. -- **Cite PRODUCT.md anti-references by name** in the Do's and Don'ts section. If PRODUCT.md lists "SaaS landing-page clichés" or "generic AI tool marketing" as anti-references, the DESIGN.md Don'ts should repeat those phrases verbatim so the visual spec enforces the strategic line. -- **Match the spec, don't invent new sections.** The six section names are fixed. If you have Layout/Motion/Responsive content to document, fold it into Overview (philosophy-level rules) or Components (per-component behavior). +- **Carry only durable product constraints.** A binding logo, identity asset, accessibility need, or brand commitment from PRODUCT.md may constrain DESIGN.md. Surface strategy stays in its surface brief. +- **Match the spec.** Use its eight canonical sections in order and omit any that are irrelevant. Put motion guidance with the world or component it affects rather than creating a token group the schema does not support. - **Descriptive > technical**: "Gently curved edges (8px radius)" > "rounded-lg". Include the technical value in parens, lead with the description. - **Functional > decorative**: for each token, explain WHERE and WHY it's used, not just WHAT it is. - **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 forceful**. The voice of a design director. "Prohibited", "forbidden", "never", "always", not "consider", "might", "prefer". Match PRODUCT.md's tone. -- **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. -- **Reference PRODUCT.md**. The anti-references section of PRODUCT.md should directly inform the Do's and Don'ts section here. Quote or paraphrase. +- **Be decisive where evidence is decisive.** Use hard language for actual invariants and softer language for provisional guidance. +- **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. ## Pitfalls @@ -423,7 +410,7 @@ Your own write is the freshest source; no reload needed. - Don't invent components that don't exist. If the project only has buttons and cards, only document those. - Don't overwrite an existing DESIGN.md without asking. - Don't duplicate content from PRODUCT.md. DESIGN.md is strictly visual. -- Don't add a "Layout Principles" or "Motion" or "Responsive Behavior" top-level section. The spec has six, not nine. Fold that content where it belongs. +- Don't replace canonical sections with near-synonyms. Put layout and responsive behavior in `Layout`; put motion with the affected world or component. - Don't rename sections even slightly. "Colors" not "Color Palette & Roles". "Typography" not "Typography Rules". Tooling parsing depends on exact headers. - Don't duplicate token values between frontmatter and prose. If a color is in `colors.primary` as hex, the prose can name it and describe its role but should not reassert a different hex. The frontmatter is normative. - Don't invent frontmatter token groups outside Stitch's schema (no `motion:`, `breakpoints:`, `shadows:` at the top level). Stitch's Zod schema only accepts `colors`, `typography`, `rounded`, `spacing`, `components`. Anything else belongs in the sidecar's `extensions`. diff --git a/.github/skills/impeccable/reference/hooks.md b/.github/skills/impeccable/reference/hooks.md index 22a7caf38..dc6160b02 100644 --- a/.github/skills/impeccable/reference/hooks.md +++ b/.github/skills/impeccable/reference/hooks.md @@ -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. diff --git a/.github/skills/impeccable/reference/init.md b/.github/skills/impeccable/reference/init.md index 969a6c0d1..d3e3a1fbd 100644 --- a/.github/skills/impeccable/reference/init.md +++ b/.github/skills/impeccable/reference/init.md @@ -1,224 +1,114 @@ -# Init Flow +# Init flow -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 [DESIGN.md format spec](https://raw.githubusercontent.com/google-labs-code/design.md/main/docs/spec.md). Answers "how it looks". -- **`.impeccable/live/config.json`** (live mode): pre-configured so `/impeccable live` boots straight into variant mode with no first-time detour. - -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. +`init` captures durable product truth in PRODUCT.md. It does not invent a visual world and does not write DESIGN.md; [new-work.md](new-work.md) creates or expands one, and [document.md](document.md) records an incumbent one. Existing runnable web projects may also receive `.impeccable/live/config.json`. ## Step 1: Load current state -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). +Use the PRODUCT.md path resolved by context.mjs. Update it instead of creating a competing authority. In a child app inheriting root context, confirm shared versus app-specific scope before writing. -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. -- **PRODUCT.md exists, DESIGN.md missing**: skip to Step 5 and offer to run `/impeccable document` for DESIGN.md. -- **PRODUCT.md exists but has no `## Register` section (legacy)**: add it. Infer a hypothesis from the codebase (see Step 2), confirm with the user, write the field. -- **PRODUCT.md exists but has no `## Platform` section (legacy)**: add it the same way, but only when the project is native (`ios` / `android` / `adaptive`) or the user wants it explicit; a missing field already means `web`. -- **Both exist**: ask the user directly to clarify what you cannot infer. Ask which file to refresh. Skip the one the user doesn't want changed. -- **Just DESIGN.md exists (unusual)**: do Steps 2-4 to produce PRODUCT.md. +- **No PRODUCT.md:** explore, interview, and write it. +- **PRODUCT.md exists:** ask what product knowledge is stale or missing; do not reopen confirmed fields without a reason. +- **Legacy PRODUCT.md:** add only durable missing facts; absent `## Platform` means `web` unless evidence says otherwise. +- **Only DESIGN.md exists:** leave it untouched and create PRODUCT.md. +- **Redesign/rebrand request:** preserve confirmed product truth unless the user changes it. Visual replacement happens later in new-work, not here. -Never silently overwrite an existing file. Always confirm first. +Never silently overwrite an existing file or offer DESIGN.md during init. If another request invoked init, finish PRODUCT.md and resume it. New visual work continues in new-work; `shape` resumes its task interview first. -If init was invoked as a setup blocker by another command, such as `/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 project -## Step 2: Explore the codebase +Before asking, scan enough to avoid making the user repeat known facts: product docs and copy; package/config and app boundaries; features, workflows, routes, and roles; names, logos, legal/proof assets, and brand commitments; platform/accessibility signals; and the dev command/entry when live mode applies. -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: +Treat repository evidence as a hypothesis, not user approval. Note visual maturity without documenting, extending, or replacing the world. -- **README and docs**: Project purpose, target audience, any stated goals -- **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 -- **Any style guides or brand documentation** +Form a platform hypothesis: `web`, `ios`, `android`, or `adaptive` (one product that genuinely adapts its design language per OS). Mobile web remains `web`; a native wrapper around a website does not make its design language native. -Also form a **register hypothesis** from what you find: +## Step 3: Interview for product truth -- Brand signals: `/`, `/about`, `/pricing`, `/blog/*`, `/docs/*`, hero sections, big typography, scroll-driven sections, landing-page-shaped content. -- Product signals: `/app/*`, `/dashboard`, `/settings`, `/(auth)`, forms, data tables, side/top nav, app-shell components. +ask the user directly to clarify what you cannot infer. Ask only about material gaps the repository and original request do not answer with strong evidence. -Register is a hypothesis at this point, not a decision; Step 3 confirms it. +Use the structured question tool when available; otherwise ask and wait. Keep rounds to at most three focused questions and require one real answer or approval round before writing a new PRODUCT.md. Confirm inferences. -Also form a **platform hypothesis**: +Start with the unknowns that most change future product decisions: -- Native signals: React Native / Expo (`react-native`, `expo`), Flutter (`pubspec.yaml`, `flutter`), SwiftUI / UIKit (`.swift`, `.xcodeproj`, an `ios/` app target), Jetpack Compose / Android (`build.gradle`, an `android/` app module, `AndroidManifest.xml`). An `ios/` and/or `android/` directory that is a real app target, not just a Capacitor/Cordova wrapper around a website. -- Web signals (the default): a web framework (Vite, Next, Nuxt, SvelteKit, Astro), an HTML entry, a CSS/Tailwind setup, no native app target. +1. Who is the primary user, in what situation, and what job are they doing? +2. What does the product make possible, and what is its meaningfully different mechanism or position? +3. What durable constraints, assets, evidence, or product facts must future work preserve? -Values: `web` / `ios` / `android` / `adaptive` (one codebase, ships both, adapts per OS). Mobile web is still `web`. Like register, this is a hypothesis; Step 3 confirms it. +Confirm ambiguous platform separately. Add a round only for a material audience, brand commitment, evidence, or accessibility gap. Record undecided facts instead of inventing them. -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. +Do not ask for an aesthetic direction, emotional feel, visual references, colors, typography, or style during init. If the user volunteers a binding visual constraint, record it without expanding it. -## Step 3: Ask strategic questions (for PRODUCT.md) +### What belongs here -ask the user directly to clarify what you cannot infer. Ask about anything the codebase doesn't answer with strong, explicit evidence. +- users, jobs, workflows, purpose, success, positioning, and operating context; +- capabilities, constraints, terminology, evidence, platform, and accessibility; +- confirmed voice, assets, and brand commitments. -### Interview mode, not confirmation mode +### What does not belong here -If the repo is empty or the user's brief is sparse, run a short interview before proposing PRODUCT.md. Do **not** turn a one-sentence request into a complete inferred PRODUCT.md and ask for blanket confirmation. - -- Use the harness's structured question tool when one exists. Otherwise, ask directly in chat and stop: one question at a time, with lettered options where the crawl suggests likely answers, waiting for each answer before the next. -- Keep skill vocabulary (register, belief ladder, anti-references) out of question text; ask for the thing in words the user would use. For the brand register, ask like a magazine editor profiling the brand: curious and narrative, drawing out the story, the feel, and what a visitor should come to believe. -- Ask in focused rounds and wait for answers between them. Keep **one topic per question**; add rounds rather than fold several topics into one either-or choice. Options obey the same rule: an option answers only the question asked; never write a compound option that bundles a feeling with a business outcome or names an additional audience. -- Use inferred answers as hypotheses or options, not as finished facts. -- Complete at least one real user-answer round before drafting PRODUCT.md, unless every required answer is directly discoverable from repo docs. -- Round 1 should establish register, platform, users, purpose, positioning, and desired outcome. -- Round 2 should establish brand personality or references, anti-references, and accessibility needs, plus conversion & proof for Persuade surfaces. - -### Minimum viable interview - -Ask enough to complete PRODUCT.md. At minimum, cover register confirmation, **platform confirmation** (`web` / `ios` / `android` / `adaptive`), users, purpose, positioning, brand personality, anti-references, and accessibility needs (plus conversion & proof for Persuade surfaces) unless each answer is directly discoverable from repo context. Never let the template's default `web` stand unconfirmed for a native or cross-platform repo. After at least one interview round, you may propose inferred answers, but the user must confirm them before you write PRODUCT.md. Never synthesize PRODUCT.md from the original task prompt alone. - -### Register (ask first; it shapes everything below) - -Every design task serves one of four visitor modes (see SKILL.md's Registers): **Persuade** (marketing, landing, campaigns), **Experience** (portfolios, albums, bodies of work), **Operate** (app UI, admin, dashboards, tools), and **Read** (documentation, editorial, long-form). PRODUCT.md stores the two-value family that covers them: `brand` covers Persuade and Experience, `product` covers Operate and Read. The brief decides the exact mode per task; the stored register only sets the default family. - -If Step 2 produced a clear hypothesis, lead with it: *"From the codebase, this looks like a [brand / product] surface. Does that match your intent, or should we treat it differently?"* - -If the signal is genuinely split (e.g. a product with a big marketing landing), ask the user directly to clarify what you cannot infer. Ask which register describes the **primary** surface. The register can be overridden per task later, but PRODUCT.md carries one default. Settle the default before drafting any register-dependent questions; never batch Persuade-only questions (Conversion & proof) into the same round as the question that decides the register. - -### Platform (ask right after register) - -Every project targets **web** (includes responsive mobile web), **ios**, **android**, or **adaptive** (one codebase, ships both, adapts per OS: Flutter, React Native, KMP). Platform picks the native rulebook: HIG for `ios`, Material 3 for `android`, both for `adaptive`, none for `web`. - -If Step 2 produced a clear hypothesis, lead with it: *"From the codebase, this looks like a [web / ios / android / adaptive] project. Does that match?"* For cross-platform apps, decide by the **design language the app renders**, not the toolchain: one look on both platforms (Flutter's Material-everywhere default) takes that platform's value; genuine per-OS adaptation (Cupertino on iOS, Material on Android) is `adaptive`. When in doubt, `web`. - -A monorepo shipping both a website and a native app gets a PRODUCT.md per app, each with its own `## Platform`; the root PRODUCT.md carries the primary surface's platform. - -### Users & Purpose -- Who uses this? What's their context when using it? -- What job are they trying to get done? -- What is this for? A purpose stated in README or docs is a hypothesis, not strong evidence; confirm it, don't transcribe it. -- What does success look like? -- If more than one kind of user is plausible, confirm a primary and secondary audience; don't manufacture a split that isn't there. An audience implied by another answer (a success metric, a CTA) is still unconfirmed; ask before writing it as secondary. -- If the surface speaks to a different audience than the people who use the product, ask the user to name both. -- For brand: what emotions should the interface evoke? (confidence, delight, calm, urgency) Ask this standalone; don't fold emotions into the success question. -- For product: what workflow are they in? What's the primary task on any given screen? - -### Positioning -- In one line, what does this do that nothing else does? The single strategic claim every screen reinforces. - -### 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? - - 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? - -### Conversion & proof (Persuade surfaces only) - -Ask these only when the primary surface is a Persuade one (marketing, landing, campaigns). Experience and Read surfaces (portfolios, albums, long-form writing, documentation) get no CTA, belief-ladder, or proof questions even when the stored register is `brand`; skip this section and its PRODUCT.md counterpart. - -- What's the primary CTA? -- What's the secondary fallback, for visitors not ready for the primary? -- The one line a visitor should remember after 10 seconds. -- What must the visitor believe, in order, before taking the primary CTA? (The template's belief ladder.) -- What proof is on hand? Ask the user to hand over any testimonials, case studies, press, or client/partner logos they already have. If you can receive files directly, collect them; otherwise create `.impeccable/assets/proof/` and ask the user to add files there. Reference supplied files by path; record text proof inline. - -### Accessibility & Inclusion -- Specific accessibility requirements? (WCAG level, known user needs) -- Considerations for reduced motion, color blindness, or other accommodations? - -Skip questions where the answer is already clear. **Do NOT ask about colors, fonts, radii, or visual styling here.** Those belong in DESIGN.md, not PRODUCT.md. +- visual worlds, palettes, typography, components, or page concepts; +- visitor mode, narrative, CTA/proof sequence, or other surface strategy; +- invented testimonials, customers, benchmarks, pricing, licensing, or deployment claims; +- a requirement to decide every optional field. ## Step 4: Write PRODUCT.md -Write PRODUCT.md only after the user has confirmed the strategic answers from Step 3. If an inferred answer is uncertain or unconfirmed, ask before writing. Confirmed means what the user actually said yes to; do not pad a confirmed answer with extras they never picked (additional anti-references, audiences, roadmap claims, a WCAG level), whether drawn from the crawl, another answer, or your own option text. If an extra belongs in the doc, ask about it first. - -Synthesize into a strategic document: +Write only confirmed facts and explicitly marked open decisions. Omit irrelevant sections rather than filling them with generic prose. ```markdown # Product -## Register - -product - ## Platform web ## Users -[Who they are, their context, the job to be done. Primary audience; a secondary audience or a surface-vs-user split only when they apply.] +[Primary users, their situation, and job. Add other audiences only when confirmed.] ## Product Purpose -[What this product does, why it exists, what success looks like] +[What the product does, why it exists, and what success means.] ## Positioning -[The single strategic claim every screen reinforces. Not a visual rule, not an anti-reference.] +[The product mechanism or claim a neighboring product could not truthfully copy.] -## Conversion & proof -[Persuade surfaces only (marketing, landing, campaigns). Omit this section entirely, heading included, for the product register and for Experience or Read surfaces such as portfolios, albums, or long-form writing.] -- Primary and secondary CTA: [...] -- The line a visitor remembers after 10 seconds: [...] -- Belief ladder: [...] -- Proof on hand: [testimonials, case studies, press, or logos, referenced by path] +## Operating Context +[Workflows, environments, tools, documents, materials, and rituals that are factual parts of using or evaluating the product.] -## Brand Personality -[Voice, tone, 3-word personality, emotional goals] +## Capabilities and Constraints +[Confirmed functionality, technical constraints, terminology, and explicitly undecided product facts.] -## Anti-references -[What this should NOT look like. Specific bad-example sites or patterns to avoid.] +## Brand Commitments +[Existing name, voice, assets, personality, identity constraints, and references the user explicitly made binding. Omit when none exist.] -## Design Principles -[3-5 strategic principles derived from the conversation. Principles like "practice what you preach", "show, don't tell", "expert confidence". NOT visual rules like "use OKLCH" or "magenta accent".] +## Evidence on Hand +[Real content, data, demonstrations, testimonials, case studies, press, or assets, with paths where applicable. State absences that future work must not fabricate.] + +## Product Principles +[Three to five durable strategic principles derived from confirmed answers; no visual recipes.] ## Accessibility & Inclusion -[WCAG level, known user needs, considerations] +[Known user needs or required standard. Omit when no product-specific requirement was established.] ``` -Register is either `brand` or `product` as a bare value. No prose, no commentary. Platform is `web`, `ios`, `android`, or `adaptive`, also a bare value; omit the section only on legacy files you're leaving untouched, otherwise write `web` explicitly. +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. -Write fields as prose, and use bold sparingly: only where a word carries a decision, never as a label lead-in on every line. +### Completion gate -Write to `PROJECT_ROOT/PRODUCT.md`. If `.impeccable.md` existed, the loader already renamed it; merge into that content rather than starting from scratch. +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: Decide on DESIGN.md +## Step 5: Configure live mode when useful -Offer `/impeccable document` either way. Two paths: +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. -- **Code exists** (CSS tokens, components, a running site): "I can generate a DESIGN.md that captures your visual system (colors, typography, components) so variants stay on-brand. Want to do that now?" -- **Pre-implementation** (empty project): "I can seed a starter DESIGN.md from five quick questions about color strategy, type direction, motion energy, and references. You can re-run once there's code, to capture the real tokens. Want to do that now?" +## Step 6: Wrap up or resume -If the user agrees, delegate to `/impeccable document` (it auto-detects scan vs seed). Load its reference and follow that flow. +Summarize captured and deliberately undecided facts. Do not offer DESIGN.md merely because it is missing. -If the user prefers to skip, mention they can run `/impeccable document` any time later. +Recommend the next action from the actual project state: -## Step 6: Configure live mode (when code exists) +- Empty or early project: ask naturally for the surface to be built, or use `/impeccable shape ` when the user wants a confirmed brief without implementation. New-work will establish a visual world only when the requested work needs one. +- Existing coherent interface without DESIGN.md: `/impeccable document` if the user wants the incumbent system recorded independently of a new build. +- Existing surface needing work: name the most relevant scoped command. +- Web project ready for visual iteration: `/impeccable live` when configured. -**Skip this step when the platform is native** (`ios` / `android` / `adaptive`): live mode drives a browser overlay. A hybrid wrapper or Expo web target serving HTML doesn't change that. - -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 .github/skills/impeccable/scripts/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) and platform captured (web / ios / android / adaptive) -- 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 or live config is pending, one line on how to set it up later - -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 platform**, offer the 2-4 most relevant (not a menu dump), and give the exact command to type. Group by intent: - -- **Build something new**: `/impeccable craft ` (shape, then build end-to-end) or `/impeccable shape ` (plan first). Lead with this for empty or early-stage projects. -- **Improve what's there**: name the specific surface. `/impeccable critique ` for a scored UX review; `/impeccable audit ` for a11y / perf / responsive checks; `/impeccable polish ` 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** (web only): `/impeccable live` (configured in Step 6) to pick elements in the browser and generate variants in place. **Skip this group for native platforms.** - -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 the user directly to clarify what you cannot infer. Ask whether they'd like a brief summary of PRODUCT.md appended to .github/copilot-instructions.md for easier agent reference. If yes, append a short **Design Context** pointer section there. +If init was invoked by another request, resume without rerunning context.mjs; new-work owns later visual decisions. diff --git a/.github/skills/impeccable/reference/ios.md b/.github/skills/impeccable/reference/ios.md index ef10ee0d2..ccef5d2c4 100644 --- a/.github/skills/impeccable/reference/ios.md +++ b/.github/skills/impeccable/reference/ios.md @@ -2,7 +2,7 @@ For native iOS / iPadOS apps: SwiftUI, UIKit, React Native, Expo, Flutter shipping to Apple hardware. -On native, register narrows. HIG conformance governs structure, navigation, and interaction whatever the register; brand expresses through the expressive layer the platform provides (tint, type, motion, content). Calm, Duolingo, and Spotify carry strong identity entirely inside HIG conventions. +On native, the visitor mode narrows what expression may override. HIG conformance governs structure, navigation, and interaction in every mode; brand expresses through the layer the platform leaves open (tint, type, motion, content). ## The iOS slop test diff --git a/.github/skills/impeccable/reference/layout.md b/.github/skills/impeccable/reference/layout.md index 129f4f9c2..3ee7c5af1 100644 --- a/.github/skills/impeccable/reference/layout.md +++ b/.github/skills/impeccable/reference/layout.md @@ -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. --- -## Register +## 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, register, 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 .github/skills/impeccable/scripts/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 `/impeccable polish` for the final pass. +When the structure holds, hand off to `/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) * )`: 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. diff --git a/.github/skills/impeccable/reference/live.md b/.github/skills/impeccable/reference/live.md index 854ff85da..15b73e819 100644 --- a/.github/skills/impeccable/reference/live.md +++ b/.github/skills/impeccable/reference/live.md @@ -33,7 +33,7 @@ Chat is overhead. No recap, no tutorial output, no pasting PRODUCT / DESIGN bodi node .github/skills/impeccable/scripts/live.mjs ``` -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. +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, DESIGN.md, and any surface brief already loaded by Setup in mind for variant generation: **DESIGN.md wins on visual decisions; PRODUCT.md wins on durable product and voice decisions; the surface brief wins on this surface's strategy.** 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 requires the user's explicit redesign/replacement intent. `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). @@ -187,7 +187,7 @@ All three carry `fallback: "agent-driven"`. Follow **Handle fallback** below. ### 3. Load the action's reference -If `event.action` is `impeccable` (the default freeform action), use SKILL.md's shared laws plus the loaded register reference (`brand.md` or `product.md`). Do not load a sub-command reference. **Freeform is not a pass to skip parameters:** you still follow the composition budget and the freeform bias in **§7 Parameters** below. Sub-command files list MUST-have signature knobs; freeform has no such file, so sizing knobs from surface weight and primary axes is entirely on you. +If `event.action` is `impeccable` (the default freeform action), use SKILL.md's shared laws and decide the visitor mode from the selected surface. Do not load a sub-command reference. **Freeform is not a pass to skip parameters:** you still follow the composition budget and the freeform bias in **§7 Parameters** below. Sub-command files list MUST-have signature knobs; freeform has no such file, so sizing knobs from surface weight and primary axes is entirely on you. Any other `event.action` (`bolder`, `quieter`, `distill`, `polish`, `typeset`, `colorize`, `layout`, `adapt`, `animate`, `delight`, `overdrive`): Read `reference/.md` before planning. Each sub-command encodes a specific discipline; skipping its reference produces generic output. Those files may require specific params; layer them on top of the §7 budget, not instead of it. @@ -216,7 +216,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. @@ -224,10 +224,7 @@ This sentence is the **identity lock**. Every variant must be readable as the sa **Default mode**: the existing identity is preserved. Variants vary expression axes within it. *This is the right mode for ~90% of live sessions.* The user picked an element on a real product they're shipping; they expect variants of *their* hero, not three different brands' heroes. -**Departure mode**: the existing identity is rejected. Variants propose alternatives consistent with PRODUCT.md voice. Trigger only when at least one is true: - -- PRODUCT.md anti-references explicitly call out the current surface ("the current `index.html` is itself an example"; "diffuse away from this"; "the page on screen is the failure"). Generic anti-references that describe what to avoid in general do **not** trigger departure mode; only ones that point at *this* surface specifically. -- The user's freeform prompt explicitly asks for departure ("rebuild this from scratch", "what if it weren't editorial at all", "show me something completely different"). +**Departure mode**: the existing identity is rejected. Variants propose alternatives consistent with durable product and brand truth. Trigger only when the user explicitly asks for departure in the current request or freeform prompt ("redesign this", "rebuild this from scratch", "what if it weren't editorial at all", "show me something completely different"). A stale page critique or an old task note is not replacement authorization. If you're unsure, you're in default mode. The cost of being wrong about default is "three on-brand variants with similar feel": recoverable, the user picks none. The cost of being wrong about departure is "three off-brand variants": unrecoverable, the user is annoyed. @@ -246,13 +243,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 the brand's stated voice and register in PRODUCT.md. Do NOT pick from a fixed catalog of lane categories. The right three directions for this brand are not the same as the right three for another brand, 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. @@ -262,7 +259,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. @@ -289,7 +286,7 @@ In **default mode**, the prompt narrows the axes you choose, not the identity. * In **departure mode**, the prompt narrows the lanes you draw from, not the families. *"Make it feel like a newspaper front page"* would itself be a departure-mode prompt; honor it but pick three meaningfully different newspaper-adjacent lanes (broadsheet vs. tabloid vs. trade journal), and run the family pass to confirm they don't collapse into one. -When the prompt and PRODUCT.md anti-references conflict (the prompt asks for X, the anti-references ban X), the anti-references win; they describe the brand's standing position, the prompt is one moment. +When the prompt conflicts with a confirmed binding brand commitment or DESIGN.md invariant, preserve the invariant unless the user explicitly revokes or replaces it. Task-local strategy from the matching surface brief may change when the user changes that surface's goal. ### 6. Write all variants in a single edit diff --git a/.github/skills/impeccable/reference/new-work.md b/.github/skills/impeccable/reference/new-work.md index 39a6bb221..9aa36b826 100644 --- a/.github/skills/impeccable/reference/new-work.md +++ b/.github/skills/impeccable/reference/new-work.md @@ -1,47 +1,178 @@ -# New identity work +# New visual work -Read this only when the project has no committed visual identity, or the user explicitly asked to replace the current one. A missing DESIGN.md is not enough: code, tokens, chosen type, components, and assets are incumbent design authority. If they exist, preserve and extend them unless the brief says to discard them. +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. -This playbook is task-scoped. PRODUCT.md owns durable product facts and DESIGN.md owns durable visual invariants; the direction for one page or feature belongs in the current task, not in either document. +## 1. Name the intent -## Choose the authority +- **Greenfield:** no coherent visual implementation. +- **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. -Classify the work before inventing anything: +A plain “redesign this page/site” authorizes replacement. “Redesign this within the current brand/system” means extension or refinement. Ask once only when the wording is genuinely ambiguous. -- **Extend**: an incumbent system exists and the task adds or improves a surface. Preserve its lineage, semantics, and affordances. -- **Surface redesign**: the current identity remains, but this surface may change its topology, hierarchy, and expression. -- **New identity**: nothing committed exists, or the user explicitly authorized a rebrand. +## 2. Resolve visual authority -When the evidence is mixed, preserve. Novelty is not permission to erase authored decisions. +Read DESIGN.md and representative code, tokens, components, and assets. Choose one path: -## Find the concept +### A. Explicit redesign -Name the subject, audience, page job, and visitor mode from SKILL.md. Then identify the product's unique mechanism: what it does, proves, or enables that a neighboring product cannot truthfully claim. +The old DESIGN.md and implementation are not authority. Keep only unrevoked product facts, content, function, native expectations, constraints, and brand commitments. Establish a replacement world. -Derive the concept from that mechanism through one primary transformation: information topology, evidence, temporal behavior, or spatial mapping. The transformation must change how the surface is organized or behaves, not merely how it is decorated. This produces stranger and more defensible work than imitating a culturally specific document or object. Borrowing a literal cultural form is allowed only when it is genuinely native to the product and audience, and never as costume. +### B. DESIGN.md covers this kind of surface -For **Operate** and **Read**, start with the task and information structure. Native controls remain native controls; expression comes from hierarchy, density, rhythm, state, and the system around them. For **Persuade** and **Experience** creating a new identity, explore more freely, but keep the product legible and the primary action obvious. +Use its invariants and normative tokens. Skip world-building and discover the surface. -Run `node .github/skills/impeccable/scripts/concept-seed.mjs` only for Persuade or Experience new-identity work when two or more directions remain equally credible. It is an entropy source, not an authority: never let the draw overrule the strongest fit. When color is genuinely unconstrained, `node .github/skills/impeccable/scripts/palette.mjs` may break a reflex palette; subject evidence and incumbent color still win. +### C. A coherent implementation exists but DESIGN.md does not -## State the direction +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. -Before code, write a task-scoped direction contract of at most 120 words in your reasoning: +### D. The brand exists, but a whole-surface family is unresolved -- the thesis this surface owns; -- the structural or behavioral transformation; -- what authority is preserved; -- the first viewport's composition and primary action; -- the signature element the surface will be remembered by. +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. -Do not embed this contract as a comment in the production artifact or turn it into a second design system. In an attended substantial build, confirm it once. In an unattended run, record it and continue. +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. -## Build once, commit fully +### E. No confirmed visual authority exists -Plan one compact token system and one layout source: the concept or the content's real structure. Build the strongest coherent direction once. Commitment means the transformation governs the whole surface; it does not mean every control must be rebuilt as a metaphor. +Establish a world. Scaffolds, framework defaults, and stray utilities are not identity. -Make the first viewport demonstrate the product's mechanism. Pace long surfaces through contrast in density and scale, and cut sections that only repeat claims. Briefs that depend on imagery ship real, verified imagery. Preserve semantic HTML, familiar interaction behavior, accessibility, and the project's technical conventions. +## 3. Discover the requested surface -## Finish proportionally +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. -Inspect desktop and mobile, write one honest critique against the brief and direction, patch material defects, then run the detector once. Repeat only when a real defect remains. A separate reviewer is optional when the harness already provides one and the extra cost is earned by the risk; it is not a default step and its presence is not evidence of quality. +Ask one attended round of at most three material questions without repeating durable facts. CTA hierarchy, proof sequence, content gaps, and interaction outcomes belong here, not PRODUCT.md. For a fully specified narrow request, state the interpretation and invite correction. + +When `shape` has already completed its discovery interview, reuse those confirmed answers and do not ask this round again. + +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. Each candidate joins a durable visual system (identity thesis, information/layout grammar, material and type behavior, color strategy, imagery, motion, reusable signature) to a concrete expression for this surface (topology, sequence, focal moment, and implementation consequence). Do not rank yet. +3. **Break the ranking rut once.** Run `node .github/skills/impeccable/scripts/concept-seed.mjs --scope direction`. Promote the assigned grounded pair and weigh its challengers only when they can become both a coherent system and a strong solution to this task. +4. **Test coupling and breadth.** Reject a pair if its surface could swap into another candidate unchanged, or if its world is merely the surface motif repeated. Test each survivor on the requested surface plus navigation, quiet and dense content, interaction/state, and one unlike future surface. Compare it with both the category habit and predictable contrarian response. +5. **Offer coupled choices.** Present two or three materially different pairs without recommendation cues. For each, show the world rules, this surface's 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. 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 .github/skills/impeccable/scripts/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 promoted candidate when a roll ran; otherwise use the strongest grounded structure. It must survive both tests. + +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). + +### Write or update DESIGN.md + +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: + +`` + +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 .github/skills/impeccable/scripts/surface-brief.mjs read ` + +Exit 0: preserve still-valid decisions and change only what the user changed. For redesign, retain valid product strategy, content, function, and open decisions; replace visual direction and contract. Exit 2: no brief. After the contract, write with `node .github/skills/impeccable/scripts/surface-brief.mjs write [related-target ...]`. + +The body is concise and contains: + +```markdown +# Surface brief: [name] + +## Scope +[Primary/related route or artifact, visitor mode, and what this surface owns.] + +## Product strategy +[Surface-specific audience and job, desired outcome, primary/secondary action or task, content and proof sequence, factual constraints, and explicitly open decisions.] + +## Selected direction +[Reference to the applicable DESIGN.md world or expression range, selected surface concept, focal moment, narrative/interaction sequence, and implementation consequence.] + +## Direction contract +[The six contract blocks below.] + +## Open decisions +[Only unresolved items that later work must not silently invent. Omit when empty.] +``` + +Commit `.impeccable/surfaces/.md` as stable later-work context. Exclude global truth, exact tokens, transient notes, and work logs. + +## 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. + +Before code, write a direction contract of at most 150 words in an opening HTML or framework comment. The first 200 characters must name `DIRECTION CONTRACT`. + +- `UNIQUE`: the task thesis tied to the product mechanism; +- `NOT-TEMPLATE`: the category-default arrangement this structure refuses; +- `OWN-WORLD`: the confirmed DESIGN.md invariants, tokens, and materials it uses; +- `STORY`: what the visitor understands, believes, and does; +- `FIRST VIEWPORT`: exact composition, hierarchy, action, and where the concept exceeds competent convention; +- `FORM`: chosen structure or behavior, signature, implementation consequence, and seed key. + +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. + +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. + +## 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. + +Build the strongest coherent direction once. Its grammar governs navigation, actions, controls, content, and transitions without disguising affordances. Give the focal form the scale that gives it force; do not trap it inside a standard hero panel. + +**Make the opening a thesis.** Demonstrate the mechanism immediately; leave an idea, interaction, or evidence, not merely mood. + +**Commit before correcting.** Land the hard move at full strength before refining it. In unattended work, safety is the known risk. + +**Commit at page scale.** Let color, material, image, or type own a region when the world calls for it. Scattered signature decoration is not commitment. + +**Pace the whole surface.** Vary density, scale, image, motion, and quiet inside one grammar. Cut repeated claims; prove the mechanism with real artifact, interaction, data, transformation, or content. + +**Author motion as material.** Motion expresses the world and task. Premium moments go beyond repeated transform/opacity through earned focus, depth, masks, continuity, light, or material change. Bound expensive effects, test in-browser, keep content visible by default, and design reduced motion. + +Preserve semantics, affordances, accessibility, performance, responsiveness, and project conventions. Operate/Read express through topology, hierarchy, density, rhythm, and state; Persuade/Experience may earn drama. + +## 8. Solidify the visual record + +After first implementation of a new/replacement world or approved expansion, refresh DESIGN.md from the build: + +- replace provisional direction with the exact type, color roles, tokens, spacing/radii, components, states, and motion that survived; +- add normative YAML tokens only for values the implementation actually uses; +- remove the directional-seed status once the record and implementation agree; +- preserve broader world invariants and expression ranges; +- do not promote the task's story, hero composition, or one-off motif into a global rule unless it is intentionally reusable. + +Ordinary extension does not rewrite DESIGN.md; only approved durable changes do. + +## 9. Finish like a 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. diff --git a/.github/skills/impeccable/reference/operate.md b/.github/skills/impeccable/reference/operate.md index 04ee9d7d2..f420cbaa2 100644 --- a/.github/skills/impeccable/reference/operate.md +++ b/.github/skills/impeccable/reference/operate.md @@ -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. - No orchestrated page-load sequences. Product loads into a task; users don't want to watch it load. -## Product bans (on top of the shared absolute bans) +## Product constraints - Decorative motion that doesn't convey state. - Inconsistent component vocabulary across screens. If the "save" button looks different in two places, one is wrong. @@ -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. diff --git a/.github/skills/impeccable/reference/polish.md b/.github/skills/impeccable/reference/polish.md index d19851ac2..139f69c9a 100644 --- a/.github/skills/impeccable/reference/polish.md +++ b/.github/skills/impeccable/reference/polish.md @@ -1,241 +1,98 @@ -> **Additional context needed**: quality bar (MVP vs flagship). +> **Additional context needed**: quality bar and shipping constraints. -Perform a meticulous final pass to catch all the small details that separate good work from great work. The difference between shipped and polished. +Polish is refinement, never concealed redesign. Preserve the incumbent visual world, content, behavior, and everything outside scope. If the concept itself is wrong, say so and recommend redesign or `bolder` instead of smuggling in a replacement. -Detector and automated QA output are defect evidence only. A clean script result is never proof that the design is strong; gather browser evidence and inspect the real interaction path. +A detector result is defect evidence, not proof of quality. Inspect the rendered experience and real interaction path. -## Design System Discovery +## 1. Establish the system -Aligning the feature to the design system is **not optional**. Polish without alignment is decoration on top of drift, and it makes the next person's job harder. Discovery comes before any other polish work. +Read DESIGN.md and representative tokens, shared components, patterns, and neighboring flows. If no formal system exists, use coherent project conventions. -1. **Find the design system**: Search for design system documentation, component libraries, style guides, or token definitions. Study the core patterns: design principles, target audience, color tokens, spacing scale, typography styles, component API, motion conventions. -2. **Note the conventions**: How are shared components imported? What spacing scale is used? Which colors come from tokens vs hard-coded values? What motion and interaction patterns are established? What flow shapes are used for comparable actions (modal vs full-page, inline vs route, save-on-blur vs explicit submit)? -3. **Identify drift, then name the root cause**: For every deviation, classify it as a **missing token** (the value should exist in the system but doesn't), a **one-off implementation** (a shared component already exists but wasn't used), or a **conceptual misalignment** (the feature's flow, IA, or hierarchy doesn't match neighboring features). The fix differs by category: patch the value, swap to the shared component, or rework the flow. Fixing the symptom without naming the cause is how drift compounds. +Classify each drift before fixing it: -If a design system exists, polish **must** align the feature with it. If none exists, polish against the conventions visible in the codebase. **If anything about the system is ambiguous, ask. Never guess at design system principles.** +- **missing token:** the system needs a reusable value; +- **one-off implementation:** an existing shared component or pattern should replace it; +- **conceptual mismatch:** the flow, information architecture, or hierarchy differs from comparable product areas; +- **local defect:** the implementation is simply incomplete or inconsistent. -## Pre-Polish Assessment +Fix the cause at the narrowest correct level. Ask when a binding system principle cannot be inferred. -Understand the current state and goals before touching anything: +## 2. Gather the evidence -1. **Review completeness**: - - Is it functionally complete? - - Are there known issues to preserve (mark with TODOs)? - - What's the quality bar? (MVP vs flagship feature?) - - When does it ship? (How much time for polish?) +Use the feature yourself at representative desktop and mobile sizes. Determine: -2. **Think experience-first**: Who actually uses this, and what's the best possible experience for them? Effective design beats decorative polish; a feature that looks beautiful but fights the user's flow is not polished. Walk the path from their perspective before opening DevTools. +- whether the path is functionally complete; +- the intended quality bar and time available; +- known constraints or deliberately unfinished work; +- the states, content lengths, roles, and input methods users will actually encounter. -3. **Identify polish areas**: - - Visual inconsistencies - - Spacing and alignment issues - - Interaction state gaps - - Copy inconsistencies - - Edge cases and error states - - Loading and transition smoothness - - Information architecture and flow drift (does this feature reveal complexity the way neighboring features do?) +If a prior critique exists, use it as one input: -4. **Pull in any prior critique** (optional signal): If `/impeccable critique` has been run on the same target, its priority issues are a useful prior for what to address first. Resolve the target to a file path or URL, then: - ```bash - slug=$(node .github/skills/impeccable/scripts/critique-storage.mjs slug "") - node .github/skills/impeccable/scripts/critique-storage.mjs latest "$slug" - ``` - Exit 0 with body = found; fold the P0/P1 items into your polish list and mention the snapshot path so the user sees what you read. Exit 2 = no snapshot, continue without it. The critique is one input among many. Do your own pass either way. +```bash +slug=$(node .github/skills/impeccable/scripts/critique-storage.mjs slug "") +node .github/skills/impeccable/scripts/critique-storage.mjs latest "$slug" +``` -5. **Triage cosmetic vs functional**: Classify each issue as **cosmetic** (looks off, doesn't impede the user) or **functional** (breaks, blocks, or confuses the experience). When polish time is tight, functional issues ship first; cosmetic ones can land in a follow-up. Quality should be consistent; never perfect one corner while leaving another rough. +Exit 0 returns the latest snapshot; incorporate relevant P0/P1 findings and name the snapshot read. Exit 2 means none exists. Perform an independent pass either way. -**CRITICAL**: Polish is the last step, not the first. Don't polish work that's not functionally complete. +## 3. Triage -## Polish Systematically +Separate functional defects from cosmetic ones and fix in this order: -Work through these dimensions methodically: +1. broken or blocked tasks, data loss, misleading state, and inaccessible paths; +2. missing loading, empty, error, success, disabled, and permission states; +3. flow, hierarchy, responsive, and design-system drift; +4. visual and motion inconsistencies; +5. code and asset cleanup. -### Visual Alignment & Spacing +Do not perfect one corner while leaving the rest below the same quality bar. -- **Pixel-perfect alignment**: Everything lines up to grid -- **Consistent spacing**: All gaps use spacing scale (no random 13px gaps) -- **Optical alignment**: Adjust for visual weight (icons may need offset for optical centering) -- **Responsive consistency**: Spacing and alignment work at all breakpoints -- **Grid adherence**: Elements snap to baseline grid +## 4. Polish the whole path -**Check**: -- Enable grid overlay and verify alignment -- Check spacing with browser inspector -- Test at multiple viewport sizes -- Look for elements that "feel" off +### Flow and hierarchy -### Information Architecture & Flow +- Match neighboring mental models, terminology, disclosure, routing, save behavior, and optimistic or pessimistic patterns. +- Make the primary task and current state obvious without flattening every element to equal weight. +- Ensure arrival, transition, empty, and recovery paths connect instead of behaving as isolated screens. -Visual polish on a misshapen flow is wasted work. Match the *shape* of the experience to the system, not just the surface. +### Layout and type -- **Progressive disclosure**: Match how much is revealed when, compared to neighboring features. A settings page exposing 40 fields when the rest of the app reveals 5 at a time is drift, even if every field is perfectly styled. -- **Established user flows**: Multi-step actions follow the same shape as comparable flows elsewhere: modal vs full-page, inline edit vs separate route, save-on-blur vs explicit submit, optimistic vs pessimistic updates. -- **Hierarchy & complexity**: The same conceptual weight gets the same visual weight throughout. Primary actions don't become tertiary in one corner of the product, and tertiary actions don't shout. -- **Empty, loading, and arrival transitions**: How content arrives, updates, and leaves matches how it does in adjacent features. -- **Naming and mental model**: The feature uses the same nouns and verbs as the rest of the system. A "Workspace" here shouldn't be a "Project" three screens away. +- Align to the project's grid and spacing scale; fix optical as well as mathematical alignment. +- Group related content tightly and separate distinct groups generously. +- Keep same-role typography consistent; test measure, wrapping, localization expansion, zoom, and font loading. +- Verify every supported viewport rather than correcting only the current screenshot. -### Typography Refinement +### Color, imagery, and icons -- **Hierarchy consistency**: Same elements use same sizes/weights throughout -- **Line length**: 45-75 characters for body text -- **Line height**: Appropriate for font size and context -- **Widows & orphans**: No single words on last line -- **Hyphenation**: Appropriate for language and column width -- **Kerning**: Adjust letter spacing where needed (especially headlines) -- **Font loading**: No FOUT/FOIT flashes +- Use semantic tokens and stable color meanings across themes. +- Verify text, control, and focus contrast in every state. +- Keep icon families, stroke/weight, sizing, and optical alignment coherent. +- Prevent image layout shift; use correct aspect ratios, responsive sources, and useful alt text. -### Color & Contrast +### Interaction and state -- **Contrast ratios**: All text meets WCAG standards -- **Consistent token usage**: No hard-coded colors, all use design tokens -- **Theme consistency**: Works in all theme variants -- **Color meaning**: Same colors mean same things throughout -- **Accessible focus**: Focus indicators visible with sufficient contrast -- **Gray on color**: Never put gray text on colored backgrounds; use a shade of that color or transparency +- Every control needs appropriate default, hover, focus, active, disabled, loading, error, and success behavior. +- Preserve visible keyboard focus, logical tab order, labels, and platform-appropriate touch targets. +- Keep motion coherent, interruptible, performant, and reduced-motion aware. Do not add animation merely to make polish visible. +- Validate long, missing, localized, offline, slow, and permission-limited content where the product can encounter it. -### Interaction States +### Content and code -Every interactive element needs all states: +- Keep terminology, capitalization, punctuation, and factual copy consistent. Ask before changing claims. +- Remove debug output, dead code, unused imports, obsolete styles, and polish-created duplication. +- Replace custom implementations with shared components where the system owns the pattern. +- Promote genuinely reusable values to tokens; do not create a system abstraction for one local exception. -- **Default**: Resting state -- **Hover**: Subtle feedback (color, scale, shadow) -- **Focus**: Keyboard focus indicator (never remove without replacement) -- **Active**: Click/tap feedback -- **Disabled**: Clearly non-interactive -- **Loading**: Async action feedback -- **Error**: Validation or error state -- **Success**: Successful completion +## 5. Verify and finish -**Missing states create confusion and broken experiences**. +Walk the complete path again with mouse, keyboard, and touch where applicable. Check: -### Micro-interactions & Transitions +- mobile, intermediate, and wide layouts; +- loading, empty, error, success, disabled, long-content, and missing-content states; +- zoom, contrast, focus, semantics, screen-reader names, and reduced motion; +- console errors, layout shift, interaction latency, image loading, and supported browsers; +- agreement with DESIGN.md, neighboring features, and the user's scope. -- **Smooth transitions**: All state changes animated appropriately (150-300ms) -- **Consistent easing**: Use ease-out-quart/quint/expo for natural deceleration. Never bounce or elastic; they feel dated. -- **No jank**: Smooth animations; use atmospheric blur/filter/mask/shadow effects when they add polish, but bound expensive paint areas and avoid casual layout-property animation -- **Appropriate motion**: Motion serves purpose, not decoration -- **Reduced motion**: Respects `prefers-reduced-motion` +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. -### Content & Copy - -- **Consistent terminology**: Same things called same names throughout -- **Consistent capitalization**: Title Case vs Sentence case applied consistently -- **Grammar & spelling**: No typos -- **Appropriate length**: Not too wordy, not too terse -- **Punctuation consistency**: Periods on sentences, not on labels (unless all labels have them) - -### Icons & Images - -- **Consistent style**: All icons from same family or matching style -- **Appropriate sizing**: Icons sized consistently for context -- **Proper alignment**: Icons align with adjacent text optically -- **Alt text**: All images have descriptive alt text -- **Loading states**: Images don't cause layout shift, proper aspect ratios -- **Retina support**: 2x assets for high-DPI screens - -### Forms & Inputs - -- **Label consistency**: All inputs properly labeled -- **Required indicators**: Clear and consistent -- **Error messages**: Helpful and consistent -- **Tab order**: Logical keyboard navigation -- **Auto-focus**: Appropriate (don't overuse) -- **Validation timing**: Consistent (on blur vs on submit) - -### Edge Cases & Error States - -- **Loading states**: All async actions have loading feedback -- **Empty states**: Helpful empty states, not just blank space -- **Error states**: Clear error messages with recovery paths -- **Success states**: Confirmation of successful actions -- **Long content**: Handles very long names, descriptions, etc. -- **No content**: Handles missing data gracefully -- **Offline**: Appropriate offline handling (if applicable) - -### Responsiveness - -- **All breakpoints**: Test mobile, tablet, desktop -- **Touch targets**: 44x44px minimum on touch devices -- **Readable text**: No text smaller than 14px on mobile -- **No horizontal scroll**: Content fits viewport -- **Appropriate reflow**: Content adapts logically - -### Performance - -- **Fast initial load**: Optimize critical path -- **No layout shift**: Elements don't jump after load (CLS) -- **Smooth interactions**: No lag or jank -- **Optimized images**: Appropriate formats and sizes -- **Lazy loading**: Off-screen content loads lazily - -### Code Quality - -- **Remove console logs**: No debug logging in production -- **Remove commented code**: Clean up dead code -- **Remove unused imports**: Clean up unused dependencies -- **Consistent naming**: Variables and functions follow conventions -- **Type safety**: No TypeScript `any` or ignored errors -- **Accessibility**: Proper ARIA labels and semantic HTML - -## Polish Checklist - -Go through systematically: - -- [ ] Aligned to the design system (drift named and resolved by root cause) -- [ ] Information architecture and flow shape match neighboring features -- [ ] Visual alignment perfect at all breakpoints -- [ ] Spacing uses design tokens consistently -- [ ] Typography hierarchy consistent -- [ ] All interactive states implemented -- [ ] All transitions smooth (60fps) -- [ ] Copy is consistent and polished -- [ ] Icons are consistent and properly sized -- [ ] All forms properly labeled and validated -- [ ] Error states are helpful -- [ ] Loading states are clear -- [ ] Empty states are welcoming -- [ ] Touch targets are 44x44px minimum -- [ ] Contrast ratios meet WCAG AA -- [ ] Keyboard navigation works -- [ ] Focus indicators visible -- [ ] No console errors or warnings -- [ ] No layout shift on load -- [ ] Works in all supported browsers -- [ ] Respects reduced motion preference -- [ ] Code is clean (no TODOs, console.logs, commented code) - -**IMPORTANT**: Polish is about details. Zoom in. Squint at it. Use it yourself. The little things add up. - -Sweat the details. Zoom in until the alignment is right and the spacing reads as deliberate. Then ship. - -**NEVER**: -- Polish before it's functionally complete -- Polish without aligning to the design system; that's decoration on drift -- Guess at design system principles instead of asking when something is ambiguous -- Spend hours on polish if it ships in 30 minutes (triage) -- Introduce bugs while polishing (test thoroughly) -- Ignore systematic issues (if spacing is off everywhere, fix the system, not just one screen) -- Perfect one thing while leaving others rough (consistent quality level) -- Create new one-off components when design system equivalents exist -- Hard-code values that should use design tokens -- Introduce new patterns or flows that diverge from established ones - -## Final Verification - -Before marking as done: - -- **Use it yourself**: Actually interact with the feature. -- **Test on real devices**: Not just browser DevTools. -- **Ask someone else to review**: Fresh eyes catch things. -- **Compare to design**: Match intended design. -- **Check all states**: Don't just test happy path. -- **Treat automation carefully**: Run detector or QA commands when they are available and relevant, fix their defects, but never cite a clean result as proof that the work is polished. - -## Clean Up - -After polishing, ensure code quality: - -- **Replace custom implementations**: If the design system provides a component you reimplemented, switch to the shared version. -- **Remove orphaned code**: Delete unused styles, components, or files made obsolete by polish. -- **Consolidate tokens**: If you introduced new values, check whether they should be tokens. -- **Verify DRYness**: Look for duplication introduced during polishing and consolidate. +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. diff --git a/.github/skills/impeccable/reference/quieter.md b/.github/skills/impeccable/reference/quieter.md index 55b00b6f3..bb163e40f 100644 --- a/.github/skills/impeccable/reference/quieter.md +++ b/.github/skills/impeccable/reference/quieter.md @@ -2,7 +2,7 @@ Quiet design is harder than bold design. Subtlety needs precision. Reduce visual --- -## Register +## Visitor mode Persuade + Experience: "quieter" means more restrained palette, more whitespace, more typographic air. Drama is reduced, not eliminated; the POV stays intact. diff --git a/.github/skills/impeccable/reference/routing.md b/.github/skills/impeccable/reference/routing.md index 5dbf293a7..7099b1db8 100644 --- a/.github/skills/impeccable/reference/routing.md +++ b/.github/skills/impeccable/reference/routing.md @@ -11,7 +11,7 @@ Reason over the signals; there is no score to obey: - `critique.latest` with a low `score` or non-zero `p0` / `p1` → `polish` (it reads that snapshot as its backlog), or re-run `critique` if the snapshot looks stale. - `git.changedFiles` pointing at one surface → scope `audit` or `polish` to those files specifically, naming them. - `devServer.running` true → `live` is available for in-browser iteration; if false, don't lead with `live`. **`live` and the bundled `detect.mjs` are web-only.** If `setup.platform` is `ios`, `android`, or `adaptive`, don't lead with either; the browser overlay and the HTML rule engine don't apply to native app code. -- Otherwise group by intent exactly as init's "Recommend starting points" step does (build new / improve what's there / iterate visually), tailored to `setup.register`. +- Otherwise group by intent (build new / improve what's there / iterate visually), tailored to the current surface and `setup.platform`. **If `scan.targets` is non-empty and `setup.platform` is not `ios`/`android`/`adaptive`, run `node .github/skills/impeccable/scripts/detect.mjs --json ` once** (the bundled detector over local files: no network, no npx; it reads HTML/CSS, so skip it for native projects). `scan.via` tells you what they are: `git-changes` (the markup/style files in your dirty tree, the most relevant set), `source-dir` (e.g. `src`, `app`), `html`, or `root`. Fold the hits into your picks: many quality / contrast hits → `audit` or `polish`; a specific slop family → the matching command (gradient text or eyebrows → `quieter` / `typeset`, flat or gray palette → `colorize`, and so on). It's a real, current signal that beats guessing. If detect errors or the tree is large and slow, skip it and recommend the user run `audit` themselves; never block the suggestion on it. diff --git a/.github/skills/impeccable/reference/shape.md b/.github/skills/impeccable/reference/shape.md index 40804f6e4..acbb26e5b 100644 --- a/.github/skills/impeccable/reference/shape.md +++ b/.github/skills/impeccable/reference/shape.md @@ -1,167 +1,61 @@ -Shape the UX and UI for a feature before any code is written. This command produces a **design brief**: a structured artifact that guides implementation through discovery, not guesswork. +# Shape -**Scope**: Design planning only. This command does NOT write code. It produces the thinking that makes code good. +Discover what should be made and how it should work, then return a confirmed design brief without code. -**Output**: A design brief that can be handed off to /impeccable craft, or directly to /impeccable for freeform implementation. When visual direction probes are used, the images are supporting artifacts, not the primary output. +**Product gate:** when context reports that PRODUCT.md is missing, load and complete [init.md](init.md) before Phase 1. Do not substitute shape questions for the product interview. Once PRODUCT.md exists, return here; product context does not replace task discovery. -## Philosophy +## Phase 1: Discovery interview -Most AI-generated UIs fail not because of bad code, but because of skipped thinking. They jump to "here's a card grid" without asking "what is the user trying to accomplish?" This command inverts that: understand deeply first, so implementation is precise. +Do not write code or choose visual direction yet. -## Phase 1: Discovery Interview +### Cadence -**Do NOT write any code or make any design decisions during this phase.** Your only job is to understand the feature deeply enough to make excellent design decisions later. +- Use the structured question tool when available; otherwise ask and stop. +- Ask two or three related questions per round, then wait. One round is the default; add a second only when the answers expose a material gap. +- Do not dump a questionnaire, repeat settled facts, or turn obvious facts into menus. Assert the likely reading and invite correction. +- A sparse prompt requires at least one answer round. A precise prompt may need only a compact confirmation. -**Unattended runs:** when no user can respond (a one-shot task, an automated run, or an explicit instruction not to ask questions), answer the interview questions yourself from the brief, record the answers as the design brief, and continue without pausing at any confirmation gate in this file. The thinking is still required; only the waiting is waived. +### Round 1: purpose, people, and outcome -This is a required interaction, not optional guidance. Ask these questions in conversation, adapting based on answers. Don't dump them all at once; have a natural dialogue. ask the user directly to clarify what you cannot infer. +Choose the two or three questions that most change the result: -### Interview cadence +- What is this surface or feature for, and what problem must it solve? +- Who specifically reaches it, in what situation and state of mind? +- What is the primary thing they must understand or do? What would success look like? +- What is uniquely true here that a neighboring product or generic template could not claim? -Discovery includes at least one user-answer round unless PRODUCT.md, DESIGN.md, or an already-confirmed brief directly answers the needed inputs. With a sparse prompt, do **not** synthesize a complete brief for confirmation on the first response. +### Round 2: material, behavior, and boundaries -- Use the harness's structured question tool when one exists. Otherwise, ask directly in chat and stop. -- Ask **2-3 questions per round**, then wait for answers. -- Treat PRODUCT.md and DESIGN.md as anchors; they reduce repeated questions but do **not** replace shape for craft. Shape is task-specific. -- One round is the default. Add a second only if the first answers leave material gaps. Don't run a second round just to feel thorough. -- Round 1 should clarify purpose, audience/context, content/scope, and (for brand) visual direction. -- Round 2, when needed, fills in whatever's still genuinely missing. +Run only for material unresolved decisions: -**Assert-then-confirm, not menu-with-escape.** When PRODUCT.md and the user's prompt make one option obvious, name it and ask the user to confirm or override. Don't enumerate "Restrained / Committed / Or something else?" as a real choice; "This reads as Restrained, confirm?" beats a four-option menu when the answer is already clear. +- What real content, evidence, data, and assets must the experience carry? What are realistic minimum, typical, and maximum ranges? +- Which states and transitions matter: first-run, empty, loading, error, success, permissions, overflow, or expert use? +- What is the intended fidelity, breadth, and interactivity: exploration, production-ready screen, full flow, or broader surface? +- What must remain untouched? What would make the result feel wrong even if it looked polished? +- Which platform, framework, performance, accessibility, localization, or delivery constraints are binding? -### Purpose & Context -- What is this feature for? What problem does it solve? -- Who specifically will use it? (Not "users"; be specific: role, context, frequency) -- What does success look like? How will you know this feature is working? -- What's the user's state of mind when they reach this feature? (Rushed? Exploring? Anxious? Focused?) +Never ask for CSS values or canned aesthetic lanes. New-work owns visual-world and concept choices. -### Content & Data -- What content or data does this feature display or collect? -- What are the realistic ranges? (Minimum, typical, maximum, e.g., 0 items, 5 items, 500 items) -- What are the edge cases? (Empty state, error state, first-time use, power user) -- Is any content dynamic? What changes and how often? -- What visual assets are real content here? Note required images, product shots, illustrations, maps, textures, diagrams, generated objects, or existing project assets. +## Phase 2: Resolve the design direction -### Design Direction +For new surfaces, brand expansion, or replacement, follow [new-work.md](new-work.md) through visual authority, any world workshop, and concept choice. Reuse discovery, then return before its contract, persistence, or implementation. Inside an established world, use its concept process only when composition or interaction remains materially open. -Force a visual decision on three fronts. Skip anything PRODUCT.md or DESIGN.md already answers; ask only what's missing. +## Phase 3: Write the brief -- **Color strategy for this surface.** Pick one: Restrained / Committed / Full palette / Drenched. Can override the project default if the surface earns it (e.g. a drenched hero inside an otherwise Restrained product). -- **Theme via scene sentence.** Write one sentence of physical context for this surface: who uses it, where, under what ambient light, in what mood. The sentence forces dark vs light. If it doesn't, add detail until it does. -- **Two or three named anchor references.** Specific products, brands, objects. Not adjectives like "modern" or "clean." +Write the smallest useful brief: -### Scope +1. **Job and audience:** who arrives, their context, need, and visitor mode. +2. **Outcome and proof:** primary task/action, success, real evidence, and product-specific truth. +3. **Selected direction:** visual authority, structural/interaction thesis, sequence, focal moment, and implementation consequence. +4. **Scope and boundaries:** fidelity, breadth, interactivity, named target, what remains untouched, and explicit anti-goals. +5. **States and ranges:** realistic content/data ranges and material states. +6. **Interaction and layout:** hierarchy, topology, responsiveness, affordances, feedback, and transitions; intent, not CSS. +7. **Constraints and open decisions:** platform, delivery, accessibility, localization, reusable components, and choices a builder must not invent. -Always ask. Sketch quality and shipped quality are different outputs; don't guess between them. +Use three to five bullets when the task is settled; use the full structure only for ambiguous, multi-screen, or standalone planning. Do not restate the conversation. -- **Fidelity.** Sketch / mid-fi / high-fi / production-ready? -- **Breadth.** One screen / a flow / a whole surface? -- **Interactivity.** Static visual / interactive prototype / shipped-quality component? -- **Time intent.** Quick exploration, or polish until it ships? +## Confirm and stop -Scope answers are task-scoped. Don't write them to PRODUCT.md or DESIGN.md; carry them through the design brief only. +Present the brief for explicit confirmation or one correction round, then stop: shape never writes code or a direction contract. -### Constraints -- Are there technical constraints? (Framework, performance budget, browser support) -- Are there content constraints? (Localization, dynamic text length, user-generated content) -- Mobile/responsive requirements? -- Accessibility requirements beyond WCAG AA? - -### Anti-Goals -- What should this NOT be? What would be a wrong direction? -- What's the biggest risk of getting this wrong? - -## Phase 1.5: Visual Direction Probe (Capability-Gated) - -After the discovery interview, generate a small set of visual direction probes **before** writing the final brief when all of these are true: - -- The work is **net-new** or directionally ambiguous enough that visual exploration will clarify the brief. -- The requested fidelity is **mid-fi, high-fi, or production-ready**. Skip for sketch-only planning. -- The current harness gives you native image generation (Codex's `image_gen`, an equivalent MCP tool, or similar). Don't ask the user to install APIs or tooling. - -When those conditions are met, this step is mandatory. If image generation isn't natively available, do not ask the user to install APIs or tooling. State in one line that the image step is skipped because the harness lacks native image generation, then proceed. The one-line announcement is required, not optional; it forces a conscious decision instead of letting the step quietly evaporate. - -Use probes to explore visual lanes, not to replace the brief. - -Do not skip probes because the final UI will be semantic, editable, code-native, responsive, or accessible. Those are implementation requirements, not reasons to avoid visual exploration. - -### What to generate - -Generate **2 to 4** distinct direction probes based on the discovery answers, especially: - -- Color strategy -- Theme scene sentence -- Named anchor references -- Scope and fidelity - -The probes should differ in primary visual direction (hierarchy, topology, density, typographic voice, or color strategy), not just palette tweaks. - -### How to use the probes - -- Treat them as **direction tests**, not final designs. -- Use them to pressure-test whether the brief is pointing at the right lane. -- Ask the user which direction feels closest, what feels off, and what should carry forward. -- If the probes reveal a mismatch, revise the brief inputs before finalizing the brief. - -### Important limits - -- Do **not** skip discovery because image generation is available. -- Do **not** treat generated imagery as final UX specification, final copy, or final accessibility behavior. -- Do **not** use this step for minor refinements of existing work. It's for shaping a new surface or clarifying a big directional choice. - -If image generation isn't natively available, announce the skip in one line and proceed to the design brief. - -## Phase 2: Design Brief - -After the interview and any required probes, present a brief and **end your response**. The user must confirm before any implementation runs. Do not present a brief and then continue to code in the same response, even if the brief feels obvious to you. The user's confirmation is the gate. - -**Choose the brief shape based on how clear the answers are:** - -- **Compact form (3-5 bullets)** when discovery was crisp and the original prompt + PRODUCT.md already pinned scope, content, and direction. State what you're building, the visual lane, and end with one or two specific questions or a clear "confirm or override?" prompt. This is the default for typical craft requests with a clear prompt. -- **Full structured form (sections below)** when the task is genuinely ambiguous, multi-screen, or when the user asked for shape as a standalone step. Use this when the discipline of structure earns its weight. - -Don't pad a clear brief into a long one to look thorough. A 70-line brief restating answers the user just gave is noise, not rigor. Equally, don't skip the confirmation pause to look efficient: the pause is the point. - -Present the brief, then **stop and wait for explicit confirmation**. You are not the judge of whether the user already approved. Even when the brief feels obviously right, ask once and wait. The pause is what separates shape from premature implementation. - -### Brief Structure - -**1. Feature Summary** (2-3 sentences) -What this is, who it's for, what it needs to accomplish. - -**2. Primary User Action** -The single most important thing a user should do or understand here. - -**3. Design Direction** -Color strategy (Restrained / Committed / Full palette / Drenched) + the theme scene sentence + 2–3 named anchor references. Reference PRODUCT.md and DESIGN.md where they already answer, and note any per-surface overrides. - -If you ran the Visual Direction Probe step, name which probe direction won and what changed in the brief because of it. - -**4. Scope** -Fidelity, breadth, interactivity, and time intent from the Scope section of the interview. Task-scoped; these don't persist beyond the brief. - -**5. Layout Strategy** -High-level spatial approach: what gets emphasis, what's secondary, how information flows. Describe the visual hierarchy and rhythm, not specific CSS. - -**6. Key States** -List every state the feature needs: default, empty, loading, error, success, edge cases. For each, note what the user needs to see and feel. - -**7. Interaction Model** -How users interact with this feature. What happens on click, hover, scroll? What feedback do they get? What's the flow from entry to completion? - -**8. Content Requirements** -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., 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. - ---- - -ask the user directly to clarify what you cannot infer. Ask for explicit confirmation of the brief before finishing. - -If the user disagrees with any part, revisit the relevant discovery questions. A shape run is incomplete until the user confirms direction. - -Once confirmed, the brief is complete. The user can now hand it to /impeccable, or use it to guide any other implementation approach. (If the user wants the full discovery-then-build flow in one step, they should use /impeccable craft instead, which runs this command internally.) +When no human or structured answer mechanism exists, mark assumptions plainly, return the brief, and stop. diff --git a/.github/skills/impeccable/reference/typeset.md b/.github/skills/impeccable/reference/typeset.md index 34b234342..1b106cd37 100644 --- a/.github/skills/impeccable/reference/typeset.md +++ b/.github/skills/impeccable/reference/typeset.md @@ -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. --- -## Register +## Visitor mode -New identity work: run the font selection procedure in [new-work.md](new-work.md). Fluid `clamp()` scale, ≥1.25 ratio between steps. +- **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, register, **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 .github/skills/impeccable/scripts/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 `/impeccable polish` for the final pass. +When the hierarchy holds, hand off to `/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) * )` 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 - -The tactical selection procedure and the reflex-reject list live in [new-work.md](new-work.md) (loaded for new identity work). 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 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. diff --git a/.github/skills/impeccable/scripts/command-metadata.json b/.github/skills/impeccable/scripts/command-metadata.json index 83796d54b..dad8ef2e0 100644 --- a/.github/skills/impeccable/scripts/command-metadata.json +++ b/.github/skills/impeccable/scripts/command-metadata.json @@ -1,6 +1,6 @@ { "craft": { - "description": "Full confirmed-brief-then-build flow. Runs multi-round shape discovery first, resolves visual probe and north-star mock gates when available, then builds and visually iterates. Use when building a new feature end-to-end.", + "description": "Deprecated compatibility alias for an ordinary Impeccable new-work request. It adds no behavior; natural build and redesign requests use the same flow.", "argumentHint": "[feature description]" }, "init": { diff --git a/.github/skills/impeccable/scripts/concept-seed.mjs b/.github/skills/impeccable/scripts/concept-seed.mjs index 1f141a80f..25f20d9ab 100644 --- a/.github/skills/impeccable/scripts/concept-seed.mjs +++ b/.github/skills/impeccable/scripts/concept-seed.mjs @@ -1,6 +1,7 @@ #!/usr/bin/env node /** - * Concept-seed picker: the dice half of the new-work concept procedure. + * External concept seed: the dice half of new-work's coupled-direction and + * established-world surface procedures. * * The model derives a grounded shortlist of candidate FORMS from the * audience's world and the subject's cultural home (see @@ -12,10 +13,9 @@ * * This script rolls them from outside, the same trick that made the * palette seed work: - * - BUILD INDEX (2-5): which entry of the model's own resonance-ordered - * shortlist to build. The dice never choose an ungrounded ingredient; - * they only refuse the argmax rut. (Index 1 is excluded: that's the - * concept every run would ship anyway.) + * - PROMOTED INDEX: which entry of the model's own resonance-ordered + * shortlist must be taken seriously beside its favorites. The dice never + * choose an ungrounded ingredient; they only refuse the argmax rut. * - CHALLENGERS (3): outside forms from concept-ingredients.json, weighed * against the derived candidates on exactly two axes — audience * identification and product clarity. They win only when they beat the @@ -23,8 +23,8 @@ * material and win over thin categories, which is the intended shape. * * Usage: - * node scripts/concept-seed.mjs # roll at random - * node scripts/concept-seed.mjs --from # deterministic (hash key) + * node scripts/concept-seed.mjs --scope direction + * node scripts/concept-seed.mjs --scope surface --from * * Env vars: * IMPECCABLE_CONCEPT_SEED — same as --from; for reproducible eval runs. @@ -40,6 +40,12 @@ const pool = JSON.parse(readFileSync(join(here, 'concept-ingredients.json'), 'ut const args = process.argv.slice(2); const fromIdx = args.indexOf('--from'); +const scopeIdx = args.indexOf('--scope'); +const scope = scopeIdx !== -1 ? args[scopeIdx + 1] : 'surface'; +if (scope !== 'surface' && scope !== 'direction') { + process.stderr.write('concept-seed: --scope must be direction or surface\n'); + process.exit(1); +} // When no key is supplied, generate one and print it: a user reporting a // bad outcome can hand us the key and we replay the exact roll. const key = fromIdx !== -1 @@ -47,7 +53,7 @@ const key = fromIdx !== -1 : (process.env.IMPECCABLE_CONCEPT_SEED || crypto.randomBytes(4).toString('hex')); function hashUnit(k, salt) { - const h = crypto.createHash('sha256').update(`${salt}:${k}`).digest(); + const h = crypto.createHash('sha256').update(`${scope}:${salt}:${k}`).digest(); return h.readUInt32BE(0) / 0xffffffff; } const unit = (salt) => hashUnit(key, salt); @@ -67,22 +73,48 @@ for (let i = 0; picks.length < 3 && i < 60; i++) { } } -process.stdout.write(`CONCEPT SEED (key: ${key}; rerun with --from ${key} to reproduce this roll) -BUILD INDEX: ${buildIndex} - After ordering your derived candidates by resonance, build the page whose - form comes from candidate number ${buildIndex}, exactly as if it had ranked - first: full commitment. Your top-ranked candidate is what every run in this - category would ship; the assignment exists to refuse that rut, not to - punish it. -CHALLENGERS (weigh against your derived candidates on the same two axes, -audience identification and product clarity; a challenger wins only when -it beats the grounded list on both): +const promotedInstruction = scope === 'direction' + ? `After ordering the grounded coupled directions by product fit, promote + candidate ${buildIndex} into the serious shortlist. Each candidate must join + a durable visual system to a concrete expression for the requested first + surface; select or revise that pair as one decision. It must survive the + current task plus navigation, quiet and dense content, interaction and state, + and a substantially different future surface.` + : `After ordering the task's grounded structural candidates by resonance, + promote candidate ${buildIndex} into the serious shortlist. In an attended + run, present it beside the strongest materially different candidates and + let the user select or revise the surface concept. In a truly unattended + run, use it when it survives audience identification and product clarity.`; + +const challengerInstruction = scope === 'direction' + ? `A challenger enters only when it can supply both reusable identity grammar + and a strong first-surface structure, not a one-page costume or a style pasted + onto an unrelated layout. Weigh audience identification, product clarity, + current-surface force, and cross-surface system breadth.` + : `A challenger wins only when it beats the grounded list on both audience + identification and product clarity. It may change task topology or + interaction, but never the committed visual identity.`; + +const authorityInstruction = scope === 'direction' + ? `PRODUCT.md and explicit incumbent brand commitments constrain every coupled +direction. The seed never chooses exact colors, fonts, tokens, or a user +preference, and it never permits the world and first surface to be selected +independently.` + : `PRODUCT.md and DESIGN.md constrain every surface candidate's identity +vocabulary; they do not cancel task-level composition. The seed never +authorizes a new palette, type system, material world, or unfamiliar control +behavior.`; + +process.stdout.write(`${scope.toUpperCase()} CONCEPT SEED (key: ${key}; rerun with --scope ${scope} --from ${key} to reproduce this roll) +PROMOTED INDEX: ${buildIndex} + ${promotedInstruction} + The promotion exists to refuse the model's ranking rut, not to outrank the + user or the brief. +CHALLENGERS: 1. ${picks[0]} 2. ${picks[1]} 3. ${picks[2]} -If a challenger wins, it replaces the assigned candidate. If the surface is -an existing world whose incumbent carries a deliberate, ownable idea, the -incumbent IS the chosen candidate: intensify its lineage and ignore the -roll entirely. The same override applies when the user, PRODUCT.md, or -DESIGN.md pins a direction: pinned direction beats the roll, always. +${challengerInstruction} +${authorityInstruction} +A user- or brief-pinned decision beats the roll, always. `); diff --git a/.github/skills/impeccable/scripts/context-signals.mjs b/.github/skills/impeccable/scripts/context-signals.mjs index 2fc27bea7..d296320d7 100644 --- a/.github/skills/impeccable/scripts/context-signals.mjs +++ b/.github/skills/impeccable/scripts/context-signals.mjs @@ -11,7 +11,7 @@ * output is always valid JSON. * * Signals: - * - setup: PRODUCT.md / DESIGN.md presence, register, whether code exists + * - setup: PRODUCT.md / DESIGN.md presence and whether code exists * - critique: the latest cached critique score (.impeccable/critique) * - git: branch + files changed vs the default branch (a scope hint) * - devServer: whether a local dev server answers on a common port (gates live) @@ -21,7 +21,7 @@ import net from 'node:net'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { execFileSync } from 'node:child_process'; -import { loadContext, extractRegister, extractPlatform } from './context.mjs'; +import { loadContext, extractPlatform } from './context.mjs'; import { getCritiqueDir } from './lib/impeccable-paths.mjs'; /** Is there code here at all, or just context files / an empty repo? */ @@ -196,7 +196,6 @@ export async function gatherSignals(cwd = process.cwd()) { hasDesign: ctx.hasDesign, designPath: ctx.designPath, hasCode: hasCode(cwd), - register: extractRegister(ctx.product), platform: extractPlatform(ctx.product), }, critique: { latest: latestCritique(cwd) }, diff --git a/.github/skills/impeccable/scripts/context.mjs b/.github/skills/impeccable/scripts/context.mjs index 2ef2d4ce8..080e7ee3a 100644 --- a/.github/skills/impeccable/scripts/context.mjs +++ b/.github/skills/impeccable/scripts/context.mjs @@ -1,8 +1,10 @@ /** - * Context loader: prints PRODUCT.md (and DESIGN.md if present) as one - * markdown block on stdout, or prints a `NO_PRODUCT_MD:` message when no + * Context loader: prints PRODUCT.md, DESIGN.md when present, the matching + * persisted surface brief when one can be resolved, and native-platform + * guidance selected from PRODUCT.md. It prints a + * `NO_PRODUCT_MD:` message when no * PRODUCT.md is found anywhere. The skill keys off that message to branch: - * from-scratch build commands (init / teach / craft / shape) and clear + * from-scratch build requests (plus init / teach / shape) and clear * build/shape intent divert into the init flow, while scoped commands proceed * using the existing code as context. * @@ -23,10 +25,13 @@ import os from 'node:os'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { parseTargetOptions } from './lib/target-args.mjs'; -import { IMPECCABLE_COMMAND } from './lib/provider.mjs'; +import { IMPECCABLE_COMMAND, IMPECCABLE_PROVIDER_ID } from './lib/provider.mjs'; +import { renderLlmOnlySlopReview } from './lib/slop-review.mjs'; +import { resolveSurfaceBrief } from './lib/surface-briefs.mjs'; const PRODUCT_NAMES = ['PRODUCT.md', 'Product.md', 'product.md']; const DESIGN_NAMES = ['DESIGN.md', 'Design.md', 'design.md']; +const SKILL_REFERENCE_DIR = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', 'reference'); const FALLBACK_DIRS = ['.agents/context', 'docs']; const MONOREPO_MARKER_FILES = ['pnpm-workspace.yaml', 'turbo.json', 'nx.json', 'lerna.json']; const MONOREPO_FALLBACK_PROJECT_DIRS = ['apps', 'packages']; @@ -41,6 +46,8 @@ const WORKSPACE_DISCOVERY_IGNORED_DIRS = new Set([ '.turbo', '.cache', 'coverage', + 'vendor', + 'vendors', ]); const VISUAL_SOURCE_DIRS = ['src', 'app', 'pages', 'components', 'site', 'public', 'styles']; const STYLE_EXTENSIONS = new Set(['.css', '.scss', '.sass', '.less', '.styl']); @@ -73,6 +80,12 @@ export function loadContext(cwd = process.cwd(), options = {}) { const designPath = resolved.designPath; const product = productPath ? safeRead(productPath) : null; const design = designPath ? safeRead(designPath) : null; + const platform = extractPlatform(product); + const surfaceResolution = resolveSurfaceBrief( + resolved.projectRoot, + hasTargetOption(options) ? options.targetPath : null, + ); + const surfaceBrief = surfaceResolution.brief; return { hasProduct: !!product, product, @@ -83,7 +96,18 @@ export function loadContext(cwd = process.cwd(), options = {}) { contextDir: resolved.contextDir, productContextDir: productPath ? path.dirname(productPath) : null, designContextDir: designPath ? path.dirname(designPath) : null, + hasSurfaceBrief: !!surfaceBrief, + surfaceBrief: surfaceBrief?.text ?? null, + surfaceBriefPath: surfaceBrief?.path ? path.relative(absCwd, surfaceBrief.path) : null, + surfaceBriefReason: surfaceResolution.reason, + surfaceBriefCandidates: surfaceResolution.candidates.map((brief) => ({ + slug: brief.slug, + path: path.relative(absCwd, brief.path), + primaryTarget: brief.primaryTarget, + relatedTargets: brief.relatedTargets, + })), hasVisualImplementation: hasVisualImplementation(resolved.projectRoot), + platform, projectRoot: resolved.projectRoot, repoRoot: resolved.repoRoot, isMonorepo: resolved.isMonorepo, @@ -696,6 +720,19 @@ function safeRead(p) { } } +function loadNativePlatformReferences(platform) { + const names = platform === 'adaptive' + ? ['ios', 'android'] + : platform === 'ios' || platform === 'android' + ? [platform] + : []; + return names.flatMap((name) => { + const filePath = path.join(SKILL_REFERENCE_DIR, `${name}.md`); + const content = safeRead(filePath); + return content ? [{ name, filePath, content }] : []; + }); +} + /** * Best-effort evidence that the project already has an incumbent visual * implementation. DESIGN.md is documentation, not the only source of design @@ -719,9 +756,11 @@ export function hasVisualImplementation(projectRoot) { let styledComponents = 0; const inspectFile = (filePath) => { - if (scannedFiles++ >= VISUAL_SCAN_FILE_LIMIT) return false; const ext = path.extname(filePath).toLowerCase(); if (!STYLE_EXTENSIONS.has(ext) && !UI_EXTENSIONS.has(ext)) return false; + const base = path.basename(filePath).toLowerCase(); + if (/\.min\.[a-z]+$/.test(base)) return false; + if (scannedFiles++ >= VISUAL_SCAN_FILE_LIMIT) return false; let body; try { body = fs.readFileSync(filePath, 'utf-8').slice(0, 64 * 1024); @@ -729,18 +768,28 @@ export function hasVisualImplementation(projectRoot) { return false; } - const base = path.basename(filePath).toLowerCase(); + const evidence = body + .replace(/\/\*[\s\S]*?\*\//g, '') + .replace(//g, '') + .replace(/^\s*\/\/.*$/gm, ''); if (STYLE_EXTENSIONS.has(ext)) { - const customProperties = body.match(/--[a-z0-9_-]+\s*:/gi)?.length ?? 0; - const visualDeclarations = body.match(/\b(?:color|background(?:-color)?|border(?:-color)?|font-family)\s*:/gi)?.length ?? 0; - if (/\b(?:tokens?|theme|design-system)\b/.test(base) && body.trim().length > 80) return true; + const customProperties = evidence.match(/--[a-z0-9_-]+\s*:/gi)?.length ?? 0; + const visualDeclarations = evidence.match(/\b(?:color|background(?:-color)?|border(?:-color)?|font-family)\s*:/gi)?.length ?? 0; + if (/\b(?:tokens?|theme|design-system)\b/.test(base) && evidence.trim().length > 80) return true; if (customProperties >= 3 || visualDeclarations >= 5) return true; } - if ((ext === '.html' || ext === '.htm') && body.length > 600 && /]+stylesheet/i.test(body)) { + if ((ext === '.html' || ext === '.htm') && evidence.length > 600 && /]+stylesheet/i.test(evidence)) { return true; } - if (!['.html', '.htm'].includes(ext) && body.length > 300 && /class(?:Name)?\s*=|style\s*=|styled\(|css`/i.test(body)) { + if (!['.html', '.htm'].includes(ext) && evidence.length > 300) { + const embeddedCustomProperties = evidence.match(/--[a-z0-9_-]+\s*:/gi)?.length ?? 0; + const embeddedVisualDeclarations = evidence.match(/\b(?:color|background(?:-color)?|border(?:-color)?|font-family)\s*:/gi)?.length ?? 0; + const classTokens = [...evidence.matchAll(/class(?:Name)?\s*=\s*["'`]([^"'`]+)["'`]/gi)] + .reduce((count, match) => count + match[1].trim().split(/\s+/).length, 0); + if ((embeddedCustomProperties >= 3 && embeddedVisualDeclarations >= 3) || embeddedVisualDeclarations >= 5 || classTokens >= 12) return true; + } + if (!['.html', '.htm'].includes(ext) && evidence.length > 300 && /class(?:Name)?\s*=|style\s*=|styled\(|css`/i.test(evidence)) { styledComponents += 1; if (styledComponents >= 3) return true; } @@ -781,9 +830,9 @@ function escapeRegExp(value) { /** * Read the first non-empty line under a bare `## ` section of - * PRODUCT.md (e.g. `## Register`, `## Platform`). Returns null when the + * PRODUCT.md (for example `## Platform`). Returns null when the * section is absent. The heading match is exact (`\s*$`) so near-miss - * headings like `## Register guidelines` don't shadow the real field. + * near-miss headings don't shadow the real field. */ export function extractSectionValue(product, heading) { if (!product) return null; @@ -802,16 +851,6 @@ export function extractSectionValue(product, heading) { return null; } -/** - * Pull the register (`brand` or `product`) out of PRODUCT.md by looking - * for a `## Register` section and reading the first non-empty line that - * follows it. Returns null when the file is legacy / register-less. - */ -export function extractRegister(product) { - const word = (extractSectionValue(product, 'Register') || '').toLowerCase(); - return word === 'brand' || word === 'product' ? word : null; -} - /** * Pull the platform (`web`, `ios`, `android`, or `adaptive`) out of PRODUCT.md * by looking for a `## Platform` section and reading the first non-empty line @@ -985,30 +1024,35 @@ async function cli() { const parts = ctx.hasVisualImplementation ? [ 'NO_PRODUCT_MD: This project has no PRODUCT.md yet, but it does have an incumbent visual implementation. ' + - 'For `init` or `teach`, load reference/init.md and create PRODUCT.md. For `craft`, `shape`, or other work ' + - 'against the existing product, read its CSS, tokens, components, and assets first and proceed without blocking; ' + - `offer \`${IMPECCABLE_COMMAND} init\` as a follow-up. Only treat the work as greenfield when the user explicitly ` + - 'asks to discard or replace the current identity.', - 'EXISTING_VISUAL_SYSTEM: Code and assets are the incumbent design authority. Missing DESIGN.md is a documentation ' + - 'gap, not permission to invent a replacement identity. Preserve and extend the implementation unless the user ' + - 'explicitly requests a rebrand, or inspection proves the detected files contain no coherent visual decisions.', + 'For `init`, `teach`, `shape`, or any request to create a new surface or replacement visual world, load reference/init.md and create PRODUCT.md with the user first. ' + + 'After init writes PRODUCT.md, reference/new-work.md preserves and documents the incumbent system for an ' + + 'extension or replaces it with the user for a redesign/rebrand. Other ' + + 'narrow refinement commands may read the CSS, tokens, components, and assets and proceed without blocking, then ' + + `offer \`${IMPECCABLE_COMMAND} init\` as a follow-up.`, + 'BUILD_INIT_REQUIRED: Before shape or any new-surface/redesign flow, init must capture PRODUCT.md with the human or structured ' + + 'simulated user. Init writes product truth only; reference/new-work.md owns every visual decision.', + 'SCOPED_EXISTING_ALLOWED: Narrow refinement commands may use the incumbent implementation as authority without ' + + 'blocking on context setup; they must preserve it and offer init afterward.', + 'EXISTING_VISUAL_SYSTEM: For refinement or extension, code and assets are incumbent design authority and missing ' + + 'DESIGN.md is a documentation gap. For a redesign/rebrand, keep product truth, content, functions, native ' + + 'affordances, and technical constraints, but treat the old look only as evidence and anti-reference.', ] : [ 'NO_PRODUCT_MD: This project has no PRODUCT.md yet. ' + - 'For `init`, `teach`, `craft`, `shape`, ' + + 'For `init`, `teach`, `shape`, ' + 'or wording that clearly maps to a from-scratch build/shape flow, load ' + - 'reference/init.md and write PRODUCT.md first, unless no user can ' + - 'respond (a one-shot or automated run, or the user said not to ask): ' + - 'then write a one-paragraph understanding of the product, audience, ' + - 'and the page\'s job from the brief, and continue. For any other ' + + 'reference/init.md, complete its human or structured simulated-user interview, and write PRODUCT.md before ' + + 'designing. If no answer mechanism truly exists, init may infer only from the explicit brief and must label its ' + + 'assumptions. It never writes DESIGN.md. For any other ' + '(scoped) command against existing code, proceed using the code as ' + `context and offer \`${IMPECCABLE_COMMAND} init\` as a suggestion (do not block).`, - 'NEW_WORK: No committed design context was found. If this task produces ' + - 'new design (a build from scratch, or a redesign that discards the ' + - 'current look), you MUST read reference/new-work.md before making any ' + - 'design decision. Scoped fixes to existing code do not need it.', + 'PRODUCT_INIT_REQUIRED: No product context or visual authority was found. New builds and redesigns ' + + 'must finish reference/init.md for PRODUCT.md, then reference/new-work.md establishes the world and surface. Scoped ' + + 'fixes to existing code do not need the new-surface flow.', ]; + appendSurfaceBriefContext(parts, ctx); parts.push(buildResolvedContextDirective(ctx, cliOptions, { targetExists })); + appendHookFallback(parts, ctx); if (shouldWarnMissingTarget(ctx, targetProvided, targetExists)) { parts.push(buildMissingTargetDirective()); } @@ -1020,37 +1064,29 @@ async function cli() { if (ctx.hasDesign) { parts.push(`# DESIGN.md\n\n${ctx.design.trim()}`); } + appendSurfaceBriefContext(parts, ctx); parts.push(buildResolvedContextDirective(ctx, cliOptions, { targetExists })); + appendHookFallback(parts, ctx); if (shouldWarnMissingTarget(ctx, targetProvided, targetExists)) { parts.push(buildMissingTargetDirective()); } - const register = extractRegister(ctx.product); - // The register field survives as a family hint (brand = Persuade/Experience, - // product = Operate/Read); SKILL.md's mode section carries the essentials - // inline, so no register-file read is mandated here. What IS mandated: - // the new-work playbook when no committed design system exists yet. if (!ctx.hasDesign) { parts.push(ctx.hasVisualImplementation - ? 'EXISTING_VISUAL_SYSTEM: PRODUCT.md exists and DESIGN.md is missing, but code contains incumbent visual decisions. ' + - 'Treat CSS, tokens, components, and assets as design authority. Do not route to new identity work merely because the ' + - 'document is absent; preserve and extend the implementation unless the user explicitly asks to discard it.' - : 'NEW_WORK: PRODUCT.md exists but no DESIGN.md or incumbent visual implementation was found. If this task produces ' + - 'new design or a redesign that discards the current look, you MUST read reference/new-work.md before making any ' + - 'design decision. Scoped fixes to existing code do not need it.'); + ? 'INCUMBENT_WORLD_UNDOCUMENTED: PRODUCT.md exists and DESIGN.md is missing, but code contains incumbent visual decisions. ' + + 'For shape or a new-surface/redesign request, load reference/new-work.md: an extension documents and preserves the code-defined world; ' + + 'a redesign replaces it with the user and uses the old look only as evidence and anti-reference. Narrow refinement ' + + 'commands may proceed using the implementation directly.' + : 'WORLD_DISCOVERY_REQUIRED: PRODUCT.md exists but no DESIGN.md or incumbent visual implementation was found. ' + + 'For a new build or redesign, load reference/new-work.md and establish the visual world with the human or structured ' + + 'simulated user before developing the task concept. Scoped fixes to existing code do not need this flow.'); } - if (register) { - parts.push(`REGISTER: \`${register}\` (family hint: brand = Persuade/Experience surfaces, product = Operate/Read). Derive the visitor's mode per SKILL.md.`); - } - const platform = extractPlatform(ctx.product); - const nativeRefs = - platform === 'adaptive' ? ['ios', 'android'] : platform === 'ios' || platform === 'android' ? [platform] : []; - if (nativeRefs.length) { - const refList = nativeRefs.map(p => `\`reference/${p}.md\``).join(' and '); - const label = platform === 'adaptive' ? '`adaptive` (both iOS and Android)' : `\`${platform}\``; + const platformReferences = loadNativePlatformReferences(ctx.platform); + for (const reference of platformReferences) { parts.push( - `NEXT STEP: This project targets ${label}. Also read ${refList} for native conventions, in addition to SKILL.md's mode guidance.`, + `# NATIVE PLATFORM REFERENCE: ${reference.name.toUpperCase()} (reference/${reference.name}.md)\n\n${reference.content.trim()}`, ); - } else if (!platform) { + } + if (!ctx.platform) { // A `## Platform` section that names something we don't recognize (a // toolchain like `flutter`, a typo) would otherwise silently fall back to // web — the wrong default exactly when the user tried to say "native". @@ -1078,6 +1114,72 @@ function pathExistsForTarget(cwd, targetPath) { return fs.existsSync(abs); } +const HOOK_MANIFESTS_BY_PROVIDER = Object.freeze({ + 'claude-code': ['.claude/settings.local.json', '.claude/settings.json'], + codex: ['.codex/hooks.json'], + agents: ['.codex/hooks.json'], + cursor: ['.cursor/hooks.json'], + github: ['.github/hooks/impeccable.json'], +}); + +function truthyEnv(value) { + return typeof value === 'string' && /^(1|true|yes|on)$/i.test(value.trim()); +} + +function valueHasHookMarker(value) { + if (typeof value === 'string') { + return value.includes('skills/impeccable/scripts/hook.mjs') + || value.includes('skills/impeccable/scripts/hook-before-edit.mjs'); + } + if (Array.isArray(value)) return value.some(valueHasHookMarker); + if (value && typeof value === 'object') return Object.values(value).some(valueHasHookMarker); + return false; +} + +function hookEnabledAt(root) { + if (truthyEnv(process.env.IMPECCABLE_HOOK_DISABLED)) return false; + let enabled = true; + for (const name of ['.impeccable/config.json', '.impeccable/config.local.json']) { + const raw = readJson(path.join(root, name)); + if (raw?.hook && Object.prototype.hasOwnProperty.call(raw.hook, 'enabled')) { + enabled = raw.hook.enabled !== false; + } + } + return enabled; +} + +const STOP_REVIEW_PROVIDERS = new Set(['claude-code', 'codex', 'agents']); + +function automaticHookMode(ctx) { + if (ctx.platform === 'ios' || ctx.platform === 'android' || ctx.platform === 'adaptive') { + return 'none'; + } + const activeRoot = path.resolve(ctx.projectRoot || process.cwd()); + if (!hookEnabledAt(activeRoot)) return 'none'; + const manifests = HOOK_MANIFESTS_BY_PROVIDER[IMPECCABLE_PROVIDER_ID] || []; + const roots = [...new Set([process.cwd(), ctx.projectRoot, ctx.repoRoot].filter(Boolean).map((root) => path.resolve(root)))]; + for (const root of roots) { + for (const rel of manifests) { + const raw = readJson(path.join(root, rel)); + if (raw?.hooks && valueHasHookMarker(raw.hooks)) { + return STOP_REVIEW_PROVIDERS.has(IMPECCABLE_PROVIDER_ID) ? 'stop' : 'per-edit'; + } + } + } + return 'none'; +} + +function appendHookFallback(parts, ctx) { + const hookMode = automaticHookMode(ctx); + if (hookMode === 'stop') return; + const native = ctx.platform === 'ios' || ctx.platform === 'android' || ctx.platform === 'adaptive'; + parts.push(renderLlmOnlySlopReview({ + automaticDetector: hookMode === 'per-edit', + manualDetector: hookMode === 'none' && !native, + scriptsPath: path.dirname(fileURLToPath(import.meta.url)), + })); +} + function buildResolvedContextDirective(ctx, options, { targetExists = null } = {}) { const targetPath = hasTargetOption(options) ? options.targetPath : null; return `RESOLVED_CONTEXT:\n${JSON.stringify({ @@ -1087,10 +1189,29 @@ function buildResolvedContextDirective(ctx, options, { targetExists = null } = { repoRoot: ctx.repoRoot, productPath: ctx.productPath, designPath: ctx.designPath, + surfaceBriefPath: ctx.surfaceBriefPath, + surfaceBriefReason: ctx.surfaceBriefReason, + surfaceBriefCandidates: ctx.surfaceBriefCandidates, hasVisualImplementation: ctx.hasVisualImplementation, + platform: ctx.platform, }, null, 2)}`; } +function appendSurfaceBriefContext(parts, ctx) { + if (ctx.hasSurfaceBrief && ctx.surfaceBrief) { + parts.push(`# SURFACE BRIEF (${ctx.surfaceBriefPath})\n\n${ctx.surfaceBrief.trim()}`); + return; + } + if (!ctx.surfaceBriefCandidates?.length) return; + const helper = path.join(path.dirname(fileURLToPath(import.meta.url)), 'surface-brief.mjs'); + parts.push( + 'SURFACE_CONTEXT_AVAILABLE: Persisted surface briefs exist, but none was selected unambiguously for this invocation. ' + + 'Resolve the requested surface to its concrete primary or related source path, then run ' + + `\`node ${helper} read \` once before changing that surface. Candidates:\n` + + JSON.stringify(ctx.surfaceBriefCandidates, null, 2), + ); +} + function shouldWarnMissingTarget(ctx, targetProvided, targetExists = null) { if (ctx.isMonorepo && targetProvided && targetExists === false) return true; return !!( diff --git a/.github/skills/impeccable/scripts/critique-storage.mjs b/.github/skills/impeccable/scripts/critique-storage.mjs index 6b4d225cf..38674285a 100644 --- a/.github/skills/impeccable/scripts/critique-storage.mjs +++ b/.github/skills/impeccable/scripts/critique-storage.mjs @@ -29,8 +29,9 @@ import fs from 'node:fs'; import path from 'node:path'; import { fileURLToPath, pathToFileURL } from 'node:url'; import { getCritiqueDir } from './lib/impeccable-paths.mjs'; +import { slugFromTarget } from './lib/target-slug.mjs'; -const SLUG_MAX = 50; +export { slugFromTarget } from './lib/target-slug.mjs'; /** * Mechanically derive a slug from a resolved target. Returns null if the @@ -40,46 +41,6 @@ const SLUG_MAX = 50; * concrete artifact before calling this — we never slug a natural-language * phrase. */ -export function slugFromTarget(resolved, { cwd = process.cwd() } = {}) { - if (!resolved || typeof resolved !== 'string') return null; - const trimmed = resolved.trim(); - if (!trimmed) return null; - - // URL - if (/^https?:\/\//i.test(trimmed)) { - let url; - try { url = new URL(trimmed); } catch { return null; } - const hostPath = `${url.hostname}${url.pathname}`; - return kebab(hostPath); - } - - // File path. Make it project-relative so two devs critiquing the same - // checkout get the same slug regardless of where their repo is cloned. - const abs = path.isAbsolute(trimmed) ? trimmed : path.resolve(cwd, trimmed); - let rel = path.relative(cwd, abs); - // If the target is outside cwd, fall back to the basename so we still - // produce a stable slug (vs the absolute path, which would include - // home dirs / usernames). - if (rel.startsWith('..') || path.isAbsolute(rel)) { - rel = path.basename(abs); - } - if (!rel || rel === '.' || rel === '') return null; - return kebab(rel); -} - -function kebab(s) { - const slug = s - .toLowerCase() - .replace(/[/\\.]+/g, '-') - .replace(/[^a-z0-9-]+/g, '-') - .replace(/-+/g, '-') - .replace(/^-|-$/g, ''); - if (!slug) return null; - // Cap from the tail — the tail (filename) is more identifying than the - // top-level directory. - return slug.length <= SLUG_MAX ? slug : slug.slice(slug.length - SLUG_MAX).replace(/^-/, ''); -} - /** * Filename-safe UTC ISO timestamp: hyphens for separators, trailing Z. * Plain colons aren't allowed on Windows filesystems. diff --git a/.github/skills/impeccable/scripts/detector/detect-antipatterns-browser.js b/.github/skills/impeccable/scripts/detector/detect-antipatterns-browser.js index 033573e36..76bf4ddeb 100644 --- a/.github/skills/impeccable/scripts/detector/detect-antipatterns-browser.js +++ b/.github/skills/impeccable/scripts/detector/detect-antipatterns-browser.js @@ -312,17 +312,6 @@ const ANTIPATTERNS = [ skillSection: 'Layout & Space', skillGuideline: 'numbered section markers', }, - { - id: 'numbered-section-markers', - category: 'slop', - scopes: ['layout'], - severity: 'advisory', - name: 'Numbered section markers (01 / 02 / 03)', - description: - 'Numbered display markers as section labels (01, 02, 03) are the AI editorial scaffold one tier deeper than tracked eyebrow chips. If you find yourself reaching for them, choose a different section cadence.', - skillSection: 'Layout & Space', - skillGuideline: 'numbered section markers', - }, { id: 'em-dash-overuse', category: 'slop', diff --git a/.github/skills/impeccable/scripts/detector/engines/regex/detect-text.mjs b/.github/skills/impeccable/scripts/detector/engines/regex/detect-text.mjs index 81c30ab99..f641d5917 100644 --- a/.github/skills/impeccable/scripts/detector/engines/regex/detect-text.mjs +++ b/.github/skills/impeccable/scripts/detector/engines/regex/detect-text.mjs @@ -280,22 +280,6 @@ const REGEX_ANALYZERS = [ if (count === 0) return []; return [finding('marketing-buzzword', filePath, `${count} buzzword phrase${count === 1 ? '' : 's'}: "${firstSample}"`)]; }, - // Numbered section markers (01 / 02 / 03 ...) - (content, filePath) => { - const text = stripHtmlToText(content); - const re = /\b(0[1-9]|1[0-2])\b/g; - const seen = new Set(); - let m; - while ((m = re.exec(text)) !== null) seen.add(m[1]); - if (seen.size < 3) return []; - const sorted = [...seen].sort(); - let sequential = 0; - for (let i = 1; i < sorted.length; i++) { - if (parseInt(sorted[i], 10) === parseInt(sorted[i - 1], 10) + 1) sequential++; - } - if (sequential < 2) return []; - return [finding('numbered-section-markers', filePath, `Sequence: ${sorted.slice(0, 6).join(', ')}`)]; - }, // Aphoristic cadence: manufactured-contrast + short-rebuttal (content, filePath) => { const text = stripHtmlToText(content); @@ -430,21 +414,20 @@ function runRegexMatchers(lines, filePath, lineOffset = 0, blockContext = null, } /** Page-level analyzers that scan rendered text content (em-dash use, - * buzzword phrases, numbered section markers, aphoristic cadence). + * buzzword phrases, aphoristic cadence). * These are detector-agnostic — they work on any HTML/text source * and don't need a parsed DOM. Exported so detectHtml can call them * for `.html` files (which otherwise skip the regex engine). */ const TEXT_CONTENT_ANALYZER_IDS = [ 'em-dash-overuse', 'marketing-buzzword', - 'numbered-section-markers', 'aphoristic-cadence', ]; function runTextContentAnalyzers(content, filePath, options = {}) { const profile = options?.profile; if (!shouldRunPageAnalyzers(content, filePath)) return []; - // The 4 text-content analyzers are at indices 3-6 in REGEX_ANALYZERS. + // The 3 text-content analyzers are at indices 3-5 in REGEX_ANALYZERS. const findings = []; for (let i = 0; i < TEXT_CONTENT_ANALYZER_IDS.length; i++) { const analyzer = REGEX_ANALYZERS[3 + i]; @@ -535,7 +518,6 @@ function detectText(content, filePath, options = {}) { 'monotonous-spacing', 'em-dash-overuse', 'marketing-buzzword', - 'numbered-section-markers', 'aphoristic-cadence', 'dark-glow', ]; diff --git a/.github/skills/impeccable/scripts/detector/registry/antipatterns.mjs b/.github/skills/impeccable/scripts/detector/registry/antipatterns.mjs index b85f7e62f..84c69b0b8 100644 --- a/.github/skills/impeccable/scripts/detector/registry/antipatterns.mjs +++ b/.github/skills/impeccable/scripts/detector/registry/antipatterns.mjs @@ -210,17 +210,6 @@ const ANTIPATTERNS = [ skillSection: 'Layout & Space', skillGuideline: 'numbered section markers', }, - { - id: 'numbered-section-markers', - category: 'slop', - scopes: ['layout'], - severity: 'advisory', - name: 'Numbered section markers (01 / 02 / 03)', - description: - 'Numbered display markers as section labels (01, 02, 03) are the AI editorial scaffold one tier deeper than tracked eyebrow chips. If you find yourself reaching for them, choose a different section cadence.', - skillSection: 'Layout & Space', - skillGuideline: 'numbered section markers', - }, { id: 'em-dash-overuse', category: 'slop', diff --git a/.github/skills/impeccable/scripts/detector/rules/checks.mjs b/.github/skills/impeccable/scripts/detector/rules/checks.mjs index 3fdd6b2dd..9bace11b3 100644 --- a/.github/skills/impeccable/scripts/detector/rules/checks.mjs +++ b/.github/skills/impeccable/scripts/detector/rules/checks.mjs @@ -352,27 +352,57 @@ function isAccentColor(cssColor) { return false; } +function resolveHeroHeadingSizePx(value) { + const input = String(value || '').trim().toLowerCase(); + if (!input) return 0; + + const simpleLengthPx = (token) => { + const match = /^(-?\d*\.?\d+)\s*(px|rem|em|%)?$/.exec(String(token || '').trim()); + if (!match) return null; + const amount = Number(match[1]); + if (!Number.isFinite(amount)) return null; + if (match[2] === 'rem' || match[2] === 'em') return amount * 16; + if (match[2] === '%') return amount * 0.16; + return amount; + }; + + const direct = simpleLengthPx(input); + if (direct !== null) return direct; + + // Static CSS engines cannot resolve viewport units, but clamp's min/max + // bounds still tell us whether the heading can ever reach hero scale. + const clamp = /^clamp\((.*)\)$/.exec(input); + if (clamp) { + const parts = clamp[1].split(','); + if (parts.length === 3) { + const bounds = [simpleLengthPx(parts[0]), simpleLengthPx(parts[2])] + .filter((candidate) => candidate !== null); + if (bounds.length > 0) return Math.max(...bounds); + } + } + + return 0; +} + // Sibling-relationship rule. Anchor on a hero-scale h1, look at the // previousElementSibling, and gate on EITHER the classic tracked- // uppercase eyebrow OR the modern accent-colored bold eyebrow. function checkHeroEyebrow(opts) { const { headingTag, headingText, headingFontSize, + headingInApplicationContext, siblingTag, siblingText, siblingTextTransform, siblingFontSize, siblingLetterSpacing, siblingFontWeight, siblingColor, siblingHasAccentDashPseudo, } = opts; if (headingTag !== 'h1') return []; - // We previously gated on headingFontSize >= 48 to anchor "hero scale". - // But modern hero h1s use clamp() / vw / var(--text-*), none of which - // jsdom can resolve — the computed value comes back as "2em" or - // "var(--text-9xl)" and parseFloat returns 2 or NaN. The gate fails - // on virtually every Tailwind v4 / framework build. The other gates - // (sibling text 2-60 chars, font-size ≤ 14px, accent-bold OR - // tracked-caps) are tight enough to avoid false positives on non- - // hero h1s — a tiny tan label directly above any h1 is the - // antipattern regardless of how big the h1 ends up. + // This is specifically a marketing-hero cliché, not a ban on compact + // context labels in product UI (for example, a station name inside a tab + // panel). Browser-computed sizes are reliable; the static adapter also + // resolves ordinary px/rem/em and clamp() bounds before reaching here. + if (headingInApplicationContext) return []; + if (!(headingFontSize >= 48)) return []; if (!siblingTag) return []; // An h2 above an h1 is a different anti-pattern (heading hierarchy / dual // headings) — never an eyebrow. @@ -1938,6 +1968,7 @@ function checkElementHeroEyebrowDOM(el) { headingTag: tag, headingText: el.textContent || '', headingFontSize: parseFloat(headStyle.fontSize) || 0, + headingInApplicationContext: !!el.closest('[role="tabpanel"], [role="dialog"], [role="application"], dialog'), siblingTag: sibling.tagName.toLowerCase(), siblingText: sibling.textContent || '', siblingTextTransform: sibStyle.textTransform || '', @@ -3367,7 +3398,8 @@ function checkElementHeroEyebrow(el, style, tag, window, customPropMap) { return checkHeroEyebrow({ headingTag: tag, headingText: el.textContent || '', - headingFontSize: parseFloat(headingFontSizeRaw) || 0, + headingFontSize: resolveHeroHeadingSizePx(headingFontSizeRaw), + headingInApplicationContext: !!el.closest?.('[role="tabpanel"], [role="dialog"], [role="application"], dialog'), siblingTag: sibling.tagName.toLowerCase(), siblingText: sibling.textContent || '', siblingTextTransform: sibStyle.textTransform || '', diff --git a/.github/skills/impeccable/scripts/hook-lib.mjs b/.github/skills/impeccable/scripts/hook-lib.mjs index e61ea692b..50aa2f8fb 100644 --- a/.github/skills/impeccable/scripts/hook-lib.mjs +++ b/.github/skills/impeccable/scripts/hook-lib.mjs @@ -46,6 +46,7 @@ import path from 'node:path'; import { pathToFileURL, fileURLToPath } from 'node:url'; import { extractPlatform, loadContext } from './context.mjs'; import { IMPECCABLE_COMMAND } from './lib/provider.mjs'; +import { renderStopSlopReview } from './lib/slop-review.mjs'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); @@ -1868,22 +1869,42 @@ export const CONTRACT_MAX_CHARS = 1800; // Cap contract sections per Stop emission so many touched artifacts cannot // stack an unbounded message. export const CONTRACT_AUDIT_MAX_FILES = 3; +export const CONTRACT_EXTS = new Set(['.html', '.htm', '.astro', '.svelte', '.vue', '.jsx', '.tsx']); +export const CONTRACT_REQUIRED_FIELDS = ['UNIQUE', 'NOT-TEMPLATE', 'OWN-WORLD', 'STORY', 'FIRST VIEWPORT', 'FORM']; /** - * Extract the artifact's own direction-contract comment: the first HTML - * comment in the head of the file, when its opening chars identify it as a - * contract/concept block. Returns the trimmed, length-capped body, or null - * when the file carries none (no comment, unclosed comment, marker missing, - * or the comment starts past the head window). + * Extract the artifact's own direction-contract comment from the head of an + * HTML or component file. Supports HTML-family comments and JSX block + * comments so the contract works in the Astro/Svelte/Vue/React scaffolds the + * skill actually builds. Returns the trimmed, length-capped body, or null + * when the file carries no valid contract block. */ export function extractDirectionContract(content) { if (typeof content !== 'string' || !content) return null; const head = content.slice(0, CONTRACT_HEAD_CHARS); - const m = //.exec(head); - if (!m) return null; - const body = m[1].trim(); - if (!body || !/contract|concept/i.test(body.slice(0, CONTRACT_MARKER_CHARS))) return null; - return body.slice(0, CONTRACT_MAX_CHARS); + const candidates = []; + for (const pattern of [//g, /\{\/\*([\s\S]*?)\*\/\}/g]) { + for (const match of head.matchAll(pattern)) { + const index = match.index ?? 0; + const linePrefix = head.slice(head.lastIndexOf('\n', index - 1) + 1, index).trim(); + if (linePrefix.startsWith('//')) continue; + candidates.push({ index, body: match[1].trim() }); + } + } + candidates.sort((a, b) => a.index - b.index); + for (const candidate of candidates) { + if (!candidate.body || !/direction\s+contract|concept\s+contract/i.test(candidate.body.slice(0, CONTRACT_MARKER_CHARS))) continue; + return candidate.body.slice(0, CONTRACT_MAX_CHARS); + } + return null; +} + +export function missingDirectionContractFields(contract) { + const body = typeof contract === 'string' ? contract : ''; + return CONTRACT_REQUIRED_FIELDS.filter((field) => { + const label = field.replace(/[.*+?^${}()|[\]\\]/g, '\\$&').replace(/\s+/g, '\\s+'); + return !new RegExp(`\\b${label}\\s*:`, 'i').test(body); + }); } /** @@ -1899,7 +1920,11 @@ export function renderContractAudit(entries, opts = {}) { const shown = entries.slice(0, CONTRACT_AUDIT_MAX_FILES); const blocks = shown.map(({ filePath, contract }) => { const display = relativize(filePath, cwd); - return `${display} opens with this direction contract, written when the direction was decided:\n\n${contract}`; + const missing = missingDirectionContractFields(contract); + const integrity = missing.length > 0 + ? `\n\nContract integrity defect: missing ${missing.join(', ')}. Repair the contract and the implementation together.` + : ''; + return `${display} opens with this direction contract, written when the direction was decided:\n\n${contract}${integrity}`; }); return [ `${ENVELOPE_PREFIX} Direction-contract audit. Before finishing, audit the rendered page against the contract it opens with, promise by promise.`, @@ -1984,9 +2009,22 @@ export async function runStopHook({ stdinJson, env = {}, cwd = process.cwd(), no return result({ skipped: 'native-platform', platform, durationMs: Date.now() - started }); } + const session = ensureSession(cache, sessionId); + const needsSlopReview = session.llmSlopReviewed !== true; + const det = detector || await loadDetector(); if (!det || typeof det.detectText !== 'function') { - return result({ skipped: 'detector-missing', durationMs: Date.now() - started }); + if (!needsSlopReview) return result({ skipped: 'detector-missing', durationMs: Date.now() - started }); + session.llmSlopReviewed = true; + session.updatedAt = Date.now(); + persistCache(projectCwd, cache); + const text = renderStopSlopReview(); + return { + exitCode: 0, + stdout: payload(text, 'Stop', harness), + emission: { kind: 'stop-llm-slop-review', llmSlopReview: true }, + audit: { ...audit, emitted: true, detectorMissing: true, llmSlopReview: true, chars: text.length, durationMs: Date.now() - started }, + }; } const scanOptions = designSystemOptions(config, det, projectCwd); @@ -2013,10 +2051,11 @@ export async function runStopHook({ stdinJson, env = {}, cwd = process.cwd(), no ? configuredExt.engine === 'html' : (ext === '.html' || ext === '.htm'); - // Direction-contract audit: HTML artifacts only, at most once per file + // Direction-contract audit: HTML and component artifacts, at most once per file // per session. The flag lives on the same session cache entry the // finding dedupe uses, so a second Stop fire stays quiet about it. - if (useHtmlEngine) { + const contractCapable = useHtmlEngine || CONTRACT_EXTS.has(ext); + if (contractCapable) { const fileEntry = ensureFile(cache, sessionId, filePath); if (!fileEntry.contractAudited) { const contract = extractDirectionContract(content); @@ -2046,10 +2085,15 @@ export async function runStopHook({ stdinJson, env = {}, cwd = process.cwd(), no } audit.scannedFiles = scanned; - if (freshGroups.length === 0 && contractEntries.length === 0) { + if (freshGroups.length === 0 && contractEntries.length === 0 && !needsSlopReview) { return result({ emitted: false, skipped: 'stop-clean', durationMs: Date.now() - started }); } + if (needsSlopReview) { + session.llmSlopReviewed = true; + session.updatedAt = Date.now(); + } + // Fresh findings and first-time contract audits earn the cache write; // both mark this batch as surfaced so the next Stop fire is silent // unless new issues appear. @@ -2064,6 +2108,9 @@ export async function runStopHook({ stdinJson, env = {}, cwd = process.cwd(), no if (contractEntries.length > 0) { parts.push(renderContractAudit(contractEntries, { cwd: projectCwd })); } + if (needsSlopReview) { + parts.push(renderStopSlopReview()); + } const text = appendDesignSystemNote(parts.join('\n\n'), scanOptions); return { exitCode: 0, @@ -2074,6 +2121,7 @@ export async function runStopHook({ stdinJson, env = {}, cwd = process.cwd(), no ...(contractEntries.length > 0 ? { contractFiles: contractEntries.map((entry) => entry.filePath) } : {}), + ...(needsSlopReview ? { llmSlopReview: true } : {}), }, audit: { ...audit, @@ -2081,6 +2129,7 @@ export async function runStopHook({ stdinJson, env = {}, cwd = process.cwd(), no freshFiles: freshGroups.length, freshFindings: freshGroups.reduce((sum, group) => sum + group.findings.length, 0), ...(contractEntries.length > 0 ? { contractAudits: contractEntries.length } : {}), + ...(needsSlopReview ? { llmSlopReview: true } : {}), chars: text.length, durationMs: Date.now() - started, }, diff --git a/.github/skills/impeccable/scripts/lib/provider.mjs b/.github/skills/impeccable/scripts/lib/provider.mjs index 7fa928a9c..02b4014a4 100644 --- a/.github/skills/impeccable/scripts/lib/provider.mjs +++ b/.github/skills/impeccable/scripts/lib/provider.mjs @@ -1,4 +1,5 @@ // Source scripts default to slash commands. The provider build replaces only // this exact declaration, avoiding heuristic rewrites across executable code. export const IMPECCABLE_COMMAND_PREFIX = "/"; +export const IMPECCABLE_PROVIDER_ID = "github"; export const IMPECCABLE_COMMAND = `${IMPECCABLE_COMMAND_PREFIX}impeccable`; diff --git a/.github/skills/impeccable/scripts/lib/slop-review.mjs b/.github/skills/impeccable/scripts/lib/slop-review.mjs new file mode 100644 index 000000000..30f03e34d --- /dev/null +++ b/.github/skills/impeccable/scripts/lib/slop-review.mjs @@ -0,0 +1,30 @@ +export const LLM_ONLY_SLOP_ITEMS = Object.freeze([ + 'Monospace used merely to signal “technical” or “developer.”', + 'Light or dark mode chosen by category habit rather than the actual use scene.', + 'Everything wrapped in cards, or identical icon-heading-text cards repeated as the page structure.', + 'Hero-metric scaffolds: one oversized number, a small label, supporting stats, and an accent treatment.', + 'Decorative glassmorphism, meaningless sparklines, or generic rounded rectangles with drop shadows.', + 'A modal chosen by reflex when the task does not require interruption or protected focus.', +]); + +export function renderLlmOnlySlopReview({ manualDetector = false, automaticDetector = false, scriptsPath = null } = {}) { + const lines = [ + automaticDetector + ? 'AI_SLOP_REVIEW_REQUIRED: The automatic detector covers mechanical rules, but this harness has no reliable late review. Before finishing changed UI, inspect the authored result for detector-blind model reflexes:' + : 'AI_SLOP_REVIEW_REQUIRED: The automatic Impeccable design hook is not available for this session. Before finishing changed UI, inspect the authored result for detector-blind model reflexes:', + ...LLM_ONLY_SLOP_ITEMS.map((item) => `- ${item}`), + 'Fix reflexes, not intentional choices required by the brief or established visual authority.', + ]; + if (manualDetector && scriptsPath) { + lines.push(`Then run the mechanical detector once over the changed web UI: \`node ${scriptsPath}/detect.mjs --json \`. Do not run it earlier during concept selection.`); + } + return lines.join('\n'); +} + +export function renderStopSlopReview() { + return [ + '[impeccable@1] Detector-blind AI-slop review. The mechanical pass is complete; now inspect the rendered result for model reflexes it cannot reliably detect:', + ...LLM_ONLY_SLOP_ITEMS.map((item) => `- ${item}`), + 'Fix reflexes, not intentional choices required by the brief or established visual authority. Do not rerun the detector; this is the authored judgment layer.', + ].join('\n'); +} diff --git a/.github/skills/impeccable/scripts/lib/surface-briefs.mjs b/.github/skills/impeccable/scripts/lib/surface-briefs.mjs new file mode 100644 index 000000000..f83416f69 --- /dev/null +++ b/.github/skills/impeccable/scripts/lib/surface-briefs.mjs @@ -0,0 +1,151 @@ +import fs from 'node:fs'; +import path from 'node:path'; +import { slugFromTarget } from './target-slug.mjs'; + +export const SURFACE_BRIEF_VERSION = 1; + +export function getSurfaceBriefDir(projectRoot) { + return path.join(projectRoot, '.impeccable', 'surfaces'); +} + +export function normalizeSurfaceTarget(target, { projectRoot = process.cwd() } = {}) { + if (!target || typeof target !== 'string' || !target.trim()) return null; + const trimmed = target.trim(); + if (/^https?:\/\//i.test(trimmed)) { + try { + const url = new URL(trimmed); + url.hash = ''; + url.search = ''; + return url.toString().replace(/\/$/, '') || url.origin; + } catch { + return null; + } + } + if (/^route:/i.test(trimmed)) { + const route = trimmed.slice(trimmed.indexOf(':') + 1).trim(); + if (!route.startsWith('/') || route.includes('..')) return null; + const normalizedRoute = route.split(/[?#]/, 1)[0].replace(/\/{2,}/g, '/').replace(/\/$/, '') || '/'; + return `route:${normalizedRoute}`; + } + if (trimmed === '/') return 'route:/'; + if (trimmed.startsWith('/')) { + const absolute = path.resolve(trimmed); + const relativeToProject = path.relative(projectRoot, absolute); + const isProjectFile = relativeToProject && !relativeToProject.startsWith('..') && !path.isAbsolute(relativeToProject); + if (!isProjectFile && !fs.existsSync(absolute) && !trimmed.includes('..')) { + const normalizedRoute = trimmed.split(/[?#]/, 1)[0].replace(/\/{2,}/g, '/').replace(/\/$/, '') || '/'; + return `route:${normalizedRoute}`; + } + } + const abs = path.isAbsolute(trimmed) ? trimmed : path.resolve(projectRoot, trimmed); + const rel = path.relative(projectRoot, abs); + if (!rel || rel === '.' || rel.startsWith('..') || path.isAbsolute(rel)) return null; + return rel.split(path.sep).join('/'); +} + +export function surfaceBriefPathForTarget(target, { projectRoot = process.cwd() } = {}) { + const normalized = normalizeSurfaceTarget(target, { projectRoot }); + if (!normalized) return null; + const slugInput = normalized.startsWith('route:') ? `route${normalized.slice('route:'.length)}` : normalized; + const slug = slugFromTarget(slugInput, { cwd: projectRoot }); + return slug ? path.join(getSurfaceBriefDir(projectRoot), `${slug}.md`) : null; +} + +export function parseSurfaceBrief(text, filePath = null) { + const match = String(text || '').match(/^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/); + const meta = {}; + if (match) { + for (const line of match[1].split(/\r?\n/)) { + const colon = line.indexOf(':'); + if (colon < 0) continue; + const key = line.slice(0, colon).trim(); + const raw = line.slice(colon + 1).trim(); + if (!key) continue; + if (/^(?:\[|\{|\")/.test(raw) || /^(?:true|false|null|-?\d+(?:\.\d+)?)$/.test(raw)) { + try { meta[key] = JSON.parse(raw); continue; } catch { /* keep string */ } + } + meta[key] = raw.replace(/^['"]|['"]$/g, ''); + } + } + const primaryTarget = typeof meta.primary_target === 'string' ? meta.primary_target : null; + const relatedTargets = Array.isArray(meta.related_targets) + ? meta.related_targets.filter((value) => typeof value === 'string') + : []; + return { + path: filePath, + text: String(text || ''), + body: match ? String(text || '').slice(match[0].length).trim() : String(text || '').trim(), + meta, + slug: typeof meta.slug === 'string' ? meta.slug : filePath ? path.basename(filePath, '.md') : null, + primaryTarget, + relatedTargets, + targets: [primaryTarget, ...relatedTargets].filter(Boolean), + }; +} + +export function listSurfaceBriefs(projectRoot = process.cwd()) { + const dir = getSurfaceBriefDir(projectRoot); + let names; + try { + names = fs.readdirSync(dir).filter((name) => name.endsWith('.md')).sort(); + } catch { + return []; + } + return names.flatMap((name) => { + const filePath = path.join(dir, name); + try { + return [parseSurfaceBrief(fs.readFileSync(filePath, 'utf-8'), filePath)]; + } catch { + return []; + } + }); +} + +export function resolveSurfaceBrief(projectRoot = process.cwd(), target = null) { + const briefs = listSurfaceBriefs(projectRoot); + if (!target) { + return { + brief: briefs.length === 1 ? briefs[0] : null, + candidates: briefs, + reason: briefs.length === 1 ? 'only-brief' : briefs.length > 1 ? 'ambiguous' : 'none', + }; + } + + const normalized = normalizeSurfaceTarget(target, { projectRoot }); + if (!normalized) return { brief: null, candidates: briefs, reason: 'invalid-target' }; + const exactPath = surfaceBriefPathForTarget(normalized, { projectRoot }); + const exact = briefs.find((brief) => brief.path === exactPath && (!brief.targets.length || brief.targets.includes(normalized))); + if (exact) return { brief: exact, candidates: briefs, reason: 'slug' }; + const mapped = briefs.filter((brief) => brief.targets.includes(normalized)); + return { + brief: mapped.length === 1 ? mapped[0] : null, + candidates: mapped.length > 1 ? mapped : briefs, + reason: mapped.length === 1 ? 'mapping' : mapped.length > 1 ? 'ambiguous-target' : 'not-found', + }; +} + +export function writeSurfaceBrief({ + projectRoot = process.cwd(), + primaryTarget, + relatedTargets = [], + body, +}) { + const normalizedPrimary = normalizeSurfaceTarget(primaryTarget, { projectRoot }); + if (!normalizedPrimary) throw new Error('surface brief requires a concrete project-relative primary target or URL'); + const normalizedRelated = [...new Set(relatedTargets + .map((target) => normalizeSurfaceTarget(target, { projectRoot })) + .filter((target) => target && target !== normalizedPrimary))]; + const slug = slugFromTarget(normalizedPrimary, { cwd: projectRoot }); + const filePath = surfaceBriefPathForTarget(normalizedPrimary, { projectRoot }); + fs.mkdirSync(path.dirname(filePath), { recursive: true }); + const frontmatter = [ + '---', + `version: ${SURFACE_BRIEF_VERSION}`, + `slug: ${JSON.stringify(slug)}`, + `primary_target: ${JSON.stringify(normalizedPrimary)}`, + `related_targets: ${JSON.stringify(normalizedRelated)}`, + '---', + ].join('\n'); + fs.writeFileSync(filePath, `${frontmatter}\n\n${String(body || '').trim()}\n`, 'utf-8'); + return filePath; +} diff --git a/.github/skills/impeccable/scripts/lib/target-slug.mjs b/.github/skills/impeccable/scripts/lib/target-slug.mjs new file mode 100644 index 000000000..025915ad5 --- /dev/null +++ b/.github/skills/impeccable/scripts/lib/target-slug.mjs @@ -0,0 +1,33 @@ +import path from 'node:path'; + +const SLUG_MAX = 50; + +/** Derive one clone-stable slug from a concrete file path or URL. */ +export function slugFromTarget(resolved, { cwd = process.cwd() } = {}) { + if (!resolved || typeof resolved !== 'string') return null; + const trimmed = resolved.trim(); + if (!trimmed) return null; + + if (/^https?:\/\//i.test(trimmed)) { + let url; + try { url = new URL(trimmed); } catch { return null; } + return kebab(`${url.hostname}${url.pathname}`); + } + + const abs = path.isAbsolute(trimmed) ? trimmed : path.resolve(cwd, trimmed); + let rel = path.relative(cwd, abs); + if (rel.startsWith('..') || path.isAbsolute(rel)) rel = path.basename(abs); + if (!rel || rel === '.') return null; + return kebab(rel); +} + +export function kebab(value) { + const slug = String(value || '') + .toLowerCase() + .replace(/[/\\.]+/g, '-') + .replace(/[^a-z0-9-]+/g, '-') + .replace(/-+/g, '-') + .replace(/^-|-$/g, ''); + if (!slug) return null; + return slug.length <= SLUG_MAX ? slug : slug.slice(slug.length - SLUG_MAX).replace(/^-/, ''); +} diff --git a/.github/skills/impeccable/scripts/live-browser.js b/.github/skills/impeccable/scripts/live-browser.js index 616f1290a..47bbfefbf 100644 --- a/.github/skills/impeccable/scripts/live-browser.js +++ b/.github/skills/impeccable/scripts/live-browser.js @@ -2592,7 +2592,7 @@ minWidth: '16px', height: '16px', padding: '0 4px', borderRadius: '999px', background: tuneOpen ? C.brand : BP.hairline, - color: tuneOpen ? 'oklch(98% 0 0)' : 'inherit', + color: tuneOpen ? C.ink : 'inherit', fontFamily: MONO, fontSize: '9.5px', fontWeight: '600', lineHeight: '1', boxSizing: 'border-box', @@ -3131,7 +3131,7 @@ position: 'absolute', top: '2px', left: initial ? '18px' : '2px', width: '16px', height: '16px', borderRadius: '50%', - background: 'oklch(98% 0 0)', + background: C.ink, transition: 'left 0.18s ' + EASE, boxShadow: '0 1px 2px oklch(0% 0 0 / 0.2)', }); @@ -3165,7 +3165,7 @@ const b = el('button', { padding: '5px 4px', border: 'none', borderRadius: '3px', background: active ? C.brand : 'transparent', - color: active ? 'oklch(98% 0 0)' : P.text, + color: active ? C.ink : P.text, fontFamily: FONT, fontSize: '10.5px', fontWeight: '500', cursor: 'pointer', whiteSpace: 'nowrap', transition: 'background 0.1s ease, color 0.1s ease', @@ -3178,7 +3178,7 @@ segBtns.forEach(({ btn, val }) => { const on = val === o.value; btn.style.background = on ? C.brand : 'transparent'; - btn.style.color = on ? 'oklch(98% 0 0)' : P.text; + btn.style.color = on ? C.ink : P.text; }); applyParamValue(variantEl, p, o.value); queueCheckpoint('param_changed'); diff --git a/.github/skills/impeccable/scripts/surface-brief.mjs b/.github/skills/impeccable/scripts/surface-brief.mjs new file mode 100644 index 000000000..723f7c1b4 --- /dev/null +++ b/.github/skills/impeccable/scripts/surface-brief.mjs @@ -0,0 +1,74 @@ +#!/usr/bin/env node +import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath, pathToFileURL } from 'node:url'; +import { resolveProjectRoot } from './context.mjs'; +import { + listSurfaceBriefs, + resolveSurfaceBrief, + surfaceBriefPathForTarget, + writeSurfaceBrief, +} from './lib/surface-briefs.mjs'; + +function summary(brief, projectRoot) { + return { + slug: brief.slug, + path: path.relative(projectRoot, brief.path).split(path.sep).join('/'), + primaryTarget: brief.primaryTarget, + relatedTargets: brief.relatedTargets, + }; +} + +function main(argv) { + const [command, target, bodyFile, ...relatedTargets] = argv; + const projectRoot = resolveProjectRoot(process.cwd(), target ? { targetPath: target } : {}); + if (command === 'path') { + const filePath = surfaceBriefPathForTarget(target, { projectRoot }); + if (!filePath) throw new Error('surface brief path requires a concrete target'); + process.stdout.write(`${path.relative(process.cwd(), filePath) || filePath}\n`); + return; + } + if (command === 'list') { + process.stdout.write(`${JSON.stringify(listSurfaceBriefs(projectRoot).map((brief) => summary(brief, projectRoot)), null, 2)}\n`); + return; + } + if (command === 'read') { + const result = resolveSurfaceBrief(projectRoot, target || null); + if (result.brief) { + process.stdout.write(result.brief.text); + return; + } + if (result.candidates.length) process.stderr.write(`${JSON.stringify(result.candidates.map((brief) => summary(brief, projectRoot)), null, 2)}\n`); + process.exit(2); + } + if (command === 'write') { + if (!target || !bodyFile) throw new Error('usage: surface-brief.mjs write '); + const filePath = writeSurfaceBrief({ + projectRoot, + primaryTarget: target, + relatedTargets, + body: fs.readFileSync(bodyFile, 'utf-8'), + }); + process.stdout.write(`${path.relative(process.cwd(), filePath) || filePath}\n`); + return; + } + throw new Error('usage: surface-brief.mjs [target] [body-file] [related-target ...]'); +} + +function isMainModule() { + if (!process.argv[1]) return false; + try { + return fs.realpathSync(fileURLToPath(import.meta.url)) === fs.realpathSync(process.argv[1]); + } catch { + return import.meta.url === pathToFileURL(process.argv[1]).href; + } +} + +if (isMainModule()) { + try { + main(process.argv.slice(2)); + } catch (error) { + process.stderr.write(`${error?.message || error}\n`); + process.exit(1); + } +} diff --git a/.impeccable/surfaces/site-pages-worlds-astro.md b/.impeccable/surfaces/site-pages-worlds-astro.md new file mode 100644 index 000000000..11493096e --- /dev/null +++ b/.impeccable/surfaces/site-pages-worlds-astro.md @@ -0,0 +1,33 @@ +--- +version: 1 +slug: "site-pages-worlds-astro" +primary_target: "site/pages/worlds/index.astro" +related_targets: [] +--- + +# Surface brief: World Catalog + +## Scope + +`/worlds` is an Operate-mode catalog review surface for the concepts that challenge grounded Impeccable design directions. It is public and read-only in production; under `bun run dev` it becomes a source-writing review tool for the repository owner. + +## Product strategy + +The primary user is Paul reviewing a large, growing concept backlog. The desired outcome is a trustworthy approved challenger pool with clear progress through pending work. Approve is the primary action; reject, restore to pending, edit, search, filter, and category navigation are secondary. The page must make the 2,304-entry catalog comprehensible without rendering an overwhelming wall. Concepts remain ineligible for seeding until approved. Existing 113 entries begin approved; new and automated entries begin pending. Review decisions persist separately from catalog content so automation cannot self-approve or overwrite human judgment. + +## Selected direction + +Use the confirmed Neo Kinpaku world and the `/detector` developer-tool lineage. The surface is a backlog-first catalog workbench: fixed family rail, compact queue, and a large central review stage that gives one concept enough space for careful judgment. Progress, status, provenance, structural tags, and keyboard actions remain visible without turning the page into a dashboard of cards. + +## Direction contract + +- **UNIQUE:** Turn an abstract challenger corpus into a human-governed editorial queue. +- **NOT-TEMPLATE:** Refuse the generic analytics dashboard and equal-card catalog grid. +- **OWN-WORLD:** Lacquer ground, kinpaku commitment, patina state, neutral hairlines, Albert Sans UI, compact geometry. +- **STORY:** Find pending work, understand one form, approve or reject it, and see the trusted pool advance. +- **FIRST VIEWPORT:** Family/progress rail, searchable queue, and one full-height review stage with immediate keyboard-visible decisions. +- **FORM:** A catalog editor crossed with a contact-sheet loupe; selection collapses thousands of entries into one precise judgment at a time. Seed key: not run—structure is inherited from the established `/detector` tool surface. + +## Open decisions + +None. diff --git a/.kiro/skills/impeccable/SKILL.md b/.kiro/skills/impeccable/SKILL.md index b9c3647ba..e80a07b26 100644 --- a/.kiro/skills/impeccable/SKILL.md +++ b/.kiro/skills/impeccable/SKILL.md @@ -1,60 +1,59 @@ --- name: impeccable description: Use when the user wants to design, redesign, shape, critique, audit, polish, clarify, distill, harden, optimize, adapt, animate, colorize, extract, or otherwise improve a frontend interface. Covers websites, landing pages, dashboards, product UI, app shells, components, forms, settings, onboarding, and empty states. Handles UX review, visual hierarchy, information architecture, cognitive load, accessibility, performance, responsive behavior, theming, anti-patterns, typography, fonts, spacing, layout, alignment, color, motion, micro-interactions, UX copy, error states, edge cases, i18n, and reusable design systems or tokens. Also use for bland designs that need to become bolder or more delightful, loud designs that should become quieter, live browser iteration on UI elements, or ambitious visual effects that should feel technically extraordinary. Not for backend-only or non-UI tasks. -version: 4.0.0-alpha.9 +version: 4.0.0-alpha.10 license: Apache 2.0 --- -Designs and iterates production-grade frontend interfaces. Real working code, committed design choices, exceptional craft. +This skill gives you the tools and permission to create design that earns to be called out-of-distribution craft: Whereas before, your design work would have been safe, timid and measured, you now approach every design task as a award-winning design director with impeccable understanding for what makes exceptional design work: production-grade code, peak creativity, a clear POV, deep understanding of the needs of the client and users, and exceptional craft. -Approach every design task as the design lead at a small studio known for giving every client a visual identity that could not be mistaken for anyone else's. The client has already rejected work that felt templated; they are paying for a point of view. Claude is capable of extraordinary work. Don't hold back. +Core principles: +- Go all out. No hedging, no shortcuts. The deliverable must be complete (except assets the user must provide). +- Dream big and bold. Distinct, beautiful, outstanding and highly inspiring work. +- Iterate with tools available to you (e.g. visual understanding, browser screenshots) until you think this meets the bar. ## Setup -1. Run `node .kiro/skills/impeccable/scripts/context.mjs` once per session (if the runtime shows this skill's loaded base directory, run `node /scripts/context.mjs`; keep cwd at the user's project). It prints the project's context and its directives; follow what it prints. Once its output is in the conversation, never rerun it on a later turn. -2. `craft` and `shape` are build-path exceptions: the new-work gate below owns their flow, and on unattended runs its checkpoints resolve without pausing. For any other invoked sub-command (`audit`, `polish`, `live`, ...), immediately read **`reference/.md`** after `context.mjs` (the `.native` variant from the Commands table when the platform is `ios`/`android`/`adaptive`) and follow it. This read is a hard gate: do not inspect the target, run command-specific scripts, or edit files until the reference is loaded. -3. Read at least one project file (CSS / tokens / theme / a representative component) to learn what world you're in. If PRODUCT.md's `## Platform` is `ios` or `android`, also read `reference/.md` (`adaptive` reads both). +1. Run `node .kiro/skills/impeccable/scripts/context.mjs` once per session (if the runtime shows this skill's loaded base directory, run `node /scripts/context.mjs`; keep cwd at the user's project). Pass a named source file or route as `--target `. It loads PRODUCT.md, DESIGN.md, the matching surface brief, and native-platform guidance when applicable; follow its directives and do not rerun it. +2. Before acting, load the one playbook that owns the request: the Commands table's reference for an explicit or clearly implied sub-command, or [reference/new-work.md](reference/new-work.md) for a new surface or replacement visual world. Then inspect the target and at least one representative source of incumbent visual truth (tokens, theme, CSS, component, or asset) before editing. ## How to design -**The brief wins.** Where the brief pins down a direction (a named aesthetic, an era, a place, a material, a specific font or palette), follow it exactly, including when it asks for a look this skill warns is saturated. Redirecting a pinned direction toward your own taste is a failure, not a save. - -**Existing worlds are sacred.** Most of impeccable's work happens inside a site or app that already exists. When the surface has a committed design system (real tokens, deliberately chosen faces, a palette the brand owns), work inside that world: extend it, sharpen it, leave it unmistakably the same brand, and never degrade a working page's performance. Inventing parallel colors, fonts, or styles on an existing surface is a defect, not creativity. A scoped refinement keeps the named scope's content and media footprint unless the brief explicitly changes them; build the added emphasis by recomposing what is already there through hierarchy, proportion, rhythm, and the system's own motifs. - -**New identity work reads the playbook first.** When nothing committed exists (greenfield, or a codebase with no real tokens or chosen faces), or the user explicitly asks for a redesign that discards the current look, you MUST read [reference/new-work.md](reference/new-work.md) before making any design decision. Missing DESIGN.md alone does not make work greenfield: code, tokens, chosen type, components, and assets are incumbent design authority. Preserve and extend them unless the user asked to replace them. Producing a genuinely new identity without the playbook yields the generic default this skill exists to prevent. Scoped fixes inside an existing world don't need it; the craft floor below governs them. `context.mjs` prints the appropriate authority directive. +- **The brief wins.** Honor pinned aesthetics, eras, materials, fonts, and palettes even when they conflict with a saturated-pattern warning. Redirecting a clear brief toward your taste is failure. +- **Refinement preserves; redesign replaces.** Refinement keeps the incumbent identity, behavior, copy, and everything outside scope. Ask before replacing factual copy or adding claims. Redesign keeps product truth, content, function, native affordances, and constraints, but treats the old look as evidence and anti-reference; choose a replacement world in new-work and replace DESIGN.md. Never split the difference into polish on the discarded look. +- **Visual authority is evidence, not a filename.** Missing DESIGN.md alone does not make a project greenfield; new-work decides whether to preserve, expand, or replace the incumbent world. ## Modes -Name the visitor's mode before designing; the page's grammar follows from it, and most ruined pages are one mode wearing another mode's grammar. **The mode belongs to the surface, not the subject**: a landing page for a dense technical tool is still Persuade, with Persuade's full permission to be striking; a docs page for a fashion house is still Read. Deciding a page can be plain because its subject is workmanlike is the same category error in reverse. The brief and the surface decide the mode; PRODUCT.md's `register` field survives only as a family hint (`brand` covers Persuade and Experience, `product` covers Operate and Read). Depth beyond the paragraphs below: [reference/new-work.md](reference/new-work.md) when inventing identity, [reference/operate.md](reference/operate.md) for substantial Operate and Read work. +Choose the mode from the requested surface, not the product, and persist it only in that surface brief. A tool's landing page is still Persuade; a fashion house's documentation is still Read. See [new-work.md](reference/new-work.md) for new surfaces and [operate.md](reference/operate.md) for deeper Operate/Read guidance. -**Persuade** (the surface exists to win someone over; design IS the product). The deliverable is an impression that stops the scroll, earns the click, converts. Spans every genre; don't collapse them into one look. On new surfaces, briefs that imply imagery must ship real, verified imagery; a colored rectangle where a photo belongs reads as incomplete. New Persuade surfaces take their typeface procedure and reject list from [reference/new-work.md](reference/new-work.md). - -**Operate** (the surface is a tool someone works in; design SERVES the task). A person getting something done: scanability and consistency outrank expressiveness. These surfaces earn trust by feeling native to their platform: system font stacks and workhorse UI faces are legitimate and often correct here (the Persuade reject list does not apply). The brand lives in the details: focus states, empty states, microcopy, one owned accent. The usage scene is part of the spec: an interface read outdoors, in motion, or at a glance must survive its real ambient light, and the theme follows the scene, not the category's habit. - -**Read** (the surface exists to be understood; long-form, reference, guidance). The deliverable is comprehension, and comprehension is earned twice: a structure the reader can hold in their head with nothing standing between them and the answer, and a reading experience good enough to stay in, through typographic quality and whatever visual or interactive support genuinely helps the reader follow. The brand lives in type, spacing, and small accents. - -**Experience** (the surface presents a body of work; the page IS the work). The artifact leads, the interface recedes, and the visitor meets the work itself in the first viewport at every screen size. Boldness here means trusting the work. +- **Persuade:** win someone over; design is the product. Earn attention and action. Ship real imagery when the brief needs it; follow the committed world, not category habit. +- **Operate:** help someone do work. Scanability, consistency, native expectations, and the real usage scene outrank expression. Brand lives in precise details. +- **Read:** make something understood. Structure for comprehension, then make the reading experience worth staying in. +- **Experience:** present a body of work. Let the artifact lead from the first viewport; the interface recedes. ## Craft floor -Build to this floor without announcing it. The design detector (the project hook, `node .kiro/skills/impeccable/scripts/detect.mjs --json `, or `audit`) verifies most of it mechanically; resolve every finding before finalizing. Fix real defects, but use context judgment rather than distorting intentional design to appease a false positive. Classify any intentional exception explicitly and use the hook system's narrowest appropriate waiver when it must persist. +Build to this floor without announcing it. -- Contrast: body text ≥4.5:1 against its background (placeholders too); large text ≥3:1. Gray text on a colored background looks washed out: use a darker shade of the background's own hue, or a transparency of the text color. -- Shadows describe real light: an offset and a soft blur. A zero-offset colored halo is decoration announcing itself. -- Spacing has rhythm: generous separations, tight groupings; cramped padding reads as broken; the space above a heading exceeds the space below it. Verify computed spacing, not intended spacing. -- Type: body line length 65-75ch; display clamp() max ≤6rem; letter-spacing ≥-0.04em; `text-wrap: balance` on headings; modular scale ≥1.25 between steps; light-on-dark adds 0.05-0.1 line-height. Pair faces on a contrast axis, never two similar-but-not-identical ones; one family with committed weight contrast beats a timid pair. Test headings at every breakpoint; overflow means reduce the clamp or rewrite the copy. -- Motion is part of the build: one orchestrated moment beats scattered effects; ease-out exponential curves; reveals enhance an already-visible default (content gated on a class-triggered transition ships blank in hidden tabs and headless renderers). Responsive down to mobile and visible keyboard focus are part of the floor. -- Ship real content (no placeholders, dead links, or fake controls) and cover the interaction states people will actually hit (hover, focus, disabled, loading, error, empty). -- Copy is design material: name things the way the page's own people speak, make every control say what it does, and make every error say what happened and what to do next. -- Before finishing, re-read the brief: every requirement it names must exist on the page, findable in seconds. A beautiful page missing an asked-for feature is unfinished. +- **Contrast:** body and placeholder text ≥4.5:1; large text ≥3:1. On colored surfaces, tint secondary text from that hue or the foreground instead of using gray. +- **Depth:** shadows describe light with offset and soft blur; zero-offset colored halos are decoration. +- **Spacing:** tight groups, generous separation, no cramped containers; space above a heading exceeds space below. Verify computed values. +- **Type:** body measure 65–75ch; display max 6rem and tracking floor -0.04em; balance headings; use clear scale/weight contrast; test overflow at every breakpoint. +- **Motion:** author one coherent moment instead of scattered effects. Use exponential ease-out and an already-visible default. Premium motion is not transform/opacity alone: it may add focus, depth, masks, light, or material change through blur/filter, backdrop-filter, clip-path/masks, or shadow when smooth. Always provide reduced motion. +- **Shipping:** real content, working controls, responsive composition, keyboard focus, and the states users hit: hover, disabled, loading, error, and empty. +- **Copy:** use the product's language; controls name their action, errors name the problem and recovery. +- **Coverage:** every brief requirement must exist and be findable within seconds. + +Before finishing changed UI, follow the quality guidance supplied by `context.mjs` and hooks. Context requests a manual scan only when no automatic detector is active; never add a second detector pass. ## Commands | Command | Category | Description | Reference | |---|---|---|---| -| `craft [feature]` | Build | The standard build flow with attended checkpoints | [reference/new-work.md](reference/new-work.md) | +| `craft [feature]` | Build | Deprecated alias for an ordinary new-work request | [reference/craft.md](reference/craft.md) | | `shape [feature]` | Build | Plan UX/UI before writing code | [reference/shape.md](reference/shape.md) | -| `init` | Build | Set up project context: PRODUCT.md, DESIGN.md, live config, next steps | [reference/init.md](reference/init.md) | +| `init` | Build | Capture durable product context in PRODUCT.md | [reference/init.md](reference/init.md) | | `document` | Build | Generate DESIGN.md from existing project code | [reference/document.md](reference/document.md) | | `extract [target]` | Build | Pull reusable tokens and components into design system | [reference/extract.md](reference/extract.md) | | `critique [target]` | Evaluate | UX design review with heuristic scoring | [reference/critique.md](reference/critique.md) | @@ -76,7 +75,14 @@ Build to this floor without announcing it. The design detector (the project hook | `optimize [target]` | Fix | Diagnose and fix UI performance | [reference/optimize.md](reference/optimize.md) | | `live` | Iterate | Visual variant mode: pick elements in the browser, generate alternatives | [reference/live.md](reference/live.md) | -Routing: **no argument** → read [reference/routing.md](reference/routing.md) and present the context-aware menu (never auto-run a command). **First word matches a command** (or `pin` / `unpin` / `hooks`) → load its reference (native variant on native platforms) and follow it; everything after the command name is the target. **Intent clearly maps to one command** ("fix the spacing" → `layout`, "rewrite this error" → `clarify`) → same; if two fit, ask once. **Otherwise** → general design invocation: apply Setup and this file's guidance; builds flow through the new-work gate above, whose playbook carries the direction checkpoint and the finishing pass. `teach` routes to `init`, and `craft` routes to the standard build flow with attended checkpoints. If setup diverted into `init` for a build request, finish init, refresh context, then resume. +Routing: + +- **No argument:** read [routing.md](reference/routing.md) and present its context-aware menu; never auto-run a command. +- **Explicit or clearly implied command:** load its reference (native variant on native platforms) and follow it. Ask once if two commands fit. +- **Otherwise:** treat the request as general design work. Missing PRODUCT.md routes through init; new surfaces and replacement worlds use new-work. +- `teach` aliases `init`. `craft` is a deprecated alias for ordinary new-work and adds nothing. `shape` owns task discovery, then enters new-work only for visual-world and surface-concept decisions. + +After init writes PRODUCT.md, resume without rerunning `context.mjs`. **Pin / Unpin:** `node .kiro/skills/impeccable/scripts/pin.mjs ` creates or removes a standalone `/` shortcut. Report the script's result concisely; relay stderr verbatim on error. diff --git a/.kiro/skills/impeccable/reference/android.md b/.kiro/skills/impeccable/reference/android.md index 3575c31fb..6337b9018 100644 --- a/.kiro/skills/impeccable/reference/android.md +++ b/.kiro/skills/impeccable/reference/android.md @@ -2,7 +2,7 @@ For native Android apps: Jetpack Compose, Android Views, React Native, Expo, Flutter shipping to Android hardware. -On native, register narrows. Material Design 3 governs structure, navigation, and interaction whatever the register; brand expresses through Material's theming (color roles, type scale, shape, motion). A Material-everywhere cross-platform app that also ships to iPhone still owes iOS its OS guarantees on that hardware: safe-area insets, Reduce Motion, edge-swipe back. +On native, the visitor mode narrows what expression may override. Material Design 3 governs structure, navigation, and interaction in every mode; brand expresses through Material's theming (color roles, type scale, shape, motion). A Material-everywhere cross-platform app that also ships to iPhone still owes iOS its OS guarantees on that hardware: safe-area insets, Reduce Motion, edge-swipe back. ## The Android slop test diff --git a/.kiro/skills/impeccable/reference/animate.md b/.kiro/skills/impeccable/reference/animate.md index 1a1098a5b..d51bb6d63 100644 --- a/.kiro/skills/impeccable/reference/animate.md +++ b/.kiro/skills/impeccable/reference/animate.md @@ -1,203 +1,88 @@ > **Additional context needed**: performance constraints. -Add motion that conveys state, gives feedback, and clarifies hierarchy. Cut motion that exists only for decoration. Animation fatigue is a real cost; spend the budget on the moments that need it. +Use motion to explain state, relationship, and hierarchy, or to create one authored moment the surface has earned. Decoration without purpose is animation debt. --- -## Register +## Visitor mode -Persuade + Experience: 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. +- **Persuade + Experience:** motion may carry the voice. Prefer one rehearsed focal sequence to repeated section reveals. +- **Operate + Read:** motion serves feedback, state, and continuity. Keep routine transitions fast and do not make users wait through page-load choreography. +- **Native (`ios` / `android` / `adaptive`):** follow the Motion section of [ios.md](ios.md) or [android.md](android.md), including the platform's Reduce Motion behavior. Do not apply the web tooling below. -Operate + Read: 150–250 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. +## Find the job -Native (`ios` / `android` / `adaptive`): implementation follows the Motion section of [ios.md](ios.md) / [android.md](android.md) (read it first if Setup hasn't already): system transitions and OS Reduce Motion, never the web tooling below. +Inspect the existing motion language, interaction states, target devices, and performance budget. Find only the places where motion would: ---- +- acknowledge an action; +- make a state change or spatial relationship legible; +- preserve continuity through navigation or layout change; +- direct attention at a meaningful moment; +- embody the selected visual world. -## Assess Animation Opportunities +Ask only when a material constraint cannot be inferred. Do not animate a static area merely because it exists. -Analyze where motion would improve the experience: +## Set the motion thesis -1. **Identify static areas**: - - **Missing feedback**: Actions without visual acknowledgment (button clicks, form submission, etc.) - - **Jarring transitions**: Instant state changes that feel abrupt (show/hide, page loads, route changes) - - **Unclear relationships**: Spatial or hierarchical relationships that aren't obvious - - **Lack of delight**: Functional but joyless interactions - - **Missed guidance**: Opportunities to direct attention or explain behavior +Write a short plan before implementation: -2. **Understand the context**: - - What's the personality? (Playful vs serious, energetic vs calm) - - What's the performance budget? (Mobile-first? Complex page?) - - Who's the audience? (Motion-sensitive users? Power users who want speed?) - - What matters most? (One hero animation vs many micro-interactions?) +- **Focal moment:** the one sequence or interaction that deserves authorship, if any. +- **Continuity:** the state, layout, or navigation changes that need explanation. +- **Feedback:** the controls and outcomes that need acknowledgment. +- **Budget:** which effects may be expensive, how often they run, and the reduced-motion equivalent. -If any of these are unclear from the codebase, ask the user directly to clarify what you cannot infer. +The focal moment must come from this product and surface concept. A generic fade-and-rise, hover lift, parallax layer, or scroll reveal is not a thesis. -**CRITICAL**: Respect `prefers-reduced-motion`. Always provide non-animated alternatives for users who need them. +## Choose material by meaning -## Plan Animation Strategy +Transform and opacity are reliable foundations, not the entire palette. Choose properties for what the transition communicates: -Create a purposeful animation plan: +- **Continuity and relationship:** shared-element motion, FLIP-style transforms, view transitions, or deliberate spatial movement. +- **Focus and depth:** bounded blur, filter, backdrop, light, or shadow changes. +- **Reveal and composition:** masks, clip paths, cropping, or controlled occlusion. +- **Material and energy:** color, gradient position, texture, distortion, or shader effects when the world and runtime support them. +- **State and feedback:** the smallest change that makes cause and result unmistakable. -- **Feedback layer**: Which interactions need acknowledgment? -- **Transition layer**: Which state changes need smoothing? -- **Entrance moment**: The ONE entrance worth rehearsing, where the mode invites it. Not every surface wants one. -- **Delight layer**: Where can we surprise and delight? +Do not stack techniques for spectacle. One strong material idea, carried through the focal sequence and quiet supporting states, is usually enough. -**IMPORTANT**: One well-orchestrated experience beats scattered animations everywhere. Focus on high-impact moments. +Sibling stagger is appropriate when a list appears as a list. Cap the total delay, and never reinterpret every scrolled section as a staggered list. -## Implement Animations +## Timing and easing -Add motion systematically across these categories: +Timing should express distance and consequence: -### Micro-interactions -- **Button feedback**: - - Hover: Subtle scale (1.02-1.05), color shift, shadow increase - - Click: Quick scale down then up (0.95 → 1), ripple effect - - Loading: Spinner or pulse state -- **Form interactions**: - - Input focus: Border color transition, slight scale or glow - - Validation: Shake on error, check mark on success, smooth color transitions -- **Toggle switches**: Smooth slide + color transition (200-300ms) -- **Checkboxes/radio**: Check mark animation, ripple effect -- **Like/favorite**: Scale + rotation, particle effects, color transition +| Duration | Typical use | +|---|---| +| 100–150 ms | immediate feedback | +| 150–300 ms | routine state change | +| 300–500 ms | layout, overlay, or view transition | +| 500–800 ms | a deliberately authored focal entrance | -### State Transitions -- **Show/hide**: Fade + slide (not instant), appropriate timing (200-300ms) -- **Expand/collapse**: Height transition with overflow handling, icon rotation -- **Loading states**: Skeleton screen fades, spinner animations, progress bars -- **Success/error**: Color transitions, icon animations, gentle scale pulse -- **Enable/disable**: Opacity transitions, cursor changes +Exit faster than entrance. Use natural deceleration such as `cubic-bezier(0.16, 1, 0.3, 1)` for confident arrivals; do not use bounce or elastic curves by reflex. Long feedback feels like latency. -### Entrance Animations -- **The one entrance moment** (where the mode invites it): a committed entrance for primary content (scale, parallax, or a creative effect) -- **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. +## Implement to the runtime - 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. +- Use CSS transitions and keyframes for declarative state and bounded sequences. +- Use Web Animations API or the project's existing motion library for interruption, sequencing, and dynamic values. +- Use View Transitions or shared-element techniques when continuity across states is the point. +- Use scroll-driven motion only when the scroll relationship itself carries meaning, with a robust fallback. +- Do not add a dependency for an effect the existing stack can express cleanly. -### Navigation & Flow -- **Page transitions**: Crossfade between routes, shared element transitions -- **Tab switching**: Slide indicator, content fade/slide -- **Carousel/slider**: Smooth transforms, snap points, momentum -- **Scroll effects**: Parallax layers, sticky headers with state changes, scroll progress indicators +Keep content visible in the default state so failed scripts do not hide the page. Avoid casually animating layout-driving properties such as `width`, `height`, `top`, `left`, and margins; use FLIP, transforms, or grid techniques when appropriate. Bound blur, filter, shadow, canvas, and shader work to isolated regions. Apply `will-change` only during known animation. Measure on target viewports and devices rather than assuming transform means fast. -### Feedback & Guidance -- **Hover hints**: Tooltip fade-ins, cursor changes, element highlights -- **Drag & drop**: Lift effect (shadow + scale), drop zone highlights, smooth repositioning -- **Copy/paste**: Brief highlight flash on paste, "copied" confirmation -- **Focus flow**: Highlight path through form or workflow +## Accessibility and control -### Delight Moments -- **Empty states**: Subtle floating animations on illustrations -- **Completed actions**: Confetti, check mark flourish, success celebrations -- **Easter eggs**: Hidden interactions for discovery -- **Contextual animation**: Weather effects, time-of-day themes, seasonal touches +Honor `prefers-reduced-motion` with an intentional alternative. Preserve state change and hierarchy while removing travel, parallax, flashing, or prolonged sequences. Do not replace every animation globally with `0.01ms` if that destroys useful feedback. Motion must never block focus, interaction, reading, or task completion. -## Technical Implementation +Respect autoplay and sound preferences. Any nonessential loop must stop when offscreen or hidden. -Use appropriate techniques for each animation: +## Verify -### Timing & Easing +- The focal motion is specific to the selected world and surface. +- Every supporting animation explains feedback, state, or relationship. +- Interruption and repeated use behave correctly. +- Desktop, mobile, keyboard, and reduced-motion paths remain usable. +- Expensive effects stay smooth on the target device. +- Removing an animation would lose meaning or authored character, not merely decoration. -**Duration: the 100/300/500 rule.** Timing matters more than easing for "feels right": - -| Duration | Use Case | Examples | -|----------|----------|----------| -| **100–150ms** | Instant feedback | Button press, toggle, color change | -| **200–300ms** | State changes | Menu open, tooltip, hover state | -| **300–500ms** | Layout changes | Accordion, modal, drawer | -| **500–800ms** | Entrance animations | Page load, hero reveal | - -**Easing curves (use these, not CSS defaults):** -```css -/* Recommended: natural deceleration */ ---ease-out-quart: cubic-bezier(0.25, 1, 0.5, 1); /* Smooth */ ---ease-out-quint: cubic-bezier(0.22, 1, 0.36, 1); /* Slightly snappier */ ---ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1); /* Confident, decisive */ - -/* AVOID: feel dated and tacky */ -/* bounce: cubic-bezier(0.34, 1.56, 0.64, 1); */ -/* elastic: cubic-bezier(0.68, -0.6, 0.32, 1.6); */ -``` - -**Exit animations are faster than entrances.** Use ~75% of enter duration. - -### CSS Animations -```css -/* Prefer for simple, declarative animations */ -- transitions for state changes -- @keyframes for complex sequences -- transform and opacity for reliable movement -- blur, filters, masks, clip paths, shadows, and color shifts for premium atmospheric effects when verified smooth -``` - -### JavaScript Animation -```javascript -/* Use for complex, interactive animations */ -- Web Animations API for programmatic control -- Framer Motion for React -- 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 -- **Layout safety**: Avoid casual animation of layout-driving properties (`width`, `height`, `top`, `left`, margins) -- **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) { - * { - animation-duration: 0.01ms !important; - animation-iteration-count: 1 !important; - transition-duration: 0.01ms !important; - } -} -``` - -**NEVER**: -- Use bounce or elastic easing curves; they feel dated and draw attention to the animation itself -- Animate layout properties casually (`width`, `height`, `top`, `left`, margins) when transform, FLIP, or grid-based techniques would work -- Use durations over 500ms for feedback (it feels laggy) -- Animate without purpose (every animation needs a reason) -- Ignore `prefers-reduced-motion` (this is an accessibility violation) -- Animate everything (animation fatigue makes interfaces feel exhausting) -- Block interaction during animations unless intentional - -## Verify Quality - -Test animations thoroughly: - -- **Smooth at 60fps**: No jank on target devices -- **Feels natural**: Easing curves feel organic, not robotic -- **Appropriate timing**: Not too fast (jarring) or too slow (laggy) -- **Reduced motion works**: Animations disabled or simplified appropriately -- **Doesn't block**: Users can interact during/after animations -- **Adds value**: Makes interface clearer or more delightful - -When the motion clarifies state instead of decorating it, hand off to `/impeccable polish` for the final pass. +When motion earns its place, hand off to `/impeccable polish` for the final pass. diff --git a/.kiro/skills/impeccable/reference/audit.md b/.kiro/skills/impeccable/reference/audit.md index 72815af69..70d80771a 100644 --- a/.kiro/skills/impeccable/reference/audit.md +++ b/.kiro/skills/impeccable/reference/audit.md @@ -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 - diff --git a/.kiro/skills/impeccable/reference/audit.native.md b/.kiro/skills/impeccable/reference/audit.native.md index 737d50e4f..0126fa157 100644 --- a/.kiro/skills/impeccable/reference/audit.native.md +++ b/.kiro/skills/impeccable/reference/audit.native.md @@ -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) diff --git a/.kiro/skills/impeccable/reference/bolder.md b/.kiro/skills/impeccable/reference/bolder.md index 6b44f0e38..78f5e4811 100644 --- a/.kiro/skills/impeccable/reference/bolder.md +++ b/.kiro/skills/impeccable/reference/bolder.md @@ -8,12 +8,12 @@ ## Why it reads flat -A section usually reads flat for reasons its neighbors have already solved. Look at what the rest of the page does that this section does not: the display type at full strength, the structural devices that carry meaning, the signature motif, the density and pacing, the specificity of the copy. A flat section is typically one that quietly opts out of the system's own strongest moves. The most reliable bolder pass brings the target up to the expressive level its neighbors already reach, in the system's own vocabulary rather than a new one. +A section usually reads flat for reasons its neighbors have already solved. Look at what the rest of the page does that this section does not: the display type at full strength, the structural devices that carry meaning, the signature motif, the density and pacing. A flat section is typically one that quietly opts out of the system's own strongest moves. The most reliable bolder pass brings the target up to the expressive level its neighbors already reach, in the system's own vocabulary rather than a new one. ## The amplification - **Amplify what the system already owns.** Reuse its motif and its type scale at full strength, turned up for this section rather than invented for it. The bolder version should look more like the same brand, not less. -- **Let the content carry the weight.** Flat copy makes a flat design, and no amount of size fixes a generic claim. Make every word specific enough to earn its place, and let the section's real evidence do the work that a decorative addition would otherwise fake. +- **Keep content true.** Existing claims are part of the scope: preserve them unless the user supplies replacements. If real evidence is essential to the direction but absent, ask for it. - **Commit, then clarify.** Half-measures read as noise. Make the one decisive move completely, then quiet everything around it so the move is legible. If every element got louder, the section got flatter. - **Give it its own rhythm.** The target should read as a peak in the scroll, a shift in density or pace from what surrounds it, not simply more of the same. diff --git a/.kiro/skills/impeccable/reference/clarify.md b/.kiro/skills/impeccable/reference/clarify.md index 00ee978c3..3047a9d82 100644 --- a/.kiro/skills/impeccable/reference/clarify.md +++ b/.kiro/skills/impeccable/reference/clarify.md @@ -1,288 +1,94 @@ -> **Additional context needed**: audience technical level and users' mental state in context. +> **Additional context needed**: audience knowledge and emotional state. -Find the unclear, confusing, or poorly written interface text and rewrite it. Vague copy creates support tickets and abandonment; specific copy gets users through the task. +Rewrite unclear interface text so users understand what happened, what matters, and what to do next. Preserve factual meaning, product terminology, and brand voice. +## Audit the language ---- +Read the entire interaction path, not isolated strings. Identify: -## Assess Current Copy +- ambiguous nouns, verbs, and actions; +- internal jargon or assumed knowledge; +- vague labels, outcomes, and system states; +- missing consequences, recovery, or timing; +- inconsistent terminology and capitalization; +- redundant headings, intros, helper text, and confirmations; +- text that breaks at realistic widths or in translation; +- tone that ignores stress, risk, success, or urgency. -Identify what makes the text unclear or ineffective: +Infer audience and task from product context and surrounding UI. Ask before changing factual claims, legal meaning, or a term that may be domain-specific. -1. **Find clarity problems**: - - **Jargon**: Technical terms users won't understand - - **Ambiguity**: Multiple interpretations possible - - **Passive voice**: "Your file has been uploaded" vs "We uploaded your file" - - **Length**: Too wordy or too terse - - **Assumptions**: Assuming user knowledge they don't have - - **Missing context**: Users don't know what to do or why - - **Tone mismatch**: Too formal, too casual, or inappropriate for situation +## Set the message hierarchy -2. **Understand the context**: - - Who's the audience? (Technical? General? First-time users?) - - What's the user's mental state? (Stressed during error? Confident during success?) - - What's the action? (What do we want users to do?) - - What's the constraint? (Character limits? Space limitations?) +For each state, decide: -**CRITICAL**: Clear copy helps users succeed. Unclear copy creates frustration, errors, and support tickets. +1. the one fact the user needs now; +2. the action available next; +3. supporting context that changes the decision; +4. the appropriate tone for this moment. -## Plan Copy Improvements +Say each idea once. If the heading already explains the state, the introduction should add new information or disappear. -Create a strategy for clearer communication: +## Rewrite by function -- **Primary message**: What's the ONE thing users need to know? -- **Action needed**: What should users do next (if anything)? -- **Tone**: How should this feel? (Helpful? Apologetic? Encouraging?) -- **Constraints**: Length limits, brand voice, localization considerations +### Actions and navigation -**IMPORTANT**: Good UX writing is invisible. Users should understand immediately without noticing the words. +Use a specific verb and object when the outcome is not already obvious. Labels should describe what will happen, not the gesture used to trigger it. Keep the same noun and verb for the same concept throughout the product. -## Improve Copy Systematically +For destructive actions, name the object and consequence. Prefer undo over confirmation when recovery is safe. When confirmation is necessary, name the action on both the message and button instead of using `Yes`, `No`, `OK`, or `Submit`. -Refine text across these common areas: +### Forms -### Error Messages -**Bad**: "Error 403: Forbidden" -**Good**: "You don't have permission to view this page. Contact your admin for access." +Use persistent labels; placeholders are examples, not labels. Put format and eligibility requirements before submission. Explain why information is requested only when it is not obvious. Required and optional treatment should be consistent. -**Bad**: "Invalid input" -**Good**: "Email addresses need an @ symbol. Try: name@example.com" +Validation says what needs attention and how to correct it without blaming the user. Keep related instructions near the field and announce errors accessibly. -**Principles**: -- Explain what went wrong in plain language -- Suggest how to fix it -- Don't blame the user -- Include examples when helpful -- Link to help/support if applicable - -### Form Labels & Instructions -**Bad**: "DOB (MM/DD/YYYY)" -**Good**: "Date of birth" (with placeholder showing format) - -**Bad**: "Enter value here" -**Good**: "Your email address" or "Company name" - -**Principles**: -- Use clear, specific labels (not generic placeholders) -- Show format expectations with examples -- Explain why you're asking (when not obvious) -- Put instructions before the field, not after -- Keep required field indicators clear - -### Button & CTA Text -**Bad**: "Click here" | "Submit" | "OK" -**Good**: "Create account" | "Save changes" | "Got it, thanks" +### Errors and permissions -**Principles**: -- Describe the action specifically -- Use active voice (verb + noun) -- Match user's mental model -- Be specific ("Save" is better than "OK") +An actionable error answers: -### Help Text & Tooltips -**Bad**: "This is the username field" -**Good**: "Choose a username. You can change this later in Settings." +1. what failed; +2. why, when known and useful; +3. how to recover or what alternative remains. -**Principles**: -- Add value (don't just repeat the label) -- Answer the implicit question ("What is this?" or "Why do you need this?") -- Keep it brief but complete -- Link to detailed docs if needed +Do not expose internal codes as the primary message. Do not promise a cause or resolution the system cannot know. Treat privacy, payment, deletion, access loss, and blocked work seriously; warmth is welcome, jokes are not. -### Empty States -**Bad**: "No items" -**Good**: "No projects yet. Create your first project to get started." +### Loading, empty, and success states -**Principles**: -- Explain why it's empty (if not obvious) -- Show next action clearly -- Make it welcoming, not dead-end +Loading text names the real operation and sets an honest expectation when the wait is meaningful. Show determinate progress when available; never invent progress. -### Success Messages -**Bad**: "Success" -**Good**: "Settings saved! Your changes will take effect immediately." +An empty state distinguishes first use, no results, filters, permissions, and failure. Explain the state and provide the next useful action. -**Principles**: -- Confirm what happened -- Explain what happens next (if relevant) -- Be brief but complete -- Match the user's emotional moment (celebrate big wins) - -### Loading States -**Bad**: "Loading..." (for 30+ seconds) -**Good**: "Analyzing your data... this usually takes 30-60 seconds" - -**Principles**: -- Set expectations (how long?) -- Explain what's happening (when it's not obvious) -- Show progress when possible -- Offer escape hatch if appropriate ("Cancel") +Success confirms the completed outcome and mentions the next consequence only when it changes what the user should do. Routine success should be brief. -### Confirmation Dialogs -**Bad**: "Are you sure?" -**Good**: "Delete 'Project Alpha'? This can't be undone." +### Help and instructional text -**Principles**: -- State the specific action -- Explain consequences (especially for destructive actions) -- Use clear button labels ("Delete project" not "Yes") -- Don't overuse confirmations (only for risky actions) +Helper text answers an implicit question instead of restating the control. Use progressive disclosure for uncommon detail. Link text must make sense out of context; icon-only controls need accessible names. -### Navigation & Wayfinding -**Bad**: Generic labels like "Items" | "Things" | "Stuff" -**Good**: Specific labels like "Your projects" | "Team members" | "Settings" +## Voice, accessibility, and localization -**Principles**: -- Be specific and descriptive -- Use language users understand (not internal jargon) -- Make hierarchy clear -- Consider information scent (breadcrumbs, current location) +Voice stays consistent; tone adapts to the moment. Use plain language without flattening terminology the audience genuinely knows. -## Apply Clarity Principles +- Write complete translatable messages rather than concatenated fragments. +- Keep variables and numbers structured so translators can reorder them. +- Allow expansion instead of abbreviating prematurely. +- Make alt text convey the image's information; use empty alt for decoration. +- Keep screen-reader names aligned with visible labels and outcomes. +- Do not rely on punctuation, color, or iconography to carry the message alone. -Every piece of copy should follow these rules: +Maintain a short terminology glossary when inconsistency spans the product. Do not vary words for literary effect in an interface. -1. **Be specific**: "Enter email" not "Enter value" -2. **Be concise**: Cut unnecessary words (but don't sacrifice clarity) -3. **Be active**: "Save changes" not "Changes will be saved" -4. **Be human**: "Oops, something went wrong" not "System error encountered" -5. **Tell users what to do**, not just what happened -6. **Be consistent**: Use same terms throughout (don't vary for variety) +## Verify -**NEVER**: -- Use jargon without explanation -- Blame users ("You made an error" → "This field is required") -- Be vague ("Something went wrong" without explanation) -- Use passive voice unnecessarily -- Write overly long explanations (be concise) -- Use humor for errors (be empathetic instead) -- Assume technical knowledge -- Vary terminology (pick one term and stick with it) -- Repeat information (headers restating intros, redundant explanations) -- Use placeholders as the only labels (they disappear when users type) +Read the flow in context and test: -## Verify Improvements +- comprehension without hidden product knowledge; +- actionability at errors, empty states, and decision points; +- factual accuracy and consistent terminology; +- scanability at target widths and 200% zoom; +- long names, localization expansion, pluralization, and dynamic values; +- accessible names and announced state changes; +- tone appropriate to consequence and emotional context. -Test that copy improvements work: +The final copy is as short as it can be without removing meaning or recovery. -- **Comprehension**: Can users understand without context? -- **Actionability**: Do users know what to do next? -- **Brevity**: Is it as short as possible while remaining clear? -- **Consistency**: Does it match terminology elsewhere? -- **Tone**: Is it appropriate for the situation? - -When the copy reads cleanly, hand off to `/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. +When the language reads cleanly, hand off to `/impeccable polish` for the final pass. diff --git a/.kiro/skills/impeccable/reference/codex.md b/.kiro/skills/impeccable/reference/codex.md index 18a924a91..6c550f1dc 100644 --- a/.kiro/skills/impeccable/reference/codex.md +++ b/.kiro/skills/impeccable/reference/codex.md @@ -1,105 +1,38 @@ -# Codex: Visual Direction & Asset Production +# Codex: Surface Probes & Asset Production -This file is loaded by `/impeccable craft` when the harness has native image generation (currently Codex via `image_gen`). Other harnesses skip it. It covers the two craft steps that depend on real image generation: landing the visual direction, and producing the raster assets the implementation will compose. +Load this from [new-work.md](new-work.md) only when the harness has native image generation and a substantial, high-fidelity surface would benefit from seeing the shortlisted concept before code. PRODUCT.md and DESIGN.md are preconditions. New-work has already resolved the visual world; this file must not reopen it. -Read this *before* generating any images. The order matters, and the per-step user pauses are what keep generated imagery from drifting away from the brief. +The purpose of a probe is to test composition, narrative, hierarchy, density, focal moment, signature use, and image requirements. It is not a second identity workshop. Keep DESIGN.md's palette, typography direction, material language, component character, imagery stance, and motion grammar fixed. -### Four stop points before code +## Generate the smallest useful probe set -Steps A through D each end with the user. Do not advance past any of them on your own read of the situation. +Generate one to three high-fidelity north-star comps using the native image-generation capability. Base them on the real content and the surface concepts already developed with the user. -1. **STOP after Step A questions.** Wait for answers. -2. **STOP after Step B palette generation.** Wait for "confirm palette." -3. **STOP after Step C mocks.** Wait for direction approval or delegation. -4. **Only after Step D approves a direction** do you return to the build (new-work.md's plan, then code). +- When the user shortlisted multiple concepts, show one clear expression of each. +- When one concept is already selected, vary only the structural uncertainty that the image can resolve: topology, sequence, density, hierarchy, focal composition, or interaction framing. +- Show enough beyond the opening moment to prove the concept can govern the whole requested surface. +- Do not generate a palette artifact, ask new atmosphere questions, introduce a different type voice, or invent a new motif. If the committed world cannot support the concept, return to the concept shortlist rather than changing the world. -Prior shape approval does **not** satisfy any of these. Shape's "confirm or override" advances you into Step A; it is not a substitute for it. +Treat each comp as a direction test, not a screenshot specification. Core UI text, responsive behavior, accessibility, semantics, and interaction states remain implementation responsibilities. -## Step A: Explore Directions with the User +## One approval point -Before generating anything, run a brief direction conversation grounded in the shape brief. +Show the probes together and ask what should carry forward, what feels false to the world, and whether the selected surface concept should be approved, combined, revised, or rejected. Then stop and wait. A structured simulated user counts as attended and receives the same question. -**Step A is required even when shape just produced a confirmed brief.** The shape questions and Step A questions cover different ground: shape pins purpose, content, scope; Step A pins palette, atmosphere, and named visual references for the comps you're about to generate. The only time you can skip Step A is when the user has already answered these exact palette/atmosphere/reference questions in the same session. +Do not begin code until the user approves a direction or explicitly delegates the choice. If they delegate, choose using the task brief, PRODUCT.md, and DESIGN.md, and state the evidence. Approval refines the task concept; it does not modify DESIGN.md. -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: +After approval, summarize the composition and the parts of the comp that must not be literalized. Return to new-work.md and write the direction contract from the approved surface concept before code. -- "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.)" +## Inventory implementation fidelity -**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. +Before building, inventory the approved comp's major visible ingredients and choose an implementation medium for each: semantic HTML/CSS/SVG, existing project asset, generated raster, sourced raster, icon library, canvas/WebGL, or accepted omission. -## Step B: Generate the Brand Palette First +Pay special attention to the dominant composition, signature use, image-native content, second-fold system, and any interaction the still image only implies. If the concept depends on a photograph, architectural scene, product object, portrait, or other raster-native material, do not silently replace it with generic CSS scenery. -Generate **one** palette artifact before any mocks. This is a small, focused image: typography pairing on the chosen background, primary + accent color swatches, one signature ornament or motif. Single image, single pass. +Treat the comp as a north star, not something to trace. Do not rasterize core UI text or controls. Do not substitute a different visual driver after approval without asking. -Why palette first: mocks generated against a vague color sense produce noise that drowns out the structural decisions. A confirmed palette is the first concrete contract for everything downstream. +## Produce only the assets the build needs -Show the palette to the user. Ask one question: "This is the palette I'm locking in for the mocks. Confirm, or call out what to shift?" +When clean raster ingredients are required and a scoped subagent is available and authorized, use `impeccable_asset_producer`. Give it the approved comp, output paths, required dimensions and formats, transparency needs, crop notes, and what must remain semantic code. Otherwise produce the minimum required assets with the native image-generation capability in the current thread. -**STOP and wait for confirmation.** Do not generate mocks against an unconfirmed palette. "Probably good enough" is the wrong call here; the palette is the contract for everything downstream. - -## Step C: Generate 1-3 Visual Mocks Against the Palette - -Once the palette is confirmed, generate **1 to 3** high-fidelity north-star comps. Each mock must use the confirmed palette and typography. Mocks differ in *structural* direction (hierarchy, topology, density, composition), not in color or motif. - -- Brand work: push visual identity, composition, mood, and signature motifs. -- Product work: push hierarchy, topology, density, tone, grounded in realistic product structure. -- Landing pages and long-form brand surfaces: show enough of the second fold to establish the system beyond the hero. - -Use the `image_gen` tool directly (or via the imagegen skill when available). Don't ask the user to install anything. - -## Step D: Approval Loop - -Show the comps. Ask what carries forward. Iterate until **one direction is approved** or the user explicitly delegates. - -**STOP and wait for the approval or the delegation.** Do not begin Step E or return to the build until a single direction is named. If the user delegates, pick the strongest direction and explain it from the brief, not personal taste. - -Before moving to assets, summarize what to carry into code and what *not* to literalize from the mock. This is the handoff between visual exploration and semantic implementation. - -## Step E: Mock Fidelity Inventory - -Inventory the approved mock's major visible ingredients. For each, decide implementation: semantic HTML/CSS/SVG, generated raster, sourced raster, icon library, canvas/WebGL, or accepted omission. - -Common ingredients to inventory: - -- Hero silhouette and dominant composition -- Signature motifs (planets, devices, portraits, charts, route lines, insets, badges, etc.) -- Nav and primary-action treatment (when the surface has one) -- Section sequence, especially the second fold -- Image-native content the concept depends on -- Typography, density, color/material treatment, motion cues - -Treat the mock as a north star, not a screenshot to trace. Don't rasterize core UI text. But if the live result lacks the mock's major ingredients, the implementation is wrong. - -If a photographic, architectural, product, or place-led mock becomes generic CSS scenery, decorative diagrams, bullets, or copy, stop and fix it. That's a broken implementation, not a harmless interpretation. - -Don't substitute a different hero composition or visual driver post-approval without user sign-off. - -## Step F: Asset Slicing via the Asset Producer - -Raster ingredients identified in Step E need clean production assets. Use the bundled `impeccable_asset_producer` subagent rather than producing inline. - -Spawn it as a scoped subagent. If you do not have explicit permission to use agents, stop and ask: - -```text -Asset production will work better as a scoped subagent job. Should I spawn the Impeccable asset producer subagent for this step? -``` - -Pass to the agent: - -- Approved mock path or screenshot reference -- Crop paths or a contact sheet with crop ids -- Output directory -- Required dimensions, format, transparency needs -- Avoid list -- Notes on what should remain semantic HTML/CSS/SVG instead of raster - -Attach image generation capability to the spawned agent when the harness supports it. Do **not** load image-generation reference material into the parent thread. - -Inline asset production is allowed only if the user declines subagents, the harness cannot spawn the authorized agent, or the user explicitly asks for single-thread mode. - -Prefer HTML/CSS/SVG/canvas when they can credibly reproduce an ingredient; reach for real, generated, or stock imagery when the mock or subject matter calls for actual visual content. - -## After This File - -Once Steps A through F are complete, return to the build: implement per new-work.md and SKILL.md's craft floor. The implementation builds against the confirmed palette, approved mock, and the assets the producer wrote. +Return to [new-work.md](new-work.md) for the direction contract, implementation, and finishing pass. diff --git a/.kiro/skills/impeccable/reference/colorize.md b/.kiro/skills/impeccable/reference/colorize.md index abee1acf8..dc45f88c5 100644 --- a/.kiro/skills/impeccable/reference/colorize.md +++ b/.kiro/skills/impeccable/reference/colorize.md @@ -1,257 +1,86 @@ > **Additional context needed**: existing brand colors. -Replace timid grayscale or single-accent designs with a strategic palette: pick a color strategy, choose a hue family that fits the brand, then apply color with intent. More color ≠ better. Strategic color beats rainbow vomit. +Introduce color as hierarchy, meaning, and atmosphere. Preserve confirmed brand and semantic conventions; do not replace a visual world under the guise of colorizing it. --- -## Register +## Visitor mode -Persuade + Experience: palette IS voice. Pick a color strategy first per SKILL.md (Restrained / Committed / Full palette / Drenched) and follow its dosage. Committed, Full palette, and Drenched deliberately exceed the ≤10% rule; that rule is Restrained only. Unexpected combinations are allowed; a dominant color can own the page when the chosen strategy calls for it. +- **Persuade + Experience:** color may carry the voice and own large regions when the selected world calls for it. +- **Operate + Read:** color primarily encodes action, selection, status, wayfinding, and reading hierarchy. Rarity gives an accent force. -Operate + Read: semantic-first and almost always Restrained. Accent color is reserved for primary action, current selection, and state indicators. Not decoration. Every color has a consistent meaning across every screen. +## Audit before choosing ---- +Read DESIGN.md, tokens, assets, current themes, and representative states. Identify: -## Assess Color Opportunity +- which colors are confirmed brand commitments; +- current surface, text, action, and semantic roles; +- places where grayscale obscures hierarchy or state; +- contrast failures and color-only communication; +- light/dark or data-visualization requirements; +- whether the task asks for more color or a new identity. -Analyze the current state and identify opportunities: +If a new identity is required, use [new-work.md](new-work.md). Ask only when a binding brand decision cannot be inferred. -1. **Understand current state**: - - **Color absence**: Pure grayscale? Limited neutrals? One timid accent? - - **Missed opportunities**: Where could color add meaning, hierarchy, or delight? - - **Context**: What's appropriate for this domain and audience? - - **Brand**: Are there existing brand colors we should use? +## Choose a strategy -2. **Identify where color adds value**: - - **Semantic meaning**: Success (green), error (red), warning (yellow/orange), info (blue) - - **Hierarchy**: Drawing attention to important elements - - **Categorization**: Different sections, types, or states - - **Emotional tone**: Warmth, energy, trust, creativity - - **Wayfinding**: Helping users navigate and understand structure - - **Delight**: Moments of visual interest and personality +Name the intended emotional temperature, dominant relationship, contrast range, and color dosage before editing. The strategy may be restrained or immersive; it must follow the brief and selected world rather than a fixed percentage rule. -If any of these are unclear from the codebase, ask the user directly to clarify what you cannot infer. +Build roles, not a bag of swatches: -**CRITICAL**: More color ≠ better. Strategic color beats rainbow vomit every time. Every color should have a purpose. +- canvas and elevated surfaces; +- primary and secondary text; +- action, focus, and selection; +- borders and separators; +- success, warning, error, and information; +- data categories or scales when needed. -## Plan Color Strategy +Use the project's existing color space. For a new web palette, prefer OKLCH because lightness and chroma can be adjusted predictably. Choose hue from product meaning and visual direction, never from a default category association. -Create a purposeful color introduction plan: +## Apply at system scale -- **Color palette**: What colors match the brand/context? (Choose 2-4 colors max beyond neutrals) -- **Dominant color**: Which color owns 60% of colored elements? -- **Accent colors**: Which colors provide contrast and highlights? (30% and 10%) -- **Application strategy**: Where does each color appear and why? +- Let the strongest color own a deliberate region or role instead of scattering tiny accents. +- Keep the primary action easy to find; do not spend its color on decoration. +- Tint neutrals only when the brand hue genuinely creates cohesion. Neutral gray is valid when it serves the world. +- On colored surfaces, derive secondary text from the foreground or surface hue rather than using washed-out generic gray. +- Keep semantic meanings consistent, but respect platform and domain conventions instead of assuming fixed hues. +- For data, use distinct lightness, chroma, shape, label, or pattern so color is not the only code. +- 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. -**IMPORTANT**: Color should enhance hierarchy and meaning, not create chaos. Less is more when it matters more. +Decoration without a relationship to hierarchy, state, content, or the visual world is not a color strategy. -## Introduce Color Strategically +## Contrast and perception -Add color systematically across these dimensions: +Verify computed foreground/background pairs: -### Semantic Color -- **State indicators**: - - Success: Green tones (emerald, forest, mint) - - Error: Red/pink tones (rose, crimson, coral) - - Warning: Orange/amber tones - - Info: Blue tones (sky, ocean, indigo) - - Neutral: Gray/slate for inactive states +| Content | WCAG AA minimum | +|---|---| +| body text | 4.5:1 | +| large text | 3:1 | +| controls, icons, focus indicators | 3:1 | -- **Status badges**: Colored backgrounds or borders for states (active, pending, completed, etc.) -- **Progress indicators**: Colored bars, rings, or charts showing completion or health +Do not rely on eyesight alone. Check interactive states, overlays, text on images, disabled content, and both themes. Simulate common vision deficiencies. Information conveyed by color also needs text, shape, iconography, or position. -### Accent Color Application -- **Primary actions**: Color the most important buttons/CTAs -- **Links**: Add color to clickable text (maintain accessibility) -- **Icons**: Colorize key icons for recognition and personality -- **Headers/titles**: Add color to section headers or key labels -- **Hover states**: Introduce color on interaction +When deriving OKLCH ramps, vary lightness and reduce chroma near white and black. Do not keep high chroma at extreme lightness merely to make the math uniform. Prefer explicit colors over chains of translucent overlays when alpha would make contrast context-dependent. -### Background & Surfaces -- **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 toward the brand, not "for warmth" by reflex +## Verify -**Use OKLCH for color**: It's perceptually uniform, meaning equal steps in lightness *look* equal. Great for generating harmonious scales. - -### Data Visualization -- **Charts & graphs**: Use color to encode categories or values -- **Heatmaps**: Color intensity shows density or importance -- **Comparison**: Color coding for different datasets or timeframes - -### Borders & Accents -- **Hairline borders**: 1px colored borders on full perimeter (not side-stripes; see the absolute ban on `border-left/right > 1px`) -- **Underlines**: Color underlines for emphasis or active states -- **Dividers**: Subtle colored dividers instead of gray lines -- **Focus rings**: Colored focus indicators matching brand -- **Surface tints**: A 4-8% background wash of the accent color instead of a stripe - -**NEVER**: `border-left` or `border-right` greater than 1px as a colored accent stripe. This is one of the three absolute bans in the parent skill. If you want to mark a card as "active" or "warning", use a full hairline border, a background tint, a leading glyph, or a numbered prefix. Not a side stripe. - -### Typography Color -- **Colored headings**: Use brand colors for section headings (maintain contrast) -- **Highlight text**: Color for emphasis or categories -- **Labels & tags**: Small colored labels for metadata or categories - -### Decorative Elements -- **Illustrations**: Add colored illustrations or icons -- **Shapes**: Geometric shapes in brand colors as background elements -- **Gradients**: Colorful gradient overlays or mesh backgrounds -- **Blobs/organic shapes**: Soft colored shapes for visual interest - -## Balance & Refinement - -Ensure color addition improves rather than overwhelms: - -### Maintain Hierarchy -- **Dominant color** (60%): Primary brand color or most used accent -- **Secondary color** (30%): Supporting color for variety -- **Accent color** (10%): High contrast for key moments -- **Neutrals** (remaining): Gray/black/white for structure - -### Accessibility -- **Contrast ratios**: Ensure WCAG compliance (4.5:1 for text, 3:1 for UI components) -- **Don't rely on color alone**: Use icons, labels, or patterns alongside color -- **Test for color blindness**: Verify red/green combinations work for all users - -### Cohesion -- **Consistent palette**: Use colors from defined palette, not arbitrary choices -- **Systematic application**: Same color meanings throughout (green always = success) -- **Temperature consistency**: Warm palette stays warm, cool stays cool - -**NEVER**: -- 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 -- Violate WCAG contrast requirements -- Use color as the only indicator (accessibility issue) -- Make everything colorful (defeats the purpose) -- Default to purple-blue gradients (AI slop aesthetic) - -## Verify Color Addition - -Test that colorization improves the experience: - -- **Better hierarchy**: Does color guide attention appropriately? -- **Clearer meaning**: Does color help users understand states/categories? -- **More engaging**: Does the interface feel warmer and more inviting? -- **Still accessible**: Do all color combinations meet WCAG standards? -- **Not overwhelming**: Is color balanced and purposeful? +- Every color has a stable role or a world-specific atmospheric purpose. +- Attention lands on the intended action, content, or state. +- The palette works across quiet, dense, interactive, error, and empty states. +- Light and dark themes are each composed, not mechanically inverted. +- Contrast and non-color cues pass in all relevant states. +- The result is recognizably this product, not a generic “colorful” treatment. When the palette earns its place, hand off to `/impeccable polish` for the final pass. ## Live-mode signature params -When invoked from live mode, each variant MUST declare a `color-amount` param so the user can dial between a restrained accent and a drenched surface without regeneration. Author the variant's CSS against `var(--p-color-amount, 0.5)`, typically as the alpha multiplier on backgrounds, or as a scaling factor on the chroma axis in an OKLCH expression. 0 = neutral/monochrome, 1 = full saturation / dominant coverage. +When invoked from live mode, every variant declares a `color-amount` parameter. Author CSS against `var(--p-color-amount, 0.5)` so the user can move from neutral to the variant's full color strategy without regeneration. ```json {"id":"color-amount","kind":"range","min":0,"max":1,"step":0.05,"default":0.5,"label":"Color amount"} ``` -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). +Add at most two variant-specific parameters, such as palette, temperature, or tint behavior. Follow [live.md](live.md)'s parameter contract. diff --git a/.kiro/skills/impeccable/reference/craft.md b/.kiro/skills/impeccable/reference/craft.md index cc7f98d37..dbbc9402c 100644 --- a/.kiro/skills/impeccable/reference/craft.md +++ b/.kiro/skills/impeccable/reference/craft.md @@ -1,3 +1,5 @@ # Craft (deprecated alias) -`craft` is a deprecated alias for the standard build flow. Builds route through SKILL.md's new-work gate; [new-work.md](new-work.md) carries the direction checkpoint ("Decide, then build"), the identity playbook, and the finishing pass. Invoking `craft` explicitly forces the attended checkpoints: pause at the stated direction for confirmation, and follow [codex.md](codex.md)'s mock flow when the harness generates images. Nothing else differs from a plain build request. +`craft` is a deprecated alias for an ordinary request to make new visual work. It adds no setup, interview, checkpoint, tool, or quality behavior. Apply SKILL.md's normal routing: create missing PRODUCT.md through [init.md](init.md), then follow [new-work.md](new-work.md) for visual authority, world and surface decisions, implementation, and finish. + +Do not tell users they need to invoke `craft`. Natural requests such as “build this feature,” “make a landing page,” or “redesign this screen” use the same flow. diff --git a/.kiro/skills/impeccable/reference/critique.md b/.kiro/skills/impeccable/reference/critique.md index 962456b56..49d314951 100644 --- a/.kiro/skills/impeccable/reference/critique.md +++ b/.kiro/skills/impeccable/reference/critique.md @@ -43,13 +43,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 @@ -115,11 +115,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. @@ -176,7 +176,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. 2. **Pass the structured metadata** through `IMPECCABLE_CRITIQUE_META` (JSON), then run the write command: ```bash diff --git a/.kiro/skills/impeccable/reference/delight.md b/.kiro/skills/impeccable/reference/delight.md index 596cff09e..40aaddc34 100644 --- a/.kiro/skills/impeccable/reference/delight.md +++ b/.kiro/skills/impeccable/reference/delight.md @@ -1,302 +1,70 @@ -> **Additional context needed**: what's appropriate for the domain (playful vs professional vs quirky vs elegant). +> **Additional context needed**: the brand's emotional range. -Find the moments where personality and unexpected polish would turn a functional interface into one users remember and tell other people about. Add only where the moment earns it; delight everywhere reads as noise. +Make the experience memorable at moments that earn it. Delight is not a layer of generic whimsy; it is product character revealed through a useful interaction, a humane response, or an unexpectedly considered detail. --- -## Register +## Visitor mode -Persuade + Experience: delight can be distributed across copy voice, section transitions, discovery rewards, seasonal touches, personality across the whole surface. +- **Persuade + Experience:** personality may run through voice, composition, motion, and discovery, provided the artifact remains the focus. +- **Operate + Read:** concentrate delight at meaningful moments such as first use, completion, recovery, or mastery. Reliability carries everything else. -Operate + Read: delight at specific moments, not pages. Completion, first-time actions, error recovery, milestone crossings. Reliability and consistency carry the rest of the experience; delight pushed everywhere reads as noise. +## Find the opportunity ---- +Inspect the target, DESIGN.md, product voice, repeated-use frequency, and emotional context. Look for: -## Assess Delight Opportunities +- effort worth acknowledging; +- waiting that can become informative; +- an empty or first-use state that can orient; +- an error or recovery moment that needs empathy; +- an interaction whose physical or verbal response could express the brand; +- a useful capability people might enjoy discovering. -Identify where delight would enhance (not distract from) the experience: +Do not manufacture a celebration for an ordinary click. Ask only when the brand's emotional range or the stakes cannot be inferred. -1. **Find natural delight moments**: - - **Success states**: Completed actions (save, send, publish) - - **Empty states**: First-time experiences, onboarding - - **Loading states**: Waiting periods that could be entertaining - - **Achievements**: Milestones, streaks, completions - - **Interactions**: Hover states, clicks, drags - - **Errors**: Softening frustrating moments - - **Easter eggs**: Hidden discoveries for curious users +## Define one delight thesis -2. **Understand the context**: - - What's the brand personality? (Playful? Professional? Quirky? Elegant?) - - Who's the audience? (Tech-savvy? Creative? Corporate?) - - What's the emotional context? (Accomplishment? Exploration? Frustration?) - - What's appropriate? (Banking app ≠ gaming app) +State in one sentence what the user should feel and why that feeling belongs to this product. Then choose the smallest system that can deliver it: -3. **Define delight strategy**: - - **Subtle sophistication**: Refined micro-interactions (luxury brands) - - **Playful personality**: Whimsical illustrations and copy (consumer apps) - - **Helpful surprises**: Anticipating needs before users ask (productivity tools) - - **Sensory richness**: Satisfying sounds, smooth animations (creative tools) +- a distinctive response to a meaningful action; +- product-specific language that clarifies while carrying voice; +- an interaction or transition with a recognizable material behavior; +- an illustration, sound, haptic, or environmental detail grounded in the product world; +- a discovery reward that reveals real utility. -If any of these are unclear from the codebase, ask the user directly to clarify what you cannot infer. +Derive the treatment from product mechanism and visual world, not a stock catalog. -**CRITICAL**: Delight should enhance usability, never obscure it. If users notice the delight more than accomplishing their goal, you've gone too far. +## Build for the emotional moment -## Delight Principles +- **Success:** match the response to the effort and consequence. Major milestones can expand; routine saves should simply feel certain. +- **Waiting:** show truthful progress, useful context, or product-specific activity. Never fake work or delay completion to stage a flourish. +- **Empty and first use:** make the next action clear before adding personality. +- **Error and recovery:** lead with the problem and recovery. Warmth may reduce stress; jokes must not trivialize loss, money, privacy, or blocked work. +- **Repeated interaction:** keep the response satisfying after the hundredth use. Variation is useful only when it remains coherent and predictable enough to trust. +- **Discovery:** reward curiosity without hiding required functionality. -Follow these guidelines: +Copy must use the product's language. Generic whimsy is worse than neutral clarity. -### Delight Amplifies, Never Blocks -- Delight moments should be quick (< 1 second) -- Never delay core functionality for delight -- Make delight skippable or subtle -- Respect user's time and task focus +## Protect the experience -### Surprise and Discovery -- Hide delightful details for users to discover -- Reward exploration and curiosity -- Don't announce every delight moment -- Let users share discoveries with others +Delight must not: -### Appropriate to Context -- Match delight to emotional moment (celebrate success, empathize with errors) -- Respect the user's state (don't be playful during critical errors) -- Match brand personality and audience expectations -- Cultural sensitivity (what's delightful varies by culture) +- delay, block, or obscure the primary task; +- override platform conventions or accessibility; +- add unrequested factual claims; +- play sound without consent or ignore mute settings; +- become mandatory, unskippable, or exhausting on repeat; +- add a dependency or asset cost disproportionate to the moment. -### Compound Over Time -- Delight should remain fresh with repeated use -- Vary responses (not same animation every time) -- Reveal deeper layers with continued use -- Build anticipation through patterns +For authored motion, load [animate.md](animate.md). Respect reduced motion, screen readers, keyboard use, touch, localization, and cultural context. Nonessential loops stop when hidden. Make celebration intensity proportional to frequency and consequence. -## Delight Techniques +## Verify -Add personality and joy through these methods: +- The moment is specific enough that a neighboring product could not use it unchanged. +- It improves comprehension, confidence, motivation, or emotional recovery. +- The interface remains fast and obvious without the flourish. +- Repetition does not turn charm into friction. +- Reduced-motion, muted, keyboard, touch, and localized paths work. +- The result feels like the selected world, not a generic “delight” treatment. -### Micro-interactions & Animation - -**Button delight**: -```css -/* Satisfying button press */ -.button { - transition: transform 0.1s, box-shadow 0.1s; -} -.button:active { - transform: translateY(2px); - box-shadow: 0 2px 4px rgba(0,0,0,0.2); -} - -/* Ripple effect on click */ -/* Smooth lift on hover */ -.button:hover { - transform: translateY(-2px); - transition: transform 0.2s cubic-bezier(0.25, 1, 0.5, 1); /* ease-out-quart */ -} -``` - -**Loading delight**: -- Playful loading animations (not just spinners) -- Personality in loading messages (write product-specific ones, not generic AI filler) -- Progress indication with encouraging messages -- Skeleton screens with subtle animations - -**Success animations**: -- Checkmark draw animation -- Confetti burst for major achievements -- Gentle scale + fade for confirmation -- Satisfying sound effects (subtle) - -**Hover surprises**: -- Icons that animate on hover -- Color shifts or glow effects -- Tooltip reveals with personality -- Cursor changes (custom cursors for branded experiences) - -### Personality in Copy - -**Playful error messages**: -``` -"Error 404" -"This page is playing hide and seek. (And winning)" - -"Connection failed" -"Looks like the internet took a coffee break. Want to retry?" -``` - -**Encouraging empty states**: -``` -"No projects" -"Your canvas awaits. Create something amazing." - -"No saved articles" -"Nothing on the nightstand yet. Save a story for later." -``` - -**Playful labels & tooltips**: -``` -"Delete" -"Send to void" (for playful brand) - -"Help" -"Rescue me" (tooltip) -``` - -**IMPORTANT**: Match copy personality to brand. Banks shouldn't be wacky, but they can be warm. - -### Illustrations & Visual Personality - -**Custom illustrations**: -- Empty state illustrations (not stock icons) -- Error state illustrations (friendly monsters, quirky characters) -- Loading state illustrations (animated characters) -- Success state illustrations (celebrations) - -**Icon personality**: -- Custom icon set matching brand personality -- Animated icons (subtle motion on hover/click) -- Illustrative icons (more detailed than generic) -- Consistent style across all icons - -**Background effects**: -- Subtle particle effects -- Gradient mesh backgrounds -- Geometric patterns -- Parallax depth -- Time-of-day themes (morning vs night) - -### Satisfying Interactions - -**Drag and drop delight**: -- Lift effect on drag (shadow, scale) -- Snap animation when dropped -- Satisfying placement sound -- Undo toast ("Dropped in wrong place? [Undo]") - -**Toggle switches**: -- Smooth slide with spring physics -- Color transition -- Haptic feedback on mobile -- Optional sound effect - -**Progress & achievements** (Operate surfaces with recurring tasks only; gamification on editorial, portfolio, or one-visit surfaces reads as noise): -- Streak counters with celebratory milestones -- Progress bars that "celebrate" at 100% -- Badge unlocks with animation -- Playful stats ("You're on fire! 5 days in a row") - -**Form interactions**: -- Input fields that animate on focus -- Checkboxes with a satisfying scale pulse when checked -- Success state that celebrates valid input -- Auto-grow textareas - -### Sound Design - -**Subtle audio cues** (when appropriate): -- Notification sounds (distinctive but not annoying) -- Success sounds (satisfying "ding") -- Error sounds (empathetic, not harsh) -- Typing sounds for chat/messaging -- Ambient background audio (very subtle) - -**IMPORTANT**: -- Respect system sound settings -- Provide mute option -- Keep volumes quiet (subtle cues, not alarms) -- Don't play on every interaction (sound fatigue is real) - -### Easter Eggs & Hidden Delights - -**Discovery rewards**: -- Konami code unlocks special theme -- Hidden keyboard shortcuts (Cmd+K for special features) -- Hover reveals on logos or illustrations -- Alt text jokes on images (for screen reader users too!) -- A view-source or console colophon (a note on the typefaces, a thank-you, a hiring line) - -**Seasonal touches**: -- Holiday themes (subtle, tasteful) -- Seasonal color shifts -- Weather-based variations -- Time-based changes (dark at night, light during day) - -**Contextual personality**: -- Different messages based on time of day -- Responses to specific user actions -- Randomized variations (not same every time) -- Progressive reveals with continued use - -### Loading & Waiting States - -**Make waiting engaging**: -- Interesting loading messages that rotate -- Progress bars with personality -- Mini-games during long loads -- Fun facts or tips while waiting -- Countdown with encouraging messages - -``` -Loading messages: write ones specific to your product, not generic AI filler: -- "Syncing with your team's changes..." -- "Fetching this week's issue..." -- "Developing your photos..." -- "Checking tomorrow's tide tables..." -``` - -**WARNING**: Avoid cliched loading messages like "Herding pixels", "Teaching robots to dance", "Consulting the magic 8-ball", "Counting backwards from infinity". These are AI-slop copy, instantly recognizable as machine-generated. Write messages that are specific to what your product actually does. - -### Celebration Moments - -**Success celebrations**: -- Confetti for major milestones -- Animated checkmarks for completions -- Progress bar celebrations at 100% -- "Achievement unlocked" style notifications -- Personalized messages ("You published your 10th article!") - -**Milestone recognition** (same scoping as Progress & achievements: Operate surfaces with recurring tasks): -- First-time actions get special treatment -- Streak tracking and celebration -- Progress toward goals -- Anniversary celebrations - -## Implementation Patterns - -**Animation libraries**: -- Framer Motion (React) -- GSAP (universal) -- Lottie (After Effects animations) -- Canvas confetti (party effects) - -**Sound libraries**: -- Howler.js (audio management) -- Use-sound (React hook) - -**Physics libraries**: -- React Spring (spring physics) -- Popmotion (animation primitives) - -**IMPORTANT**: File size matters. Compress images, optimize animations, lazy load delight features. - -**NEVER**: -- Delay core functionality for delight -- Force users through delightful moments (make skippable) -- Use delight to hide poor UX -- Overdo it (less is more) -- Ignore accessibility (animate responsibly, provide alternatives) -- Make every interaction delightful (special moments should be special) -- Sacrifice performance for delight -- Be inappropriate for context (read the room) - -## Verify Delight Quality - -Test that delight actually delights: - -- **User reactions**: Do users smile? Share screenshots? -- **Doesn't annoy**: Still pleasant after 100th time? -- **Doesn't block**: Can users opt out or skip? -- **Performant**: No jank, no slowdown -- **Appropriate**: Matches brand and context -- **Accessible**: Works with reduced motion, screen readers - -When the moments feel earned, hand off to `/impeccable polish` for the final pass. +When the personality feels earned, hand off to `/impeccable polish` for the final pass. diff --git a/.kiro/skills/impeccable/reference/document.md b/.kiro/skills/impeccable/reference/document.md index 0d38eb568..121a00ab1 100644 --- a/.kiro/skills/impeccable/reference/document.md +++ b/.kiro/skills/impeccable/reference/document.md @@ -1,6 +1,6 @@ Generate a `DESIGN.md` file at the project root that captures the current visual design system, so AI agents generating new screens stay on-brand. -DESIGN.md follows the [official DESIGN.md format spec](https://raw.githubusercontent.com/google-labs-code/design.md/main/docs/spec.md): YAML frontmatter carrying machine-readable design tokens, followed by a markdown body with exactly six sections in a fixed order. **Tokens are normative; prose provides context for how to apply them.** Sections may be omitted when not relevant, but **do not reorder them and do not rename them**. Section headers must match the spec character-for-character so the file stays parseable by other DESIGN.md-aware tools (Stitch itself, awesome-design-md, skill-rest, etc.). +DESIGN.md follows the [official DESIGN.md format spec](https://raw.githubusercontent.com/google-labs-code/design.md/main/docs/spec.md): optional YAML frontmatter carrying machine-readable design tokens, followed by up to eight markdown sections in a fixed order. **Tokens are normative; prose provides context for how to apply them.** Sections may be omitted when not relevant, but those present stay in the specified order. Use the canonical headings below so the file remains portable across DESIGN.md-aware tools. ## The frontmatter: token schema @@ -43,26 +43,28 @@ components: 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. +- **Colors accept any valid CSS color string.** Hex is the recommended default for portability, but preserve an incumbent `rgb()`, `hsl()`, `oklch()`, wide-gamut, or mixed-color value when it is the project's normative source. 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 (`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) +## The markdown body: eight sections (canonical order) 1. `## Overview` 2. `## Colors` 3. `## Typography` -4. `## Elevation` -5. `## Components` -6. `## Do's and Don'ts` +4. `## Layout` +5. `## Elevation & Depth` +6. `## Shapes` +7. `## Components` +8. `## Do's and Don'ts` -Optional evocative subtitles are allowed in the form `## 2. Colors: The [Name] Palette` (Stitch's own outputs do this), but the literal word in each header (Overview, Colors, Typography, Elevation, Components, Do's and Don'ts) must be present. Do NOT add extra top-level sections (Layout Principles, Responsive Behavior, Motion, Agent Prompt Guide). Fold that content into the six spec sections where it naturally belongs. +Omit irrelevant sections rather than filling them with invented rules. Put responsive layout in Layout, depth in Elevation & Depth, radius and form language in Shapes, and per-component behavior in Components. Unknown sections are preserved by the format, but new visual guidance should use the canonical structure whenever it fits. ## When to run -- 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. +- New-work found a coherent incumbent visual system but no `DESIGN.md`. +- The first implementation of a new world is complete and its provisional decisions need to be carbonized. - An existing `DESIGN.md` is stale (the design has drifted). - Before a large redesign, to capture the current state as a reference. @@ -71,9 +73,9 @@ 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 init, nothing built yet). Interview for five high-level answers, write a minimal DESIGN.md marked ``. Re-run in scan mode once there's code. +- **Seed mode**: the project is pre-implementation. Ensure PRODUCT.md exists, then reuse new-work's visual-world workshop and write its directional DESIGN.md 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. +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` requests new-work's world workshop, but it does not authorize replacing coherent code: when an incumbent system exists, offer scan mode or route an explicit identity-replacement request through new-work. ## Scan mode (approach C: auto-extract, then confirm descriptive language) @@ -97,7 +99,8 @@ Build a structured draft from the discovered tokens. For each token class: - **Typography**: Map observed sizes and weights to the Material hierarchy (display / headline / title / body / label). Note font-family stacks and the scale ratio. - **Elevation**: Catalogue the shadow vocabulary. If the project is flat and uses tonal layering instead, that's a valid answer; state it explicitly. - **Components**: For each common component (button, card, input, chip, list item, tooltip, nav), extract shape (radius), color assignment, hover/focus treatment, internal padding. -- **Spacing + layout**: Fold into Overview or relevant Components. The spec does NOT have a Layout section. +- **Layout + spacing**: Extract grid, container, breakpoint, rhythm, and density behavior into Layout. +- **Shapes**: Extract radius, corner, border, clipping, and recurring form behavior into Shapes. ### Step 2b: Stage the frontmatter @@ -112,19 +115,19 @@ Skip anything the project doesn't have. Empty scale keys or fabricated tokens po ### Step 3: Ask the user for qualitative language -The following require creative input that cannot be auto-extracted. Group them into one `AskUserQuestion` interaction: +The following require creative input that cannot be auto-extracted. Ask them in two structured rounds of no more than three questions each (or the harness's lower limit), waiting between rounds: - **Creative North Star**: a single named metaphor for the whole system ("The Editorial Sanctuary", "The Golden State Curator", "The Lab Notebook"). Offer 2-3 options that honor PRODUCT.md's brand personality. -- **Overview voice**: mood adjectives, aesthetic philosophy in 2-3 sentences, anti-references (what the system should not feel like). +- **Overview voice**: mood adjectives, aesthetic philosophy in 2-3 sentences, and any confirmed visual anti-reference. - **Color character** (for auto-extracted colors): descriptive names ("Deep Muted Teal-Navy", not "blue-800"). Suggest 2-3 options per key color based on hue/saturation. - **Elevation philosophy**: flat/layered/lifted. If shadows exist, is their role ambient or structural? - **Component philosophy**: the feel of buttons, cards, inputs in one phrase ("tactile and confident" vs. "refined and restrained"). -Quote a line from PRODUCT.md when possible so the user sees their own strategic language carry forward. +Carry a line from PRODUCT.md only when it is a durable brand commitment that actually constrains the visual system. Page strategy and surface concepts do not belong here. ### Step 4: Write DESIGN.md -The file opens with the YAML frontmatter staged in Step 2b (schema documented at the top of this reference), then the markdown body using the structure below. Headers must match character-for-character. Optional evocative subtitles (e.g. `## 2. Colors: The Coastal Palette`) are allowed. +The file opens with the YAML frontmatter staged in Step 2b (schema documented at the top of this reference), then the markdown body using the canonical structure below. ```markdown --- @@ -136,13 +139,13 @@ colors: # Design System: [Project Title] -## 1. Overview +## Overview **Creative North Star: "[Named metaphor in quotes]"** -[2-3 paragraph holistic description: personality, density, aesthetic philosophy. Start from the North Star and work outward. State what this system explicitly rejects (pulled from PRODUCT.md's anti-references). End with a short **Key Characteristics:** bullet list.] +[2-3 paragraph holistic description: personality, density, and aesthetic philosophy. Start from the North Star and work outward. State only confirmed visual rejections. End with a short **Key Characteristics:** bullet list.] -## 2. Colors +## Colors [Describe the palette character in one sentence.] @@ -162,7 +165,7 @@ colors: ### Named Rules (optional, powerful) **The [Rule Name] Rule.** [Short, forceful prohibition or doctrine, e.g. "The One Voice Rule. The primary accent is used on ≤10% of any given screen. Its rarity is the point."] -## 3. Typography +## Typography **Display Font:** [Family] (with [fallback]) **Body Font:** [Family] (with [fallback]) @@ -180,7 +183,11 @@ colors: ### Named Rules (optional) **The [Rule Name] Rule.** [Short doctrine about type use.] -## 4. Elevation +## Layout + +[Describe the grid or spatial model, container behavior, density, responsive changes, and the spacing rhythm. Include exact values only when observed.] + +## Elevation & Depth [One paragraph: does this system use shadows, tonal layering, or a hybrid? If "no shadows", say so explicitly and describe how depth is conveyed instead.] @@ -191,7 +198,11 @@ colors: ### Named Rules (optional) **The [Rule Name] Rule.** [e.g. "The Flat-By-Default Rule. Surfaces are flat at rest. Shadows appear only as a response to state (hover, elevation, focus)."] -## 5. Components +## Shapes + +[Describe the form language: corner/radius strategy, borders, clipping, and any recurring silhouette or geometry.] + +## Components For each component, lead with a short character line, then specify shape, color assignment, states, and any distinctive behavior. @@ -223,16 +234,16 @@ For each component, lead with a short character line, then specify shape, color ### [Signature Component] (optional; if the project has a distinctive custom component worth documenting) [Description.] -## 6. Do's and Don'ts +## Do's and Don'ts -Concrete, forceful guardrails. Lead each with "Do" or "Don't". Be specific: include exact colors, pixel values, and named anti-patterns the user mentioned in PRODUCT.md. **Every anti-reference in PRODUCT.md should show up here as a "Don't" with the same language**, so the visual spec carries the strategic line through. Quote PRODUCT.md directly where possible: if PRODUCT.md says *"avoid dark mode with purple gradients, neon accents, glassmorphism"*, the Don'ts here should repeat that by name. +Concrete visual guardrails grounded in the incumbent implementation or the user's chosen world. Lead each with "Do" or "Don't" and include exact values only when established. Do not turn a task-specific concept or surface strategy into a system-wide prohibition. ### Do: - **Do** [specific prescription with exact values / named rule]. - **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** [...] ``` @@ -307,7 +318,7 @@ The `html` and `css` fields must be **self-contained, drop-in snippets** that re Aim for a tight set of **5-10 components** that best represent the visual system: - **Canonical primitives (always include if the project has them):** button (each variant as a separate component entry), input/text field, navigation, chip/tag, card. -- **Signature components (include if distinctive):** hero CTA, featured card, filter pill, a distinctive table or list-row treatment, any custom pattern the user mentioned as important in PRODUCT.md. +- **Signature components (include if distinctive):** the recurring custom patterns that actually define the implemented system. - **Skip the rest.** Utility components, form building blocks, wrapper layouts: not worth documenting unless visually distinctive. If the project has **no component library yet** (bare landing page, new project), synthesize canonical primitives from the tokens using best-practice defaults consistent with the DESIGN.md's rules. Every `.impeccable/design.json` has *something* to render, even on day zero. @@ -338,64 +349,40 @@ Your own write is the freshest source; subsequent commands in this session don't ## Seed mode -For projects with no visual system to extract yet. Produces a minimal scaffold, not a full spec. +For projects with no visual system to extract yet. Produces a user-chosen visual-world scaffold, not a fabricated token spec. -### Step 1: Confirm seed mode +### Step 1: Route through new-work's workshop -Before interviewing: "There's no existing visual system to scan. I'll ask five quick questions to seed a starter DESIGN.md. You can re-run `/impeccable document` once there's code, to capture the real tokens and components. OK?" +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 the user prefers to skip, stop. No file. +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. -### Step 2: Five questions +If new-work already completed the workshop in this session, use its chosen direction directly. Do not ask again. -Group into one `AskUserQuestion` interaction. Options must be concrete. +### Step 2: Write seed DESIGN.md -1. **Color strategy.** Pick one: - - Restrained: tinted neutrals + one accent ≤10% - - Committed: one saturated color carries 30–60% of the surface - - Full palette: 3–4 named color roles, each deliberate - - Drenched: the surface IS the color - - Then: one hue family or anchor reference ("deep teal", "mustard", "Klim #ff4500 orange"). - -2. **Typography direction.** Pick one (specific fonts come later): - - Serif display + sans body - - Single sans (warm / technical / geometric / humanist; pick a feel) - - Display + mono - - Mono-forward - - Editorial script + sans - -3. **Motion energy.** Pick one: - - Restrained: state changes only - - Responsive: feedback + transitions, no choreography - - Choreographed: orchestrated entrances, scroll-driven sequences - -4. **Three named references.** Brands, products, printed objects. Not adjectives. - -5. **One anti-reference.** What it should NOT feel like. Also named. - -### Step 3: Write seed DESIGN.md - -Use the six-section spec from Scan mode. Populate what the interview answers; leave the rest as honest placeholders. The seed is a scaffold, not a fabricated spec. +Use the canonical section order from Scan mode. Populate the selected workshop direction and leave unresolved implementation facts as honest placeholders. The seed commits a world and its invariants; it does not pretend implementation tokens already exist. Lead the file with: ```markdown - + ``` Per-section guidance in seed mode: -- **Overview**: Creative North Star and philosophy phrased from the answers (color strategy + motion energy + references). Reference the user's anti-reference directly. -- **Colors**: Color strategy as a Named Rule (e.g. *"The Drenched Rule. The surface IS the color."*). Hue family or anchor reference. No hex values; mark as `[to be resolved during implementation]`. -- **Typography**: the direction the user picked (e.g. "Serif display + sans body"). No font names yet: `[font pairing to be chosen at implementation]`. -- **Elevation**: inferred from motion energy. Restrained/Responsive → flat by default; Choreographed → layered. One sentence. +- **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. +- **Elevation & Depth**: the selected material and depth behavior, stated as an invariant rather than inferred from a generic preset. +- **Shapes**: the selected form and corner language. - **Components**: omit entirely; no components exist yet. -- **Do's and Don'ts**: carry PRODUCT.md's anti-references directly plus the anti-reference named in Q5. +- **Do's and Don'ts**: record the durable guardrails confirmed during the world choice, not task-local refusals. 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 +### Step 3: 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." @@ -405,15 +392,15 @@ Your own write is the freshest source; no reload needed. ## Style guidelines - **Frontmatter first, prose second.** Tokens go in the YAML frontmatter; prose contextualizes them. Don't redefine a token value in two places; the frontmatter is normative. -- **Cite PRODUCT.md anti-references by name** in the Do's and Don'ts section. If PRODUCT.md lists "SaaS landing-page clichés" or "generic AI tool marketing" as anti-references, the DESIGN.md Don'ts should repeat those phrases verbatim so the visual spec enforces the strategic line. -- **Match the spec, don't invent new sections.** The six section names are fixed. If you have Layout/Motion/Responsive content to document, fold it into Overview (philosophy-level rules) or Components (per-component behavior). +- **Carry only durable product constraints.** A binding logo, identity asset, accessibility need, or brand commitment from PRODUCT.md may constrain DESIGN.md. Surface strategy stays in its surface brief. +- **Match the spec.** Use its eight canonical sections in order and omit any that are irrelevant. Put motion guidance with the world or component it affects rather than creating a token group the schema does not support. - **Descriptive > technical**: "Gently curved edges (8px radius)" > "rounded-lg". Include the technical value in parens, lead with the description. - **Functional > decorative**: for each token, explain WHERE and WHY it's used, not just WHAT it is. - **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 forceful**. The voice of a design director. "Prohibited", "forbidden", "never", "always", not "consider", "might", "prefer". Match PRODUCT.md's tone. -- **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. -- **Reference PRODUCT.md**. The anti-references section of PRODUCT.md should directly inform the Do's and Don'ts section here. Quote or paraphrase. +- **Be decisive where evidence is decisive.** Use hard language for actual invariants and softer language for provisional guidance. +- **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. ## Pitfalls @@ -423,7 +410,7 @@ Your own write is the freshest source; no reload needed. - Don't invent components that don't exist. If the project only has buttons and cards, only document those. - Don't overwrite an existing DESIGN.md without asking. - Don't duplicate content from PRODUCT.md. DESIGN.md is strictly visual. -- Don't add a "Layout Principles" or "Motion" or "Responsive Behavior" top-level section. The spec has six, not nine. Fold that content where it belongs. +- Don't replace canonical sections with near-synonyms. Put layout and responsive behavior in `Layout`; put motion with the affected world or component. - Don't rename sections even slightly. "Colors" not "Color Palette & Roles". "Typography" not "Typography Rules". Tooling parsing depends on exact headers. - Don't duplicate token values between frontmatter and prose. If a color is in `colors.primary` as hex, the prose can name it and describe its role but should not reassert a different hex. The frontmatter is normative. - Don't invent frontmatter token groups outside Stitch's schema (no `motion:`, `breakpoints:`, `shadows:` at the top level). Stitch's Zod schema only accepts `colors`, `typography`, `rounded`, `spacing`, `components`. Anything else belongs in the sidecar's `extensions`. diff --git a/.kiro/skills/impeccable/reference/hooks.md b/.kiro/skills/impeccable/reference/hooks.md index d461b71d6..11bdb7f62 100644 --- a/.kiro/skills/impeccable/reference/hooks.md +++ b/.kiro/skills/impeccable/reference/hooks.md @@ -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. diff --git a/.kiro/skills/impeccable/reference/init.md b/.kiro/skills/impeccable/reference/init.md index 073b2fa62..d3e3a1fbd 100644 --- a/.kiro/skills/impeccable/reference/init.md +++ b/.kiro/skills/impeccable/reference/init.md @@ -1,224 +1,114 @@ -# Init Flow +# Init flow -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 [DESIGN.md format spec](https://raw.githubusercontent.com/google-labs-code/design.md/main/docs/spec.md). Answers "how it looks". -- **`.impeccable/live/config.json`** (live mode): pre-configured so `/impeccable live` boots straight into variant mode with no first-time detour. - -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. +`init` captures durable product truth in PRODUCT.md. It does not invent a visual world and does not write DESIGN.md; [new-work.md](new-work.md) creates or expands one, and [document.md](document.md) records an incumbent one. Existing runnable web projects may also receive `.impeccable/live/config.json`. ## Step 1: Load current state -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). +Use the PRODUCT.md path resolved by context.mjs. Update it instead of creating a competing authority. In a child app inheriting root context, confirm shared versus app-specific scope before writing. -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. -- **PRODUCT.md exists, DESIGN.md missing**: skip to Step 5 and offer to run `/impeccable document` for DESIGN.md. -- **PRODUCT.md exists but has no `## Register` section (legacy)**: add it. Infer a hypothesis from the codebase (see Step 2), confirm with the user, write the field. -- **PRODUCT.md exists but has no `## Platform` section (legacy)**: add it the same way, but only when the project is native (`ios` / `android` / `adaptive`) or the user wants it explicit; a missing field already means `web`. -- **Both exist**: ask the user directly to clarify what you cannot infer. Ask which file to refresh. Skip the one the user doesn't want changed. -- **Just DESIGN.md exists (unusual)**: do Steps 2-4 to produce PRODUCT.md. +- **No PRODUCT.md:** explore, interview, and write it. +- **PRODUCT.md exists:** ask what product knowledge is stale or missing; do not reopen confirmed fields without a reason. +- **Legacy PRODUCT.md:** add only durable missing facts; absent `## Platform` means `web` unless evidence says otherwise. +- **Only DESIGN.md exists:** leave it untouched and create PRODUCT.md. +- **Redesign/rebrand request:** preserve confirmed product truth unless the user changes it. Visual replacement happens later in new-work, not here. -Never silently overwrite an existing file. Always confirm first. +Never silently overwrite an existing file or offer DESIGN.md during init. If another request invoked init, finish PRODUCT.md and resume it. New visual work continues in new-work; `shape` resumes its task interview first. -If init was invoked as a setup blocker by another command, such as `/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 project -## Step 2: Explore the codebase +Before asking, scan enough to avoid making the user repeat known facts: product docs and copy; package/config and app boundaries; features, workflows, routes, and roles; names, logos, legal/proof assets, and brand commitments; platform/accessibility signals; and the dev command/entry when live mode applies. -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: +Treat repository evidence as a hypothesis, not user approval. Note visual maturity without documenting, extending, or replacing the world. -- **README and docs**: Project purpose, target audience, any stated goals -- **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 -- **Any style guides or brand documentation** +Form a platform hypothesis: `web`, `ios`, `android`, or `adaptive` (one product that genuinely adapts its design language per OS). Mobile web remains `web`; a native wrapper around a website does not make its design language native. -Also form a **register hypothesis** from what you find: +## Step 3: Interview for product truth -- Brand signals: `/`, `/about`, `/pricing`, `/blog/*`, `/docs/*`, hero sections, big typography, scroll-driven sections, landing-page-shaped content. -- Product signals: `/app/*`, `/dashboard`, `/settings`, `/(auth)`, forms, data tables, side/top nav, app-shell components. +ask the user directly to clarify what you cannot infer. Ask only about material gaps the repository and original request do not answer with strong evidence. -Register is a hypothesis at this point, not a decision; Step 3 confirms it. +Use the structured question tool when available; otherwise ask and wait. Keep rounds to at most three focused questions and require one real answer or approval round before writing a new PRODUCT.md. Confirm inferences. -Also form a **platform hypothesis**: +Start with the unknowns that most change future product decisions: -- Native signals: React Native / Expo (`react-native`, `expo`), Flutter (`pubspec.yaml`, `flutter`), SwiftUI / UIKit (`.swift`, `.xcodeproj`, an `ios/` app target), Jetpack Compose / Android (`build.gradle`, an `android/` app module, `AndroidManifest.xml`). An `ios/` and/or `android/` directory that is a real app target, not just a Capacitor/Cordova wrapper around a website. -- Web signals (the default): a web framework (Vite, Next, Nuxt, SvelteKit, Astro), an HTML entry, a CSS/Tailwind setup, no native app target. +1. Who is the primary user, in what situation, and what job are they doing? +2. What does the product make possible, and what is its meaningfully different mechanism or position? +3. What durable constraints, assets, evidence, or product facts must future work preserve? -Values: `web` / `ios` / `android` / `adaptive` (one codebase, ships both, adapts per OS). Mobile web is still `web`. Like register, this is a hypothesis; Step 3 confirms it. +Confirm ambiguous platform separately. Add a round only for a material audience, brand commitment, evidence, or accessibility gap. Record undecided facts instead of inventing them. -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. +Do not ask for an aesthetic direction, emotional feel, visual references, colors, typography, or style during init. If the user volunteers a binding visual constraint, record it without expanding it. -## Step 3: Ask strategic questions (for PRODUCT.md) +### What belongs here -ask the user directly to clarify what you cannot infer. Ask about anything the codebase doesn't answer with strong, explicit evidence. +- users, jobs, workflows, purpose, success, positioning, and operating context; +- capabilities, constraints, terminology, evidence, platform, and accessibility; +- confirmed voice, assets, and brand commitments. -### Interview mode, not confirmation mode +### What does not belong here -If the repo is empty or the user's brief is sparse, run a short interview before proposing PRODUCT.md. Do **not** turn a one-sentence request into a complete inferred PRODUCT.md and ask for blanket confirmation. - -- Use the harness's structured question tool when one exists. Otherwise, ask directly in chat and stop: one question at a time, with lettered options where the crawl suggests likely answers, waiting for each answer before the next. -- Keep skill vocabulary (register, belief ladder, anti-references) out of question text; ask for the thing in words the user would use. For the brand register, ask like a magazine editor profiling the brand: curious and narrative, drawing out the story, the feel, and what a visitor should come to believe. -- Ask in focused rounds and wait for answers between them. Keep **one topic per question**; add rounds rather than fold several topics into one either-or choice. Options obey the same rule: an option answers only the question asked; never write a compound option that bundles a feeling with a business outcome or names an additional audience. -- Use inferred answers as hypotheses or options, not as finished facts. -- Complete at least one real user-answer round before drafting PRODUCT.md, unless every required answer is directly discoverable from repo docs. -- Round 1 should establish register, platform, users, purpose, positioning, and desired outcome. -- Round 2 should establish brand personality or references, anti-references, and accessibility needs, plus conversion & proof for Persuade surfaces. - -### Minimum viable interview - -Ask enough to complete PRODUCT.md. At minimum, cover register confirmation, **platform confirmation** (`web` / `ios` / `android` / `adaptive`), users, purpose, positioning, brand personality, anti-references, and accessibility needs (plus conversion & proof for Persuade surfaces) unless each answer is directly discoverable from repo context. Never let the template's default `web` stand unconfirmed for a native or cross-platform repo. After at least one interview round, you may propose inferred answers, but the user must confirm them before you write PRODUCT.md. Never synthesize PRODUCT.md from the original task prompt alone. - -### Register (ask first; it shapes everything below) - -Every design task serves one of four visitor modes (see SKILL.md's Registers): **Persuade** (marketing, landing, campaigns), **Experience** (portfolios, albums, bodies of work), **Operate** (app UI, admin, dashboards, tools), and **Read** (documentation, editorial, long-form). PRODUCT.md stores the two-value family that covers them: `brand` covers Persuade and Experience, `product` covers Operate and Read. The brief decides the exact mode per task; the stored register only sets the default family. - -If Step 2 produced a clear hypothesis, lead with it: *"From the codebase, this looks like a [brand / product] surface. Does that match your intent, or should we treat it differently?"* - -If the signal is genuinely split (e.g. a product with a big marketing landing), ask the user directly to clarify what you cannot infer. Ask which register describes the **primary** surface. The register can be overridden per task later, but PRODUCT.md carries one default. Settle the default before drafting any register-dependent questions; never batch Persuade-only questions (Conversion & proof) into the same round as the question that decides the register. - -### Platform (ask right after register) - -Every project targets **web** (includes responsive mobile web), **ios**, **android**, or **adaptive** (one codebase, ships both, adapts per OS: Flutter, React Native, KMP). Platform picks the native rulebook: HIG for `ios`, Material 3 for `android`, both for `adaptive`, none for `web`. - -If Step 2 produced a clear hypothesis, lead with it: *"From the codebase, this looks like a [web / ios / android / adaptive] project. Does that match?"* For cross-platform apps, decide by the **design language the app renders**, not the toolchain: one look on both platforms (Flutter's Material-everywhere default) takes that platform's value; genuine per-OS adaptation (Cupertino on iOS, Material on Android) is `adaptive`. When in doubt, `web`. - -A monorepo shipping both a website and a native app gets a PRODUCT.md per app, each with its own `## Platform`; the root PRODUCT.md carries the primary surface's platform. - -### Users & Purpose -- Who uses this? What's their context when using it? -- What job are they trying to get done? -- What is this for? A purpose stated in README or docs is a hypothesis, not strong evidence; confirm it, don't transcribe it. -- What does success look like? -- If more than one kind of user is plausible, confirm a primary and secondary audience; don't manufacture a split that isn't there. An audience implied by another answer (a success metric, a CTA) is still unconfirmed; ask before writing it as secondary. -- If the surface speaks to a different audience than the people who use the product, ask the user to name both. -- For brand: what emotions should the interface evoke? (confidence, delight, calm, urgency) Ask this standalone; don't fold emotions into the success question. -- For product: what workflow are they in? What's the primary task on any given screen? - -### Positioning -- In one line, what does this do that nothing else does? The single strategic claim every screen reinforces. - -### 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? - - 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? - -### Conversion & proof (Persuade surfaces only) - -Ask these only when the primary surface is a Persuade one (marketing, landing, campaigns). Experience and Read surfaces (portfolios, albums, long-form writing, documentation) get no CTA, belief-ladder, or proof questions even when the stored register is `brand`; skip this section and its PRODUCT.md counterpart. - -- What's the primary CTA? -- What's the secondary fallback, for visitors not ready for the primary? -- The one line a visitor should remember after 10 seconds. -- What must the visitor believe, in order, before taking the primary CTA? (The template's belief ladder.) -- What proof is on hand? Ask the user to hand over any testimonials, case studies, press, or client/partner logos they already have. If you can receive files directly, collect them; otherwise create `.impeccable/assets/proof/` and ask the user to add files there. Reference supplied files by path; record text proof inline. - -### Accessibility & Inclusion -- Specific accessibility requirements? (WCAG level, known user needs) -- Considerations for reduced motion, color blindness, or other accommodations? - -Skip questions where the answer is already clear. **Do NOT ask about colors, fonts, radii, or visual styling here.** Those belong in DESIGN.md, not PRODUCT.md. +- visual worlds, palettes, typography, components, or page concepts; +- visitor mode, narrative, CTA/proof sequence, or other surface strategy; +- invented testimonials, customers, benchmarks, pricing, licensing, or deployment claims; +- a requirement to decide every optional field. ## Step 4: Write PRODUCT.md -Write PRODUCT.md only after the user has confirmed the strategic answers from Step 3. If an inferred answer is uncertain or unconfirmed, ask before writing. Confirmed means what the user actually said yes to; do not pad a confirmed answer with extras they never picked (additional anti-references, audiences, roadmap claims, a WCAG level), whether drawn from the crawl, another answer, or your own option text. If an extra belongs in the doc, ask about it first. - -Synthesize into a strategic document: +Write only confirmed facts and explicitly marked open decisions. Omit irrelevant sections rather than filling them with generic prose. ```markdown # Product -## Register - -product - ## Platform web ## Users -[Who they are, their context, the job to be done. Primary audience; a secondary audience or a surface-vs-user split only when they apply.] +[Primary users, their situation, and job. Add other audiences only when confirmed.] ## Product Purpose -[What this product does, why it exists, what success looks like] +[What the product does, why it exists, and what success means.] ## Positioning -[The single strategic claim every screen reinforces. Not a visual rule, not an anti-reference.] +[The product mechanism or claim a neighboring product could not truthfully copy.] -## Conversion & proof -[Persuade surfaces only (marketing, landing, campaigns). Omit this section entirely, heading included, for the product register and for Experience or Read surfaces such as portfolios, albums, or long-form writing.] -- Primary and secondary CTA: [...] -- The line a visitor remembers after 10 seconds: [...] -- Belief ladder: [...] -- Proof on hand: [testimonials, case studies, press, or logos, referenced by path] +## Operating Context +[Workflows, environments, tools, documents, materials, and rituals that are factual parts of using or evaluating the product.] -## Brand Personality -[Voice, tone, 3-word personality, emotional goals] +## Capabilities and Constraints +[Confirmed functionality, technical constraints, terminology, and explicitly undecided product facts.] -## Anti-references -[What this should NOT look like. Specific bad-example sites or patterns to avoid.] +## Brand Commitments +[Existing name, voice, assets, personality, identity constraints, and references the user explicitly made binding. Omit when none exist.] -## Design Principles -[3-5 strategic principles derived from the conversation. Principles like "practice what you preach", "show, don't tell", "expert confidence". NOT visual rules like "use OKLCH" or "magenta accent".] +## Evidence on Hand +[Real content, data, demonstrations, testimonials, case studies, press, or assets, with paths where applicable. State absences that future work must not fabricate.] + +## Product Principles +[Three to five durable strategic principles derived from confirmed answers; no visual recipes.] ## Accessibility & Inclusion -[WCAG level, known user needs, considerations] +[Known user needs or required standard. Omit when no product-specific requirement was established.] ``` -Register is either `brand` or `product` as a bare value. No prose, no commentary. Platform is `web`, `ios`, `android`, or `adaptive`, also a bare value; omit the section only on legacy files you're leaving untouched, otherwise write `web` explicitly. +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. -Write fields as prose, and use bold sparingly: only where a word carries a decision, never as a label lead-in on every line. +### Completion gate -Write to `PROJECT_ROOT/PRODUCT.md`. If `.impeccable.md` existed, the loader already renamed it; merge into that content rather than starting from scratch. +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: Decide on DESIGN.md +## Step 5: Configure live mode when useful -Offer `/impeccable document` either way. Two paths: +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. -- **Code exists** (CSS tokens, components, a running site): "I can generate a DESIGN.md that captures your visual system (colors, typography, components) so variants stay on-brand. Want to do that now?" -- **Pre-implementation** (empty project): "I can seed a starter DESIGN.md from five quick questions about color strategy, type direction, motion energy, and references. You can re-run once there's code, to capture the real tokens. Want to do that now?" +## Step 6: Wrap up or resume -If the user agrees, delegate to `/impeccable document` (it auto-detects scan vs seed). Load its reference and follow that flow. +Summarize captured and deliberately undecided facts. Do not offer DESIGN.md merely because it is missing. -If the user prefers to skip, mention they can run `/impeccable document` any time later. +Recommend the next action from the actual project state: -## Step 6: Configure live mode (when code exists) +- Empty or early project: ask naturally for the surface to be built, or use `/impeccable shape ` when the user wants a confirmed brief without implementation. New-work will establish a visual world only when the requested work needs one. +- Existing coherent interface without DESIGN.md: `/impeccable document` if the user wants the incumbent system recorded independently of a new build. +- Existing surface needing work: name the most relevant scoped command. +- Web project ready for visual iteration: `/impeccable live` when configured. -**Skip this step when the platform is native** (`ios` / `android` / `adaptive`): live mode drives a browser overlay. A hybrid wrapper or Expo web target serving HTML doesn't change that. - -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 .kiro/skills/impeccable/scripts/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) and platform captured (web / ios / android / adaptive) -- 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 or live config is pending, one line on how to set it up later - -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 platform**, offer the 2-4 most relevant (not a menu dump), and give the exact command to type. Group by intent: - -- **Build something new**: `/impeccable craft ` (shape, then build end-to-end) or `/impeccable shape ` (plan first). Lead with this for empty or early-stage projects. -- **Improve what's there**: name the specific surface. `/impeccable critique ` for a scored UX review; `/impeccable audit ` for a11y / perf / responsive checks; `/impeccable polish ` 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** (web only): `/impeccable live` (configured in Step 6) to pick elements in the browser and generate variants in place. **Skip this group for native platforms.** - -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 the user directly to clarify what you cannot infer. Ask whether they'd like a brief summary of PRODUCT.md appended to .kiro/settings.json for easier agent reference. If yes, append a short **Design Context** pointer section there. +If init was invoked by another request, resume without rerunning context.mjs; new-work owns later visual decisions. diff --git a/.kiro/skills/impeccable/reference/ios.md b/.kiro/skills/impeccable/reference/ios.md index ef10ee0d2..ccef5d2c4 100644 --- a/.kiro/skills/impeccable/reference/ios.md +++ b/.kiro/skills/impeccable/reference/ios.md @@ -2,7 +2,7 @@ For native iOS / iPadOS apps: SwiftUI, UIKit, React Native, Expo, Flutter shipping to Apple hardware. -On native, register narrows. HIG conformance governs structure, navigation, and interaction whatever the register; brand expresses through the expressive layer the platform provides (tint, type, motion, content). Calm, Duolingo, and Spotify carry strong identity entirely inside HIG conventions. +On native, the visitor mode narrows what expression may override. HIG conformance governs structure, navigation, and interaction in every mode; brand expresses through the layer the platform leaves open (tint, type, motion, content). ## The iOS slop test diff --git a/.kiro/skills/impeccable/reference/layout.md b/.kiro/skills/impeccable/reference/layout.md index bce3d957d..a9b9882df 100644 --- a/.kiro/skills/impeccable/reference/layout.md +++ b/.kiro/skills/impeccable/reference/layout.md @@ -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. --- -## Register +## 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, register, 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 .kiro/skills/impeccable/scripts/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 `/impeccable polish` for the final pass. +When the structure holds, hand off to `/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) * )`: 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. diff --git a/.kiro/skills/impeccable/reference/live.md b/.kiro/skills/impeccable/reference/live.md index 00d1812c4..35afc30ca 100644 --- a/.kiro/skills/impeccable/reference/live.md +++ b/.kiro/skills/impeccable/reference/live.md @@ -33,7 +33,7 @@ Chat is overhead. No recap, no tutorial output, no pasting PRODUCT / DESIGN bodi node .kiro/skills/impeccable/scripts/live.mjs ``` -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. +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, DESIGN.md, and any surface brief already loaded by Setup in mind for variant generation: **DESIGN.md wins on visual decisions; PRODUCT.md wins on durable product and voice decisions; the surface brief wins on this surface's strategy.** 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 requires the user's explicit redesign/replacement intent. `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). @@ -187,7 +187,7 @@ All three carry `fallback: "agent-driven"`. Follow **Handle fallback** below. ### 3. Load the action's reference -If `event.action` is `impeccable` (the default freeform action), use SKILL.md's shared laws plus the loaded register reference (`brand.md` or `product.md`). Do not load a sub-command reference. **Freeform is not a pass to skip parameters:** you still follow the composition budget and the freeform bias in **§7 Parameters** below. Sub-command files list MUST-have signature knobs; freeform has no such file, so sizing knobs from surface weight and primary axes is entirely on you. +If `event.action` is `impeccable` (the default freeform action), use SKILL.md's shared laws and decide the visitor mode from the selected surface. Do not load a sub-command reference. **Freeform is not a pass to skip parameters:** you still follow the composition budget and the freeform bias in **§7 Parameters** below. Sub-command files list MUST-have signature knobs; freeform has no such file, so sizing knobs from surface weight and primary axes is entirely on you. Any other `event.action` (`bolder`, `quieter`, `distill`, `polish`, `typeset`, `colorize`, `layout`, `adapt`, `animate`, `delight`, `overdrive`): Read `reference/.md` before planning. Each sub-command encodes a specific discipline; skipping its reference produces generic output. Those files may require specific params; layer them on top of the §7 budget, not instead of it. @@ -216,7 +216,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. @@ -224,10 +224,7 @@ This sentence is the **identity lock**. Every variant must be readable as the sa **Default mode**: the existing identity is preserved. Variants vary expression axes within it. *This is the right mode for ~90% of live sessions.* The user picked an element on a real product they're shipping; they expect variants of *their* hero, not three different brands' heroes. -**Departure mode**: the existing identity is rejected. Variants propose alternatives consistent with PRODUCT.md voice. Trigger only when at least one is true: - -- PRODUCT.md anti-references explicitly call out the current surface ("the current `index.html` is itself an example"; "diffuse away from this"; "the page on screen is the failure"). Generic anti-references that describe what to avoid in general do **not** trigger departure mode; only ones that point at *this* surface specifically. -- The user's freeform prompt explicitly asks for departure ("rebuild this from scratch", "what if it weren't editorial at all", "show me something completely different"). +**Departure mode**: the existing identity is rejected. Variants propose alternatives consistent with durable product and brand truth. Trigger only when the user explicitly asks for departure in the current request or freeform prompt ("redesign this", "rebuild this from scratch", "what if it weren't editorial at all", "show me something completely different"). A stale page critique or an old task note is not replacement authorization. If you're unsure, you're in default mode. The cost of being wrong about default is "three on-brand variants with similar feel": recoverable, the user picks none. The cost of being wrong about departure is "three off-brand variants": unrecoverable, the user is annoyed. @@ -246,13 +243,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 the brand's stated voice and register in PRODUCT.md. Do NOT pick from a fixed catalog of lane categories. The right three directions for this brand are not the same as the right three for another brand, 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. @@ -262,7 +259,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. @@ -289,7 +286,7 @@ In **default mode**, the prompt narrows the axes you choose, not the identity. * In **departure mode**, the prompt narrows the lanes you draw from, not the families. *"Make it feel like a newspaper front page"* would itself be a departure-mode prompt; honor it but pick three meaningfully different newspaper-adjacent lanes (broadsheet vs. tabloid vs. trade journal), and run the family pass to confirm they don't collapse into one. -When the prompt and PRODUCT.md anti-references conflict (the prompt asks for X, the anti-references ban X), the anti-references win; they describe the brand's standing position, the prompt is one moment. +When the prompt conflicts with a confirmed binding brand commitment or DESIGN.md invariant, preserve the invariant unless the user explicitly revokes or replaces it. Task-local strategy from the matching surface brief may change when the user changes that surface's goal. ### 6. Write all variants in a single edit diff --git a/.kiro/skills/impeccable/reference/new-work.md b/.kiro/skills/impeccable/reference/new-work.md index 5304451f9..8d6770823 100644 --- a/.kiro/skills/impeccable/reference/new-work.md +++ b/.kiro/skills/impeccable/reference/new-work.md @@ -1,47 +1,178 @@ -# New identity work +# New visual work -Read this only when the project has no committed visual identity, or the user explicitly asked to replace the current one. A missing DESIGN.md is not enough: code, tokens, chosen type, components, and assets are incumbent design authority. If they exist, preserve and extend them unless the brief says to discard them. +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. -This playbook is task-scoped. PRODUCT.md owns durable product facts and DESIGN.md owns durable visual invariants; the direction for one page or feature belongs in the current task, not in either document. +## 1. Name the intent -## Choose the authority +- **Greenfield:** no coherent visual implementation. +- **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. -Classify the work before inventing anything: +A plain “redesign this page/site” authorizes replacement. “Redesign this within the current brand/system” means extension or refinement. Ask once only when the wording is genuinely ambiguous. -- **Extend**: an incumbent system exists and the task adds or improves a surface. Preserve its lineage, semantics, and affordances. -- **Surface redesign**: the current identity remains, but this surface may change its topology, hierarchy, and expression. -- **New identity**: nothing committed exists, or the user explicitly authorized a rebrand. +## 2. Resolve visual authority -When the evidence is mixed, preserve. Novelty is not permission to erase authored decisions. +Read DESIGN.md and representative code, tokens, components, and assets. Choose one path: -## Find the concept +### A. Explicit redesign -Name the subject, audience, page job, and visitor mode from SKILL.md. Then identify the product's unique mechanism: what it does, proves, or enables that a neighboring product cannot truthfully claim. +The old DESIGN.md and implementation are not authority. Keep only unrevoked product facts, content, function, native expectations, constraints, and brand commitments. Establish a replacement world. -Derive the concept from that mechanism through one primary transformation: information topology, evidence, temporal behavior, or spatial mapping. The transformation must change how the surface is organized or behaves, not merely how it is decorated. This produces stranger and more defensible work than imitating a culturally specific document or object. Borrowing a literal cultural form is allowed only when it is genuinely native to the product and audience, and never as costume. +### B. DESIGN.md covers this kind of surface -For **Operate** and **Read**, start with the task and information structure. Native controls remain native controls; expression comes from hierarchy, density, rhythm, state, and the system around them. For **Persuade** and **Experience** creating a new identity, explore more freely, but keep the product legible and the primary action obvious. +Use its invariants and normative tokens. Skip world-building and discover the surface. -Run `node .kiro/skills/impeccable/scripts/concept-seed.mjs` only for Persuade or Experience new-identity work when two or more directions remain equally credible. It is an entropy source, not an authority: never let the draw overrule the strongest fit. When color is genuinely unconstrained, `node .kiro/skills/impeccable/scripts/palette.mjs` may break a reflex palette; subject evidence and incumbent color still win. +### C. A coherent implementation exists but DESIGN.md does not -## State the direction +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. -Before code, write a task-scoped direction contract of at most 120 words in your reasoning: +### D. The brand exists, but a whole-surface family is unresolved -- the thesis this surface owns; -- the structural or behavioral transformation; -- what authority is preserved; -- the first viewport's composition and primary action; -- the signature element the surface will be remembered by. +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. -Do not embed this contract as a comment in the production artifact or turn it into a second design system. In an attended substantial build, confirm it once. In an unattended run, record it and continue. +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. -## Build once, commit fully +### E. No confirmed visual authority exists -Plan one compact token system and one layout source: the concept or the content's real structure. Build the strongest coherent direction once. Commitment means the transformation governs the whole surface; it does not mean every control must be rebuilt as a metaphor. +Establish a world. Scaffolds, framework defaults, and stray utilities are not identity. -Make the first viewport demonstrate the product's mechanism. Pace long surfaces through contrast in density and scale, and cut sections that only repeat claims. Briefs that depend on imagery ship real, verified imagery. Preserve semantic HTML, familiar interaction behavior, accessibility, and the project's technical conventions. +## 3. Discover the requested surface -## Finish proportionally +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. -Inspect desktop and mobile, write one honest critique against the brief and direction, patch material defects, then run the detector once. Repeat only when a real defect remains. A separate reviewer is optional when the harness already provides one and the extra cost is earned by the risk; it is not a default step and its presence is not evidence of quality. +Ask one attended round of at most three material questions without repeating durable facts. CTA hierarchy, proof sequence, content gaps, and interaction outcomes belong here, not PRODUCT.md. For a fully specified narrow request, state the interpretation and invite correction. + +When `shape` has already completed its discovery interview, reuse those confirmed answers and do not ask this round again. + +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. Each candidate joins a durable visual system (identity thesis, information/layout grammar, material and type behavior, color strategy, imagery, motion, reusable signature) to a concrete expression for this surface (topology, sequence, focal moment, and implementation consequence). Do not rank yet. +3. **Break the ranking rut once.** Run `node .kiro/skills/impeccable/scripts/concept-seed.mjs --scope direction`. Promote the assigned grounded pair and weigh its challengers only when they can become both a coherent system and a strong solution to this task. +4. **Test coupling and breadth.** Reject a pair if its surface could swap into another candidate unchanged, or if its world is merely the surface motif repeated. Test each survivor on the requested surface plus navigation, quiet and dense content, interaction/state, and one unlike future surface. Compare it with both the category habit and predictable contrarian response. +5. **Offer coupled choices.** Present two or three materially different pairs without recommendation cues. For each, show the world rules, this surface's 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. 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 .kiro/skills/impeccable/scripts/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 promoted candidate when a roll ran; otherwise use the strongest grounded structure. It must survive both tests. + +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). + +### Write or update DESIGN.md + +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: + +`` + +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 .kiro/skills/impeccable/scripts/surface-brief.mjs read ` + +Exit 0: preserve still-valid decisions and change only what the user changed. For redesign, retain valid product strategy, content, function, and open decisions; replace visual direction and contract. Exit 2: no brief. After the contract, write with `node .kiro/skills/impeccable/scripts/surface-brief.mjs write [related-target ...]`. + +The body is concise and contains: + +```markdown +# Surface brief: [name] + +## Scope +[Primary/related route or artifact, visitor mode, and what this surface owns.] + +## Product strategy +[Surface-specific audience and job, desired outcome, primary/secondary action or task, content and proof sequence, factual constraints, and explicitly open decisions.] + +## Selected direction +[Reference to the applicable DESIGN.md world or expression range, selected surface concept, focal moment, narrative/interaction sequence, and implementation consequence.] + +## Direction contract +[The six contract blocks below.] + +## Open decisions +[Only unresolved items that later work must not silently invent. Omit when empty.] +``` + +Commit `.impeccable/surfaces/.md` as stable later-work context. Exclude global truth, exact tokens, transient notes, and work logs. + +## 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. + +Before code, write a direction contract of at most 150 words in an opening HTML or framework comment. The first 200 characters must name `DIRECTION CONTRACT`. + +- `UNIQUE`: the task thesis tied to the product mechanism; +- `NOT-TEMPLATE`: the category-default arrangement this structure refuses; +- `OWN-WORLD`: the confirmed DESIGN.md invariants, tokens, and materials it uses; +- `STORY`: what the visitor understands, believes, and does; +- `FIRST VIEWPORT`: exact composition, hierarchy, action, and where the concept exceeds competent convention; +- `FORM`: chosen structure or behavior, signature, implementation consequence, and seed key. + +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. + +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. + +## 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. + +Build the strongest coherent direction once. Its grammar governs navigation, actions, controls, content, and transitions without disguising affordances. Give the focal form the scale that gives it force; do not trap it inside a standard hero panel. + +**Make the opening a thesis.** Demonstrate the mechanism immediately; leave an idea, interaction, or evidence, not merely mood. + +**Commit before correcting.** Land the hard move at full strength before refining it. In unattended work, safety is the known risk. + +**Commit at page scale.** Let color, material, image, or type own a region when the world calls for it. Scattered signature decoration is not commitment. + +**Pace the whole surface.** Vary density, scale, image, motion, and quiet inside one grammar. Cut repeated claims; prove the mechanism with real artifact, interaction, data, transformation, or content. + +**Author motion as material.** Motion expresses the world and task. Premium moments go beyond repeated transform/opacity through earned focus, depth, masks, continuity, light, or material change. Bound expensive effects, test in-browser, keep content visible by default, and design reduced motion. + +Preserve semantics, affordances, accessibility, performance, responsiveness, and project conventions. Operate/Read express through topology, hierarchy, density, rhythm, and state; Persuade/Experience may earn drama. + +## 8. Solidify the visual record + +After first implementation of a new/replacement world or approved expansion, refresh DESIGN.md from the build: + +- replace provisional direction with the exact type, color roles, tokens, spacing/radii, components, states, and motion that survived; +- add normative YAML tokens only for values the implementation actually uses; +- remove the directional-seed status once the record and implementation agree; +- preserve broader world invariants and expression ranges; +- do not promote the task's story, hero composition, or one-off motif into a global rule unless it is intentionally reusable. + +Ordinary extension does not rewrite DESIGN.md; only approved durable changes do. + +## 9. Finish like a 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. diff --git a/.kiro/skills/impeccable/reference/operate.md b/.kiro/skills/impeccable/reference/operate.md index 04ee9d7d2..f420cbaa2 100644 --- a/.kiro/skills/impeccable/reference/operate.md +++ b/.kiro/skills/impeccable/reference/operate.md @@ -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. - No orchestrated page-load sequences. Product loads into a task; users don't want to watch it load. -## Product bans (on top of the shared absolute bans) +## Product constraints - Decorative motion that doesn't convey state. - Inconsistent component vocabulary across screens. If the "save" button looks different in two places, one is wrong. @@ -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. diff --git a/.kiro/skills/impeccable/reference/polish.md b/.kiro/skills/impeccable/reference/polish.md index 5ac13d316..82e0de6a9 100644 --- a/.kiro/skills/impeccable/reference/polish.md +++ b/.kiro/skills/impeccable/reference/polish.md @@ -1,241 +1,98 @@ -> **Additional context needed**: quality bar (MVP vs flagship). +> **Additional context needed**: quality bar and shipping constraints. -Perform a meticulous final pass to catch all the small details that separate good work from great work. The difference between shipped and polished. +Polish is refinement, never concealed redesign. Preserve the incumbent visual world, content, behavior, and everything outside scope. If the concept itself is wrong, say so and recommend redesign or `bolder` instead of smuggling in a replacement. -Detector and automated QA output are defect evidence only. A clean script result is never proof that the design is strong; gather browser evidence and inspect the real interaction path. +A detector result is defect evidence, not proof of quality. Inspect the rendered experience and real interaction path. -## Design System Discovery +## 1. Establish the system -Aligning the feature to the design system is **not optional**. Polish without alignment is decoration on top of drift, and it makes the next person's job harder. Discovery comes before any other polish work. +Read DESIGN.md and representative tokens, shared components, patterns, and neighboring flows. If no formal system exists, use coherent project conventions. -1. **Find the design system**: Search for design system documentation, component libraries, style guides, or token definitions. Study the core patterns: design principles, target audience, color tokens, spacing scale, typography styles, component API, motion conventions. -2. **Note the conventions**: How are shared components imported? What spacing scale is used? Which colors come from tokens vs hard-coded values? What motion and interaction patterns are established? What flow shapes are used for comparable actions (modal vs full-page, inline vs route, save-on-blur vs explicit submit)? -3. **Identify drift, then name the root cause**: For every deviation, classify it as a **missing token** (the value should exist in the system but doesn't), a **one-off implementation** (a shared component already exists but wasn't used), or a **conceptual misalignment** (the feature's flow, IA, or hierarchy doesn't match neighboring features). The fix differs by category: patch the value, swap to the shared component, or rework the flow. Fixing the symptom without naming the cause is how drift compounds. +Classify each drift before fixing it: -If a design system exists, polish **must** align the feature with it. If none exists, polish against the conventions visible in the codebase. **If anything about the system is ambiguous, ask. Never guess at design system principles.** +- **missing token:** the system needs a reusable value; +- **one-off implementation:** an existing shared component or pattern should replace it; +- **conceptual mismatch:** the flow, information architecture, or hierarchy differs from comparable product areas; +- **local defect:** the implementation is simply incomplete or inconsistent. -## Pre-Polish Assessment +Fix the cause at the narrowest correct level. Ask when a binding system principle cannot be inferred. -Understand the current state and goals before touching anything: +## 2. Gather the evidence -1. **Review completeness**: - - Is it functionally complete? - - Are there known issues to preserve (mark with TODOs)? - - What's the quality bar? (MVP vs flagship feature?) - - When does it ship? (How much time for polish?) +Use the feature yourself at representative desktop and mobile sizes. Determine: -2. **Think experience-first**: Who actually uses this, and what's the best possible experience for them? Effective design beats decorative polish; a feature that looks beautiful but fights the user's flow is not polished. Walk the path from their perspective before opening DevTools. +- whether the path is functionally complete; +- the intended quality bar and time available; +- known constraints or deliberately unfinished work; +- the states, content lengths, roles, and input methods users will actually encounter. -3. **Identify polish areas**: - - Visual inconsistencies - - Spacing and alignment issues - - Interaction state gaps - - Copy inconsistencies - - Edge cases and error states - - Loading and transition smoothness - - Information architecture and flow drift (does this feature reveal complexity the way neighboring features do?) +If a prior critique exists, use it as one input: -4. **Pull in any prior critique** (optional signal): If `/impeccable critique` has been run on the same target, its priority issues are a useful prior for what to address first. Resolve the target to a file path or URL, then: - ```bash - slug=$(node .kiro/skills/impeccable/scripts/critique-storage.mjs slug "") - node .kiro/skills/impeccable/scripts/critique-storage.mjs latest "$slug" - ``` - Exit 0 with body = found; fold the P0/P1 items into your polish list and mention the snapshot path so the user sees what you read. Exit 2 = no snapshot, continue without it. The critique is one input among many. Do your own pass either way. +```bash +slug=$(node .kiro/skills/impeccable/scripts/critique-storage.mjs slug "") +node .kiro/skills/impeccable/scripts/critique-storage.mjs latest "$slug" +``` -5. **Triage cosmetic vs functional**: Classify each issue as **cosmetic** (looks off, doesn't impede the user) or **functional** (breaks, blocks, or confuses the experience). When polish time is tight, functional issues ship first; cosmetic ones can land in a follow-up. Quality should be consistent; never perfect one corner while leaving another rough. +Exit 0 returns the latest snapshot; incorporate relevant P0/P1 findings and name the snapshot read. Exit 2 means none exists. Perform an independent pass either way. -**CRITICAL**: Polish is the last step, not the first. Don't polish work that's not functionally complete. +## 3. Triage -## Polish Systematically +Separate functional defects from cosmetic ones and fix in this order: -Work through these dimensions methodically: +1. broken or blocked tasks, data loss, misleading state, and inaccessible paths; +2. missing loading, empty, error, success, disabled, and permission states; +3. flow, hierarchy, responsive, and design-system drift; +4. visual and motion inconsistencies; +5. code and asset cleanup. -### Visual Alignment & Spacing +Do not perfect one corner while leaving the rest below the same quality bar. -- **Pixel-perfect alignment**: Everything lines up to grid -- **Consistent spacing**: All gaps use spacing scale (no random 13px gaps) -- **Optical alignment**: Adjust for visual weight (icons may need offset for optical centering) -- **Responsive consistency**: Spacing and alignment work at all breakpoints -- **Grid adherence**: Elements snap to baseline grid +## 4. Polish the whole path -**Check**: -- Enable grid overlay and verify alignment -- Check spacing with browser inspector -- Test at multiple viewport sizes -- Look for elements that "feel" off +### Flow and hierarchy -### Information Architecture & Flow +- Match neighboring mental models, terminology, disclosure, routing, save behavior, and optimistic or pessimistic patterns. +- Make the primary task and current state obvious without flattening every element to equal weight. +- Ensure arrival, transition, empty, and recovery paths connect instead of behaving as isolated screens. -Visual polish on a misshapen flow is wasted work. Match the *shape* of the experience to the system, not just the surface. +### Layout and type -- **Progressive disclosure**: Match how much is revealed when, compared to neighboring features. A settings page exposing 40 fields when the rest of the app reveals 5 at a time is drift, even if every field is perfectly styled. -- **Established user flows**: Multi-step actions follow the same shape as comparable flows elsewhere: modal vs full-page, inline edit vs separate route, save-on-blur vs explicit submit, optimistic vs pessimistic updates. -- **Hierarchy & complexity**: The same conceptual weight gets the same visual weight throughout. Primary actions don't become tertiary in one corner of the product, and tertiary actions don't shout. -- **Empty, loading, and arrival transitions**: How content arrives, updates, and leaves matches how it does in adjacent features. -- **Naming and mental model**: The feature uses the same nouns and verbs as the rest of the system. A "Workspace" here shouldn't be a "Project" three screens away. +- Align to the project's grid and spacing scale; fix optical as well as mathematical alignment. +- Group related content tightly and separate distinct groups generously. +- Keep same-role typography consistent; test measure, wrapping, localization expansion, zoom, and font loading. +- Verify every supported viewport rather than correcting only the current screenshot. -### Typography Refinement +### Color, imagery, and icons -- **Hierarchy consistency**: Same elements use same sizes/weights throughout -- **Line length**: 45-75 characters for body text -- **Line height**: Appropriate for font size and context -- **Widows & orphans**: No single words on last line -- **Hyphenation**: Appropriate for language and column width -- **Kerning**: Adjust letter spacing where needed (especially headlines) -- **Font loading**: No FOUT/FOIT flashes +- Use semantic tokens and stable color meanings across themes. +- Verify text, control, and focus contrast in every state. +- Keep icon families, stroke/weight, sizing, and optical alignment coherent. +- Prevent image layout shift; use correct aspect ratios, responsive sources, and useful alt text. -### Color & Contrast +### Interaction and state -- **Contrast ratios**: All text meets WCAG standards -- **Consistent token usage**: No hard-coded colors, all use design tokens -- **Theme consistency**: Works in all theme variants -- **Color meaning**: Same colors mean same things throughout -- **Accessible focus**: Focus indicators visible with sufficient contrast -- **Gray on color**: Never put gray text on colored backgrounds; use a shade of that color or transparency +- Every control needs appropriate default, hover, focus, active, disabled, loading, error, and success behavior. +- Preserve visible keyboard focus, logical tab order, labels, and platform-appropriate touch targets. +- Keep motion coherent, interruptible, performant, and reduced-motion aware. Do not add animation merely to make polish visible. +- Validate long, missing, localized, offline, slow, and permission-limited content where the product can encounter it. -### Interaction States +### Content and code -Every interactive element needs all states: +- Keep terminology, capitalization, punctuation, and factual copy consistent. Ask before changing claims. +- Remove debug output, dead code, unused imports, obsolete styles, and polish-created duplication. +- Replace custom implementations with shared components where the system owns the pattern. +- Promote genuinely reusable values to tokens; do not create a system abstraction for one local exception. -- **Default**: Resting state -- **Hover**: Subtle feedback (color, scale, shadow) -- **Focus**: Keyboard focus indicator (never remove without replacement) -- **Active**: Click/tap feedback -- **Disabled**: Clearly non-interactive -- **Loading**: Async action feedback -- **Error**: Validation or error state -- **Success**: Successful completion +## 5. Verify and finish -**Missing states create confusion and broken experiences**. +Walk the complete path again with mouse, keyboard, and touch where applicable. Check: -### Micro-interactions & Transitions +- mobile, intermediate, and wide layouts; +- loading, empty, error, success, disabled, long-content, and missing-content states; +- zoom, contrast, focus, semantics, screen-reader names, and reduced motion; +- console errors, layout shift, interaction latency, image loading, and supported browsers; +- agreement with DESIGN.md, neighboring features, and the user's scope. -- **Smooth transitions**: All state changes animated appropriately (150-300ms) -- **Consistent easing**: Use ease-out-quart/quint/expo for natural deceleration. Never bounce or elastic; they feel dated. -- **No jank**: Smooth animations; use atmospheric blur/filter/mask/shadow effects when they add polish, but bound expensive paint areas and avoid casual layout-property animation -- **Appropriate motion**: Motion serves purpose, not decoration -- **Reduced motion**: Respects `prefers-reduced-motion` +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. -### Content & Copy - -- **Consistent terminology**: Same things called same names throughout -- **Consistent capitalization**: Title Case vs Sentence case applied consistently -- **Grammar & spelling**: No typos -- **Appropriate length**: Not too wordy, not too terse -- **Punctuation consistency**: Periods on sentences, not on labels (unless all labels have them) - -### Icons & Images - -- **Consistent style**: All icons from same family or matching style -- **Appropriate sizing**: Icons sized consistently for context -- **Proper alignment**: Icons align with adjacent text optically -- **Alt text**: All images have descriptive alt text -- **Loading states**: Images don't cause layout shift, proper aspect ratios -- **Retina support**: 2x assets for high-DPI screens - -### Forms & Inputs - -- **Label consistency**: All inputs properly labeled -- **Required indicators**: Clear and consistent -- **Error messages**: Helpful and consistent -- **Tab order**: Logical keyboard navigation -- **Auto-focus**: Appropriate (don't overuse) -- **Validation timing**: Consistent (on blur vs on submit) - -### Edge Cases & Error States - -- **Loading states**: All async actions have loading feedback -- **Empty states**: Helpful empty states, not just blank space -- **Error states**: Clear error messages with recovery paths -- **Success states**: Confirmation of successful actions -- **Long content**: Handles very long names, descriptions, etc. -- **No content**: Handles missing data gracefully -- **Offline**: Appropriate offline handling (if applicable) - -### Responsiveness - -- **All breakpoints**: Test mobile, tablet, desktop -- **Touch targets**: 44x44px minimum on touch devices -- **Readable text**: No text smaller than 14px on mobile -- **No horizontal scroll**: Content fits viewport -- **Appropriate reflow**: Content adapts logically - -### Performance - -- **Fast initial load**: Optimize critical path -- **No layout shift**: Elements don't jump after load (CLS) -- **Smooth interactions**: No lag or jank -- **Optimized images**: Appropriate formats and sizes -- **Lazy loading**: Off-screen content loads lazily - -### Code Quality - -- **Remove console logs**: No debug logging in production -- **Remove commented code**: Clean up dead code -- **Remove unused imports**: Clean up unused dependencies -- **Consistent naming**: Variables and functions follow conventions -- **Type safety**: No TypeScript `any` or ignored errors -- **Accessibility**: Proper ARIA labels and semantic HTML - -## Polish Checklist - -Go through systematically: - -- [ ] Aligned to the design system (drift named and resolved by root cause) -- [ ] Information architecture and flow shape match neighboring features -- [ ] Visual alignment perfect at all breakpoints -- [ ] Spacing uses design tokens consistently -- [ ] Typography hierarchy consistent -- [ ] All interactive states implemented -- [ ] All transitions smooth (60fps) -- [ ] Copy is consistent and polished -- [ ] Icons are consistent and properly sized -- [ ] All forms properly labeled and validated -- [ ] Error states are helpful -- [ ] Loading states are clear -- [ ] Empty states are welcoming -- [ ] Touch targets are 44x44px minimum -- [ ] Contrast ratios meet WCAG AA -- [ ] Keyboard navigation works -- [ ] Focus indicators visible -- [ ] No console errors or warnings -- [ ] No layout shift on load -- [ ] Works in all supported browsers -- [ ] Respects reduced motion preference -- [ ] Code is clean (no TODOs, console.logs, commented code) - -**IMPORTANT**: Polish is about details. Zoom in. Squint at it. Use it yourself. The little things add up. - -Sweat the details. Zoom in until the alignment is right and the spacing reads as deliberate. Then ship. - -**NEVER**: -- Polish before it's functionally complete -- Polish without aligning to the design system; that's decoration on drift -- Guess at design system principles instead of asking when something is ambiguous -- Spend hours on polish if it ships in 30 minutes (triage) -- Introduce bugs while polishing (test thoroughly) -- Ignore systematic issues (if spacing is off everywhere, fix the system, not just one screen) -- Perfect one thing while leaving others rough (consistent quality level) -- Create new one-off components when design system equivalents exist -- Hard-code values that should use design tokens -- Introduce new patterns or flows that diverge from established ones - -## Final Verification - -Before marking as done: - -- **Use it yourself**: Actually interact with the feature. -- **Test on real devices**: Not just browser DevTools. -- **Ask someone else to review**: Fresh eyes catch things. -- **Compare to design**: Match intended design. -- **Check all states**: Don't just test happy path. -- **Treat automation carefully**: Run detector or QA commands when they are available and relevant, fix their defects, but never cite a clean result as proof that the work is polished. - -## Clean Up - -After polishing, ensure code quality: - -- **Replace custom implementations**: If the design system provides a component you reimplemented, switch to the shared version. -- **Remove orphaned code**: Delete unused styles, components, or files made obsolete by polish. -- **Consolidate tokens**: If you introduced new values, check whether they should be tokens. -- **Verify DRYness**: Look for duplication introduced during polishing and consolidate. +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. diff --git a/.kiro/skills/impeccable/reference/quieter.md b/.kiro/skills/impeccable/reference/quieter.md index 55b00b6f3..bb163e40f 100644 --- a/.kiro/skills/impeccable/reference/quieter.md +++ b/.kiro/skills/impeccable/reference/quieter.md @@ -2,7 +2,7 @@ Quiet design is harder than bold design. Subtlety needs precision. Reduce visual --- -## Register +## Visitor mode Persuade + Experience: "quieter" means more restrained palette, more whitespace, more typographic air. Drama is reduced, not eliminated; the POV stays intact. diff --git a/.kiro/skills/impeccable/reference/routing.md b/.kiro/skills/impeccable/reference/routing.md index 4bbd37a33..26efca80e 100644 --- a/.kiro/skills/impeccable/reference/routing.md +++ b/.kiro/skills/impeccable/reference/routing.md @@ -11,7 +11,7 @@ Reason over the signals; there is no score to obey: - `critique.latest` with a low `score` or non-zero `p0` / `p1` → `polish` (it reads that snapshot as its backlog), or re-run `critique` if the snapshot looks stale. - `git.changedFiles` pointing at one surface → scope `audit` or `polish` to those files specifically, naming them. - `devServer.running` true → `live` is available for in-browser iteration; if false, don't lead with `live`. **`live` and the bundled `detect.mjs` are web-only.** If `setup.platform` is `ios`, `android`, or `adaptive`, don't lead with either; the browser overlay and the HTML rule engine don't apply to native app code. -- Otherwise group by intent exactly as init's "Recommend starting points" step does (build new / improve what's there / iterate visually), tailored to `setup.register`. +- Otherwise group by intent (build new / improve what's there / iterate visually), tailored to the current surface and `setup.platform`. **If `scan.targets` is non-empty and `setup.platform` is not `ios`/`android`/`adaptive`, run `node .kiro/skills/impeccable/scripts/detect.mjs --json ` once** (the bundled detector over local files: no network, no npx; it reads HTML/CSS, so skip it for native projects). `scan.via` tells you what they are: `git-changes` (the markup/style files in your dirty tree, the most relevant set), `source-dir` (e.g. `src`, `app`), `html`, or `root`. Fold the hits into your picks: many quality / contrast hits → `audit` or `polish`; a specific slop family → the matching command (gradient text or eyebrows → `quieter` / `typeset`, flat or gray palette → `colorize`, and so on). It's a real, current signal that beats guessing. If detect errors or the tree is large and slow, skip it and recommend the user run `audit` themselves; never block the suggestion on it. diff --git a/.kiro/skills/impeccable/reference/shape.md b/.kiro/skills/impeccable/reference/shape.md index 40804f6e4..acbb26e5b 100644 --- a/.kiro/skills/impeccable/reference/shape.md +++ b/.kiro/skills/impeccable/reference/shape.md @@ -1,167 +1,61 @@ -Shape the UX and UI for a feature before any code is written. This command produces a **design brief**: a structured artifact that guides implementation through discovery, not guesswork. +# Shape -**Scope**: Design planning only. This command does NOT write code. It produces the thinking that makes code good. +Discover what should be made and how it should work, then return a confirmed design brief without code. -**Output**: A design brief that can be handed off to /impeccable craft, or directly to /impeccable for freeform implementation. When visual direction probes are used, the images are supporting artifacts, not the primary output. +**Product gate:** when context reports that PRODUCT.md is missing, load and complete [init.md](init.md) before Phase 1. Do not substitute shape questions for the product interview. Once PRODUCT.md exists, return here; product context does not replace task discovery. -## Philosophy +## Phase 1: Discovery interview -Most AI-generated UIs fail not because of bad code, but because of skipped thinking. They jump to "here's a card grid" without asking "what is the user trying to accomplish?" This command inverts that: understand deeply first, so implementation is precise. +Do not write code or choose visual direction yet. -## Phase 1: Discovery Interview +### Cadence -**Do NOT write any code or make any design decisions during this phase.** Your only job is to understand the feature deeply enough to make excellent design decisions later. +- Use the structured question tool when available; otherwise ask and stop. +- Ask two or three related questions per round, then wait. One round is the default; add a second only when the answers expose a material gap. +- Do not dump a questionnaire, repeat settled facts, or turn obvious facts into menus. Assert the likely reading and invite correction. +- A sparse prompt requires at least one answer round. A precise prompt may need only a compact confirmation. -**Unattended runs:** when no user can respond (a one-shot task, an automated run, or an explicit instruction not to ask questions), answer the interview questions yourself from the brief, record the answers as the design brief, and continue without pausing at any confirmation gate in this file. The thinking is still required; only the waiting is waived. +### Round 1: purpose, people, and outcome -This is a required interaction, not optional guidance. Ask these questions in conversation, adapting based on answers. Don't dump them all at once; have a natural dialogue. ask the user directly to clarify what you cannot infer. +Choose the two or three questions that most change the result: -### Interview cadence +- What is this surface or feature for, and what problem must it solve? +- Who specifically reaches it, in what situation and state of mind? +- What is the primary thing they must understand or do? What would success look like? +- What is uniquely true here that a neighboring product or generic template could not claim? -Discovery includes at least one user-answer round unless PRODUCT.md, DESIGN.md, or an already-confirmed brief directly answers the needed inputs. With a sparse prompt, do **not** synthesize a complete brief for confirmation on the first response. +### Round 2: material, behavior, and boundaries -- Use the harness's structured question tool when one exists. Otherwise, ask directly in chat and stop. -- Ask **2-3 questions per round**, then wait for answers. -- Treat PRODUCT.md and DESIGN.md as anchors; they reduce repeated questions but do **not** replace shape for craft. Shape is task-specific. -- One round is the default. Add a second only if the first answers leave material gaps. Don't run a second round just to feel thorough. -- Round 1 should clarify purpose, audience/context, content/scope, and (for brand) visual direction. -- Round 2, when needed, fills in whatever's still genuinely missing. +Run only for material unresolved decisions: -**Assert-then-confirm, not menu-with-escape.** When PRODUCT.md and the user's prompt make one option obvious, name it and ask the user to confirm or override. Don't enumerate "Restrained / Committed / Or something else?" as a real choice; "This reads as Restrained, confirm?" beats a four-option menu when the answer is already clear. +- What real content, evidence, data, and assets must the experience carry? What are realistic minimum, typical, and maximum ranges? +- Which states and transitions matter: first-run, empty, loading, error, success, permissions, overflow, or expert use? +- What is the intended fidelity, breadth, and interactivity: exploration, production-ready screen, full flow, or broader surface? +- What must remain untouched? What would make the result feel wrong even if it looked polished? +- Which platform, framework, performance, accessibility, localization, or delivery constraints are binding? -### Purpose & Context -- What is this feature for? What problem does it solve? -- Who specifically will use it? (Not "users"; be specific: role, context, frequency) -- What does success look like? How will you know this feature is working? -- What's the user's state of mind when they reach this feature? (Rushed? Exploring? Anxious? Focused?) +Never ask for CSS values or canned aesthetic lanes. New-work owns visual-world and concept choices. -### Content & Data -- What content or data does this feature display or collect? -- What are the realistic ranges? (Minimum, typical, maximum, e.g., 0 items, 5 items, 500 items) -- What are the edge cases? (Empty state, error state, first-time use, power user) -- Is any content dynamic? What changes and how often? -- What visual assets are real content here? Note required images, product shots, illustrations, maps, textures, diagrams, generated objects, or existing project assets. +## Phase 2: Resolve the design direction -### Design Direction +For new surfaces, brand expansion, or replacement, follow [new-work.md](new-work.md) through visual authority, any world workshop, and concept choice. Reuse discovery, then return before its contract, persistence, or implementation. Inside an established world, use its concept process only when composition or interaction remains materially open. -Force a visual decision on three fronts. Skip anything PRODUCT.md or DESIGN.md already answers; ask only what's missing. +## Phase 3: Write the brief -- **Color strategy for this surface.** Pick one: Restrained / Committed / Full palette / Drenched. Can override the project default if the surface earns it (e.g. a drenched hero inside an otherwise Restrained product). -- **Theme via scene sentence.** Write one sentence of physical context for this surface: who uses it, where, under what ambient light, in what mood. The sentence forces dark vs light. If it doesn't, add detail until it does. -- **Two or three named anchor references.** Specific products, brands, objects. Not adjectives like "modern" or "clean." +Write the smallest useful brief: -### Scope +1. **Job and audience:** who arrives, their context, need, and visitor mode. +2. **Outcome and proof:** primary task/action, success, real evidence, and product-specific truth. +3. **Selected direction:** visual authority, structural/interaction thesis, sequence, focal moment, and implementation consequence. +4. **Scope and boundaries:** fidelity, breadth, interactivity, named target, what remains untouched, and explicit anti-goals. +5. **States and ranges:** realistic content/data ranges and material states. +6. **Interaction and layout:** hierarchy, topology, responsiveness, affordances, feedback, and transitions; intent, not CSS. +7. **Constraints and open decisions:** platform, delivery, accessibility, localization, reusable components, and choices a builder must not invent. -Always ask. Sketch quality and shipped quality are different outputs; don't guess between them. +Use three to five bullets when the task is settled; use the full structure only for ambiguous, multi-screen, or standalone planning. Do not restate the conversation. -- **Fidelity.** Sketch / mid-fi / high-fi / production-ready? -- **Breadth.** One screen / a flow / a whole surface? -- **Interactivity.** Static visual / interactive prototype / shipped-quality component? -- **Time intent.** Quick exploration, or polish until it ships? +## Confirm and stop -Scope answers are task-scoped. Don't write them to PRODUCT.md or DESIGN.md; carry them through the design brief only. +Present the brief for explicit confirmation or one correction round, then stop: shape never writes code or a direction contract. -### Constraints -- Are there technical constraints? (Framework, performance budget, browser support) -- Are there content constraints? (Localization, dynamic text length, user-generated content) -- Mobile/responsive requirements? -- Accessibility requirements beyond WCAG AA? - -### Anti-Goals -- What should this NOT be? What would be a wrong direction? -- What's the biggest risk of getting this wrong? - -## Phase 1.5: Visual Direction Probe (Capability-Gated) - -After the discovery interview, generate a small set of visual direction probes **before** writing the final brief when all of these are true: - -- The work is **net-new** or directionally ambiguous enough that visual exploration will clarify the brief. -- The requested fidelity is **mid-fi, high-fi, or production-ready**. Skip for sketch-only planning. -- The current harness gives you native image generation (Codex's `image_gen`, an equivalent MCP tool, or similar). Don't ask the user to install APIs or tooling. - -When those conditions are met, this step is mandatory. If image generation isn't natively available, do not ask the user to install APIs or tooling. State in one line that the image step is skipped because the harness lacks native image generation, then proceed. The one-line announcement is required, not optional; it forces a conscious decision instead of letting the step quietly evaporate. - -Use probes to explore visual lanes, not to replace the brief. - -Do not skip probes because the final UI will be semantic, editable, code-native, responsive, or accessible. Those are implementation requirements, not reasons to avoid visual exploration. - -### What to generate - -Generate **2 to 4** distinct direction probes based on the discovery answers, especially: - -- Color strategy -- Theme scene sentence -- Named anchor references -- Scope and fidelity - -The probes should differ in primary visual direction (hierarchy, topology, density, typographic voice, or color strategy), not just palette tweaks. - -### How to use the probes - -- Treat them as **direction tests**, not final designs. -- Use them to pressure-test whether the brief is pointing at the right lane. -- Ask the user which direction feels closest, what feels off, and what should carry forward. -- If the probes reveal a mismatch, revise the brief inputs before finalizing the brief. - -### Important limits - -- Do **not** skip discovery because image generation is available. -- Do **not** treat generated imagery as final UX specification, final copy, or final accessibility behavior. -- Do **not** use this step for minor refinements of existing work. It's for shaping a new surface or clarifying a big directional choice. - -If image generation isn't natively available, announce the skip in one line and proceed to the design brief. - -## Phase 2: Design Brief - -After the interview and any required probes, present a brief and **end your response**. The user must confirm before any implementation runs. Do not present a brief and then continue to code in the same response, even if the brief feels obvious to you. The user's confirmation is the gate. - -**Choose the brief shape based on how clear the answers are:** - -- **Compact form (3-5 bullets)** when discovery was crisp and the original prompt + PRODUCT.md already pinned scope, content, and direction. State what you're building, the visual lane, and end with one or two specific questions or a clear "confirm or override?" prompt. This is the default for typical craft requests with a clear prompt. -- **Full structured form (sections below)** when the task is genuinely ambiguous, multi-screen, or when the user asked for shape as a standalone step. Use this when the discipline of structure earns its weight. - -Don't pad a clear brief into a long one to look thorough. A 70-line brief restating answers the user just gave is noise, not rigor. Equally, don't skip the confirmation pause to look efficient: the pause is the point. - -Present the brief, then **stop and wait for explicit confirmation**. You are not the judge of whether the user already approved. Even when the brief feels obviously right, ask once and wait. The pause is what separates shape from premature implementation. - -### Brief Structure - -**1. Feature Summary** (2-3 sentences) -What this is, who it's for, what it needs to accomplish. - -**2. Primary User Action** -The single most important thing a user should do or understand here. - -**3. Design Direction** -Color strategy (Restrained / Committed / Full palette / Drenched) + the theme scene sentence + 2–3 named anchor references. Reference PRODUCT.md and DESIGN.md where they already answer, and note any per-surface overrides. - -If you ran the Visual Direction Probe step, name which probe direction won and what changed in the brief because of it. - -**4. Scope** -Fidelity, breadth, interactivity, and time intent from the Scope section of the interview. Task-scoped; these don't persist beyond the brief. - -**5. Layout Strategy** -High-level spatial approach: what gets emphasis, what's secondary, how information flows. Describe the visual hierarchy and rhythm, not specific CSS. - -**6. Key States** -List every state the feature needs: default, empty, loading, error, success, edge cases. For each, note what the user needs to see and feel. - -**7. Interaction Model** -How users interact with this feature. What happens on click, hover, scroll? What feedback do they get? What's the flow from entry to completion? - -**8. Content Requirements** -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., 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. - ---- - -ask the user directly to clarify what you cannot infer. Ask for explicit confirmation of the brief before finishing. - -If the user disagrees with any part, revisit the relevant discovery questions. A shape run is incomplete until the user confirms direction. - -Once confirmed, the brief is complete. The user can now hand it to /impeccable, or use it to guide any other implementation approach. (If the user wants the full discovery-then-build flow in one step, they should use /impeccable craft instead, which runs this command internally.) +When no human or structured answer mechanism exists, mark assumptions plainly, return the brief, and stop. diff --git a/.kiro/skills/impeccable/reference/typeset.md b/.kiro/skills/impeccable/reference/typeset.md index defa6fb43..63d7588c9 100644 --- a/.kiro/skills/impeccable/reference/typeset.md +++ b/.kiro/skills/impeccable/reference/typeset.md @@ -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. --- -## Register +## Visitor mode -New identity work: run the font selection procedure in [new-work.md](new-work.md). Fluid `clamp()` scale, ≥1.25 ratio between steps. +- **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, register, **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 .kiro/skills/impeccable/scripts/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 `/impeccable polish` for the final pass. +When the hierarchy holds, hand off to `/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) * )` 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 - -The tactical selection procedure and the reflex-reject list live in [new-work.md](new-work.md) (loaded for new identity work). 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 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. diff --git a/.kiro/skills/impeccable/scripts/command-metadata.json b/.kiro/skills/impeccable/scripts/command-metadata.json index 83796d54b..dad8ef2e0 100644 --- a/.kiro/skills/impeccable/scripts/command-metadata.json +++ b/.kiro/skills/impeccable/scripts/command-metadata.json @@ -1,6 +1,6 @@ { "craft": { - "description": "Full confirmed-brief-then-build flow. Runs multi-round shape discovery first, resolves visual probe and north-star mock gates when available, then builds and visually iterates. Use when building a new feature end-to-end.", + "description": "Deprecated compatibility alias for an ordinary Impeccable new-work request. It adds no behavior; natural build and redesign requests use the same flow.", "argumentHint": "[feature description]" }, "init": { diff --git a/.kiro/skills/impeccable/scripts/concept-seed.mjs b/.kiro/skills/impeccable/scripts/concept-seed.mjs index 1f141a80f..25f20d9ab 100644 --- a/.kiro/skills/impeccable/scripts/concept-seed.mjs +++ b/.kiro/skills/impeccable/scripts/concept-seed.mjs @@ -1,6 +1,7 @@ #!/usr/bin/env node /** - * Concept-seed picker: the dice half of the new-work concept procedure. + * External concept seed: the dice half of new-work's coupled-direction and + * established-world surface procedures. * * The model derives a grounded shortlist of candidate FORMS from the * audience's world and the subject's cultural home (see @@ -12,10 +13,9 @@ * * This script rolls them from outside, the same trick that made the * palette seed work: - * - BUILD INDEX (2-5): which entry of the model's own resonance-ordered - * shortlist to build. The dice never choose an ungrounded ingredient; - * they only refuse the argmax rut. (Index 1 is excluded: that's the - * concept every run would ship anyway.) + * - PROMOTED INDEX: which entry of the model's own resonance-ordered + * shortlist must be taken seriously beside its favorites. The dice never + * choose an ungrounded ingredient; they only refuse the argmax rut. * - CHALLENGERS (3): outside forms from concept-ingredients.json, weighed * against the derived candidates on exactly two axes — audience * identification and product clarity. They win only when they beat the @@ -23,8 +23,8 @@ * material and win over thin categories, which is the intended shape. * * Usage: - * node scripts/concept-seed.mjs # roll at random - * node scripts/concept-seed.mjs --from # deterministic (hash key) + * node scripts/concept-seed.mjs --scope direction + * node scripts/concept-seed.mjs --scope surface --from * * Env vars: * IMPECCABLE_CONCEPT_SEED — same as --from; for reproducible eval runs. @@ -40,6 +40,12 @@ const pool = JSON.parse(readFileSync(join(here, 'concept-ingredients.json'), 'ut const args = process.argv.slice(2); const fromIdx = args.indexOf('--from'); +const scopeIdx = args.indexOf('--scope'); +const scope = scopeIdx !== -1 ? args[scopeIdx + 1] : 'surface'; +if (scope !== 'surface' && scope !== 'direction') { + process.stderr.write('concept-seed: --scope must be direction or surface\n'); + process.exit(1); +} // When no key is supplied, generate one and print it: a user reporting a // bad outcome can hand us the key and we replay the exact roll. const key = fromIdx !== -1 @@ -47,7 +53,7 @@ const key = fromIdx !== -1 : (process.env.IMPECCABLE_CONCEPT_SEED || crypto.randomBytes(4).toString('hex')); function hashUnit(k, salt) { - const h = crypto.createHash('sha256').update(`${salt}:${k}`).digest(); + const h = crypto.createHash('sha256').update(`${scope}:${salt}:${k}`).digest(); return h.readUInt32BE(0) / 0xffffffff; } const unit = (salt) => hashUnit(key, salt); @@ -67,22 +73,48 @@ for (let i = 0; picks.length < 3 && i < 60; i++) { } } -process.stdout.write(`CONCEPT SEED (key: ${key}; rerun with --from ${key} to reproduce this roll) -BUILD INDEX: ${buildIndex} - After ordering your derived candidates by resonance, build the page whose - form comes from candidate number ${buildIndex}, exactly as if it had ranked - first: full commitment. Your top-ranked candidate is what every run in this - category would ship; the assignment exists to refuse that rut, not to - punish it. -CHALLENGERS (weigh against your derived candidates on the same two axes, -audience identification and product clarity; a challenger wins only when -it beats the grounded list on both): +const promotedInstruction = scope === 'direction' + ? `After ordering the grounded coupled directions by product fit, promote + candidate ${buildIndex} into the serious shortlist. Each candidate must join + a durable visual system to a concrete expression for the requested first + surface; select or revise that pair as one decision. It must survive the + current task plus navigation, quiet and dense content, interaction and state, + and a substantially different future surface.` + : `After ordering the task's grounded structural candidates by resonance, + promote candidate ${buildIndex} into the serious shortlist. In an attended + run, present it beside the strongest materially different candidates and + let the user select or revise the surface concept. In a truly unattended + run, use it when it survives audience identification and product clarity.`; + +const challengerInstruction = scope === 'direction' + ? `A challenger enters only when it can supply both reusable identity grammar + and a strong first-surface structure, not a one-page costume or a style pasted + onto an unrelated layout. Weigh audience identification, product clarity, + current-surface force, and cross-surface system breadth.` + : `A challenger wins only when it beats the grounded list on both audience + identification and product clarity. It may change task topology or + interaction, but never the committed visual identity.`; + +const authorityInstruction = scope === 'direction' + ? `PRODUCT.md and explicit incumbent brand commitments constrain every coupled +direction. The seed never chooses exact colors, fonts, tokens, or a user +preference, and it never permits the world and first surface to be selected +independently.` + : `PRODUCT.md and DESIGN.md constrain every surface candidate's identity +vocabulary; they do not cancel task-level composition. The seed never +authorizes a new palette, type system, material world, or unfamiliar control +behavior.`; + +process.stdout.write(`${scope.toUpperCase()} CONCEPT SEED (key: ${key}; rerun with --scope ${scope} --from ${key} to reproduce this roll) +PROMOTED INDEX: ${buildIndex} + ${promotedInstruction} + The promotion exists to refuse the model's ranking rut, not to outrank the + user or the brief. +CHALLENGERS: 1. ${picks[0]} 2. ${picks[1]} 3. ${picks[2]} -If a challenger wins, it replaces the assigned candidate. If the surface is -an existing world whose incumbent carries a deliberate, ownable idea, the -incumbent IS the chosen candidate: intensify its lineage and ignore the -roll entirely. The same override applies when the user, PRODUCT.md, or -DESIGN.md pins a direction: pinned direction beats the roll, always. +${challengerInstruction} +${authorityInstruction} +A user- or brief-pinned decision beats the roll, always. `); diff --git a/.kiro/skills/impeccable/scripts/context-signals.mjs b/.kiro/skills/impeccable/scripts/context-signals.mjs index 2fc27bea7..d296320d7 100644 --- a/.kiro/skills/impeccable/scripts/context-signals.mjs +++ b/.kiro/skills/impeccable/scripts/context-signals.mjs @@ -11,7 +11,7 @@ * output is always valid JSON. * * Signals: - * - setup: PRODUCT.md / DESIGN.md presence, register, whether code exists + * - setup: PRODUCT.md / DESIGN.md presence and whether code exists * - critique: the latest cached critique score (.impeccable/critique) * - git: branch + files changed vs the default branch (a scope hint) * - devServer: whether a local dev server answers on a common port (gates live) @@ -21,7 +21,7 @@ import net from 'node:net'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { execFileSync } from 'node:child_process'; -import { loadContext, extractRegister, extractPlatform } from './context.mjs'; +import { loadContext, extractPlatform } from './context.mjs'; import { getCritiqueDir } from './lib/impeccable-paths.mjs'; /** Is there code here at all, or just context files / an empty repo? */ @@ -196,7 +196,6 @@ export async function gatherSignals(cwd = process.cwd()) { hasDesign: ctx.hasDesign, designPath: ctx.designPath, hasCode: hasCode(cwd), - register: extractRegister(ctx.product), platform: extractPlatform(ctx.product), }, critique: { latest: latestCritique(cwd) }, diff --git a/.kiro/skills/impeccable/scripts/context.mjs b/.kiro/skills/impeccable/scripts/context.mjs index 2ef2d4ce8..080e7ee3a 100644 --- a/.kiro/skills/impeccable/scripts/context.mjs +++ b/.kiro/skills/impeccable/scripts/context.mjs @@ -1,8 +1,10 @@ /** - * Context loader: prints PRODUCT.md (and DESIGN.md if present) as one - * markdown block on stdout, or prints a `NO_PRODUCT_MD:` message when no + * Context loader: prints PRODUCT.md, DESIGN.md when present, the matching + * persisted surface brief when one can be resolved, and native-platform + * guidance selected from PRODUCT.md. It prints a + * `NO_PRODUCT_MD:` message when no * PRODUCT.md is found anywhere. The skill keys off that message to branch: - * from-scratch build commands (init / teach / craft / shape) and clear + * from-scratch build requests (plus init / teach / shape) and clear * build/shape intent divert into the init flow, while scoped commands proceed * using the existing code as context. * @@ -23,10 +25,13 @@ import os from 'node:os'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { parseTargetOptions } from './lib/target-args.mjs'; -import { IMPECCABLE_COMMAND } from './lib/provider.mjs'; +import { IMPECCABLE_COMMAND, IMPECCABLE_PROVIDER_ID } from './lib/provider.mjs'; +import { renderLlmOnlySlopReview } from './lib/slop-review.mjs'; +import { resolveSurfaceBrief } from './lib/surface-briefs.mjs'; const PRODUCT_NAMES = ['PRODUCT.md', 'Product.md', 'product.md']; const DESIGN_NAMES = ['DESIGN.md', 'Design.md', 'design.md']; +const SKILL_REFERENCE_DIR = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', 'reference'); const FALLBACK_DIRS = ['.agents/context', 'docs']; const MONOREPO_MARKER_FILES = ['pnpm-workspace.yaml', 'turbo.json', 'nx.json', 'lerna.json']; const MONOREPO_FALLBACK_PROJECT_DIRS = ['apps', 'packages']; @@ -41,6 +46,8 @@ const WORKSPACE_DISCOVERY_IGNORED_DIRS = new Set([ '.turbo', '.cache', 'coverage', + 'vendor', + 'vendors', ]); const VISUAL_SOURCE_DIRS = ['src', 'app', 'pages', 'components', 'site', 'public', 'styles']; const STYLE_EXTENSIONS = new Set(['.css', '.scss', '.sass', '.less', '.styl']); @@ -73,6 +80,12 @@ export function loadContext(cwd = process.cwd(), options = {}) { const designPath = resolved.designPath; const product = productPath ? safeRead(productPath) : null; const design = designPath ? safeRead(designPath) : null; + const platform = extractPlatform(product); + const surfaceResolution = resolveSurfaceBrief( + resolved.projectRoot, + hasTargetOption(options) ? options.targetPath : null, + ); + const surfaceBrief = surfaceResolution.brief; return { hasProduct: !!product, product, @@ -83,7 +96,18 @@ export function loadContext(cwd = process.cwd(), options = {}) { contextDir: resolved.contextDir, productContextDir: productPath ? path.dirname(productPath) : null, designContextDir: designPath ? path.dirname(designPath) : null, + hasSurfaceBrief: !!surfaceBrief, + surfaceBrief: surfaceBrief?.text ?? null, + surfaceBriefPath: surfaceBrief?.path ? path.relative(absCwd, surfaceBrief.path) : null, + surfaceBriefReason: surfaceResolution.reason, + surfaceBriefCandidates: surfaceResolution.candidates.map((brief) => ({ + slug: brief.slug, + path: path.relative(absCwd, brief.path), + primaryTarget: brief.primaryTarget, + relatedTargets: brief.relatedTargets, + })), hasVisualImplementation: hasVisualImplementation(resolved.projectRoot), + platform, projectRoot: resolved.projectRoot, repoRoot: resolved.repoRoot, isMonorepo: resolved.isMonorepo, @@ -696,6 +720,19 @@ function safeRead(p) { } } +function loadNativePlatformReferences(platform) { + const names = platform === 'adaptive' + ? ['ios', 'android'] + : platform === 'ios' || platform === 'android' + ? [platform] + : []; + return names.flatMap((name) => { + const filePath = path.join(SKILL_REFERENCE_DIR, `${name}.md`); + const content = safeRead(filePath); + return content ? [{ name, filePath, content }] : []; + }); +} + /** * Best-effort evidence that the project already has an incumbent visual * implementation. DESIGN.md is documentation, not the only source of design @@ -719,9 +756,11 @@ export function hasVisualImplementation(projectRoot) { let styledComponents = 0; const inspectFile = (filePath) => { - if (scannedFiles++ >= VISUAL_SCAN_FILE_LIMIT) return false; const ext = path.extname(filePath).toLowerCase(); if (!STYLE_EXTENSIONS.has(ext) && !UI_EXTENSIONS.has(ext)) return false; + const base = path.basename(filePath).toLowerCase(); + if (/\.min\.[a-z]+$/.test(base)) return false; + if (scannedFiles++ >= VISUAL_SCAN_FILE_LIMIT) return false; let body; try { body = fs.readFileSync(filePath, 'utf-8').slice(0, 64 * 1024); @@ -729,18 +768,28 @@ export function hasVisualImplementation(projectRoot) { return false; } - const base = path.basename(filePath).toLowerCase(); + const evidence = body + .replace(/\/\*[\s\S]*?\*\//g, '') + .replace(//g, '') + .replace(/^\s*\/\/.*$/gm, ''); if (STYLE_EXTENSIONS.has(ext)) { - const customProperties = body.match(/--[a-z0-9_-]+\s*:/gi)?.length ?? 0; - const visualDeclarations = body.match(/\b(?:color|background(?:-color)?|border(?:-color)?|font-family)\s*:/gi)?.length ?? 0; - if (/\b(?:tokens?|theme|design-system)\b/.test(base) && body.trim().length > 80) return true; + const customProperties = evidence.match(/--[a-z0-9_-]+\s*:/gi)?.length ?? 0; + const visualDeclarations = evidence.match(/\b(?:color|background(?:-color)?|border(?:-color)?|font-family)\s*:/gi)?.length ?? 0; + if (/\b(?:tokens?|theme|design-system)\b/.test(base) && evidence.trim().length > 80) return true; if (customProperties >= 3 || visualDeclarations >= 5) return true; } - if ((ext === '.html' || ext === '.htm') && body.length > 600 && /]+stylesheet/i.test(body)) { + if ((ext === '.html' || ext === '.htm') && evidence.length > 600 && /]+stylesheet/i.test(evidence)) { return true; } - if (!['.html', '.htm'].includes(ext) && body.length > 300 && /class(?:Name)?\s*=|style\s*=|styled\(|css`/i.test(body)) { + if (!['.html', '.htm'].includes(ext) && evidence.length > 300) { + const embeddedCustomProperties = evidence.match(/--[a-z0-9_-]+\s*:/gi)?.length ?? 0; + const embeddedVisualDeclarations = evidence.match(/\b(?:color|background(?:-color)?|border(?:-color)?|font-family)\s*:/gi)?.length ?? 0; + const classTokens = [...evidence.matchAll(/class(?:Name)?\s*=\s*["'`]([^"'`]+)["'`]/gi)] + .reduce((count, match) => count + match[1].trim().split(/\s+/).length, 0); + if ((embeddedCustomProperties >= 3 && embeddedVisualDeclarations >= 3) || embeddedVisualDeclarations >= 5 || classTokens >= 12) return true; + } + if (!['.html', '.htm'].includes(ext) && evidence.length > 300 && /class(?:Name)?\s*=|style\s*=|styled\(|css`/i.test(evidence)) { styledComponents += 1; if (styledComponents >= 3) return true; } @@ -781,9 +830,9 @@ function escapeRegExp(value) { /** * Read the first non-empty line under a bare `## ` section of - * PRODUCT.md (e.g. `## Register`, `## Platform`). Returns null when the + * PRODUCT.md (for example `## Platform`). Returns null when the * section is absent. The heading match is exact (`\s*$`) so near-miss - * headings like `## Register guidelines` don't shadow the real field. + * near-miss headings don't shadow the real field. */ export function extractSectionValue(product, heading) { if (!product) return null; @@ -802,16 +851,6 @@ export function extractSectionValue(product, heading) { return null; } -/** - * Pull the register (`brand` or `product`) out of PRODUCT.md by looking - * for a `## Register` section and reading the first non-empty line that - * follows it. Returns null when the file is legacy / register-less. - */ -export function extractRegister(product) { - const word = (extractSectionValue(product, 'Register') || '').toLowerCase(); - return word === 'brand' || word === 'product' ? word : null; -} - /** * Pull the platform (`web`, `ios`, `android`, or `adaptive`) out of PRODUCT.md * by looking for a `## Platform` section and reading the first non-empty line @@ -985,30 +1024,35 @@ async function cli() { const parts = ctx.hasVisualImplementation ? [ 'NO_PRODUCT_MD: This project has no PRODUCT.md yet, but it does have an incumbent visual implementation. ' + - 'For `init` or `teach`, load reference/init.md and create PRODUCT.md. For `craft`, `shape`, or other work ' + - 'against the existing product, read its CSS, tokens, components, and assets first and proceed without blocking; ' + - `offer \`${IMPECCABLE_COMMAND} init\` as a follow-up. Only treat the work as greenfield when the user explicitly ` + - 'asks to discard or replace the current identity.', - 'EXISTING_VISUAL_SYSTEM: Code and assets are the incumbent design authority. Missing DESIGN.md is a documentation ' + - 'gap, not permission to invent a replacement identity. Preserve and extend the implementation unless the user ' + - 'explicitly requests a rebrand, or inspection proves the detected files contain no coherent visual decisions.', + 'For `init`, `teach`, `shape`, or any request to create a new surface or replacement visual world, load reference/init.md and create PRODUCT.md with the user first. ' + + 'After init writes PRODUCT.md, reference/new-work.md preserves and documents the incumbent system for an ' + + 'extension or replaces it with the user for a redesign/rebrand. Other ' + + 'narrow refinement commands may read the CSS, tokens, components, and assets and proceed without blocking, then ' + + `offer \`${IMPECCABLE_COMMAND} init\` as a follow-up.`, + 'BUILD_INIT_REQUIRED: Before shape or any new-surface/redesign flow, init must capture PRODUCT.md with the human or structured ' + + 'simulated user. Init writes product truth only; reference/new-work.md owns every visual decision.', + 'SCOPED_EXISTING_ALLOWED: Narrow refinement commands may use the incumbent implementation as authority without ' + + 'blocking on context setup; they must preserve it and offer init afterward.', + 'EXISTING_VISUAL_SYSTEM: For refinement or extension, code and assets are incumbent design authority and missing ' + + 'DESIGN.md is a documentation gap. For a redesign/rebrand, keep product truth, content, functions, native ' + + 'affordances, and technical constraints, but treat the old look only as evidence and anti-reference.', ] : [ 'NO_PRODUCT_MD: This project has no PRODUCT.md yet. ' + - 'For `init`, `teach`, `craft`, `shape`, ' + + 'For `init`, `teach`, `shape`, ' + 'or wording that clearly maps to a from-scratch build/shape flow, load ' + - 'reference/init.md and write PRODUCT.md first, unless no user can ' + - 'respond (a one-shot or automated run, or the user said not to ask): ' + - 'then write a one-paragraph understanding of the product, audience, ' + - 'and the page\'s job from the brief, and continue. For any other ' + + 'reference/init.md, complete its human or structured simulated-user interview, and write PRODUCT.md before ' + + 'designing. If no answer mechanism truly exists, init may infer only from the explicit brief and must label its ' + + 'assumptions. It never writes DESIGN.md. For any other ' + '(scoped) command against existing code, proceed using the code as ' + `context and offer \`${IMPECCABLE_COMMAND} init\` as a suggestion (do not block).`, - 'NEW_WORK: No committed design context was found. If this task produces ' + - 'new design (a build from scratch, or a redesign that discards the ' + - 'current look), you MUST read reference/new-work.md before making any ' + - 'design decision. Scoped fixes to existing code do not need it.', + 'PRODUCT_INIT_REQUIRED: No product context or visual authority was found. New builds and redesigns ' + + 'must finish reference/init.md for PRODUCT.md, then reference/new-work.md establishes the world and surface. Scoped ' + + 'fixes to existing code do not need the new-surface flow.', ]; + appendSurfaceBriefContext(parts, ctx); parts.push(buildResolvedContextDirective(ctx, cliOptions, { targetExists })); + appendHookFallback(parts, ctx); if (shouldWarnMissingTarget(ctx, targetProvided, targetExists)) { parts.push(buildMissingTargetDirective()); } @@ -1020,37 +1064,29 @@ async function cli() { if (ctx.hasDesign) { parts.push(`# DESIGN.md\n\n${ctx.design.trim()}`); } + appendSurfaceBriefContext(parts, ctx); parts.push(buildResolvedContextDirective(ctx, cliOptions, { targetExists })); + appendHookFallback(parts, ctx); if (shouldWarnMissingTarget(ctx, targetProvided, targetExists)) { parts.push(buildMissingTargetDirective()); } - const register = extractRegister(ctx.product); - // The register field survives as a family hint (brand = Persuade/Experience, - // product = Operate/Read); SKILL.md's mode section carries the essentials - // inline, so no register-file read is mandated here. What IS mandated: - // the new-work playbook when no committed design system exists yet. if (!ctx.hasDesign) { parts.push(ctx.hasVisualImplementation - ? 'EXISTING_VISUAL_SYSTEM: PRODUCT.md exists and DESIGN.md is missing, but code contains incumbent visual decisions. ' + - 'Treat CSS, tokens, components, and assets as design authority. Do not route to new identity work merely because the ' + - 'document is absent; preserve and extend the implementation unless the user explicitly asks to discard it.' - : 'NEW_WORK: PRODUCT.md exists but no DESIGN.md or incumbent visual implementation was found. If this task produces ' + - 'new design or a redesign that discards the current look, you MUST read reference/new-work.md before making any ' + - 'design decision. Scoped fixes to existing code do not need it.'); + ? 'INCUMBENT_WORLD_UNDOCUMENTED: PRODUCT.md exists and DESIGN.md is missing, but code contains incumbent visual decisions. ' + + 'For shape or a new-surface/redesign request, load reference/new-work.md: an extension documents and preserves the code-defined world; ' + + 'a redesign replaces it with the user and uses the old look only as evidence and anti-reference. Narrow refinement ' + + 'commands may proceed using the implementation directly.' + : 'WORLD_DISCOVERY_REQUIRED: PRODUCT.md exists but no DESIGN.md or incumbent visual implementation was found. ' + + 'For a new build or redesign, load reference/new-work.md and establish the visual world with the human or structured ' + + 'simulated user before developing the task concept. Scoped fixes to existing code do not need this flow.'); } - if (register) { - parts.push(`REGISTER: \`${register}\` (family hint: brand = Persuade/Experience surfaces, product = Operate/Read). Derive the visitor's mode per SKILL.md.`); - } - const platform = extractPlatform(ctx.product); - const nativeRefs = - platform === 'adaptive' ? ['ios', 'android'] : platform === 'ios' || platform === 'android' ? [platform] : []; - if (nativeRefs.length) { - const refList = nativeRefs.map(p => `\`reference/${p}.md\``).join(' and '); - const label = platform === 'adaptive' ? '`adaptive` (both iOS and Android)' : `\`${platform}\``; + const platformReferences = loadNativePlatformReferences(ctx.platform); + for (const reference of platformReferences) { parts.push( - `NEXT STEP: This project targets ${label}. Also read ${refList} for native conventions, in addition to SKILL.md's mode guidance.`, + `# NATIVE PLATFORM REFERENCE: ${reference.name.toUpperCase()} (reference/${reference.name}.md)\n\n${reference.content.trim()}`, ); - } else if (!platform) { + } + if (!ctx.platform) { // A `## Platform` section that names something we don't recognize (a // toolchain like `flutter`, a typo) would otherwise silently fall back to // web — the wrong default exactly when the user tried to say "native". @@ -1078,6 +1114,72 @@ function pathExistsForTarget(cwd, targetPath) { return fs.existsSync(abs); } +const HOOK_MANIFESTS_BY_PROVIDER = Object.freeze({ + 'claude-code': ['.claude/settings.local.json', '.claude/settings.json'], + codex: ['.codex/hooks.json'], + agents: ['.codex/hooks.json'], + cursor: ['.cursor/hooks.json'], + github: ['.github/hooks/impeccable.json'], +}); + +function truthyEnv(value) { + return typeof value === 'string' && /^(1|true|yes|on)$/i.test(value.trim()); +} + +function valueHasHookMarker(value) { + if (typeof value === 'string') { + return value.includes('skills/impeccable/scripts/hook.mjs') + || value.includes('skills/impeccable/scripts/hook-before-edit.mjs'); + } + if (Array.isArray(value)) return value.some(valueHasHookMarker); + if (value && typeof value === 'object') return Object.values(value).some(valueHasHookMarker); + return false; +} + +function hookEnabledAt(root) { + if (truthyEnv(process.env.IMPECCABLE_HOOK_DISABLED)) return false; + let enabled = true; + for (const name of ['.impeccable/config.json', '.impeccable/config.local.json']) { + const raw = readJson(path.join(root, name)); + if (raw?.hook && Object.prototype.hasOwnProperty.call(raw.hook, 'enabled')) { + enabled = raw.hook.enabled !== false; + } + } + return enabled; +} + +const STOP_REVIEW_PROVIDERS = new Set(['claude-code', 'codex', 'agents']); + +function automaticHookMode(ctx) { + if (ctx.platform === 'ios' || ctx.platform === 'android' || ctx.platform === 'adaptive') { + return 'none'; + } + const activeRoot = path.resolve(ctx.projectRoot || process.cwd()); + if (!hookEnabledAt(activeRoot)) return 'none'; + const manifests = HOOK_MANIFESTS_BY_PROVIDER[IMPECCABLE_PROVIDER_ID] || []; + const roots = [...new Set([process.cwd(), ctx.projectRoot, ctx.repoRoot].filter(Boolean).map((root) => path.resolve(root)))]; + for (const root of roots) { + for (const rel of manifests) { + const raw = readJson(path.join(root, rel)); + if (raw?.hooks && valueHasHookMarker(raw.hooks)) { + return STOP_REVIEW_PROVIDERS.has(IMPECCABLE_PROVIDER_ID) ? 'stop' : 'per-edit'; + } + } + } + return 'none'; +} + +function appendHookFallback(parts, ctx) { + const hookMode = automaticHookMode(ctx); + if (hookMode === 'stop') return; + const native = ctx.platform === 'ios' || ctx.platform === 'android' || ctx.platform === 'adaptive'; + parts.push(renderLlmOnlySlopReview({ + automaticDetector: hookMode === 'per-edit', + manualDetector: hookMode === 'none' && !native, + scriptsPath: path.dirname(fileURLToPath(import.meta.url)), + })); +} + function buildResolvedContextDirective(ctx, options, { targetExists = null } = {}) { const targetPath = hasTargetOption(options) ? options.targetPath : null; return `RESOLVED_CONTEXT:\n${JSON.stringify({ @@ -1087,10 +1189,29 @@ function buildResolvedContextDirective(ctx, options, { targetExists = null } = { repoRoot: ctx.repoRoot, productPath: ctx.productPath, designPath: ctx.designPath, + surfaceBriefPath: ctx.surfaceBriefPath, + surfaceBriefReason: ctx.surfaceBriefReason, + surfaceBriefCandidates: ctx.surfaceBriefCandidates, hasVisualImplementation: ctx.hasVisualImplementation, + platform: ctx.platform, }, null, 2)}`; } +function appendSurfaceBriefContext(parts, ctx) { + if (ctx.hasSurfaceBrief && ctx.surfaceBrief) { + parts.push(`# SURFACE BRIEF (${ctx.surfaceBriefPath})\n\n${ctx.surfaceBrief.trim()}`); + return; + } + if (!ctx.surfaceBriefCandidates?.length) return; + const helper = path.join(path.dirname(fileURLToPath(import.meta.url)), 'surface-brief.mjs'); + parts.push( + 'SURFACE_CONTEXT_AVAILABLE: Persisted surface briefs exist, but none was selected unambiguously for this invocation. ' + + 'Resolve the requested surface to its concrete primary or related source path, then run ' + + `\`node ${helper} read \` once before changing that surface. Candidates:\n` + + JSON.stringify(ctx.surfaceBriefCandidates, null, 2), + ); +} + function shouldWarnMissingTarget(ctx, targetProvided, targetExists = null) { if (ctx.isMonorepo && targetProvided && targetExists === false) return true; return !!( diff --git a/.kiro/skills/impeccable/scripts/critique-storage.mjs b/.kiro/skills/impeccable/scripts/critique-storage.mjs index 6b4d225cf..38674285a 100644 --- a/.kiro/skills/impeccable/scripts/critique-storage.mjs +++ b/.kiro/skills/impeccable/scripts/critique-storage.mjs @@ -29,8 +29,9 @@ import fs from 'node:fs'; import path from 'node:path'; import { fileURLToPath, pathToFileURL } from 'node:url'; import { getCritiqueDir } from './lib/impeccable-paths.mjs'; +import { slugFromTarget } from './lib/target-slug.mjs'; -const SLUG_MAX = 50; +export { slugFromTarget } from './lib/target-slug.mjs'; /** * Mechanically derive a slug from a resolved target. Returns null if the @@ -40,46 +41,6 @@ const SLUG_MAX = 50; * concrete artifact before calling this — we never slug a natural-language * phrase. */ -export function slugFromTarget(resolved, { cwd = process.cwd() } = {}) { - if (!resolved || typeof resolved !== 'string') return null; - const trimmed = resolved.trim(); - if (!trimmed) return null; - - // URL - if (/^https?:\/\//i.test(trimmed)) { - let url; - try { url = new URL(trimmed); } catch { return null; } - const hostPath = `${url.hostname}${url.pathname}`; - return kebab(hostPath); - } - - // File path. Make it project-relative so two devs critiquing the same - // checkout get the same slug regardless of where their repo is cloned. - const abs = path.isAbsolute(trimmed) ? trimmed : path.resolve(cwd, trimmed); - let rel = path.relative(cwd, abs); - // If the target is outside cwd, fall back to the basename so we still - // produce a stable slug (vs the absolute path, which would include - // home dirs / usernames). - if (rel.startsWith('..') || path.isAbsolute(rel)) { - rel = path.basename(abs); - } - if (!rel || rel === '.' || rel === '') return null; - return kebab(rel); -} - -function kebab(s) { - const slug = s - .toLowerCase() - .replace(/[/\\.]+/g, '-') - .replace(/[^a-z0-9-]+/g, '-') - .replace(/-+/g, '-') - .replace(/^-|-$/g, ''); - if (!slug) return null; - // Cap from the tail — the tail (filename) is more identifying than the - // top-level directory. - return slug.length <= SLUG_MAX ? slug : slug.slice(slug.length - SLUG_MAX).replace(/^-/, ''); -} - /** * Filename-safe UTC ISO timestamp: hyphens for separators, trailing Z. * Plain colons aren't allowed on Windows filesystems. diff --git a/.kiro/skills/impeccable/scripts/detector/detect-antipatterns-browser.js b/.kiro/skills/impeccable/scripts/detector/detect-antipatterns-browser.js index 033573e36..76bf4ddeb 100644 --- a/.kiro/skills/impeccable/scripts/detector/detect-antipatterns-browser.js +++ b/.kiro/skills/impeccable/scripts/detector/detect-antipatterns-browser.js @@ -312,17 +312,6 @@ const ANTIPATTERNS = [ skillSection: 'Layout & Space', skillGuideline: 'numbered section markers', }, - { - id: 'numbered-section-markers', - category: 'slop', - scopes: ['layout'], - severity: 'advisory', - name: 'Numbered section markers (01 / 02 / 03)', - description: - 'Numbered display markers as section labels (01, 02, 03) are the AI editorial scaffold one tier deeper than tracked eyebrow chips. If you find yourself reaching for them, choose a different section cadence.', - skillSection: 'Layout & Space', - skillGuideline: 'numbered section markers', - }, { id: 'em-dash-overuse', category: 'slop', diff --git a/.kiro/skills/impeccable/scripts/detector/engines/regex/detect-text.mjs b/.kiro/skills/impeccable/scripts/detector/engines/regex/detect-text.mjs index 81c30ab99..f641d5917 100644 --- a/.kiro/skills/impeccable/scripts/detector/engines/regex/detect-text.mjs +++ b/.kiro/skills/impeccable/scripts/detector/engines/regex/detect-text.mjs @@ -280,22 +280,6 @@ const REGEX_ANALYZERS = [ if (count === 0) return []; return [finding('marketing-buzzword', filePath, `${count} buzzword phrase${count === 1 ? '' : 's'}: "${firstSample}"`)]; }, - // Numbered section markers (01 / 02 / 03 ...) - (content, filePath) => { - const text = stripHtmlToText(content); - const re = /\b(0[1-9]|1[0-2])\b/g; - const seen = new Set(); - let m; - while ((m = re.exec(text)) !== null) seen.add(m[1]); - if (seen.size < 3) return []; - const sorted = [...seen].sort(); - let sequential = 0; - for (let i = 1; i < sorted.length; i++) { - if (parseInt(sorted[i], 10) === parseInt(sorted[i - 1], 10) + 1) sequential++; - } - if (sequential < 2) return []; - return [finding('numbered-section-markers', filePath, `Sequence: ${sorted.slice(0, 6).join(', ')}`)]; - }, // Aphoristic cadence: manufactured-contrast + short-rebuttal (content, filePath) => { const text = stripHtmlToText(content); @@ -430,21 +414,20 @@ function runRegexMatchers(lines, filePath, lineOffset = 0, blockContext = null, } /** Page-level analyzers that scan rendered text content (em-dash use, - * buzzword phrases, numbered section markers, aphoristic cadence). + * buzzword phrases, aphoristic cadence). * These are detector-agnostic — they work on any HTML/text source * and don't need a parsed DOM. Exported so detectHtml can call them * for `.html` files (which otherwise skip the regex engine). */ const TEXT_CONTENT_ANALYZER_IDS = [ 'em-dash-overuse', 'marketing-buzzword', - 'numbered-section-markers', 'aphoristic-cadence', ]; function runTextContentAnalyzers(content, filePath, options = {}) { const profile = options?.profile; if (!shouldRunPageAnalyzers(content, filePath)) return []; - // The 4 text-content analyzers are at indices 3-6 in REGEX_ANALYZERS. + // The 3 text-content analyzers are at indices 3-5 in REGEX_ANALYZERS. const findings = []; for (let i = 0; i < TEXT_CONTENT_ANALYZER_IDS.length; i++) { const analyzer = REGEX_ANALYZERS[3 + i]; @@ -535,7 +518,6 @@ function detectText(content, filePath, options = {}) { 'monotonous-spacing', 'em-dash-overuse', 'marketing-buzzword', - 'numbered-section-markers', 'aphoristic-cadence', 'dark-glow', ]; diff --git a/.kiro/skills/impeccable/scripts/detector/registry/antipatterns.mjs b/.kiro/skills/impeccable/scripts/detector/registry/antipatterns.mjs index b85f7e62f..84c69b0b8 100644 --- a/.kiro/skills/impeccable/scripts/detector/registry/antipatterns.mjs +++ b/.kiro/skills/impeccable/scripts/detector/registry/antipatterns.mjs @@ -210,17 +210,6 @@ const ANTIPATTERNS = [ skillSection: 'Layout & Space', skillGuideline: 'numbered section markers', }, - { - id: 'numbered-section-markers', - category: 'slop', - scopes: ['layout'], - severity: 'advisory', - name: 'Numbered section markers (01 / 02 / 03)', - description: - 'Numbered display markers as section labels (01, 02, 03) are the AI editorial scaffold one tier deeper than tracked eyebrow chips. If you find yourself reaching for them, choose a different section cadence.', - skillSection: 'Layout & Space', - skillGuideline: 'numbered section markers', - }, { id: 'em-dash-overuse', category: 'slop', diff --git a/.kiro/skills/impeccable/scripts/detector/rules/checks.mjs b/.kiro/skills/impeccable/scripts/detector/rules/checks.mjs index 3fdd6b2dd..9bace11b3 100644 --- a/.kiro/skills/impeccable/scripts/detector/rules/checks.mjs +++ b/.kiro/skills/impeccable/scripts/detector/rules/checks.mjs @@ -352,27 +352,57 @@ function isAccentColor(cssColor) { return false; } +function resolveHeroHeadingSizePx(value) { + const input = String(value || '').trim().toLowerCase(); + if (!input) return 0; + + const simpleLengthPx = (token) => { + const match = /^(-?\d*\.?\d+)\s*(px|rem|em|%)?$/.exec(String(token || '').trim()); + if (!match) return null; + const amount = Number(match[1]); + if (!Number.isFinite(amount)) return null; + if (match[2] === 'rem' || match[2] === 'em') return amount * 16; + if (match[2] === '%') return amount * 0.16; + return amount; + }; + + const direct = simpleLengthPx(input); + if (direct !== null) return direct; + + // Static CSS engines cannot resolve viewport units, but clamp's min/max + // bounds still tell us whether the heading can ever reach hero scale. + const clamp = /^clamp\((.*)\)$/.exec(input); + if (clamp) { + const parts = clamp[1].split(','); + if (parts.length === 3) { + const bounds = [simpleLengthPx(parts[0]), simpleLengthPx(parts[2])] + .filter((candidate) => candidate !== null); + if (bounds.length > 0) return Math.max(...bounds); + } + } + + return 0; +} + // Sibling-relationship rule. Anchor on a hero-scale h1, look at the // previousElementSibling, and gate on EITHER the classic tracked- // uppercase eyebrow OR the modern accent-colored bold eyebrow. function checkHeroEyebrow(opts) { const { headingTag, headingText, headingFontSize, + headingInApplicationContext, siblingTag, siblingText, siblingTextTransform, siblingFontSize, siblingLetterSpacing, siblingFontWeight, siblingColor, siblingHasAccentDashPseudo, } = opts; if (headingTag !== 'h1') return []; - // We previously gated on headingFontSize >= 48 to anchor "hero scale". - // But modern hero h1s use clamp() / vw / var(--text-*), none of which - // jsdom can resolve — the computed value comes back as "2em" or - // "var(--text-9xl)" and parseFloat returns 2 or NaN. The gate fails - // on virtually every Tailwind v4 / framework build. The other gates - // (sibling text 2-60 chars, font-size ≤ 14px, accent-bold OR - // tracked-caps) are tight enough to avoid false positives on non- - // hero h1s — a tiny tan label directly above any h1 is the - // antipattern regardless of how big the h1 ends up. + // This is specifically a marketing-hero cliché, not a ban on compact + // context labels in product UI (for example, a station name inside a tab + // panel). Browser-computed sizes are reliable; the static adapter also + // resolves ordinary px/rem/em and clamp() bounds before reaching here. + if (headingInApplicationContext) return []; + if (!(headingFontSize >= 48)) return []; if (!siblingTag) return []; // An h2 above an h1 is a different anti-pattern (heading hierarchy / dual // headings) — never an eyebrow. @@ -1938,6 +1968,7 @@ function checkElementHeroEyebrowDOM(el) { headingTag: tag, headingText: el.textContent || '', headingFontSize: parseFloat(headStyle.fontSize) || 0, + headingInApplicationContext: !!el.closest('[role="tabpanel"], [role="dialog"], [role="application"], dialog'), siblingTag: sibling.tagName.toLowerCase(), siblingText: sibling.textContent || '', siblingTextTransform: sibStyle.textTransform || '', @@ -3367,7 +3398,8 @@ function checkElementHeroEyebrow(el, style, tag, window, customPropMap) { return checkHeroEyebrow({ headingTag: tag, headingText: el.textContent || '', - headingFontSize: parseFloat(headingFontSizeRaw) || 0, + headingFontSize: resolveHeroHeadingSizePx(headingFontSizeRaw), + headingInApplicationContext: !!el.closest?.('[role="tabpanel"], [role="dialog"], [role="application"], dialog'), siblingTag: sibling.tagName.toLowerCase(), siblingText: sibling.textContent || '', siblingTextTransform: sibStyle.textTransform || '', diff --git a/.kiro/skills/impeccable/scripts/hook-lib.mjs b/.kiro/skills/impeccable/scripts/hook-lib.mjs index e61ea692b..50aa2f8fb 100644 --- a/.kiro/skills/impeccable/scripts/hook-lib.mjs +++ b/.kiro/skills/impeccable/scripts/hook-lib.mjs @@ -46,6 +46,7 @@ import path from 'node:path'; import { pathToFileURL, fileURLToPath } from 'node:url'; import { extractPlatform, loadContext } from './context.mjs'; import { IMPECCABLE_COMMAND } from './lib/provider.mjs'; +import { renderStopSlopReview } from './lib/slop-review.mjs'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); @@ -1868,22 +1869,42 @@ export const CONTRACT_MAX_CHARS = 1800; // Cap contract sections per Stop emission so many touched artifacts cannot // stack an unbounded message. export const CONTRACT_AUDIT_MAX_FILES = 3; +export const CONTRACT_EXTS = new Set(['.html', '.htm', '.astro', '.svelte', '.vue', '.jsx', '.tsx']); +export const CONTRACT_REQUIRED_FIELDS = ['UNIQUE', 'NOT-TEMPLATE', 'OWN-WORLD', 'STORY', 'FIRST VIEWPORT', 'FORM']; /** - * Extract the artifact's own direction-contract comment: the first HTML - * comment in the head of the file, when its opening chars identify it as a - * contract/concept block. Returns the trimmed, length-capped body, or null - * when the file carries none (no comment, unclosed comment, marker missing, - * or the comment starts past the head window). + * Extract the artifact's own direction-contract comment from the head of an + * HTML or component file. Supports HTML-family comments and JSX block + * comments so the contract works in the Astro/Svelte/Vue/React scaffolds the + * skill actually builds. Returns the trimmed, length-capped body, or null + * when the file carries no valid contract block. */ export function extractDirectionContract(content) { if (typeof content !== 'string' || !content) return null; const head = content.slice(0, CONTRACT_HEAD_CHARS); - const m = //.exec(head); - if (!m) return null; - const body = m[1].trim(); - if (!body || !/contract|concept/i.test(body.slice(0, CONTRACT_MARKER_CHARS))) return null; - return body.slice(0, CONTRACT_MAX_CHARS); + const candidates = []; + for (const pattern of [//g, /\{\/\*([\s\S]*?)\*\/\}/g]) { + for (const match of head.matchAll(pattern)) { + const index = match.index ?? 0; + const linePrefix = head.slice(head.lastIndexOf('\n', index - 1) + 1, index).trim(); + if (linePrefix.startsWith('//')) continue; + candidates.push({ index, body: match[1].trim() }); + } + } + candidates.sort((a, b) => a.index - b.index); + for (const candidate of candidates) { + if (!candidate.body || !/direction\s+contract|concept\s+contract/i.test(candidate.body.slice(0, CONTRACT_MARKER_CHARS))) continue; + return candidate.body.slice(0, CONTRACT_MAX_CHARS); + } + return null; +} + +export function missingDirectionContractFields(contract) { + const body = typeof contract === 'string' ? contract : ''; + return CONTRACT_REQUIRED_FIELDS.filter((field) => { + const label = field.replace(/[.*+?^${}()|[\]\\]/g, '\\$&').replace(/\s+/g, '\\s+'); + return !new RegExp(`\\b${label}\\s*:`, 'i').test(body); + }); } /** @@ -1899,7 +1920,11 @@ export function renderContractAudit(entries, opts = {}) { const shown = entries.slice(0, CONTRACT_AUDIT_MAX_FILES); const blocks = shown.map(({ filePath, contract }) => { const display = relativize(filePath, cwd); - return `${display} opens with this direction contract, written when the direction was decided:\n\n${contract}`; + const missing = missingDirectionContractFields(contract); + const integrity = missing.length > 0 + ? `\n\nContract integrity defect: missing ${missing.join(', ')}. Repair the contract and the implementation together.` + : ''; + return `${display} opens with this direction contract, written when the direction was decided:\n\n${contract}${integrity}`; }); return [ `${ENVELOPE_PREFIX} Direction-contract audit. Before finishing, audit the rendered page against the contract it opens with, promise by promise.`, @@ -1984,9 +2009,22 @@ export async function runStopHook({ stdinJson, env = {}, cwd = process.cwd(), no return result({ skipped: 'native-platform', platform, durationMs: Date.now() - started }); } + const session = ensureSession(cache, sessionId); + const needsSlopReview = session.llmSlopReviewed !== true; + const det = detector || await loadDetector(); if (!det || typeof det.detectText !== 'function') { - return result({ skipped: 'detector-missing', durationMs: Date.now() - started }); + if (!needsSlopReview) return result({ skipped: 'detector-missing', durationMs: Date.now() - started }); + session.llmSlopReviewed = true; + session.updatedAt = Date.now(); + persistCache(projectCwd, cache); + const text = renderStopSlopReview(); + return { + exitCode: 0, + stdout: payload(text, 'Stop', harness), + emission: { kind: 'stop-llm-slop-review', llmSlopReview: true }, + audit: { ...audit, emitted: true, detectorMissing: true, llmSlopReview: true, chars: text.length, durationMs: Date.now() - started }, + }; } const scanOptions = designSystemOptions(config, det, projectCwd); @@ -2013,10 +2051,11 @@ export async function runStopHook({ stdinJson, env = {}, cwd = process.cwd(), no ? configuredExt.engine === 'html' : (ext === '.html' || ext === '.htm'); - // Direction-contract audit: HTML artifacts only, at most once per file + // Direction-contract audit: HTML and component artifacts, at most once per file // per session. The flag lives on the same session cache entry the // finding dedupe uses, so a second Stop fire stays quiet about it. - if (useHtmlEngine) { + const contractCapable = useHtmlEngine || CONTRACT_EXTS.has(ext); + if (contractCapable) { const fileEntry = ensureFile(cache, sessionId, filePath); if (!fileEntry.contractAudited) { const contract = extractDirectionContract(content); @@ -2046,10 +2085,15 @@ export async function runStopHook({ stdinJson, env = {}, cwd = process.cwd(), no } audit.scannedFiles = scanned; - if (freshGroups.length === 0 && contractEntries.length === 0) { + if (freshGroups.length === 0 && contractEntries.length === 0 && !needsSlopReview) { return result({ emitted: false, skipped: 'stop-clean', durationMs: Date.now() - started }); } + if (needsSlopReview) { + session.llmSlopReviewed = true; + session.updatedAt = Date.now(); + } + // Fresh findings and first-time contract audits earn the cache write; // both mark this batch as surfaced so the next Stop fire is silent // unless new issues appear. @@ -2064,6 +2108,9 @@ export async function runStopHook({ stdinJson, env = {}, cwd = process.cwd(), no if (contractEntries.length > 0) { parts.push(renderContractAudit(contractEntries, { cwd: projectCwd })); } + if (needsSlopReview) { + parts.push(renderStopSlopReview()); + } const text = appendDesignSystemNote(parts.join('\n\n'), scanOptions); return { exitCode: 0, @@ -2074,6 +2121,7 @@ export async function runStopHook({ stdinJson, env = {}, cwd = process.cwd(), no ...(contractEntries.length > 0 ? { contractFiles: contractEntries.map((entry) => entry.filePath) } : {}), + ...(needsSlopReview ? { llmSlopReview: true } : {}), }, audit: { ...audit, @@ -2081,6 +2129,7 @@ export async function runStopHook({ stdinJson, env = {}, cwd = process.cwd(), no freshFiles: freshGroups.length, freshFindings: freshGroups.reduce((sum, group) => sum + group.findings.length, 0), ...(contractEntries.length > 0 ? { contractAudits: contractEntries.length } : {}), + ...(needsSlopReview ? { llmSlopReview: true } : {}), chars: text.length, durationMs: Date.now() - started, }, diff --git a/.kiro/skills/impeccable/scripts/lib/provider.mjs b/.kiro/skills/impeccable/scripts/lib/provider.mjs index 7fa928a9c..bf9557d1c 100644 --- a/.kiro/skills/impeccable/scripts/lib/provider.mjs +++ b/.kiro/skills/impeccable/scripts/lib/provider.mjs @@ -1,4 +1,5 @@ // Source scripts default to slash commands. The provider build replaces only // this exact declaration, avoiding heuristic rewrites across executable code. export const IMPECCABLE_COMMAND_PREFIX = "/"; +export const IMPECCABLE_PROVIDER_ID = "kiro"; export const IMPECCABLE_COMMAND = `${IMPECCABLE_COMMAND_PREFIX}impeccable`; diff --git a/.kiro/skills/impeccable/scripts/lib/slop-review.mjs b/.kiro/skills/impeccable/scripts/lib/slop-review.mjs new file mode 100644 index 000000000..30f03e34d --- /dev/null +++ b/.kiro/skills/impeccable/scripts/lib/slop-review.mjs @@ -0,0 +1,30 @@ +export const LLM_ONLY_SLOP_ITEMS = Object.freeze([ + 'Monospace used merely to signal “technical” or “developer.”', + 'Light or dark mode chosen by category habit rather than the actual use scene.', + 'Everything wrapped in cards, or identical icon-heading-text cards repeated as the page structure.', + 'Hero-metric scaffolds: one oversized number, a small label, supporting stats, and an accent treatment.', + 'Decorative glassmorphism, meaningless sparklines, or generic rounded rectangles with drop shadows.', + 'A modal chosen by reflex when the task does not require interruption or protected focus.', +]); + +export function renderLlmOnlySlopReview({ manualDetector = false, automaticDetector = false, scriptsPath = null } = {}) { + const lines = [ + automaticDetector + ? 'AI_SLOP_REVIEW_REQUIRED: The automatic detector covers mechanical rules, but this harness has no reliable late review. Before finishing changed UI, inspect the authored result for detector-blind model reflexes:' + : 'AI_SLOP_REVIEW_REQUIRED: The automatic Impeccable design hook is not available for this session. Before finishing changed UI, inspect the authored result for detector-blind model reflexes:', + ...LLM_ONLY_SLOP_ITEMS.map((item) => `- ${item}`), + 'Fix reflexes, not intentional choices required by the brief or established visual authority.', + ]; + if (manualDetector && scriptsPath) { + lines.push(`Then run the mechanical detector once over the changed web UI: \`node ${scriptsPath}/detect.mjs --json \`. Do not run it earlier during concept selection.`); + } + return lines.join('\n'); +} + +export function renderStopSlopReview() { + return [ + '[impeccable@1] Detector-blind AI-slop review. The mechanical pass is complete; now inspect the rendered result for model reflexes it cannot reliably detect:', + ...LLM_ONLY_SLOP_ITEMS.map((item) => `- ${item}`), + 'Fix reflexes, not intentional choices required by the brief or established visual authority. Do not rerun the detector; this is the authored judgment layer.', + ].join('\n'); +} diff --git a/.kiro/skills/impeccable/scripts/lib/surface-briefs.mjs b/.kiro/skills/impeccable/scripts/lib/surface-briefs.mjs new file mode 100644 index 000000000..f83416f69 --- /dev/null +++ b/.kiro/skills/impeccable/scripts/lib/surface-briefs.mjs @@ -0,0 +1,151 @@ +import fs from 'node:fs'; +import path from 'node:path'; +import { slugFromTarget } from './target-slug.mjs'; + +export const SURFACE_BRIEF_VERSION = 1; + +export function getSurfaceBriefDir(projectRoot) { + return path.join(projectRoot, '.impeccable', 'surfaces'); +} + +export function normalizeSurfaceTarget(target, { projectRoot = process.cwd() } = {}) { + if (!target || typeof target !== 'string' || !target.trim()) return null; + const trimmed = target.trim(); + if (/^https?:\/\//i.test(trimmed)) { + try { + const url = new URL(trimmed); + url.hash = ''; + url.search = ''; + return url.toString().replace(/\/$/, '') || url.origin; + } catch { + return null; + } + } + if (/^route:/i.test(trimmed)) { + const route = trimmed.slice(trimmed.indexOf(':') + 1).trim(); + if (!route.startsWith('/') || route.includes('..')) return null; + const normalizedRoute = route.split(/[?#]/, 1)[0].replace(/\/{2,}/g, '/').replace(/\/$/, '') || '/'; + return `route:${normalizedRoute}`; + } + if (trimmed === '/') return 'route:/'; + if (trimmed.startsWith('/')) { + const absolute = path.resolve(trimmed); + const relativeToProject = path.relative(projectRoot, absolute); + const isProjectFile = relativeToProject && !relativeToProject.startsWith('..') && !path.isAbsolute(relativeToProject); + if (!isProjectFile && !fs.existsSync(absolute) && !trimmed.includes('..')) { + const normalizedRoute = trimmed.split(/[?#]/, 1)[0].replace(/\/{2,}/g, '/').replace(/\/$/, '') || '/'; + return `route:${normalizedRoute}`; + } + } + const abs = path.isAbsolute(trimmed) ? trimmed : path.resolve(projectRoot, trimmed); + const rel = path.relative(projectRoot, abs); + if (!rel || rel === '.' || rel.startsWith('..') || path.isAbsolute(rel)) return null; + return rel.split(path.sep).join('/'); +} + +export function surfaceBriefPathForTarget(target, { projectRoot = process.cwd() } = {}) { + const normalized = normalizeSurfaceTarget(target, { projectRoot }); + if (!normalized) return null; + const slugInput = normalized.startsWith('route:') ? `route${normalized.slice('route:'.length)}` : normalized; + const slug = slugFromTarget(slugInput, { cwd: projectRoot }); + return slug ? path.join(getSurfaceBriefDir(projectRoot), `${slug}.md`) : null; +} + +export function parseSurfaceBrief(text, filePath = null) { + const match = String(text || '').match(/^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/); + const meta = {}; + if (match) { + for (const line of match[1].split(/\r?\n/)) { + const colon = line.indexOf(':'); + if (colon < 0) continue; + const key = line.slice(0, colon).trim(); + const raw = line.slice(colon + 1).trim(); + if (!key) continue; + if (/^(?:\[|\{|\")/.test(raw) || /^(?:true|false|null|-?\d+(?:\.\d+)?)$/.test(raw)) { + try { meta[key] = JSON.parse(raw); continue; } catch { /* keep string */ } + } + meta[key] = raw.replace(/^['"]|['"]$/g, ''); + } + } + const primaryTarget = typeof meta.primary_target === 'string' ? meta.primary_target : null; + const relatedTargets = Array.isArray(meta.related_targets) + ? meta.related_targets.filter((value) => typeof value === 'string') + : []; + return { + path: filePath, + text: String(text || ''), + body: match ? String(text || '').slice(match[0].length).trim() : String(text || '').trim(), + meta, + slug: typeof meta.slug === 'string' ? meta.slug : filePath ? path.basename(filePath, '.md') : null, + primaryTarget, + relatedTargets, + targets: [primaryTarget, ...relatedTargets].filter(Boolean), + }; +} + +export function listSurfaceBriefs(projectRoot = process.cwd()) { + const dir = getSurfaceBriefDir(projectRoot); + let names; + try { + names = fs.readdirSync(dir).filter((name) => name.endsWith('.md')).sort(); + } catch { + return []; + } + return names.flatMap((name) => { + const filePath = path.join(dir, name); + try { + return [parseSurfaceBrief(fs.readFileSync(filePath, 'utf-8'), filePath)]; + } catch { + return []; + } + }); +} + +export function resolveSurfaceBrief(projectRoot = process.cwd(), target = null) { + const briefs = listSurfaceBriefs(projectRoot); + if (!target) { + return { + brief: briefs.length === 1 ? briefs[0] : null, + candidates: briefs, + reason: briefs.length === 1 ? 'only-brief' : briefs.length > 1 ? 'ambiguous' : 'none', + }; + } + + const normalized = normalizeSurfaceTarget(target, { projectRoot }); + if (!normalized) return { brief: null, candidates: briefs, reason: 'invalid-target' }; + const exactPath = surfaceBriefPathForTarget(normalized, { projectRoot }); + const exact = briefs.find((brief) => brief.path === exactPath && (!brief.targets.length || brief.targets.includes(normalized))); + if (exact) return { brief: exact, candidates: briefs, reason: 'slug' }; + const mapped = briefs.filter((brief) => brief.targets.includes(normalized)); + return { + brief: mapped.length === 1 ? mapped[0] : null, + candidates: mapped.length > 1 ? mapped : briefs, + reason: mapped.length === 1 ? 'mapping' : mapped.length > 1 ? 'ambiguous-target' : 'not-found', + }; +} + +export function writeSurfaceBrief({ + projectRoot = process.cwd(), + primaryTarget, + relatedTargets = [], + body, +}) { + const normalizedPrimary = normalizeSurfaceTarget(primaryTarget, { projectRoot }); + if (!normalizedPrimary) throw new Error('surface brief requires a concrete project-relative primary target or URL'); + const normalizedRelated = [...new Set(relatedTargets + .map((target) => normalizeSurfaceTarget(target, { projectRoot })) + .filter((target) => target && target !== normalizedPrimary))]; + const slug = slugFromTarget(normalizedPrimary, { cwd: projectRoot }); + const filePath = surfaceBriefPathForTarget(normalizedPrimary, { projectRoot }); + fs.mkdirSync(path.dirname(filePath), { recursive: true }); + const frontmatter = [ + '---', + `version: ${SURFACE_BRIEF_VERSION}`, + `slug: ${JSON.stringify(slug)}`, + `primary_target: ${JSON.stringify(normalizedPrimary)}`, + `related_targets: ${JSON.stringify(normalizedRelated)}`, + '---', + ].join('\n'); + fs.writeFileSync(filePath, `${frontmatter}\n\n${String(body || '').trim()}\n`, 'utf-8'); + return filePath; +} diff --git a/.kiro/skills/impeccable/scripts/lib/target-slug.mjs b/.kiro/skills/impeccable/scripts/lib/target-slug.mjs new file mode 100644 index 000000000..025915ad5 --- /dev/null +++ b/.kiro/skills/impeccable/scripts/lib/target-slug.mjs @@ -0,0 +1,33 @@ +import path from 'node:path'; + +const SLUG_MAX = 50; + +/** Derive one clone-stable slug from a concrete file path or URL. */ +export function slugFromTarget(resolved, { cwd = process.cwd() } = {}) { + if (!resolved || typeof resolved !== 'string') return null; + const trimmed = resolved.trim(); + if (!trimmed) return null; + + if (/^https?:\/\//i.test(trimmed)) { + let url; + try { url = new URL(trimmed); } catch { return null; } + return kebab(`${url.hostname}${url.pathname}`); + } + + const abs = path.isAbsolute(trimmed) ? trimmed : path.resolve(cwd, trimmed); + let rel = path.relative(cwd, abs); + if (rel.startsWith('..') || path.isAbsolute(rel)) rel = path.basename(abs); + if (!rel || rel === '.') return null; + return kebab(rel); +} + +export function kebab(value) { + const slug = String(value || '') + .toLowerCase() + .replace(/[/\\.]+/g, '-') + .replace(/[^a-z0-9-]+/g, '-') + .replace(/-+/g, '-') + .replace(/^-|-$/g, ''); + if (!slug) return null; + return slug.length <= SLUG_MAX ? slug : slug.slice(slug.length - SLUG_MAX).replace(/^-/, ''); +} diff --git a/.kiro/skills/impeccable/scripts/live-browser.js b/.kiro/skills/impeccable/scripts/live-browser.js index 616f1290a..47bbfefbf 100644 --- a/.kiro/skills/impeccable/scripts/live-browser.js +++ b/.kiro/skills/impeccable/scripts/live-browser.js @@ -2592,7 +2592,7 @@ minWidth: '16px', height: '16px', padding: '0 4px', borderRadius: '999px', background: tuneOpen ? C.brand : BP.hairline, - color: tuneOpen ? 'oklch(98% 0 0)' : 'inherit', + color: tuneOpen ? C.ink : 'inherit', fontFamily: MONO, fontSize: '9.5px', fontWeight: '600', lineHeight: '1', boxSizing: 'border-box', @@ -3131,7 +3131,7 @@ position: 'absolute', top: '2px', left: initial ? '18px' : '2px', width: '16px', height: '16px', borderRadius: '50%', - background: 'oklch(98% 0 0)', + background: C.ink, transition: 'left 0.18s ' + EASE, boxShadow: '0 1px 2px oklch(0% 0 0 / 0.2)', }); @@ -3165,7 +3165,7 @@ const b = el('button', { padding: '5px 4px', border: 'none', borderRadius: '3px', background: active ? C.brand : 'transparent', - color: active ? 'oklch(98% 0 0)' : P.text, + color: active ? C.ink : P.text, fontFamily: FONT, fontSize: '10.5px', fontWeight: '500', cursor: 'pointer', whiteSpace: 'nowrap', transition: 'background 0.1s ease, color 0.1s ease', @@ -3178,7 +3178,7 @@ segBtns.forEach(({ btn, val }) => { const on = val === o.value; btn.style.background = on ? C.brand : 'transparent'; - btn.style.color = on ? 'oklch(98% 0 0)' : P.text; + btn.style.color = on ? C.ink : P.text; }); applyParamValue(variantEl, p, o.value); queueCheckpoint('param_changed'); diff --git a/.kiro/skills/impeccable/scripts/surface-brief.mjs b/.kiro/skills/impeccable/scripts/surface-brief.mjs new file mode 100644 index 000000000..723f7c1b4 --- /dev/null +++ b/.kiro/skills/impeccable/scripts/surface-brief.mjs @@ -0,0 +1,74 @@ +#!/usr/bin/env node +import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath, pathToFileURL } from 'node:url'; +import { resolveProjectRoot } from './context.mjs'; +import { + listSurfaceBriefs, + resolveSurfaceBrief, + surfaceBriefPathForTarget, + writeSurfaceBrief, +} from './lib/surface-briefs.mjs'; + +function summary(brief, projectRoot) { + return { + slug: brief.slug, + path: path.relative(projectRoot, brief.path).split(path.sep).join('/'), + primaryTarget: brief.primaryTarget, + relatedTargets: brief.relatedTargets, + }; +} + +function main(argv) { + const [command, target, bodyFile, ...relatedTargets] = argv; + const projectRoot = resolveProjectRoot(process.cwd(), target ? { targetPath: target } : {}); + if (command === 'path') { + const filePath = surfaceBriefPathForTarget(target, { projectRoot }); + if (!filePath) throw new Error('surface brief path requires a concrete target'); + process.stdout.write(`${path.relative(process.cwd(), filePath) || filePath}\n`); + return; + } + if (command === 'list') { + process.stdout.write(`${JSON.stringify(listSurfaceBriefs(projectRoot).map((brief) => summary(brief, projectRoot)), null, 2)}\n`); + return; + } + if (command === 'read') { + const result = resolveSurfaceBrief(projectRoot, target || null); + if (result.brief) { + process.stdout.write(result.brief.text); + return; + } + if (result.candidates.length) process.stderr.write(`${JSON.stringify(result.candidates.map((brief) => summary(brief, projectRoot)), null, 2)}\n`); + process.exit(2); + } + if (command === 'write') { + if (!target || !bodyFile) throw new Error('usage: surface-brief.mjs write '); + const filePath = writeSurfaceBrief({ + projectRoot, + primaryTarget: target, + relatedTargets, + body: fs.readFileSync(bodyFile, 'utf-8'), + }); + process.stdout.write(`${path.relative(process.cwd(), filePath) || filePath}\n`); + return; + } + throw new Error('usage: surface-brief.mjs [target] [body-file] [related-target ...]'); +} + +function isMainModule() { + if (!process.argv[1]) return false; + try { + return fs.realpathSync(fileURLToPath(import.meta.url)) === fs.realpathSync(process.argv[1]); + } catch { + return import.meta.url === pathToFileURL(process.argv[1]).href; + } +} + +if (isMainModule()) { + try { + main(process.argv.slice(2)); + } catch (error) { + process.stderr.write(`${error?.message || error}\n`); + process.exit(1); + } +} diff --git a/.opencode/skills/impeccable/SKILL.md b/.opencode/skills/impeccable/SKILL.md index 0e477ab10..f16532d9c 100644 --- a/.opencode/skills/impeccable/SKILL.md +++ b/.opencode/skills/impeccable/SKILL.md @@ -1,7 +1,7 @@ --- name: impeccable description: Use when the user wants to design, redesign, shape, critique, audit, polish, clarify, distill, harden, optimize, adapt, animate, colorize, extract, or otherwise improve a frontend interface. Covers websites, landing pages, dashboards, product UI, app shells, components, forms, settings, onboarding, and empty states. Handles UX review, visual hierarchy, information architecture, cognitive load, accessibility, performance, responsive behavior, theming, anti-patterns, typography, fonts, spacing, layout, alignment, color, motion, micro-interactions, UX copy, error states, edge cases, i18n, and reusable design systems or tokens. Also use for bland designs that need to become bolder or more delightful, loud designs that should become quieter, live browser iteration on UI elements, or ambitious visual effects that should feel technically extraordinary. Not for backend-only or non-UI tasks. -version: 4.0.0-alpha.9 +version: 4.0.0-alpha.10 user-invocable: true argument-hint: "[craft|shape · audit|critique · animate|bolder|colorize|delight|layout|overdrive|quieter|typeset · adapt|clarify|distill · harden|onboard|optimize|polish · init|document|extract|live] [target]" license: Apache 2.0 @@ -10,56 +10,55 @@ allowed-tools: - Bash(node .opencode/skills/impeccable/scripts/*) --- -Designs and iterates production-grade frontend interfaces. Real working code, committed design choices, exceptional craft. +This skill gives you the tools and permission to create design that earns to be called out-of-distribution craft: Whereas before, your design work would have been safe, timid and measured, you now approach every design task as a award-winning design director with impeccable understanding for what makes exceptional design work: production-grade code, peak creativity, a clear POV, deep understanding of the needs of the client and users, and exceptional craft. -Approach every design task as the design lead at a small studio known for giving every client a visual identity that could not be mistaken for anyone else's. The client has already rejected work that felt templated; they are paying for a point of view. Claude is capable of extraordinary work. Don't hold back. +Core principles: +- Go all out. No hedging, no shortcuts. The deliverable must be complete (except assets the user must provide). +- Dream big and bold. Distinct, beautiful, outstanding and highly inspiring work. +- Iterate with tools available to you (e.g. visual understanding, browser screenshots) until you think this meets the bar. ## Setup -1. Run `node .opencode/skills/impeccable/scripts/context.mjs` once per session (if the runtime shows this skill's loaded base directory, run `node /scripts/context.mjs`; keep cwd at the user's project). It prints the project's context and its directives; follow what it prints. Once its output is in the conversation, never rerun it on a later turn. -2. `craft` and `shape` are build-path exceptions: the new-work gate below owns their flow, and on unattended runs its checkpoints resolve without pausing. For any other invoked sub-command (`audit`, `polish`, `live`, ...), immediately read **`reference/.md`** after `context.mjs` (the `.native` variant from the Commands table when the platform is `ios`/`android`/`adaptive`) and follow it. This read is a hard gate: do not inspect the target, run command-specific scripts, or edit files until the reference is loaded. -3. Read at least one project file (CSS / tokens / theme / a representative component) to learn what world you're in. If PRODUCT.md's `## Platform` is `ios` or `android`, also read `reference/.md` (`adaptive` reads both). +1. Run `node .opencode/skills/impeccable/scripts/context.mjs` once per session (if the runtime shows this skill's loaded base directory, run `node /scripts/context.mjs`; keep cwd at the user's project). Pass a named source file or route as `--target `. It loads PRODUCT.md, DESIGN.md, the matching surface brief, and native-platform guidance when applicable; follow its directives and do not rerun it. +2. Before acting, load the one playbook that owns the request: the Commands table's reference for an explicit or clearly implied sub-command, or [reference/new-work.md](reference/new-work.md) for a new surface or replacement visual world. Then inspect the target and at least one representative source of incumbent visual truth (tokens, theme, CSS, component, or asset) before editing. ## How to design -**The brief wins.** Where the brief pins down a direction (a named aesthetic, an era, a place, a material, a specific font or palette), follow it exactly, including when it asks for a look this skill warns is saturated. Redirecting a pinned direction toward your own taste is a failure, not a save. - -**Existing worlds are sacred.** Most of impeccable's work happens inside a site or app that already exists. When the surface has a committed design system (real tokens, deliberately chosen faces, a palette the brand owns), work inside that world: extend it, sharpen it, leave it unmistakably the same brand, and never degrade a working page's performance. Inventing parallel colors, fonts, or styles on an existing surface is a defect, not creativity. A scoped refinement keeps the named scope's content and media footprint unless the brief explicitly changes them; build the added emphasis by recomposing what is already there through hierarchy, proportion, rhythm, and the system's own motifs. - -**New identity work reads the playbook first.** When nothing committed exists (greenfield, or a codebase with no real tokens or chosen faces), or the user explicitly asks for a redesign that discards the current look, you MUST read [reference/new-work.md](reference/new-work.md) before making any design decision. Missing DESIGN.md alone does not make work greenfield: code, tokens, chosen type, components, and assets are incumbent design authority. Preserve and extend them unless the user asked to replace them. Producing a genuinely new identity without the playbook yields the generic default this skill exists to prevent. Scoped fixes inside an existing world don't need it; the craft floor below governs them. `context.mjs` prints the appropriate authority directive. +- **The brief wins.** Honor pinned aesthetics, eras, materials, fonts, and palettes even when they conflict with a saturated-pattern warning. Redirecting a clear brief toward your taste is failure. +- **Refinement preserves; redesign replaces.** Refinement keeps the incumbent identity, behavior, copy, and everything outside scope. Ask before replacing factual copy or adding claims. Redesign keeps product truth, content, function, native affordances, and constraints, but treats the old look as evidence and anti-reference; choose a replacement world in new-work and replace DESIGN.md. Never split the difference into polish on the discarded look. +- **Visual authority is evidence, not a filename.** Missing DESIGN.md alone does not make a project greenfield; new-work decides whether to preserve, expand, or replace the incumbent world. ## Modes -Name the visitor's mode before designing; the page's grammar follows from it, and most ruined pages are one mode wearing another mode's grammar. **The mode belongs to the surface, not the subject**: a landing page for a dense technical tool is still Persuade, with Persuade's full permission to be striking; a docs page for a fashion house is still Read. Deciding a page can be plain because its subject is workmanlike is the same category error in reverse. The brief and the surface decide the mode; PRODUCT.md's `register` field survives only as a family hint (`brand` covers Persuade and Experience, `product` covers Operate and Read). Depth beyond the paragraphs below: [reference/new-work.md](reference/new-work.md) when inventing identity, [reference/operate.md](reference/operate.md) for substantial Operate and Read work. +Choose the mode from the requested surface, not the product, and persist it only in that surface brief. A tool's landing page is still Persuade; a fashion house's documentation is still Read. See [new-work.md](reference/new-work.md) for new surfaces and [operate.md](reference/operate.md) for deeper Operate/Read guidance. -**Persuade** (the surface exists to win someone over; design IS the product). The deliverable is an impression that stops the scroll, earns the click, converts. Spans every genre; don't collapse them into one look. On new surfaces, briefs that imply imagery must ship real, verified imagery; a colored rectangle where a photo belongs reads as incomplete. New Persuade surfaces take their typeface procedure and reject list from [reference/new-work.md](reference/new-work.md). - -**Operate** (the surface is a tool someone works in; design SERVES the task). A person getting something done: scanability and consistency outrank expressiveness. These surfaces earn trust by feeling native to their platform: system font stacks and workhorse UI faces are legitimate and often correct here (the Persuade reject list does not apply). The brand lives in the details: focus states, empty states, microcopy, one owned accent. The usage scene is part of the spec: an interface read outdoors, in motion, or at a glance must survive its real ambient light, and the theme follows the scene, not the category's habit. - -**Read** (the surface exists to be understood; long-form, reference, guidance). The deliverable is comprehension, and comprehension is earned twice: a structure the reader can hold in their head with nothing standing between them and the answer, and a reading experience good enough to stay in, through typographic quality and whatever visual or interactive support genuinely helps the reader follow. The brand lives in type, spacing, and small accents. - -**Experience** (the surface presents a body of work; the page IS the work). The artifact leads, the interface recedes, and the visitor meets the work itself in the first viewport at every screen size. Boldness here means trusting the work. +- **Persuade:** win someone over; design is the product. Earn attention and action. Ship real imagery when the brief needs it; follow the committed world, not category habit. +- **Operate:** help someone do work. Scanability, consistency, native expectations, and the real usage scene outrank expression. Brand lives in precise details. +- **Read:** make something understood. Structure for comprehension, then make the reading experience worth staying in. +- **Experience:** present a body of work. Let the artifact lead from the first viewport; the interface recedes. ## Craft floor -Build to this floor without announcing it. The design detector (the project hook, `node .opencode/skills/impeccable/scripts/detect.mjs --json `, or `audit`) verifies most of it mechanically; resolve every finding before finalizing. Fix real defects, but use context judgment rather than distorting intentional design to appease a false positive. Classify any intentional exception explicitly and use the hook system's narrowest appropriate waiver when it must persist. +Build to this floor without announcing it. -- Contrast: body text ≥4.5:1 against its background (placeholders too); large text ≥3:1. Gray text on a colored background looks washed out: use a darker shade of the background's own hue, or a transparency of the text color. -- Shadows describe real light: an offset and a soft blur. A zero-offset colored halo is decoration announcing itself. -- Spacing has rhythm: generous separations, tight groupings; cramped padding reads as broken; the space above a heading exceeds the space below it. Verify computed spacing, not intended spacing. -- Type: body line length 65-75ch; display clamp() max ≤6rem; letter-spacing ≥-0.04em; `text-wrap: balance` on headings; modular scale ≥1.25 between steps; light-on-dark adds 0.05-0.1 line-height. Pair faces on a contrast axis, never two similar-but-not-identical ones; one family with committed weight contrast beats a timid pair. Test headings at every breakpoint; overflow means reduce the clamp or rewrite the copy. -- Motion is part of the build: one orchestrated moment beats scattered effects; ease-out exponential curves; reveals enhance an already-visible default (content gated on a class-triggered transition ships blank in hidden tabs and headless renderers). Responsive down to mobile and visible keyboard focus are part of the floor. -- Ship real content (no placeholders, dead links, or fake controls) and cover the interaction states people will actually hit (hover, focus, disabled, loading, error, empty). -- Copy is design material: name things the way the page's own people speak, make every control say what it does, and make every error say what happened and what to do next. -- Before finishing, re-read the brief: every requirement it names must exist on the page, findable in seconds. A beautiful page missing an asked-for feature is unfinished. +- **Contrast:** body and placeholder text ≥4.5:1; large text ≥3:1. On colored surfaces, tint secondary text from that hue or the foreground instead of using gray. +- **Depth:** shadows describe light with offset and soft blur; zero-offset colored halos are decoration. +- **Spacing:** tight groups, generous separation, no cramped containers; space above a heading exceeds space below. Verify computed values. +- **Type:** body measure 65–75ch; display max 6rem and tracking floor -0.04em; balance headings; use clear scale/weight contrast; test overflow at every breakpoint. +- **Motion:** author one coherent moment instead of scattered effects. Use exponential ease-out and an already-visible default. Premium motion is not transform/opacity alone: it may add focus, depth, masks, light, or material change through blur/filter, backdrop-filter, clip-path/masks, or shadow when smooth. Always provide reduced motion. +- **Shipping:** real content, working controls, responsive composition, keyboard focus, and the states users hit: hover, disabled, loading, error, and empty. +- **Copy:** use the product's language; controls name their action, errors name the problem and recovery. +- **Coverage:** every brief requirement must exist and be findable within seconds. + +Before finishing changed UI, follow the quality guidance supplied by `context.mjs` and hooks. Context requests a manual scan only when no automatic detector is active; never add a second detector pass. ## Commands | Command | Category | Description | Reference | |---|---|---|---| -| `craft [feature]` | Build | The standard build flow with attended checkpoints | [reference/new-work.md](reference/new-work.md) | +| `craft [feature]` | Build | Deprecated alias for an ordinary new-work request | [reference/craft.md](reference/craft.md) | | `shape [feature]` | Build | Plan UX/UI before writing code | [reference/shape.md](reference/shape.md) | -| `init` | Build | Set up project context: PRODUCT.md, DESIGN.md, live config, next steps | [reference/init.md](reference/init.md) | +| `init` | Build | Capture durable product context in PRODUCT.md | [reference/init.md](reference/init.md) | | `document` | Build | Generate DESIGN.md from existing project code | [reference/document.md](reference/document.md) | | `extract [target]` | Build | Pull reusable tokens and components into design system | [reference/extract.md](reference/extract.md) | | `critique [target]` | Evaluate | UX design review with heuristic scoring | [reference/critique.md](reference/critique.md) | @@ -81,7 +80,14 @@ Build to this floor without announcing it. The design detector (the project hook | `optimize [target]` | Fix | Diagnose and fix UI performance | [reference/optimize.md](reference/optimize.md) | | `live` | Iterate | Visual variant mode: pick elements in the browser, generate alternatives | [reference/live.md](reference/live.md) | -Routing: **no argument** → read [reference/routing.md](reference/routing.md) and present the context-aware menu (never auto-run a command). **First word matches a command** (or `pin` / `unpin` / `hooks`) → load its reference (native variant on native platforms) and follow it; everything after the command name is the target. **Intent clearly maps to one command** ("fix the spacing" → `layout`, "rewrite this error" → `clarify`) → same; if two fit, ask once. **Otherwise** → general design invocation: apply Setup and this file's guidance; builds flow through the new-work gate above, whose playbook carries the direction checkpoint and the finishing pass. `teach` routes to `init`, and `craft` routes to the standard build flow with attended checkpoints. If setup diverted into `init` for a build request, finish init, refresh context, then resume. +Routing: + +- **No argument:** read [routing.md](reference/routing.md) and present its context-aware menu; never auto-run a command. +- **Explicit or clearly implied command:** load its reference (native variant on native platforms) and follow it. Ask once if two commands fit. +- **Otherwise:** treat the request as general design work. Missing PRODUCT.md routes through init; new surfaces and replacement worlds use new-work. +- `teach` aliases `init`. `craft` is a deprecated alias for ordinary new-work and adds nothing. `shape` owns task discovery, then enters new-work only for visual-world and surface-concept decisions. + +After init writes PRODUCT.md, resume without rerunning `context.mjs`. **Pin / Unpin:** `node .opencode/skills/impeccable/scripts/pin.mjs ` creates or removes a standalone `/` shortcut. Report the script's result concisely; relay stderr verbatim on error. diff --git a/.opencode/skills/impeccable/reference/android.md b/.opencode/skills/impeccable/reference/android.md index 3575c31fb..6337b9018 100644 --- a/.opencode/skills/impeccable/reference/android.md +++ b/.opencode/skills/impeccable/reference/android.md @@ -2,7 +2,7 @@ For native Android apps: Jetpack Compose, Android Views, React Native, Expo, Flutter shipping to Android hardware. -On native, register narrows. Material Design 3 governs structure, navigation, and interaction whatever the register; brand expresses through Material's theming (color roles, type scale, shape, motion). A Material-everywhere cross-platform app that also ships to iPhone still owes iOS its OS guarantees on that hardware: safe-area insets, Reduce Motion, edge-swipe back. +On native, the visitor mode narrows what expression may override. Material Design 3 governs structure, navigation, and interaction in every mode; brand expresses through Material's theming (color roles, type scale, shape, motion). A Material-everywhere cross-platform app that also ships to iPhone still owes iOS its OS guarantees on that hardware: safe-area insets, Reduce Motion, edge-swipe back. ## The Android slop test diff --git a/.opencode/skills/impeccable/reference/animate.md b/.opencode/skills/impeccable/reference/animate.md index d632a5c14..d51bb6d63 100644 --- a/.opencode/skills/impeccable/reference/animate.md +++ b/.opencode/skills/impeccable/reference/animate.md @@ -1,203 +1,88 @@ > **Additional context needed**: performance constraints. -Add motion that conveys state, gives feedback, and clarifies hierarchy. Cut motion that exists only for decoration. Animation fatigue is a real cost; spend the budget on the moments that need it. +Use motion to explain state, relationship, and hierarchy, or to create one authored moment the surface has earned. Decoration without purpose is animation debt. --- -## Register +## Visitor mode -Persuade + Experience: 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. +- **Persuade + Experience:** motion may carry the voice. Prefer one rehearsed focal sequence to repeated section reveals. +- **Operate + Read:** motion serves feedback, state, and continuity. Keep routine transitions fast and do not make users wait through page-load choreography. +- **Native (`ios` / `android` / `adaptive`):** follow the Motion section of [ios.md](ios.md) or [android.md](android.md), including the platform's Reduce Motion behavior. Do not apply the web tooling below. -Operate + Read: 150–250 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. +## Find the job -Native (`ios` / `android` / `adaptive`): implementation follows the Motion section of [ios.md](ios.md) / [android.md](android.md) (read it first if Setup hasn't already): system transitions and OS Reduce Motion, never the web tooling below. +Inspect the existing motion language, interaction states, target devices, and performance budget. Find only the places where motion would: ---- +- acknowledge an action; +- make a state change or spatial relationship legible; +- preserve continuity through navigation or layout change; +- direct attention at a meaningful moment; +- embody the selected visual world. -## Assess Animation Opportunities +Ask only when a material constraint cannot be inferred. Do not animate a static area merely because it exists. -Analyze where motion would improve the experience: +## Set the motion thesis -1. **Identify static areas**: - - **Missing feedback**: Actions without visual acknowledgment (button clicks, form submission, etc.) - - **Jarring transitions**: Instant state changes that feel abrupt (show/hide, page loads, route changes) - - **Unclear relationships**: Spatial or hierarchical relationships that aren't obvious - - **Lack of delight**: Functional but joyless interactions - - **Missed guidance**: Opportunities to direct attention or explain behavior +Write a short plan before implementation: -2. **Understand the context**: - - What's the personality? (Playful vs serious, energetic vs calm) - - What's the performance budget? (Mobile-first? Complex page?) - - Who's the audience? (Motion-sensitive users? Power users who want speed?) - - What matters most? (One hero animation vs many micro-interactions?) +- **Focal moment:** the one sequence or interaction that deserves authorship, if any. +- **Continuity:** the state, layout, or navigation changes that need explanation. +- **Feedback:** the controls and outcomes that need acknowledgment. +- **Budget:** which effects may be expensive, how often they run, and the reduced-motion equivalent. -If any of these are unclear from the codebase, STOP and call the `question` tool to clarify. +The focal moment must come from this product and surface concept. A generic fade-and-rise, hover lift, parallax layer, or scroll reveal is not a thesis. -**CRITICAL**: Respect `prefers-reduced-motion`. Always provide non-animated alternatives for users who need them. +## Choose material by meaning -## Plan Animation Strategy +Transform and opacity are reliable foundations, not the entire palette. Choose properties for what the transition communicates: -Create a purposeful animation plan: +- **Continuity and relationship:** shared-element motion, FLIP-style transforms, view transitions, or deliberate spatial movement. +- **Focus and depth:** bounded blur, filter, backdrop, light, or shadow changes. +- **Reveal and composition:** masks, clip paths, cropping, or controlled occlusion. +- **Material and energy:** color, gradient position, texture, distortion, or shader effects when the world and runtime support them. +- **State and feedback:** the smallest change that makes cause and result unmistakable. -- **Feedback layer**: Which interactions need acknowledgment? -- **Transition layer**: Which state changes need smoothing? -- **Entrance moment**: The ONE entrance worth rehearsing, where the mode invites it. Not every surface wants one. -- **Delight layer**: Where can we surprise and delight? +Do not stack techniques for spectacle. One strong material idea, carried through the focal sequence and quiet supporting states, is usually enough. -**IMPORTANT**: One well-orchestrated experience beats scattered animations everywhere. Focus on high-impact moments. +Sibling stagger is appropriate when a list appears as a list. Cap the total delay, and never reinterpret every scrolled section as a staggered list. -## Implement Animations +## Timing and easing -Add motion systematically across these categories: +Timing should express distance and consequence: -### Micro-interactions -- **Button feedback**: - - Hover: Subtle scale (1.02-1.05), color shift, shadow increase - - Click: Quick scale down then up (0.95 → 1), ripple effect - - Loading: Spinner or pulse state -- **Form interactions**: - - Input focus: Border color transition, slight scale or glow - - Validation: Shake on error, check mark on success, smooth color transitions -- **Toggle switches**: Smooth slide + color transition (200-300ms) -- **Checkboxes/radio**: Check mark animation, ripple effect -- **Like/favorite**: Scale + rotation, particle effects, color transition +| Duration | Typical use | +|---|---| +| 100–150 ms | immediate feedback | +| 150–300 ms | routine state change | +| 300–500 ms | layout, overlay, or view transition | +| 500–800 ms | a deliberately authored focal entrance | -### State Transitions -- **Show/hide**: Fade + slide (not instant), appropriate timing (200-300ms) -- **Expand/collapse**: Height transition with overflow handling, icon rotation -- **Loading states**: Skeleton screen fades, spinner animations, progress bars -- **Success/error**: Color transitions, icon animations, gentle scale pulse -- **Enable/disable**: Opacity transitions, cursor changes +Exit faster than entrance. Use natural deceleration such as `cubic-bezier(0.16, 1, 0.3, 1)` for confident arrivals; do not use bounce or elastic curves by reflex. Long feedback feels like latency. -### Entrance Animations -- **The one entrance moment** (where the mode invites it): a committed entrance for primary content (scale, parallax, or a creative effect) -- **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. +## Implement to the runtime - 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. +- Use CSS transitions and keyframes for declarative state and bounded sequences. +- Use Web Animations API or the project's existing motion library for interruption, sequencing, and dynamic values. +- Use View Transitions or shared-element techniques when continuity across states is the point. +- Use scroll-driven motion only when the scroll relationship itself carries meaning, with a robust fallback. +- Do not add a dependency for an effect the existing stack can express cleanly. -### Navigation & Flow -- **Page transitions**: Crossfade between routes, shared element transitions -- **Tab switching**: Slide indicator, content fade/slide -- **Carousel/slider**: Smooth transforms, snap points, momentum -- **Scroll effects**: Parallax layers, sticky headers with state changes, scroll progress indicators +Keep content visible in the default state so failed scripts do not hide the page. Avoid casually animating layout-driving properties such as `width`, `height`, `top`, `left`, and margins; use FLIP, transforms, or grid techniques when appropriate. Bound blur, filter, shadow, canvas, and shader work to isolated regions. Apply `will-change` only during known animation. Measure on target viewports and devices rather than assuming transform means fast. -### Feedback & Guidance -- **Hover hints**: Tooltip fade-ins, cursor changes, element highlights -- **Drag & drop**: Lift effect (shadow + scale), drop zone highlights, smooth repositioning -- **Copy/paste**: Brief highlight flash on paste, "copied" confirmation -- **Focus flow**: Highlight path through form or workflow +## Accessibility and control -### Delight Moments -- **Empty states**: Subtle floating animations on illustrations -- **Completed actions**: Confetti, check mark flourish, success celebrations -- **Easter eggs**: Hidden interactions for discovery -- **Contextual animation**: Weather effects, time-of-day themes, seasonal touches +Honor `prefers-reduced-motion` with an intentional alternative. Preserve state change and hierarchy while removing travel, parallax, flashing, or prolonged sequences. Do not replace every animation globally with `0.01ms` if that destroys useful feedback. Motion must never block focus, interaction, reading, or task completion. -## Technical Implementation +Respect autoplay and sound preferences. Any nonessential loop must stop when offscreen or hidden. -Use appropriate techniques for each animation: +## Verify -### Timing & Easing +- The focal motion is specific to the selected world and surface. +- Every supporting animation explains feedback, state, or relationship. +- Interruption and repeated use behave correctly. +- Desktop, mobile, keyboard, and reduced-motion paths remain usable. +- Expensive effects stay smooth on the target device. +- Removing an animation would lose meaning or authored character, not merely decoration. -**Duration: the 100/300/500 rule.** Timing matters more than easing for "feels right": - -| Duration | Use Case | Examples | -|----------|----------|----------| -| **100–150ms** | Instant feedback | Button press, toggle, color change | -| **200–300ms** | State changes | Menu open, tooltip, hover state | -| **300–500ms** | Layout changes | Accordion, modal, drawer | -| **500–800ms** | Entrance animations | Page load, hero reveal | - -**Easing curves (use these, not CSS defaults):** -```css -/* Recommended: natural deceleration */ ---ease-out-quart: cubic-bezier(0.25, 1, 0.5, 1); /* Smooth */ ---ease-out-quint: cubic-bezier(0.22, 1, 0.36, 1); /* Slightly snappier */ ---ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1); /* Confident, decisive */ - -/* AVOID: feel dated and tacky */ -/* bounce: cubic-bezier(0.34, 1.56, 0.64, 1); */ -/* elastic: cubic-bezier(0.68, -0.6, 0.32, 1.6); */ -``` - -**Exit animations are faster than entrances.** Use ~75% of enter duration. - -### CSS Animations -```css -/* Prefer for simple, declarative animations */ -- transitions for state changes -- @keyframes for complex sequences -- transform and opacity for reliable movement -- blur, filters, masks, clip paths, shadows, and color shifts for premium atmospheric effects when verified smooth -``` - -### JavaScript Animation -```javascript -/* Use for complex, interactive animations */ -- Web Animations API for programmatic control -- Framer Motion for React -- 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 -- **Layout safety**: Avoid casual animation of layout-driving properties (`width`, `height`, `top`, `left`, margins) -- **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) { - * { - animation-duration: 0.01ms !important; - animation-iteration-count: 1 !important; - transition-duration: 0.01ms !important; - } -} -``` - -**NEVER**: -- Use bounce or elastic easing curves; they feel dated and draw attention to the animation itself -- Animate layout properties casually (`width`, `height`, `top`, `left`, margins) when transform, FLIP, or grid-based techniques would work -- Use durations over 500ms for feedback (it feels laggy) -- Animate without purpose (every animation needs a reason) -- Ignore `prefers-reduced-motion` (this is an accessibility violation) -- Animate everything (animation fatigue makes interfaces feel exhausting) -- Block interaction during animations unless intentional - -## Verify Quality - -Test animations thoroughly: - -- **Smooth at 60fps**: No jank on target devices -- **Feels natural**: Easing curves feel organic, not robotic -- **Appropriate timing**: Not too fast (jarring) or too slow (laggy) -- **Reduced motion works**: Animations disabled or simplified appropriately -- **Doesn't block**: Users can interact during/after animations -- **Adds value**: Makes interface clearer or more delightful - -When the motion clarifies state instead of decorating it, hand off to `/impeccable polish` for the final pass. +When motion earns its place, hand off to `/impeccable polish` for the final pass. diff --git a/.opencode/skills/impeccable/reference/audit.md b/.opencode/skills/impeccable/reference/audit.md index 72815af69..70d80771a 100644 --- a/.opencode/skills/impeccable/reference/audit.md +++ b/.opencode/skills/impeccable/reference/audit.md @@ -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 - diff --git a/.opencode/skills/impeccable/reference/audit.native.md b/.opencode/skills/impeccable/reference/audit.native.md index 737d50e4f..0126fa157 100644 --- a/.opencode/skills/impeccable/reference/audit.native.md +++ b/.opencode/skills/impeccable/reference/audit.native.md @@ -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) diff --git a/.opencode/skills/impeccable/reference/bolder.md b/.opencode/skills/impeccable/reference/bolder.md index 9307d0d06..5408e49d0 100644 --- a/.opencode/skills/impeccable/reference/bolder.md +++ b/.opencode/skills/impeccable/reference/bolder.md @@ -8,12 +8,12 @@ ## Why it reads flat -A section usually reads flat for reasons its neighbors have already solved. Look at what the rest of the page does that this section does not: the display type at full strength, the structural devices that carry meaning, the signature motif, the density and pacing, the specificity of the copy. A flat section is typically one that quietly opts out of the system's own strongest moves. The most reliable bolder pass brings the target up to the expressive level its neighbors already reach, in the system's own vocabulary rather than a new one. +A section usually reads flat for reasons its neighbors have already solved. Look at what the rest of the page does that this section does not: the display type at full strength, the structural devices that carry meaning, the signature motif, the density and pacing. A flat section is typically one that quietly opts out of the system's own strongest moves. The most reliable bolder pass brings the target up to the expressive level its neighbors already reach, in the system's own vocabulary rather than a new one. ## The amplification - **Amplify what the system already owns.** Reuse its motif and its type scale at full strength, turned up for this section rather than invented for it. The bolder version should look more like the same brand, not less. -- **Let the content carry the weight.** Flat copy makes a flat design, and no amount of size fixes a generic claim. Make every word specific enough to earn its place, and let the section's real evidence do the work that a decorative addition would otherwise fake. +- **Keep content true.** Existing claims are part of the scope: preserve them unless the user supplies replacements. If real evidence is essential to the direction but absent, ask for it. - **Commit, then clarify.** Half-measures read as noise. Make the one decisive move completely, then quiet everything around it so the move is legible. If every element got louder, the section got flatter. - **Give it its own rhythm.** The target should read as a peak in the scroll, a shift in density or pace from what surrounds it, not simply more of the same. diff --git a/.opencode/skills/impeccable/reference/clarify.md b/.opencode/skills/impeccable/reference/clarify.md index 00ee978c3..3047a9d82 100644 --- a/.opencode/skills/impeccable/reference/clarify.md +++ b/.opencode/skills/impeccable/reference/clarify.md @@ -1,288 +1,94 @@ -> **Additional context needed**: audience technical level and users' mental state in context. +> **Additional context needed**: audience knowledge and emotional state. -Find the unclear, confusing, or poorly written interface text and rewrite it. Vague copy creates support tickets and abandonment; specific copy gets users through the task. +Rewrite unclear interface text so users understand what happened, what matters, and what to do next. Preserve factual meaning, product terminology, and brand voice. +## Audit the language ---- +Read the entire interaction path, not isolated strings. Identify: -## Assess Current Copy +- ambiguous nouns, verbs, and actions; +- internal jargon or assumed knowledge; +- vague labels, outcomes, and system states; +- missing consequences, recovery, or timing; +- inconsistent terminology and capitalization; +- redundant headings, intros, helper text, and confirmations; +- text that breaks at realistic widths or in translation; +- tone that ignores stress, risk, success, or urgency. -Identify what makes the text unclear or ineffective: +Infer audience and task from product context and surrounding UI. Ask before changing factual claims, legal meaning, or a term that may be domain-specific. -1. **Find clarity problems**: - - **Jargon**: Technical terms users won't understand - - **Ambiguity**: Multiple interpretations possible - - **Passive voice**: "Your file has been uploaded" vs "We uploaded your file" - - **Length**: Too wordy or too terse - - **Assumptions**: Assuming user knowledge they don't have - - **Missing context**: Users don't know what to do or why - - **Tone mismatch**: Too formal, too casual, or inappropriate for situation +## Set the message hierarchy -2. **Understand the context**: - - Who's the audience? (Technical? General? First-time users?) - - What's the user's mental state? (Stressed during error? Confident during success?) - - What's the action? (What do we want users to do?) - - What's the constraint? (Character limits? Space limitations?) +For each state, decide: -**CRITICAL**: Clear copy helps users succeed. Unclear copy creates frustration, errors, and support tickets. +1. the one fact the user needs now; +2. the action available next; +3. supporting context that changes the decision; +4. the appropriate tone for this moment. -## Plan Copy Improvements +Say each idea once. If the heading already explains the state, the introduction should add new information or disappear. -Create a strategy for clearer communication: +## Rewrite by function -- **Primary message**: What's the ONE thing users need to know? -- **Action needed**: What should users do next (if anything)? -- **Tone**: How should this feel? (Helpful? Apologetic? Encouraging?) -- **Constraints**: Length limits, brand voice, localization considerations +### Actions and navigation -**IMPORTANT**: Good UX writing is invisible. Users should understand immediately without noticing the words. +Use a specific verb and object when the outcome is not already obvious. Labels should describe what will happen, not the gesture used to trigger it. Keep the same noun and verb for the same concept throughout the product. -## Improve Copy Systematically +For destructive actions, name the object and consequence. Prefer undo over confirmation when recovery is safe. When confirmation is necessary, name the action on both the message and button instead of using `Yes`, `No`, `OK`, or `Submit`. -Refine text across these common areas: +### Forms -### Error Messages -**Bad**: "Error 403: Forbidden" -**Good**: "You don't have permission to view this page. Contact your admin for access." +Use persistent labels; placeholders are examples, not labels. Put format and eligibility requirements before submission. Explain why information is requested only when it is not obvious. Required and optional treatment should be consistent. -**Bad**: "Invalid input" -**Good**: "Email addresses need an @ symbol. Try: name@example.com" +Validation says what needs attention and how to correct it without blaming the user. Keep related instructions near the field and announce errors accessibly. -**Principles**: -- Explain what went wrong in plain language -- Suggest how to fix it -- Don't blame the user -- Include examples when helpful -- Link to help/support if applicable - -### Form Labels & Instructions -**Bad**: "DOB (MM/DD/YYYY)" -**Good**: "Date of birth" (with placeholder showing format) - -**Bad**: "Enter value here" -**Good**: "Your email address" or "Company name" - -**Principles**: -- Use clear, specific labels (not generic placeholders) -- Show format expectations with examples -- Explain why you're asking (when not obvious) -- Put instructions before the field, not after -- Keep required field indicators clear - -### Button & CTA Text -**Bad**: "Click here" | "Submit" | "OK" -**Good**: "Create account" | "Save changes" | "Got it, thanks" +### Errors and permissions -**Principles**: -- Describe the action specifically -- Use active voice (verb + noun) -- Match user's mental model -- Be specific ("Save" is better than "OK") +An actionable error answers: -### Help Text & Tooltips -**Bad**: "This is the username field" -**Good**: "Choose a username. You can change this later in Settings." +1. what failed; +2. why, when known and useful; +3. how to recover or what alternative remains. -**Principles**: -- Add value (don't just repeat the label) -- Answer the implicit question ("What is this?" or "Why do you need this?") -- Keep it brief but complete -- Link to detailed docs if needed +Do not expose internal codes as the primary message. Do not promise a cause or resolution the system cannot know. Treat privacy, payment, deletion, access loss, and blocked work seriously; warmth is welcome, jokes are not. -### Empty States -**Bad**: "No items" -**Good**: "No projects yet. Create your first project to get started." +### Loading, empty, and success states -**Principles**: -- Explain why it's empty (if not obvious) -- Show next action clearly -- Make it welcoming, not dead-end +Loading text names the real operation and sets an honest expectation when the wait is meaningful. Show determinate progress when available; never invent progress. -### Success Messages -**Bad**: "Success" -**Good**: "Settings saved! Your changes will take effect immediately." +An empty state distinguishes first use, no results, filters, permissions, and failure. Explain the state and provide the next useful action. -**Principles**: -- Confirm what happened -- Explain what happens next (if relevant) -- Be brief but complete -- Match the user's emotional moment (celebrate big wins) - -### Loading States -**Bad**: "Loading..." (for 30+ seconds) -**Good**: "Analyzing your data... this usually takes 30-60 seconds" - -**Principles**: -- Set expectations (how long?) -- Explain what's happening (when it's not obvious) -- Show progress when possible -- Offer escape hatch if appropriate ("Cancel") +Success confirms the completed outcome and mentions the next consequence only when it changes what the user should do. Routine success should be brief. -### Confirmation Dialogs -**Bad**: "Are you sure?" -**Good**: "Delete 'Project Alpha'? This can't be undone." +### Help and instructional text -**Principles**: -- State the specific action -- Explain consequences (especially for destructive actions) -- Use clear button labels ("Delete project" not "Yes") -- Don't overuse confirmations (only for risky actions) +Helper text answers an implicit question instead of restating the control. Use progressive disclosure for uncommon detail. Link text must make sense out of context; icon-only controls need accessible names. -### Navigation & Wayfinding -**Bad**: Generic labels like "Items" | "Things" | "Stuff" -**Good**: Specific labels like "Your projects" | "Team members" | "Settings" +## Voice, accessibility, and localization -**Principles**: -- Be specific and descriptive -- Use language users understand (not internal jargon) -- Make hierarchy clear -- Consider information scent (breadcrumbs, current location) +Voice stays consistent; tone adapts to the moment. Use plain language without flattening terminology the audience genuinely knows. -## Apply Clarity Principles +- Write complete translatable messages rather than concatenated fragments. +- Keep variables and numbers structured so translators can reorder them. +- Allow expansion instead of abbreviating prematurely. +- Make alt text convey the image's information; use empty alt for decoration. +- Keep screen-reader names aligned with visible labels and outcomes. +- Do not rely on punctuation, color, or iconography to carry the message alone. -Every piece of copy should follow these rules: +Maintain a short terminology glossary when inconsistency spans the product. Do not vary words for literary effect in an interface. -1. **Be specific**: "Enter email" not "Enter value" -2. **Be concise**: Cut unnecessary words (but don't sacrifice clarity) -3. **Be active**: "Save changes" not "Changes will be saved" -4. **Be human**: "Oops, something went wrong" not "System error encountered" -5. **Tell users what to do**, not just what happened -6. **Be consistent**: Use same terms throughout (don't vary for variety) +## Verify -**NEVER**: -- Use jargon without explanation -- Blame users ("You made an error" → "This field is required") -- Be vague ("Something went wrong" without explanation) -- Use passive voice unnecessarily -- Write overly long explanations (be concise) -- Use humor for errors (be empathetic instead) -- Assume technical knowledge -- Vary terminology (pick one term and stick with it) -- Repeat information (headers restating intros, redundant explanations) -- Use placeholders as the only labels (they disappear when users type) +Read the flow in context and test: -## Verify Improvements +- comprehension without hidden product knowledge; +- actionability at errors, empty states, and decision points; +- factual accuracy and consistent terminology; +- scanability at target widths and 200% zoom; +- long names, localization expansion, pluralization, and dynamic values; +- accessible names and announced state changes; +- tone appropriate to consequence and emotional context. -Test that copy improvements work: +The final copy is as short as it can be without removing meaning or recovery. -- **Comprehension**: Can users understand without context? -- **Actionability**: Do users know what to do next? -- **Brevity**: Is it as short as possible while remaining clear? -- **Consistency**: Does it match terminology elsewhere? -- **Tone**: Is it appropriate for the situation? - -When the copy reads cleanly, hand off to `/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. +When the language reads cleanly, hand off to `/impeccable polish` for the final pass. diff --git a/.opencode/skills/impeccable/reference/codex.md b/.opencode/skills/impeccable/reference/codex.md index 18a924a91..6c550f1dc 100644 --- a/.opencode/skills/impeccable/reference/codex.md +++ b/.opencode/skills/impeccable/reference/codex.md @@ -1,105 +1,38 @@ -# Codex: Visual Direction & Asset Production +# Codex: Surface Probes & Asset Production -This file is loaded by `/impeccable craft` when the harness has native image generation (currently Codex via `image_gen`). Other harnesses skip it. It covers the two craft steps that depend on real image generation: landing the visual direction, and producing the raster assets the implementation will compose. +Load this from [new-work.md](new-work.md) only when the harness has native image generation and a substantial, high-fidelity surface would benefit from seeing the shortlisted concept before code. PRODUCT.md and DESIGN.md are preconditions. New-work has already resolved the visual world; this file must not reopen it. -Read this *before* generating any images. The order matters, and the per-step user pauses are what keep generated imagery from drifting away from the brief. +The purpose of a probe is to test composition, narrative, hierarchy, density, focal moment, signature use, and image requirements. It is not a second identity workshop. Keep DESIGN.md's palette, typography direction, material language, component character, imagery stance, and motion grammar fixed. -### Four stop points before code +## Generate the smallest useful probe set -Steps A through D each end with the user. Do not advance past any of them on your own read of the situation. +Generate one to three high-fidelity north-star comps using the native image-generation capability. Base them on the real content and the surface concepts already developed with the user. -1. **STOP after Step A questions.** Wait for answers. -2. **STOP after Step B palette generation.** Wait for "confirm palette." -3. **STOP after Step C mocks.** Wait for direction approval or delegation. -4. **Only after Step D approves a direction** do you return to the build (new-work.md's plan, then code). +- When the user shortlisted multiple concepts, show one clear expression of each. +- When one concept is already selected, vary only the structural uncertainty that the image can resolve: topology, sequence, density, hierarchy, focal composition, or interaction framing. +- Show enough beyond the opening moment to prove the concept can govern the whole requested surface. +- Do not generate a palette artifact, ask new atmosphere questions, introduce a different type voice, or invent a new motif. If the committed world cannot support the concept, return to the concept shortlist rather than changing the world. -Prior shape approval does **not** satisfy any of these. Shape's "confirm or override" advances you into Step A; it is not a substitute for it. +Treat each comp as a direction test, not a screenshot specification. Core UI text, responsive behavior, accessibility, semantics, and interaction states remain implementation responsibilities. -## Step A: Explore Directions with the User +## One approval point -Before generating anything, run a brief direction conversation grounded in the shape brief. +Show the probes together and ask what should carry forward, what feels false to the world, and whether the selected surface concept should be approved, combined, revised, or rejected. Then stop and wait. A structured simulated user counts as attended and receives the same question. -**Step A is required even when shape just produced a confirmed brief.** The shape questions and Step A questions cover different ground: shape pins purpose, content, scope; Step A pins palette, atmosphere, and named visual references for the comps you're about to generate. The only time you can skip Step A is when the user has already answered these exact palette/atmosphere/reference questions in the same session. +Do not begin code until the user approves a direction or explicitly delegates the choice. If they delegate, choose using the task brief, PRODUCT.md, and DESIGN.md, and state the evidence. Approval refines the task concept; it does not modify DESIGN.md. -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: +After approval, summarize the composition and the parts of the comp that must not be literalized. Return to new-work.md and write the direction contract from the approved surface concept before code. -- "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.)" +## Inventory implementation fidelity -**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. +Before building, inventory the approved comp's major visible ingredients and choose an implementation medium for each: semantic HTML/CSS/SVG, existing project asset, generated raster, sourced raster, icon library, canvas/WebGL, or accepted omission. -## Step B: Generate the Brand Palette First +Pay special attention to the dominant composition, signature use, image-native content, second-fold system, and any interaction the still image only implies. If the concept depends on a photograph, architectural scene, product object, portrait, or other raster-native material, do not silently replace it with generic CSS scenery. -Generate **one** palette artifact before any mocks. This is a small, focused image: typography pairing on the chosen background, primary + accent color swatches, one signature ornament or motif. Single image, single pass. +Treat the comp as a north star, not something to trace. Do not rasterize core UI text or controls. Do not substitute a different visual driver after approval without asking. -Why palette first: mocks generated against a vague color sense produce noise that drowns out the structural decisions. A confirmed palette is the first concrete contract for everything downstream. +## Produce only the assets the build needs -Show the palette to the user. Ask one question: "This is the palette I'm locking in for the mocks. Confirm, or call out what to shift?" +When clean raster ingredients are required and a scoped subagent is available and authorized, use `impeccable_asset_producer`. Give it the approved comp, output paths, required dimensions and formats, transparency needs, crop notes, and what must remain semantic code. Otherwise produce the minimum required assets with the native image-generation capability in the current thread. -**STOP and wait for confirmation.** Do not generate mocks against an unconfirmed palette. "Probably good enough" is the wrong call here; the palette is the contract for everything downstream. - -## Step C: Generate 1-3 Visual Mocks Against the Palette - -Once the palette is confirmed, generate **1 to 3** high-fidelity north-star comps. Each mock must use the confirmed palette and typography. Mocks differ in *structural* direction (hierarchy, topology, density, composition), not in color or motif. - -- Brand work: push visual identity, composition, mood, and signature motifs. -- Product work: push hierarchy, topology, density, tone, grounded in realistic product structure. -- Landing pages and long-form brand surfaces: show enough of the second fold to establish the system beyond the hero. - -Use the `image_gen` tool directly (or via the imagegen skill when available). Don't ask the user to install anything. - -## Step D: Approval Loop - -Show the comps. Ask what carries forward. Iterate until **one direction is approved** or the user explicitly delegates. - -**STOP and wait for the approval or the delegation.** Do not begin Step E or return to the build until a single direction is named. If the user delegates, pick the strongest direction and explain it from the brief, not personal taste. - -Before moving to assets, summarize what to carry into code and what *not* to literalize from the mock. This is the handoff between visual exploration and semantic implementation. - -## Step E: Mock Fidelity Inventory - -Inventory the approved mock's major visible ingredients. For each, decide implementation: semantic HTML/CSS/SVG, generated raster, sourced raster, icon library, canvas/WebGL, or accepted omission. - -Common ingredients to inventory: - -- Hero silhouette and dominant composition -- Signature motifs (planets, devices, portraits, charts, route lines, insets, badges, etc.) -- Nav and primary-action treatment (when the surface has one) -- Section sequence, especially the second fold -- Image-native content the concept depends on -- Typography, density, color/material treatment, motion cues - -Treat the mock as a north star, not a screenshot to trace. Don't rasterize core UI text. But if the live result lacks the mock's major ingredients, the implementation is wrong. - -If a photographic, architectural, product, or place-led mock becomes generic CSS scenery, decorative diagrams, bullets, or copy, stop and fix it. That's a broken implementation, not a harmless interpretation. - -Don't substitute a different hero composition or visual driver post-approval without user sign-off. - -## Step F: Asset Slicing via the Asset Producer - -Raster ingredients identified in Step E need clean production assets. Use the bundled `impeccable_asset_producer` subagent rather than producing inline. - -Spawn it as a scoped subagent. If you do not have explicit permission to use agents, stop and ask: - -```text -Asset production will work better as a scoped subagent job. Should I spawn the Impeccable asset producer subagent for this step? -``` - -Pass to the agent: - -- Approved mock path or screenshot reference -- Crop paths or a contact sheet with crop ids -- Output directory -- Required dimensions, format, transparency needs -- Avoid list -- Notes on what should remain semantic HTML/CSS/SVG instead of raster - -Attach image generation capability to the spawned agent when the harness supports it. Do **not** load image-generation reference material into the parent thread. - -Inline asset production is allowed only if the user declines subagents, the harness cannot spawn the authorized agent, or the user explicitly asks for single-thread mode. - -Prefer HTML/CSS/SVG/canvas when they can credibly reproduce an ingredient; reach for real, generated, or stock imagery when the mock or subject matter calls for actual visual content. - -## After This File - -Once Steps A through F are complete, return to the build: implement per new-work.md and SKILL.md's craft floor. The implementation builds against the confirmed palette, approved mock, and the assets the producer wrote. +Return to [new-work.md](new-work.md) for the direction contract, implementation, and finishing pass. diff --git a/.opencode/skills/impeccable/reference/colorize.md b/.opencode/skills/impeccable/reference/colorize.md index 29a4d0215..dc45f88c5 100644 --- a/.opencode/skills/impeccable/reference/colorize.md +++ b/.opencode/skills/impeccable/reference/colorize.md @@ -1,257 +1,86 @@ > **Additional context needed**: existing brand colors. -Replace timid grayscale or single-accent designs with a strategic palette: pick a color strategy, choose a hue family that fits the brand, then apply color with intent. More color ≠ better. Strategic color beats rainbow vomit. +Introduce color as hierarchy, meaning, and atmosphere. Preserve confirmed brand and semantic conventions; do not replace a visual world under the guise of colorizing it. --- -## Register +## Visitor mode -Persuade + Experience: palette IS voice. Pick a color strategy first per SKILL.md (Restrained / Committed / Full palette / Drenched) and follow its dosage. Committed, Full palette, and Drenched deliberately exceed the ≤10% rule; that rule is Restrained only. Unexpected combinations are allowed; a dominant color can own the page when the chosen strategy calls for it. +- **Persuade + Experience:** color may carry the voice and own large regions when the selected world calls for it. +- **Operate + Read:** color primarily encodes action, selection, status, wayfinding, and reading hierarchy. Rarity gives an accent force. -Operate + Read: semantic-first and almost always Restrained. Accent color is reserved for primary action, current selection, and state indicators. Not decoration. Every color has a consistent meaning across every screen. +## Audit before choosing ---- +Read DESIGN.md, tokens, assets, current themes, and representative states. Identify: -## Assess Color Opportunity +- which colors are confirmed brand commitments; +- current surface, text, action, and semantic roles; +- places where grayscale obscures hierarchy or state; +- contrast failures and color-only communication; +- light/dark or data-visualization requirements; +- whether the task asks for more color or a new identity. -Analyze the current state and identify opportunities: +If a new identity is required, use [new-work.md](new-work.md). Ask only when a binding brand decision cannot be inferred. -1. **Understand current state**: - - **Color absence**: Pure grayscale? Limited neutrals? One timid accent? - - **Missed opportunities**: Where could color add meaning, hierarchy, or delight? - - **Context**: What's appropriate for this domain and audience? - - **Brand**: Are there existing brand colors we should use? +## Choose a strategy -2. **Identify where color adds value**: - - **Semantic meaning**: Success (green), error (red), warning (yellow/orange), info (blue) - - **Hierarchy**: Drawing attention to important elements - - **Categorization**: Different sections, types, or states - - **Emotional tone**: Warmth, energy, trust, creativity - - **Wayfinding**: Helping users navigate and understand structure - - **Delight**: Moments of visual interest and personality +Name the intended emotional temperature, dominant relationship, contrast range, and color dosage before editing. The strategy may be restrained or immersive; it must follow the brief and selected world rather than a fixed percentage rule. -If any of these are unclear from the codebase, STOP and call the `question` tool to clarify. +Build roles, not a bag of swatches: -**CRITICAL**: More color ≠ better. Strategic color beats rainbow vomit every time. Every color should have a purpose. +- canvas and elevated surfaces; +- primary and secondary text; +- action, focus, and selection; +- borders and separators; +- success, warning, error, and information; +- data categories or scales when needed. -## Plan Color Strategy +Use the project's existing color space. For a new web palette, prefer OKLCH because lightness and chroma can be adjusted predictably. Choose hue from product meaning and visual direction, never from a default category association. -Create a purposeful color introduction plan: +## Apply at system scale -- **Color palette**: What colors match the brand/context? (Choose 2-4 colors max beyond neutrals) -- **Dominant color**: Which color owns 60% of colored elements? -- **Accent colors**: Which colors provide contrast and highlights? (30% and 10%) -- **Application strategy**: Where does each color appear and why? +- Let the strongest color own a deliberate region or role instead of scattering tiny accents. +- Keep the primary action easy to find; do not spend its color on decoration. +- Tint neutrals only when the brand hue genuinely creates cohesion. Neutral gray is valid when it serves the world. +- On colored surfaces, derive secondary text from the foreground or surface hue rather than using washed-out generic gray. +- Keep semantic meanings consistent, but respect platform and domain conventions instead of assuming fixed hues. +- For data, use distinct lightness, chroma, shape, label, or pattern so color is not the only code. +- 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. -**IMPORTANT**: Color should enhance hierarchy and meaning, not create chaos. Less is more when it matters more. +Decoration without a relationship to hierarchy, state, content, or the visual world is not a color strategy. -## Introduce Color Strategically +## Contrast and perception -Add color systematically across these dimensions: +Verify computed foreground/background pairs: -### Semantic Color -- **State indicators**: - - Success: Green tones (emerald, forest, mint) - - Error: Red/pink tones (rose, crimson, coral) - - Warning: Orange/amber tones - - Info: Blue tones (sky, ocean, indigo) - - Neutral: Gray/slate for inactive states +| Content | WCAG AA minimum | +|---|---| +| body text | 4.5:1 | +| large text | 3:1 | +| controls, icons, focus indicators | 3:1 | -- **Status badges**: Colored backgrounds or borders for states (active, pending, completed, etc.) -- **Progress indicators**: Colored bars, rings, or charts showing completion or health +Do not rely on eyesight alone. Check interactive states, overlays, text on images, disabled content, and both themes. Simulate common vision deficiencies. Information conveyed by color also needs text, shape, iconography, or position. -### Accent Color Application -- **Primary actions**: Color the most important buttons/CTAs -- **Links**: Add color to clickable text (maintain accessibility) -- **Icons**: Colorize key icons for recognition and personality -- **Headers/titles**: Add color to section headers or key labels -- **Hover states**: Introduce color on interaction +When deriving OKLCH ramps, vary lightness and reduce chroma near white and black. Do not keep high chroma at extreme lightness merely to make the math uniform. Prefer explicit colors over chains of translucent overlays when alpha would make contrast context-dependent. -### Background & Surfaces -- **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 toward the brand, not "for warmth" by reflex +## Verify -**Use OKLCH for color**: It's perceptually uniform, meaning equal steps in lightness *look* equal. Great for generating harmonious scales. - -### Data Visualization -- **Charts & graphs**: Use color to encode categories or values -- **Heatmaps**: Color intensity shows density or importance -- **Comparison**: Color coding for different datasets or timeframes - -### Borders & Accents -- **Hairline borders**: 1px colored borders on full perimeter (not side-stripes; see the absolute ban on `border-left/right > 1px`) -- **Underlines**: Color underlines for emphasis or active states -- **Dividers**: Subtle colored dividers instead of gray lines -- **Focus rings**: Colored focus indicators matching brand -- **Surface tints**: A 4-8% background wash of the accent color instead of a stripe - -**NEVER**: `border-left` or `border-right` greater than 1px as a colored accent stripe. This is one of the three absolute bans in the parent skill. If you want to mark a card as "active" or "warning", use a full hairline border, a background tint, a leading glyph, or a numbered prefix. Not a side stripe. - -### Typography Color -- **Colored headings**: Use brand colors for section headings (maintain contrast) -- **Highlight text**: Color for emphasis or categories -- **Labels & tags**: Small colored labels for metadata or categories - -### Decorative Elements -- **Illustrations**: Add colored illustrations or icons -- **Shapes**: Geometric shapes in brand colors as background elements -- **Gradients**: Colorful gradient overlays or mesh backgrounds -- **Blobs/organic shapes**: Soft colored shapes for visual interest - -## Balance & Refinement - -Ensure color addition improves rather than overwhelms: - -### Maintain Hierarchy -- **Dominant color** (60%): Primary brand color or most used accent -- **Secondary color** (30%): Supporting color for variety -- **Accent color** (10%): High contrast for key moments -- **Neutrals** (remaining): Gray/black/white for structure - -### Accessibility -- **Contrast ratios**: Ensure WCAG compliance (4.5:1 for text, 3:1 for UI components) -- **Don't rely on color alone**: Use icons, labels, or patterns alongside color -- **Test for color blindness**: Verify red/green combinations work for all users - -### Cohesion -- **Consistent palette**: Use colors from defined palette, not arbitrary choices -- **Systematic application**: Same color meanings throughout (green always = success) -- **Temperature consistency**: Warm palette stays warm, cool stays cool - -**NEVER**: -- 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 -- Violate WCAG contrast requirements -- Use color as the only indicator (accessibility issue) -- Make everything colorful (defeats the purpose) -- Default to purple-blue gradients (AI slop aesthetic) - -## Verify Color Addition - -Test that colorization improves the experience: - -- **Better hierarchy**: Does color guide attention appropriately? -- **Clearer meaning**: Does color help users understand states/categories? -- **More engaging**: Does the interface feel warmer and more inviting? -- **Still accessible**: Do all color combinations meet WCAG standards? -- **Not overwhelming**: Is color balanced and purposeful? +- Every color has a stable role or a world-specific atmospheric purpose. +- Attention lands on the intended action, content, or state. +- The palette works across quiet, dense, interactive, error, and empty states. +- Light and dark themes are each composed, not mechanically inverted. +- Contrast and non-color cues pass in all relevant states. +- The result is recognizably this product, not a generic “colorful” treatment. When the palette earns its place, hand off to `/impeccable polish` for the final pass. ## Live-mode signature params -When invoked from live mode, each variant MUST declare a `color-amount` param so the user can dial between a restrained accent and a drenched surface without regeneration. Author the variant's CSS against `var(--p-color-amount, 0.5)`, typically as the alpha multiplier on backgrounds, or as a scaling factor on the chroma axis in an OKLCH expression. 0 = neutral/monochrome, 1 = full saturation / dominant coverage. +When invoked from live mode, every variant declares a `color-amount` parameter. Author CSS against `var(--p-color-amount, 0.5)` so the user can move from neutral to the variant's full color strategy without regeneration. ```json {"id":"color-amount","kind":"range","min":0,"max":1,"step":0.05,"default":0.5,"label":"Color amount"} ``` -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). +Add at most two variant-specific parameters, such as palette, temperature, or tint behavior. Follow [live.md](live.md)'s parameter contract. diff --git a/.opencode/skills/impeccable/reference/craft.md b/.opencode/skills/impeccable/reference/craft.md index cc7f98d37..dbbc9402c 100644 --- a/.opencode/skills/impeccable/reference/craft.md +++ b/.opencode/skills/impeccable/reference/craft.md @@ -1,3 +1,5 @@ # Craft (deprecated alias) -`craft` is a deprecated alias for the standard build flow. Builds route through SKILL.md's new-work gate; [new-work.md](new-work.md) carries the direction checkpoint ("Decide, then build"), the identity playbook, and the finishing pass. Invoking `craft` explicitly forces the attended checkpoints: pause at the stated direction for confirmation, and follow [codex.md](codex.md)'s mock flow when the harness generates images. Nothing else differs from a plain build request. +`craft` is a deprecated alias for an ordinary request to make new visual work. It adds no setup, interview, checkpoint, tool, or quality behavior. Apply SKILL.md's normal routing: create missing PRODUCT.md through [init.md](init.md), then follow [new-work.md](new-work.md) for visual authority, world and surface decisions, implementation, and finish. + +Do not tell users they need to invoke `craft`. Natural requests such as “build this feature,” “make a landing page,” or “redesign this screen” use the same flow. diff --git a/.opencode/skills/impeccable/reference/critique.md b/.opencode/skills/impeccable/reference/critique.md index 65b1fbf7a..e4d534d1f 100644 --- a/.opencode/skills/impeccable/reference/critique.md +++ b/.opencode/skills/impeccable/reference/critique.md @@ -43,13 +43,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 @@ -115,11 +115,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. @@ -176,7 +176,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. 2. **Pass the structured metadata** through `IMPECCABLE_CRITIQUE_META` (JSON), then run the write command: ```bash diff --git a/.opencode/skills/impeccable/reference/delight.md b/.opencode/skills/impeccable/reference/delight.md index 203f1bddd..40aaddc34 100644 --- a/.opencode/skills/impeccable/reference/delight.md +++ b/.opencode/skills/impeccable/reference/delight.md @@ -1,302 +1,70 @@ -> **Additional context needed**: what's appropriate for the domain (playful vs professional vs quirky vs elegant). +> **Additional context needed**: the brand's emotional range. -Find the moments where personality and unexpected polish would turn a functional interface into one users remember and tell other people about. Add only where the moment earns it; delight everywhere reads as noise. +Make the experience memorable at moments that earn it. Delight is not a layer of generic whimsy; it is product character revealed through a useful interaction, a humane response, or an unexpectedly considered detail. --- -## Register +## Visitor mode -Persuade + Experience: delight can be distributed across copy voice, section transitions, discovery rewards, seasonal touches, personality across the whole surface. +- **Persuade + Experience:** personality may run through voice, composition, motion, and discovery, provided the artifact remains the focus. +- **Operate + Read:** concentrate delight at meaningful moments such as first use, completion, recovery, or mastery. Reliability carries everything else. -Operate + Read: delight at specific moments, not pages. Completion, first-time actions, error recovery, milestone crossings. Reliability and consistency carry the rest of the experience; delight pushed everywhere reads as noise. +## Find the opportunity ---- +Inspect the target, DESIGN.md, product voice, repeated-use frequency, and emotional context. Look for: -## Assess Delight Opportunities +- effort worth acknowledging; +- waiting that can become informative; +- an empty or first-use state that can orient; +- an error or recovery moment that needs empathy; +- an interaction whose physical or verbal response could express the brand; +- a useful capability people might enjoy discovering. -Identify where delight would enhance (not distract from) the experience: +Do not manufacture a celebration for an ordinary click. Ask only when the brand's emotional range or the stakes cannot be inferred. -1. **Find natural delight moments**: - - **Success states**: Completed actions (save, send, publish) - - **Empty states**: First-time experiences, onboarding - - **Loading states**: Waiting periods that could be entertaining - - **Achievements**: Milestones, streaks, completions - - **Interactions**: Hover states, clicks, drags - - **Errors**: Softening frustrating moments - - **Easter eggs**: Hidden discoveries for curious users +## Define one delight thesis -2. **Understand the context**: - - What's the brand personality? (Playful? Professional? Quirky? Elegant?) - - Who's the audience? (Tech-savvy? Creative? Corporate?) - - What's the emotional context? (Accomplishment? Exploration? Frustration?) - - What's appropriate? (Banking app ≠ gaming app) +State in one sentence what the user should feel and why that feeling belongs to this product. Then choose the smallest system that can deliver it: -3. **Define delight strategy**: - - **Subtle sophistication**: Refined micro-interactions (luxury brands) - - **Playful personality**: Whimsical illustrations and copy (consumer apps) - - **Helpful surprises**: Anticipating needs before users ask (productivity tools) - - **Sensory richness**: Satisfying sounds, smooth animations (creative tools) +- a distinctive response to a meaningful action; +- product-specific language that clarifies while carrying voice; +- an interaction or transition with a recognizable material behavior; +- an illustration, sound, haptic, or environmental detail grounded in the product world; +- a discovery reward that reveals real utility. -If any of these are unclear from the codebase, STOP and call the `question` tool to clarify. +Derive the treatment from product mechanism and visual world, not a stock catalog. -**CRITICAL**: Delight should enhance usability, never obscure it. If users notice the delight more than accomplishing their goal, you've gone too far. +## Build for the emotional moment -## Delight Principles +- **Success:** match the response to the effort and consequence. Major milestones can expand; routine saves should simply feel certain. +- **Waiting:** show truthful progress, useful context, or product-specific activity. Never fake work or delay completion to stage a flourish. +- **Empty and first use:** make the next action clear before adding personality. +- **Error and recovery:** lead with the problem and recovery. Warmth may reduce stress; jokes must not trivialize loss, money, privacy, or blocked work. +- **Repeated interaction:** keep the response satisfying after the hundredth use. Variation is useful only when it remains coherent and predictable enough to trust. +- **Discovery:** reward curiosity without hiding required functionality. -Follow these guidelines: +Copy must use the product's language. Generic whimsy is worse than neutral clarity. -### Delight Amplifies, Never Blocks -- Delight moments should be quick (< 1 second) -- Never delay core functionality for delight -- Make delight skippable or subtle -- Respect user's time and task focus +## Protect the experience -### Surprise and Discovery -- Hide delightful details for users to discover -- Reward exploration and curiosity -- Don't announce every delight moment -- Let users share discoveries with others +Delight must not: -### Appropriate to Context -- Match delight to emotional moment (celebrate success, empathize with errors) -- Respect the user's state (don't be playful during critical errors) -- Match brand personality and audience expectations -- Cultural sensitivity (what's delightful varies by culture) +- delay, block, or obscure the primary task; +- override platform conventions or accessibility; +- add unrequested factual claims; +- play sound without consent or ignore mute settings; +- become mandatory, unskippable, or exhausting on repeat; +- add a dependency or asset cost disproportionate to the moment. -### Compound Over Time -- Delight should remain fresh with repeated use -- Vary responses (not same animation every time) -- Reveal deeper layers with continued use -- Build anticipation through patterns +For authored motion, load [animate.md](animate.md). Respect reduced motion, screen readers, keyboard use, touch, localization, and cultural context. Nonessential loops stop when hidden. Make celebration intensity proportional to frequency and consequence. -## Delight Techniques +## Verify -Add personality and joy through these methods: +- The moment is specific enough that a neighboring product could not use it unchanged. +- It improves comprehension, confidence, motivation, or emotional recovery. +- The interface remains fast and obvious without the flourish. +- Repetition does not turn charm into friction. +- Reduced-motion, muted, keyboard, touch, and localized paths work. +- The result feels like the selected world, not a generic “delight” treatment. -### Micro-interactions & Animation - -**Button delight**: -```css -/* Satisfying button press */ -.button { - transition: transform 0.1s, box-shadow 0.1s; -} -.button:active { - transform: translateY(2px); - box-shadow: 0 2px 4px rgba(0,0,0,0.2); -} - -/* Ripple effect on click */ -/* Smooth lift on hover */ -.button:hover { - transform: translateY(-2px); - transition: transform 0.2s cubic-bezier(0.25, 1, 0.5, 1); /* ease-out-quart */ -} -``` - -**Loading delight**: -- Playful loading animations (not just spinners) -- Personality in loading messages (write product-specific ones, not generic AI filler) -- Progress indication with encouraging messages -- Skeleton screens with subtle animations - -**Success animations**: -- Checkmark draw animation -- Confetti burst for major achievements -- Gentle scale + fade for confirmation -- Satisfying sound effects (subtle) - -**Hover surprises**: -- Icons that animate on hover -- Color shifts or glow effects -- Tooltip reveals with personality -- Cursor changes (custom cursors for branded experiences) - -### Personality in Copy - -**Playful error messages**: -``` -"Error 404" -"This page is playing hide and seek. (And winning)" - -"Connection failed" -"Looks like the internet took a coffee break. Want to retry?" -``` - -**Encouraging empty states**: -``` -"No projects" -"Your canvas awaits. Create something amazing." - -"No saved articles" -"Nothing on the nightstand yet. Save a story for later." -``` - -**Playful labels & tooltips**: -``` -"Delete" -"Send to void" (for playful brand) - -"Help" -"Rescue me" (tooltip) -``` - -**IMPORTANT**: Match copy personality to brand. Banks shouldn't be wacky, but they can be warm. - -### Illustrations & Visual Personality - -**Custom illustrations**: -- Empty state illustrations (not stock icons) -- Error state illustrations (friendly monsters, quirky characters) -- Loading state illustrations (animated characters) -- Success state illustrations (celebrations) - -**Icon personality**: -- Custom icon set matching brand personality -- Animated icons (subtle motion on hover/click) -- Illustrative icons (more detailed than generic) -- Consistent style across all icons - -**Background effects**: -- Subtle particle effects -- Gradient mesh backgrounds -- Geometric patterns -- Parallax depth -- Time-of-day themes (morning vs night) - -### Satisfying Interactions - -**Drag and drop delight**: -- Lift effect on drag (shadow, scale) -- Snap animation when dropped -- Satisfying placement sound -- Undo toast ("Dropped in wrong place? [Undo]") - -**Toggle switches**: -- Smooth slide with spring physics -- Color transition -- Haptic feedback on mobile -- Optional sound effect - -**Progress & achievements** (Operate surfaces with recurring tasks only; gamification on editorial, portfolio, or one-visit surfaces reads as noise): -- Streak counters with celebratory milestones -- Progress bars that "celebrate" at 100% -- Badge unlocks with animation -- Playful stats ("You're on fire! 5 days in a row") - -**Form interactions**: -- Input fields that animate on focus -- Checkboxes with a satisfying scale pulse when checked -- Success state that celebrates valid input -- Auto-grow textareas - -### Sound Design - -**Subtle audio cues** (when appropriate): -- Notification sounds (distinctive but not annoying) -- Success sounds (satisfying "ding") -- Error sounds (empathetic, not harsh) -- Typing sounds for chat/messaging -- Ambient background audio (very subtle) - -**IMPORTANT**: -- Respect system sound settings -- Provide mute option -- Keep volumes quiet (subtle cues, not alarms) -- Don't play on every interaction (sound fatigue is real) - -### Easter Eggs & Hidden Delights - -**Discovery rewards**: -- Konami code unlocks special theme -- Hidden keyboard shortcuts (Cmd+K for special features) -- Hover reveals on logos or illustrations -- Alt text jokes on images (for screen reader users too!) -- A view-source or console colophon (a note on the typefaces, a thank-you, a hiring line) - -**Seasonal touches**: -- Holiday themes (subtle, tasteful) -- Seasonal color shifts -- Weather-based variations -- Time-based changes (dark at night, light during day) - -**Contextual personality**: -- Different messages based on time of day -- Responses to specific user actions -- Randomized variations (not same every time) -- Progressive reveals with continued use - -### Loading & Waiting States - -**Make waiting engaging**: -- Interesting loading messages that rotate -- Progress bars with personality -- Mini-games during long loads -- Fun facts or tips while waiting -- Countdown with encouraging messages - -``` -Loading messages: write ones specific to your product, not generic AI filler: -- "Syncing with your team's changes..." -- "Fetching this week's issue..." -- "Developing your photos..." -- "Checking tomorrow's tide tables..." -``` - -**WARNING**: Avoid cliched loading messages like "Herding pixels", "Teaching robots to dance", "Consulting the magic 8-ball", "Counting backwards from infinity". These are AI-slop copy, instantly recognizable as machine-generated. Write messages that are specific to what your product actually does. - -### Celebration Moments - -**Success celebrations**: -- Confetti for major milestones -- Animated checkmarks for completions -- Progress bar celebrations at 100% -- "Achievement unlocked" style notifications -- Personalized messages ("You published your 10th article!") - -**Milestone recognition** (same scoping as Progress & achievements: Operate surfaces with recurring tasks): -- First-time actions get special treatment -- Streak tracking and celebration -- Progress toward goals -- Anniversary celebrations - -## Implementation Patterns - -**Animation libraries**: -- Framer Motion (React) -- GSAP (universal) -- Lottie (After Effects animations) -- Canvas confetti (party effects) - -**Sound libraries**: -- Howler.js (audio management) -- Use-sound (React hook) - -**Physics libraries**: -- React Spring (spring physics) -- Popmotion (animation primitives) - -**IMPORTANT**: File size matters. Compress images, optimize animations, lazy load delight features. - -**NEVER**: -- Delay core functionality for delight -- Force users through delightful moments (make skippable) -- Use delight to hide poor UX -- Overdo it (less is more) -- Ignore accessibility (animate responsibly, provide alternatives) -- Make every interaction delightful (special moments should be special) -- Sacrifice performance for delight -- Be inappropriate for context (read the room) - -## Verify Delight Quality - -Test that delight actually delights: - -- **User reactions**: Do users smile? Share screenshots? -- **Doesn't annoy**: Still pleasant after 100th time? -- **Doesn't block**: Can users opt out or skip? -- **Performant**: No jank, no slowdown -- **Appropriate**: Matches brand and context -- **Accessible**: Works with reduced motion, screen readers - -When the moments feel earned, hand off to `/impeccable polish` for the final pass. +When the personality feels earned, hand off to `/impeccable polish` for the final pass. diff --git a/.opencode/skills/impeccable/reference/document.md b/.opencode/skills/impeccable/reference/document.md index 97a369611..13befd663 100644 --- a/.opencode/skills/impeccable/reference/document.md +++ b/.opencode/skills/impeccable/reference/document.md @@ -1,6 +1,6 @@ Generate a `DESIGN.md` file at the project root that captures the current visual design system, so AI agents generating new screens stay on-brand. -DESIGN.md follows the [official DESIGN.md format spec](https://raw.githubusercontent.com/google-labs-code/design.md/main/docs/spec.md): YAML frontmatter carrying machine-readable design tokens, followed by a markdown body with exactly six sections in a fixed order. **Tokens are normative; prose provides context for how to apply them.** Sections may be omitted when not relevant, but **do not reorder them and do not rename them**. Section headers must match the spec character-for-character so the file stays parseable by other DESIGN.md-aware tools (Stitch itself, awesome-design-md, skill-rest, etc.). +DESIGN.md follows the [official DESIGN.md format spec](https://raw.githubusercontent.com/google-labs-code/design.md/main/docs/spec.md): optional YAML frontmatter carrying machine-readable design tokens, followed by up to eight markdown sections in a fixed order. **Tokens are normative; prose provides context for how to apply them.** Sections may be omitted when not relevant, but those present stay in the specified order. Use the canonical headings below so the file remains portable across DESIGN.md-aware tools. ## The frontmatter: token schema @@ -43,26 +43,28 @@ components: 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. +- **Colors accept any valid CSS color string.** Hex is the recommended default for portability, but preserve an incumbent `rgb()`, `hsl()`, `oklch()`, wide-gamut, or mixed-color value when it is the project's normative source. 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 (`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) +## The markdown body: eight sections (canonical order) 1. `## Overview` 2. `## Colors` 3. `## Typography` -4. `## Elevation` -5. `## Components` -6. `## Do's and Don'ts` +4. `## Layout` +5. `## Elevation & Depth` +6. `## Shapes` +7. `## Components` +8. `## Do's and Don'ts` -Optional evocative subtitles are allowed in the form `## 2. Colors: The [Name] Palette` (Stitch's own outputs do this), but the literal word in each header (Overview, Colors, Typography, Elevation, Components, Do's and Don'ts) must be present. Do NOT add extra top-level sections (Layout Principles, Responsive Behavior, Motion, Agent Prompt Guide). Fold that content into the six spec sections where it naturally belongs. +Omit irrelevant sections rather than filling them with invented rules. Put responsive layout in Layout, depth in Elevation & Depth, radius and form language in Shapes, and per-component behavior in Components. Unknown sections are preserved by the format, but new visual guidance should use the canonical structure whenever it fits. ## When to run -- 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. +- New-work found a coherent incumbent visual system but no `DESIGN.md`. +- The first implementation of a new world is complete and its provisional decisions need to be carbonized. - An existing `DESIGN.md` is stale (the design has drifted). - Before a large redesign, to capture the current state as a reference. @@ -71,9 +73,9 @@ 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 init, nothing built yet). Interview for five high-level answers, write a minimal DESIGN.md marked ``. Re-run in scan mode once there's code. +- **Seed mode**: the project is pre-implementation. Ensure PRODUCT.md exists, then reuse new-work's visual-world workshop and write its directional DESIGN.md 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. +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` requests new-work's world workshop, but it does not authorize replacing coherent code: when an incumbent system exists, offer scan mode or route an explicit identity-replacement request through new-work. ## Scan mode (approach C: auto-extract, then confirm descriptive language) @@ -97,7 +99,8 @@ Build a structured draft from the discovered tokens. For each token class: - **Typography**: Map observed sizes and weights to the Material hierarchy (display / headline / title / body / label). Note font-family stacks and the scale ratio. - **Elevation**: Catalogue the shadow vocabulary. If the project is flat and uses tonal layering instead, that's a valid answer; state it explicitly. - **Components**: For each common component (button, card, input, chip, list item, tooltip, nav), extract shape (radius), color assignment, hover/focus treatment, internal padding. -- **Spacing + layout**: Fold into Overview or relevant Components. The spec does NOT have a Layout section. +- **Layout + spacing**: Extract grid, container, breakpoint, rhythm, and density behavior into Layout. +- **Shapes**: Extract radius, corner, border, clipping, and recurring form behavior into Shapes. ### Step 2b: Stage the frontmatter @@ -112,19 +115,19 @@ Skip anything the project doesn't have. Empty scale keys or fabricated tokens po ### Step 3: Ask the user for qualitative language -The following require creative input that cannot be auto-extracted. Group them into one `AskUserQuestion` interaction: +The following require creative input that cannot be auto-extracted. Ask them in two structured rounds of no more than three questions each (or the harness's lower limit), waiting between rounds: - **Creative North Star**: a single named metaphor for the whole system ("The Editorial Sanctuary", "The Golden State Curator", "The Lab Notebook"). Offer 2-3 options that honor PRODUCT.md's brand personality. -- **Overview voice**: mood adjectives, aesthetic philosophy in 2-3 sentences, anti-references (what the system should not feel like). +- **Overview voice**: mood adjectives, aesthetic philosophy in 2-3 sentences, and any confirmed visual anti-reference. - **Color character** (for auto-extracted colors): descriptive names ("Deep Muted Teal-Navy", not "blue-800"). Suggest 2-3 options per key color based on hue/saturation. - **Elevation philosophy**: flat/layered/lifted. If shadows exist, is their role ambient or structural? - **Component philosophy**: the feel of buttons, cards, inputs in one phrase ("tactile and confident" vs. "refined and restrained"). -Quote a line from PRODUCT.md when possible so the user sees their own strategic language carry forward. +Carry a line from PRODUCT.md only when it is a durable brand commitment that actually constrains the visual system. Page strategy and surface concepts do not belong here. ### Step 4: Write DESIGN.md -The file opens with the YAML frontmatter staged in Step 2b (schema documented at the top of this reference), then the markdown body using the structure below. Headers must match character-for-character. Optional evocative subtitles (e.g. `## 2. Colors: The Coastal Palette`) are allowed. +The file opens with the YAML frontmatter staged in Step 2b (schema documented at the top of this reference), then the markdown body using the canonical structure below. ```markdown --- @@ -136,13 +139,13 @@ colors: # Design System: [Project Title] -## 1. Overview +## Overview **Creative North Star: "[Named metaphor in quotes]"** -[2-3 paragraph holistic description: personality, density, aesthetic philosophy. Start from the North Star and work outward. State what this system explicitly rejects (pulled from PRODUCT.md's anti-references). End with a short **Key Characteristics:** bullet list.] +[2-3 paragraph holistic description: personality, density, and aesthetic philosophy. Start from the North Star and work outward. State only confirmed visual rejections. End with a short **Key Characteristics:** bullet list.] -## 2. Colors +## Colors [Describe the palette character in one sentence.] @@ -162,7 +165,7 @@ colors: ### Named Rules (optional, powerful) **The [Rule Name] Rule.** [Short, forceful prohibition or doctrine, e.g. "The One Voice Rule. The primary accent is used on ≤10% of any given screen. Its rarity is the point."] -## 3. Typography +## Typography **Display Font:** [Family] (with [fallback]) **Body Font:** [Family] (with [fallback]) @@ -180,7 +183,11 @@ colors: ### Named Rules (optional) **The [Rule Name] Rule.** [Short doctrine about type use.] -## 4. Elevation +## Layout + +[Describe the grid or spatial model, container behavior, density, responsive changes, and the spacing rhythm. Include exact values only when observed.] + +## Elevation & Depth [One paragraph: does this system use shadows, tonal layering, or a hybrid? If "no shadows", say so explicitly and describe how depth is conveyed instead.] @@ -191,7 +198,11 @@ colors: ### Named Rules (optional) **The [Rule Name] Rule.** [e.g. "The Flat-By-Default Rule. Surfaces are flat at rest. Shadows appear only as a response to state (hover, elevation, focus)."] -## 5. Components +## Shapes + +[Describe the form language: corner/radius strategy, borders, clipping, and any recurring silhouette or geometry.] + +## Components For each component, lead with a short character line, then specify shape, color assignment, states, and any distinctive behavior. @@ -223,16 +234,16 @@ For each component, lead with a short character line, then specify shape, color ### [Signature Component] (optional; if the project has a distinctive custom component worth documenting) [Description.] -## 6. Do's and Don'ts +## Do's and Don'ts -Concrete, forceful guardrails. Lead each with "Do" or "Don't". Be specific: include exact colors, pixel values, and named anti-patterns the user mentioned in PRODUCT.md. **Every anti-reference in PRODUCT.md should show up here as a "Don't" with the same language**, so the visual spec carries the strategic line through. Quote PRODUCT.md directly where possible: if PRODUCT.md says *"avoid dark mode with purple gradients, neon accents, glassmorphism"*, the Don'ts here should repeat that by name. +Concrete visual guardrails grounded in the incumbent implementation or the user's chosen world. Lead each with "Do" or "Don't" and include exact values only when established. Do not turn a task-specific concept or surface strategy into a system-wide prohibition. ### Do: - **Do** [specific prescription with exact values / named rule]. - **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** [...] ``` @@ -307,7 +318,7 @@ The `html` and `css` fields must be **self-contained, drop-in snippets** that re Aim for a tight set of **5-10 components** that best represent the visual system: - **Canonical primitives (always include if the project has them):** button (each variant as a separate component entry), input/text field, navigation, chip/tag, card. -- **Signature components (include if distinctive):** hero CTA, featured card, filter pill, a distinctive table or list-row treatment, any custom pattern the user mentioned as important in PRODUCT.md. +- **Signature components (include if distinctive):** the recurring custom patterns that actually define the implemented system. - **Skip the rest.** Utility components, form building blocks, wrapper layouts: not worth documenting unless visually distinctive. If the project has **no component library yet** (bare landing page, new project), synthesize canonical primitives from the tokens using best-practice defaults consistent with the DESIGN.md's rules. Every `.impeccable/design.json` has *something* to render, even on day zero. @@ -338,64 +349,40 @@ Your own write is the freshest source; subsequent commands in this session don't ## Seed mode -For projects with no visual system to extract yet. Produces a minimal scaffold, not a full spec. +For projects with no visual system to extract yet. Produces a user-chosen visual-world scaffold, not a fabricated token spec. -### Step 1: Confirm seed mode +### Step 1: Route through new-work's workshop -Before interviewing: "There's no existing visual system to scan. I'll ask five quick questions to seed a starter DESIGN.md. You can re-run `/impeccable document` once there's code, to capture the real tokens and components. OK?" +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 the user prefers to skip, stop. No file. +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. -### Step 2: Five questions +If new-work already completed the workshop in this session, use its chosen direction directly. Do not ask again. -Group into one `AskUserQuestion` interaction. Options must be concrete. +### Step 2: Write seed DESIGN.md -1. **Color strategy.** Pick one: - - Restrained: tinted neutrals + one accent ≤10% - - Committed: one saturated color carries 30–60% of the surface - - Full palette: 3–4 named color roles, each deliberate - - Drenched: the surface IS the color - - Then: one hue family or anchor reference ("deep teal", "mustard", "Klim #ff4500 orange"). - -2. **Typography direction.** Pick one (specific fonts come later): - - Serif display + sans body - - Single sans (warm / technical / geometric / humanist; pick a feel) - - Display + mono - - Mono-forward - - Editorial script + sans - -3. **Motion energy.** Pick one: - - Restrained: state changes only - - Responsive: feedback + transitions, no choreography - - Choreographed: orchestrated entrances, scroll-driven sequences - -4. **Three named references.** Brands, products, printed objects. Not adjectives. - -5. **One anti-reference.** What it should NOT feel like. Also named. - -### Step 3: Write seed DESIGN.md - -Use the six-section spec from Scan mode. Populate what the interview answers; leave the rest as honest placeholders. The seed is a scaffold, not a fabricated spec. +Use the canonical section order from Scan mode. Populate the selected workshop direction and leave unresolved implementation facts as honest placeholders. The seed commits a world and its invariants; it does not pretend implementation tokens already exist. Lead the file with: ```markdown - + ``` Per-section guidance in seed mode: -- **Overview**: Creative North Star and philosophy phrased from the answers (color strategy + motion energy + references). Reference the user's anti-reference directly. -- **Colors**: Color strategy as a Named Rule (e.g. *"The Drenched Rule. The surface IS the color."*). Hue family or anchor reference. No hex values; mark as `[to be resolved during implementation]`. -- **Typography**: the direction the user picked (e.g. "Serif display + sans body"). No font names yet: `[font pairing to be chosen at implementation]`. -- **Elevation**: inferred from motion energy. Restrained/Responsive → flat by default; Choreographed → layered. One sentence. +- **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. +- **Elevation & Depth**: the selected material and depth behavior, stated as an invariant rather than inferred from a generic preset. +- **Shapes**: the selected form and corner language. - **Components**: omit entirely; no components exist yet. -- **Do's and Don'ts**: carry PRODUCT.md's anti-references directly plus the anti-reference named in Q5. +- **Do's and Don'ts**: record the durable guardrails confirmed during the world choice, not task-local refusals. 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 +### Step 3: 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." @@ -405,15 +392,15 @@ Your own write is the freshest source; no reload needed. ## Style guidelines - **Frontmatter first, prose second.** Tokens go in the YAML frontmatter; prose contextualizes them. Don't redefine a token value in two places; the frontmatter is normative. -- **Cite PRODUCT.md anti-references by name** in the Do's and Don'ts section. If PRODUCT.md lists "SaaS landing-page clichés" or "generic AI tool marketing" as anti-references, the DESIGN.md Don'ts should repeat those phrases verbatim so the visual spec enforces the strategic line. -- **Match the spec, don't invent new sections.** The six section names are fixed. If you have Layout/Motion/Responsive content to document, fold it into Overview (philosophy-level rules) or Components (per-component behavior). +- **Carry only durable product constraints.** A binding logo, identity asset, accessibility need, or brand commitment from PRODUCT.md may constrain DESIGN.md. Surface strategy stays in its surface brief. +- **Match the spec.** Use its eight canonical sections in order and omit any that are irrelevant. Put motion guidance with the world or component it affects rather than creating a token group the schema does not support. - **Descriptive > technical**: "Gently curved edges (8px radius)" > "rounded-lg". Include the technical value in parens, lead with the description. - **Functional > decorative**: for each token, explain WHERE and WHY it's used, not just WHAT it is. - **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 forceful**. The voice of a design director. "Prohibited", "forbidden", "never", "always", not "consider", "might", "prefer". Match PRODUCT.md's tone. -- **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. -- **Reference PRODUCT.md**. The anti-references section of PRODUCT.md should directly inform the Do's and Don'ts section here. Quote or paraphrase. +- **Be decisive where evidence is decisive.** Use hard language for actual invariants and softer language for provisional guidance. +- **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. ## Pitfalls @@ -423,7 +410,7 @@ Your own write is the freshest source; no reload needed. - Don't invent components that don't exist. If the project only has buttons and cards, only document those. - Don't overwrite an existing DESIGN.md without asking. - Don't duplicate content from PRODUCT.md. DESIGN.md is strictly visual. -- Don't add a "Layout Principles" or "Motion" or "Responsive Behavior" top-level section. The spec has six, not nine. Fold that content where it belongs. +- Don't replace canonical sections with near-synonyms. Put layout and responsive behavior in `Layout`; put motion with the affected world or component. - Don't rename sections even slightly. "Colors" not "Color Palette & Roles". "Typography" not "Typography Rules". Tooling parsing depends on exact headers. - Don't duplicate token values between frontmatter and prose. If a color is in `colors.primary` as hex, the prose can name it and describe its role but should not reassert a different hex. The frontmatter is normative. - Don't invent frontmatter token groups outside Stitch's schema (no `motion:`, `breakpoints:`, `shadows:` at the top level). Stitch's Zod schema only accepts `colors`, `typography`, `rounded`, `spacing`, `components`. Anything else belongs in the sidecar's `extensions`. diff --git a/.opencode/skills/impeccable/reference/hooks.md b/.opencode/skills/impeccable/reference/hooks.md index 05352df20..70cbb0f27 100644 --- a/.opencode/skills/impeccable/reference/hooks.md +++ b/.opencode/skills/impeccable/reference/hooks.md @@ -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. diff --git a/.opencode/skills/impeccable/reference/init.md b/.opencode/skills/impeccable/reference/init.md index 24cab7166..e85092cfa 100644 --- a/.opencode/skills/impeccable/reference/init.md +++ b/.opencode/skills/impeccable/reference/init.md @@ -1,224 +1,114 @@ -# Init Flow +# Init flow -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 [DESIGN.md format spec](https://raw.githubusercontent.com/google-labs-code/design.md/main/docs/spec.md). Answers "how it looks". -- **`.impeccable/live/config.json`** (live mode): pre-configured so `/impeccable live` boots straight into variant mode with no first-time detour. - -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. +`init` captures durable product truth in PRODUCT.md. It does not invent a visual world and does not write DESIGN.md; [new-work.md](new-work.md) creates or expands one, and [document.md](document.md) records an incumbent one. Existing runnable web projects may also receive `.impeccable/live/config.json`. ## Step 1: Load current state -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). +Use the PRODUCT.md path resolved by context.mjs. Update it instead of creating a competing authority. In a child app inheriting root context, confirm shared versus app-specific scope before writing. -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. -- **PRODUCT.md exists, DESIGN.md missing**: skip to Step 5 and offer to run `/impeccable document` for DESIGN.md. -- **PRODUCT.md exists but has no `## Register` section (legacy)**: add it. Infer a hypothesis from the codebase (see Step 2), confirm with the user, write the field. -- **PRODUCT.md exists but has no `## Platform` section (legacy)**: add it the same way, but only when the project is native (`ios` / `android` / `adaptive`) or the user wants it explicit; a missing field already means `web`. -- **Both exist**: STOP and call the `question` tool to clarify. Ask which file to refresh. Skip the one the user doesn't want changed. -- **Just DESIGN.md exists (unusual)**: do Steps 2-4 to produce PRODUCT.md. +- **No PRODUCT.md:** explore, interview, and write it. +- **PRODUCT.md exists:** ask what product knowledge is stale or missing; do not reopen confirmed fields without a reason. +- **Legacy PRODUCT.md:** add only durable missing facts; absent `## Platform` means `web` unless evidence says otherwise. +- **Only DESIGN.md exists:** leave it untouched and create PRODUCT.md. +- **Redesign/rebrand request:** preserve confirmed product truth unless the user changes it. Visual replacement happens later in new-work, not here. -Never silently overwrite an existing file. Always confirm first. +Never silently overwrite an existing file or offer DESIGN.md during init. If another request invoked init, finish PRODUCT.md and resume it. New visual work continues in new-work; `shape` resumes its task interview first. -If init was invoked as a setup blocker by another command, such as `/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 project -## Step 2: Explore the codebase +Before asking, scan enough to avoid making the user repeat known facts: product docs and copy; package/config and app boundaries; features, workflows, routes, and roles; names, logos, legal/proof assets, and brand commitments; platform/accessibility signals; and the dev command/entry when live mode applies. -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: +Treat repository evidence as a hypothesis, not user approval. Note visual maturity without documenting, extending, or replacing the world. -- **README and docs**: Project purpose, target audience, any stated goals -- **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 -- **Any style guides or brand documentation** +Form a platform hypothesis: `web`, `ios`, `android`, or `adaptive` (one product that genuinely adapts its design language per OS). Mobile web remains `web`; a native wrapper around a website does not make its design language native. -Also form a **register hypothesis** from what you find: +## Step 3: Interview for product truth -- Brand signals: `/`, `/about`, `/pricing`, `/blog/*`, `/docs/*`, hero sections, big typography, scroll-driven sections, landing-page-shaped content. -- Product signals: `/app/*`, `/dashboard`, `/settings`, `/(auth)`, forms, data tables, side/top nav, app-shell components. +STOP and call the `question` tool to clarify. Ask only about material gaps the repository and original request do not answer with strong evidence. -Register is a hypothesis at this point, not a decision; Step 3 confirms it. +Use the structured question tool when available; otherwise ask and wait. Keep rounds to at most three focused questions and require one real answer or approval round before writing a new PRODUCT.md. Confirm inferences. -Also form a **platform hypothesis**: +Start with the unknowns that most change future product decisions: -- Native signals: React Native / Expo (`react-native`, `expo`), Flutter (`pubspec.yaml`, `flutter`), SwiftUI / UIKit (`.swift`, `.xcodeproj`, an `ios/` app target), Jetpack Compose / Android (`build.gradle`, an `android/` app module, `AndroidManifest.xml`). An `ios/` and/or `android/` directory that is a real app target, not just a Capacitor/Cordova wrapper around a website. -- Web signals (the default): a web framework (Vite, Next, Nuxt, SvelteKit, Astro), an HTML entry, a CSS/Tailwind setup, no native app target. +1. Who is the primary user, in what situation, and what job are they doing? +2. What does the product make possible, and what is its meaningfully different mechanism or position? +3. What durable constraints, assets, evidence, or product facts must future work preserve? -Values: `web` / `ios` / `android` / `adaptive` (one codebase, ships both, adapts per OS). Mobile web is still `web`. Like register, this is a hypothesis; Step 3 confirms it. +Confirm ambiguous platform separately. Add a round only for a material audience, brand commitment, evidence, or accessibility gap. Record undecided facts instead of inventing them. -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. +Do not ask for an aesthetic direction, emotional feel, visual references, colors, typography, or style during init. If the user volunteers a binding visual constraint, record it without expanding it. -## Step 3: Ask strategic questions (for PRODUCT.md) +### What belongs here -STOP and call the `question` tool to clarify. Ask about anything the codebase doesn't answer with strong, explicit evidence. +- users, jobs, workflows, purpose, success, positioning, and operating context; +- capabilities, constraints, terminology, evidence, platform, and accessibility; +- confirmed voice, assets, and brand commitments. -### Interview mode, not confirmation mode +### What does not belong here -If the repo is empty or the user's brief is sparse, run a short interview before proposing PRODUCT.md. Do **not** turn a one-sentence request into a complete inferred PRODUCT.md and ask for blanket confirmation. - -- Use the harness's structured question tool when one exists. Otherwise, ask directly in chat and stop: one question at a time, with lettered options where the crawl suggests likely answers, waiting for each answer before the next. -- Keep skill vocabulary (register, belief ladder, anti-references) out of question text; ask for the thing in words the user would use. For the brand register, ask like a magazine editor profiling the brand: curious and narrative, drawing out the story, the feel, and what a visitor should come to believe. -- Ask in focused rounds and wait for answers between them. Keep **one topic per question**; add rounds rather than fold several topics into one either-or choice. Options obey the same rule: an option answers only the question asked; never write a compound option that bundles a feeling with a business outcome or names an additional audience. -- Use inferred answers as hypotheses or options, not as finished facts. -- Complete at least one real user-answer round before drafting PRODUCT.md, unless every required answer is directly discoverable from repo docs. -- Round 1 should establish register, platform, users, purpose, positioning, and desired outcome. -- Round 2 should establish brand personality or references, anti-references, and accessibility needs, plus conversion & proof for Persuade surfaces. - -### Minimum viable interview - -Ask enough to complete PRODUCT.md. At minimum, cover register confirmation, **platform confirmation** (`web` / `ios` / `android` / `adaptive`), users, purpose, positioning, brand personality, anti-references, and accessibility needs (plus conversion & proof for Persuade surfaces) unless each answer is directly discoverable from repo context. Never let the template's default `web` stand unconfirmed for a native or cross-platform repo. After at least one interview round, you may propose inferred answers, but the user must confirm them before you write PRODUCT.md. Never synthesize PRODUCT.md from the original task prompt alone. - -### Register (ask first; it shapes everything below) - -Every design task serves one of four visitor modes (see SKILL.md's Registers): **Persuade** (marketing, landing, campaigns), **Experience** (portfolios, albums, bodies of work), **Operate** (app UI, admin, dashboards, tools), and **Read** (documentation, editorial, long-form). PRODUCT.md stores the two-value family that covers them: `brand` covers Persuade and Experience, `product` covers Operate and Read. The brief decides the exact mode per task; the stored register only sets the default family. - -If Step 2 produced a clear hypothesis, lead with it: *"From the codebase, this looks like a [brand / product] surface. Does that match your intent, or should we treat it differently?"* - -If the signal is genuinely split (e.g. a product with a big marketing landing), STOP and call the `question` tool to clarify. Ask which register describes the **primary** surface. The register can be overridden per task later, but PRODUCT.md carries one default. Settle the default before drafting any register-dependent questions; never batch Persuade-only questions (Conversion & proof) into the same round as the question that decides the register. - -### Platform (ask right after register) - -Every project targets **web** (includes responsive mobile web), **ios**, **android**, or **adaptive** (one codebase, ships both, adapts per OS: Flutter, React Native, KMP). Platform picks the native rulebook: HIG for `ios`, Material 3 for `android`, both for `adaptive`, none for `web`. - -If Step 2 produced a clear hypothesis, lead with it: *"From the codebase, this looks like a [web / ios / android / adaptive] project. Does that match?"* For cross-platform apps, decide by the **design language the app renders**, not the toolchain: one look on both platforms (Flutter's Material-everywhere default) takes that platform's value; genuine per-OS adaptation (Cupertino on iOS, Material on Android) is `adaptive`. When in doubt, `web`. - -A monorepo shipping both a website and a native app gets a PRODUCT.md per app, each with its own `## Platform`; the root PRODUCT.md carries the primary surface's platform. - -### Users & Purpose -- Who uses this? What's their context when using it? -- What job are they trying to get done? -- What is this for? A purpose stated in README or docs is a hypothesis, not strong evidence; confirm it, don't transcribe it. -- What does success look like? -- If more than one kind of user is plausible, confirm a primary and secondary audience; don't manufacture a split that isn't there. An audience implied by another answer (a success metric, a CTA) is still unconfirmed; ask before writing it as secondary. -- If the surface speaks to a different audience than the people who use the product, ask the user to name both. -- For brand: what emotions should the interface evoke? (confidence, delight, calm, urgency) Ask this standalone; don't fold emotions into the success question. -- For product: what workflow are they in? What's the primary task on any given screen? - -### Positioning -- In one line, what does this do that nothing else does? The single strategic claim every screen reinforces. - -### 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? - - 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? - -### Conversion & proof (Persuade surfaces only) - -Ask these only when the primary surface is a Persuade one (marketing, landing, campaigns). Experience and Read surfaces (portfolios, albums, long-form writing, documentation) get no CTA, belief-ladder, or proof questions even when the stored register is `brand`; skip this section and its PRODUCT.md counterpart. - -- What's the primary CTA? -- What's the secondary fallback, for visitors not ready for the primary? -- The one line a visitor should remember after 10 seconds. -- What must the visitor believe, in order, before taking the primary CTA? (The template's belief ladder.) -- What proof is on hand? Ask the user to hand over any testimonials, case studies, press, or client/partner logos they already have. If you can receive files directly, collect them; otherwise create `.impeccable/assets/proof/` and ask the user to add files there. Reference supplied files by path; record text proof inline. - -### Accessibility & Inclusion -- Specific accessibility requirements? (WCAG level, known user needs) -- Considerations for reduced motion, color blindness, or other accommodations? - -Skip questions where the answer is already clear. **Do NOT ask about colors, fonts, radii, or visual styling here.** Those belong in DESIGN.md, not PRODUCT.md. +- visual worlds, palettes, typography, components, or page concepts; +- visitor mode, narrative, CTA/proof sequence, or other surface strategy; +- invented testimonials, customers, benchmarks, pricing, licensing, or deployment claims; +- a requirement to decide every optional field. ## Step 4: Write PRODUCT.md -Write PRODUCT.md only after the user has confirmed the strategic answers from Step 3. If an inferred answer is uncertain or unconfirmed, ask before writing. Confirmed means what the user actually said yes to; do not pad a confirmed answer with extras they never picked (additional anti-references, audiences, roadmap claims, a WCAG level), whether drawn from the crawl, another answer, or your own option text. If an extra belongs in the doc, ask about it first. - -Synthesize into a strategic document: +Write only confirmed facts and explicitly marked open decisions. Omit irrelevant sections rather than filling them with generic prose. ```markdown # Product -## Register - -product - ## Platform web ## Users -[Who they are, their context, the job to be done. Primary audience; a secondary audience or a surface-vs-user split only when they apply.] +[Primary users, their situation, and job. Add other audiences only when confirmed.] ## Product Purpose -[What this product does, why it exists, what success looks like] +[What the product does, why it exists, and what success means.] ## Positioning -[The single strategic claim every screen reinforces. Not a visual rule, not an anti-reference.] +[The product mechanism or claim a neighboring product could not truthfully copy.] -## Conversion & proof -[Persuade surfaces only (marketing, landing, campaigns). Omit this section entirely, heading included, for the product register and for Experience or Read surfaces such as portfolios, albums, or long-form writing.] -- Primary and secondary CTA: [...] -- The line a visitor remembers after 10 seconds: [...] -- Belief ladder: [...] -- Proof on hand: [testimonials, case studies, press, or logos, referenced by path] +## Operating Context +[Workflows, environments, tools, documents, materials, and rituals that are factual parts of using or evaluating the product.] -## Brand Personality -[Voice, tone, 3-word personality, emotional goals] +## Capabilities and Constraints +[Confirmed functionality, technical constraints, terminology, and explicitly undecided product facts.] -## Anti-references -[What this should NOT look like. Specific bad-example sites or patterns to avoid.] +## Brand Commitments +[Existing name, voice, assets, personality, identity constraints, and references the user explicitly made binding. Omit when none exist.] -## Design Principles -[3-5 strategic principles derived from the conversation. Principles like "practice what you preach", "show, don't tell", "expert confidence". NOT visual rules like "use OKLCH" or "magenta accent".] +## Evidence on Hand +[Real content, data, demonstrations, testimonials, case studies, press, or assets, with paths where applicable. State absences that future work must not fabricate.] + +## Product Principles +[Three to five durable strategic principles derived from confirmed answers; no visual recipes.] ## Accessibility & Inclusion -[WCAG level, known user needs, considerations] +[Known user needs or required standard. Omit when no product-specific requirement was established.] ``` -Register is either `brand` or `product` as a bare value. No prose, no commentary. Platform is `web`, `ios`, `android`, or `adaptive`, also a bare value; omit the section only on legacy files you're leaving untouched, otherwise write `web` explicitly. +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. -Write fields as prose, and use bold sparingly: only where a word carries a decision, never as a label lead-in on every line. +### Completion gate -Write to `PROJECT_ROOT/PRODUCT.md`. If `.impeccable.md` existed, the loader already renamed it; merge into that content rather than starting from scratch. +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: Decide on DESIGN.md +## Step 5: Configure live mode when useful -Offer `/impeccable document` either way. Two paths: +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. -- **Code exists** (CSS tokens, components, a running site): "I can generate a DESIGN.md that captures your visual system (colors, typography, components) so variants stay on-brand. Want to do that now?" -- **Pre-implementation** (empty project): "I can seed a starter DESIGN.md from five quick questions about color strategy, type direction, motion energy, and references. You can re-run once there's code, to capture the real tokens. Want to do that now?" +## Step 6: Wrap up or resume -If the user agrees, delegate to `/impeccable document` (it auto-detects scan vs seed). Load its reference and follow that flow. +Summarize captured and deliberately undecided facts. Do not offer DESIGN.md merely because it is missing. -If the user prefers to skip, mention they can run `/impeccable document` any time later. +Recommend the next action from the actual project state: -## Step 6: Configure live mode (when code exists) +- Empty or early project: ask naturally for the surface to be built, or use `/impeccable shape ` when the user wants a confirmed brief without implementation. New-work will establish a visual world only when the requested work needs one. +- Existing coherent interface without DESIGN.md: `/impeccable document` if the user wants the incumbent system recorded independently of a new build. +- Existing surface needing work: name the most relevant scoped command. +- Web project ready for visual iteration: `/impeccable live` when configured. -**Skip this step when the platform is native** (`ios` / `android` / `adaptive`): live mode drives a browser overlay. A hybrid wrapper or Expo web target serving HTML doesn't change that. - -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 .opencode/skills/impeccable/scripts/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) and platform captured (web / ios / android / adaptive) -- 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 or live config is pending, one line on how to set it up later - -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 platform**, offer the 2-4 most relevant (not a menu dump), and give the exact command to type. Group by intent: - -- **Build something new**: `/impeccable craft ` (shape, then build end-to-end) or `/impeccable shape ` (plan first). Lead with this for empty or early-stage projects. -- **Improve what's there**: name the specific surface. `/impeccable critique ` for a scored UX review; `/impeccable audit ` for a11y / perf / responsive checks; `/impeccable polish ` 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** (web only): `/impeccable live` (configured in Step 6) to pick elements in the browser and generate variants in place. **Skip this group for native platforms.** - -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 STOP and call the `question` tool to clarify. Ask whether they'd like a brief summary of PRODUCT.md appended to AGENTS.md for easier agent reference. If yes, append a short **Design Context** pointer section there. +If init was invoked by another request, resume without rerunning context.mjs; new-work owns later visual decisions. diff --git a/.opencode/skills/impeccable/reference/ios.md b/.opencode/skills/impeccable/reference/ios.md index ef10ee0d2..ccef5d2c4 100644 --- a/.opencode/skills/impeccable/reference/ios.md +++ b/.opencode/skills/impeccable/reference/ios.md @@ -2,7 +2,7 @@ For native iOS / iPadOS apps: SwiftUI, UIKit, React Native, Expo, Flutter shipping to Apple hardware. -On native, register narrows. HIG conformance governs structure, navigation, and interaction whatever the register; brand expresses through the expressive layer the platform provides (tint, type, motion, content). Calm, Duolingo, and Spotify carry strong identity entirely inside HIG conventions. +On native, the visitor mode narrows what expression may override. HIG conformance governs structure, navigation, and interaction in every mode; brand expresses through the layer the platform leaves open (tint, type, motion, content). ## The iOS slop test diff --git a/.opencode/skills/impeccable/reference/layout.md b/.opencode/skills/impeccable/reference/layout.md index a59665c47..781bec519 100644 --- a/.opencode/skills/impeccable/reference/layout.md +++ b/.opencode/skills/impeccable/reference/layout.md @@ -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. --- -## Register +## 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, register, 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 .opencode/skills/impeccable/scripts/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 `/impeccable polish` for the final pass. +When the structure holds, hand off to `/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) * )`: 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. diff --git a/.opencode/skills/impeccable/reference/live.md b/.opencode/skills/impeccable/reference/live.md index bf49a7c40..1419861ab 100644 --- a/.opencode/skills/impeccable/reference/live.md +++ b/.opencode/skills/impeccable/reference/live.md @@ -33,7 +33,7 @@ Chat is overhead. No recap, no tutorial output, no pasting PRODUCT / DESIGN bodi node .opencode/skills/impeccable/scripts/live.mjs ``` -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. +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, DESIGN.md, and any surface brief already loaded by Setup in mind for variant generation: **DESIGN.md wins on visual decisions; PRODUCT.md wins on durable product and voice decisions; the surface brief wins on this surface's strategy.** 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 requires the user's explicit redesign/replacement intent. `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). @@ -187,7 +187,7 @@ All three carry `fallback: "agent-driven"`. Follow **Handle fallback** below. ### 3. Load the action's reference -If `event.action` is `impeccable` (the default freeform action), use SKILL.md's shared laws plus the loaded register reference (`brand.md` or `product.md`). Do not load a sub-command reference. **Freeform is not a pass to skip parameters:** you still follow the composition budget and the freeform bias in **§7 Parameters** below. Sub-command files list MUST-have signature knobs; freeform has no such file, so sizing knobs from surface weight and primary axes is entirely on you. +If `event.action` is `impeccable` (the default freeform action), use SKILL.md's shared laws and decide the visitor mode from the selected surface. Do not load a sub-command reference. **Freeform is not a pass to skip parameters:** you still follow the composition budget and the freeform bias in **§7 Parameters** below. Sub-command files list MUST-have signature knobs; freeform has no such file, so sizing knobs from surface weight and primary axes is entirely on you. Any other `event.action` (`bolder`, `quieter`, `distill`, `polish`, `typeset`, `colorize`, `layout`, `adapt`, `animate`, `delight`, `overdrive`): Read `reference/.md` before planning. Each sub-command encodes a specific discipline; skipping its reference produces generic output. Those files may require specific params; layer them on top of the §7 budget, not instead of it. @@ -216,7 +216,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. @@ -224,10 +224,7 @@ This sentence is the **identity lock**. Every variant must be readable as the sa **Default mode**: the existing identity is preserved. Variants vary expression axes within it. *This is the right mode for ~90% of live sessions.* The user picked an element on a real product they're shipping; they expect variants of *their* hero, not three different brands' heroes. -**Departure mode**: the existing identity is rejected. Variants propose alternatives consistent with PRODUCT.md voice. Trigger only when at least one is true: - -- PRODUCT.md anti-references explicitly call out the current surface ("the current `index.html` is itself an example"; "diffuse away from this"; "the page on screen is the failure"). Generic anti-references that describe what to avoid in general do **not** trigger departure mode; only ones that point at *this* surface specifically. -- The user's freeform prompt explicitly asks for departure ("rebuild this from scratch", "what if it weren't editorial at all", "show me something completely different"). +**Departure mode**: the existing identity is rejected. Variants propose alternatives consistent with durable product and brand truth. Trigger only when the user explicitly asks for departure in the current request or freeform prompt ("redesign this", "rebuild this from scratch", "what if it weren't editorial at all", "show me something completely different"). A stale page critique or an old task note is not replacement authorization. If you're unsure, you're in default mode. The cost of being wrong about default is "three on-brand variants with similar feel": recoverable, the user picks none. The cost of being wrong about departure is "three off-brand variants": unrecoverable, the user is annoyed. @@ -246,13 +243,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 the brand's stated voice and register in PRODUCT.md. Do NOT pick from a fixed catalog of lane categories. The right three directions for this brand are not the same as the right three for another brand, 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. @@ -262,7 +259,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. @@ -289,7 +286,7 @@ In **default mode**, the prompt narrows the axes you choose, not the identity. * In **departure mode**, the prompt narrows the lanes you draw from, not the families. *"Make it feel like a newspaper front page"* would itself be a departure-mode prompt; honor it but pick three meaningfully different newspaper-adjacent lanes (broadsheet vs. tabloid vs. trade journal), and run the family pass to confirm they don't collapse into one. -When the prompt and PRODUCT.md anti-references conflict (the prompt asks for X, the anti-references ban X), the anti-references win; they describe the brand's standing position, the prompt is one moment. +When the prompt conflicts with a confirmed binding brand commitment or DESIGN.md invariant, preserve the invariant unless the user explicitly revokes or replaces it. Task-local strategy from the matching surface brief may change when the user changes that surface's goal. ### 6. Write all variants in a single edit diff --git a/.opencode/skills/impeccable/reference/new-work.md b/.opencode/skills/impeccable/reference/new-work.md index 44b1c360d..5f76f12da 100644 --- a/.opencode/skills/impeccable/reference/new-work.md +++ b/.opencode/skills/impeccable/reference/new-work.md @@ -1,47 +1,178 @@ -# New identity work +# New visual work -Read this only when the project has no committed visual identity, or the user explicitly asked to replace the current one. A missing DESIGN.md is not enough: code, tokens, chosen type, components, and assets are incumbent design authority. If they exist, preserve and extend them unless the brief says to discard them. +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. -This playbook is task-scoped. PRODUCT.md owns durable product facts and DESIGN.md owns durable visual invariants; the direction for one page or feature belongs in the current task, not in either document. +## 1. Name the intent -## Choose the authority +- **Greenfield:** no coherent visual implementation. +- **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. -Classify the work before inventing anything: +A plain “redesign this page/site” authorizes replacement. “Redesign this within the current brand/system” means extension or refinement. Ask once only when the wording is genuinely ambiguous. -- **Extend**: an incumbent system exists and the task adds or improves a surface. Preserve its lineage, semantics, and affordances. -- **Surface redesign**: the current identity remains, but this surface may change its topology, hierarchy, and expression. -- **New identity**: nothing committed exists, or the user explicitly authorized a rebrand. +## 2. Resolve visual authority -When the evidence is mixed, preserve. Novelty is not permission to erase authored decisions. +Read DESIGN.md and representative code, tokens, components, and assets. Choose one path: -## Find the concept +### A. Explicit redesign -Name the subject, audience, page job, and visitor mode from SKILL.md. Then identify the product's unique mechanism: what it does, proves, or enables that a neighboring product cannot truthfully claim. +The old DESIGN.md and implementation are not authority. Keep only unrevoked product facts, content, function, native expectations, constraints, and brand commitments. Establish a replacement world. -Derive the concept from that mechanism through one primary transformation: information topology, evidence, temporal behavior, or spatial mapping. The transformation must change how the surface is organized or behaves, not merely how it is decorated. This produces stranger and more defensible work than imitating a culturally specific document or object. Borrowing a literal cultural form is allowed only when it is genuinely native to the product and audience, and never as costume. +### B. DESIGN.md covers this kind of surface -For **Operate** and **Read**, start with the task and information structure. Native controls remain native controls; expression comes from hierarchy, density, rhythm, state, and the system around them. For **Persuade** and **Experience** creating a new identity, explore more freely, but keep the product legible and the primary action obvious. +Use its invariants and normative tokens. Skip world-building and discover the surface. -Run `node .opencode/skills/impeccable/scripts/concept-seed.mjs` only for Persuade or Experience new-identity work when two or more directions remain equally credible. It is an entropy source, not an authority: never let the draw overrule the strongest fit. When color is genuinely unconstrained, `node .opencode/skills/impeccable/scripts/palette.mjs` may break a reflex palette; subject evidence and incumbent color still win. +### C. A coherent implementation exists but DESIGN.md does not -## State the direction +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. -Before code, write a task-scoped direction contract of at most 120 words in your reasoning: +### D. The brand exists, but a whole-surface family is unresolved -- the thesis this surface owns; -- the structural or behavioral transformation; -- what authority is preserved; -- the first viewport's composition and primary action; -- the signature element the surface will be remembered by. +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. -Do not embed this contract as a comment in the production artifact or turn it into a second design system. In an attended substantial build, confirm it once. In an unattended run, record it and continue. +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. -## Build once, commit fully +### E. No confirmed visual authority exists -Plan one compact token system and one layout source: the concept or the content's real structure. Build the strongest coherent direction once. Commitment means the transformation governs the whole surface; it does not mean every control must be rebuilt as a metaphor. +Establish a world. Scaffolds, framework defaults, and stray utilities are not identity. -Make the first viewport demonstrate the product's mechanism. Pace long surfaces through contrast in density and scale, and cut sections that only repeat claims. Briefs that depend on imagery ship real, verified imagery. Preserve semantic HTML, familiar interaction behavior, accessibility, and the project's technical conventions. +## 3. Discover the requested surface -## Finish proportionally +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. -Inspect desktop and mobile, write one honest critique against the brief and direction, patch material defects, then run the detector once. Repeat only when a real defect remains. A separate reviewer is optional when the harness already provides one and the extra cost is earned by the risk; it is not a default step and its presence is not evidence of quality. +Ask one attended round of at most three material questions without repeating durable facts. CTA hierarchy, proof sequence, content gaps, and interaction outcomes belong here, not PRODUCT.md. For a fully specified narrow request, state the interpretation and invite correction. + +When `shape` has already completed its discovery interview, reuse those confirmed answers and do not ask this round again. + +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. Each candidate joins a durable visual system (identity thesis, information/layout grammar, material and type behavior, color strategy, imagery, motion, reusable signature) to a concrete expression for this surface (topology, sequence, focal moment, and implementation consequence). Do not rank yet. +3. **Break the ranking rut once.** Run `node .opencode/skills/impeccable/scripts/concept-seed.mjs --scope direction`. Promote the assigned grounded pair and weigh its challengers only when they can become both a coherent system and a strong solution to this task. +4. **Test coupling and breadth.** Reject a pair if its surface could swap into another candidate unchanged, or if its world is merely the surface motif repeated. Test each survivor on the requested surface plus navigation, quiet and dense content, interaction/state, and one unlike future surface. Compare it with both the category habit and predictable contrarian response. +5. **Offer coupled choices.** Present two or three materially different pairs without recommendation cues. For each, show the world rules, this surface's 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. 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 .opencode/skills/impeccable/scripts/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 promoted candidate when a roll ran; otherwise use the strongest grounded structure. It must survive both tests. + +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). + +### Write or update DESIGN.md + +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: + +`` + +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 .opencode/skills/impeccable/scripts/surface-brief.mjs read ` + +Exit 0: preserve still-valid decisions and change only what the user changed. For redesign, retain valid product strategy, content, function, and open decisions; replace visual direction and contract. Exit 2: no brief. After the contract, write with `node .opencode/skills/impeccable/scripts/surface-brief.mjs write [related-target ...]`. + +The body is concise and contains: + +```markdown +# Surface brief: [name] + +## Scope +[Primary/related route or artifact, visitor mode, and what this surface owns.] + +## Product strategy +[Surface-specific audience and job, desired outcome, primary/secondary action or task, content and proof sequence, factual constraints, and explicitly open decisions.] + +## Selected direction +[Reference to the applicable DESIGN.md world or expression range, selected surface concept, focal moment, narrative/interaction sequence, and implementation consequence.] + +## Direction contract +[The six contract blocks below.] + +## Open decisions +[Only unresolved items that later work must not silently invent. Omit when empty.] +``` + +Commit `.impeccable/surfaces/.md` as stable later-work context. Exclude global truth, exact tokens, transient notes, and work logs. + +## 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. + +Before code, write a direction contract of at most 150 words in an opening HTML or framework comment. The first 200 characters must name `DIRECTION CONTRACT`. + +- `UNIQUE`: the task thesis tied to the product mechanism; +- `NOT-TEMPLATE`: the category-default arrangement this structure refuses; +- `OWN-WORLD`: the confirmed DESIGN.md invariants, tokens, and materials it uses; +- `STORY`: what the visitor understands, believes, and does; +- `FIRST VIEWPORT`: exact composition, hierarchy, action, and where the concept exceeds competent convention; +- `FORM`: chosen structure or behavior, signature, implementation consequence, and seed key. + +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. + +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. + +## 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. + +Build the strongest coherent direction once. Its grammar governs navigation, actions, controls, content, and transitions without disguising affordances. Give the focal form the scale that gives it force; do not trap it inside a standard hero panel. + +**Make the opening a thesis.** Demonstrate the mechanism immediately; leave an idea, interaction, or evidence, not merely mood. + +**Commit before correcting.** Land the hard move at full strength before refining it. In unattended work, safety is the known risk. + +**Commit at page scale.** Let color, material, image, or type own a region when the world calls for it. Scattered signature decoration is not commitment. + +**Pace the whole surface.** Vary density, scale, image, motion, and quiet inside one grammar. Cut repeated claims; prove the mechanism with real artifact, interaction, data, transformation, or content. + +**Author motion as material.** Motion expresses the world and task. Premium moments go beyond repeated transform/opacity through earned focus, depth, masks, continuity, light, or material change. Bound expensive effects, test in-browser, keep content visible by default, and design reduced motion. + +Preserve semantics, affordances, accessibility, performance, responsiveness, and project conventions. Operate/Read express through topology, hierarchy, density, rhythm, and state; Persuade/Experience may earn drama. + +## 8. Solidify the visual record + +After first implementation of a new/replacement world or approved expansion, refresh DESIGN.md from the build: + +- replace provisional direction with the exact type, color roles, tokens, spacing/radii, components, states, and motion that survived; +- add normative YAML tokens only for values the implementation actually uses; +- remove the directional-seed status once the record and implementation agree; +- preserve broader world invariants and expression ranges; +- do not promote the task's story, hero composition, or one-off motif into a global rule unless it is intentionally reusable. + +Ordinary extension does not rewrite DESIGN.md; only approved durable changes do. + +## 9. Finish like a 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. diff --git a/.opencode/skills/impeccable/reference/operate.md b/.opencode/skills/impeccable/reference/operate.md index 04ee9d7d2..f420cbaa2 100644 --- a/.opencode/skills/impeccable/reference/operate.md +++ b/.opencode/skills/impeccable/reference/operate.md @@ -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. - No orchestrated page-load sequences. Product loads into a task; users don't want to watch it load. -## Product bans (on top of the shared absolute bans) +## Product constraints - Decorative motion that doesn't convey state. - Inconsistent component vocabulary across screens. If the "save" button looks different in two places, one is wrong. @@ -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. diff --git a/.opencode/skills/impeccable/reference/polish.md b/.opencode/skills/impeccable/reference/polish.md index 8905ff24d..5a59c344b 100644 --- a/.opencode/skills/impeccable/reference/polish.md +++ b/.opencode/skills/impeccable/reference/polish.md @@ -1,241 +1,98 @@ -> **Additional context needed**: quality bar (MVP vs flagship). +> **Additional context needed**: quality bar and shipping constraints. -Perform a meticulous final pass to catch all the small details that separate good work from great work. The difference between shipped and polished. +Polish is refinement, never concealed redesign. Preserve the incumbent visual world, content, behavior, and everything outside scope. If the concept itself is wrong, say so and recommend redesign or `bolder` instead of smuggling in a replacement. -Detector and automated QA output are defect evidence only. A clean script result is never proof that the design is strong; gather browser evidence and inspect the real interaction path. +A detector result is defect evidence, not proof of quality. Inspect the rendered experience and real interaction path. -## Design System Discovery +## 1. Establish the system -Aligning the feature to the design system is **not optional**. Polish without alignment is decoration on top of drift, and it makes the next person's job harder. Discovery comes before any other polish work. +Read DESIGN.md and representative tokens, shared components, patterns, and neighboring flows. If no formal system exists, use coherent project conventions. -1. **Find the design system**: Search for design system documentation, component libraries, style guides, or token definitions. Study the core patterns: design principles, target audience, color tokens, spacing scale, typography styles, component API, motion conventions. -2. **Note the conventions**: How are shared components imported? What spacing scale is used? Which colors come from tokens vs hard-coded values? What motion and interaction patterns are established? What flow shapes are used for comparable actions (modal vs full-page, inline vs route, save-on-blur vs explicit submit)? -3. **Identify drift, then name the root cause**: For every deviation, classify it as a **missing token** (the value should exist in the system but doesn't), a **one-off implementation** (a shared component already exists but wasn't used), or a **conceptual misalignment** (the feature's flow, IA, or hierarchy doesn't match neighboring features). The fix differs by category: patch the value, swap to the shared component, or rework the flow. Fixing the symptom without naming the cause is how drift compounds. +Classify each drift before fixing it: -If a design system exists, polish **must** align the feature with it. If none exists, polish against the conventions visible in the codebase. **If anything about the system is ambiguous, ask. Never guess at design system principles.** +- **missing token:** the system needs a reusable value; +- **one-off implementation:** an existing shared component or pattern should replace it; +- **conceptual mismatch:** the flow, information architecture, or hierarchy differs from comparable product areas; +- **local defect:** the implementation is simply incomplete or inconsistent. -## Pre-Polish Assessment +Fix the cause at the narrowest correct level. Ask when a binding system principle cannot be inferred. -Understand the current state and goals before touching anything: +## 2. Gather the evidence -1. **Review completeness**: - - Is it functionally complete? - - Are there known issues to preserve (mark with TODOs)? - - What's the quality bar? (MVP vs flagship feature?) - - When does it ship? (How much time for polish?) +Use the feature yourself at representative desktop and mobile sizes. Determine: -2. **Think experience-first**: Who actually uses this, and what's the best possible experience for them? Effective design beats decorative polish; a feature that looks beautiful but fights the user's flow is not polished. Walk the path from their perspective before opening DevTools. +- whether the path is functionally complete; +- the intended quality bar and time available; +- known constraints or deliberately unfinished work; +- the states, content lengths, roles, and input methods users will actually encounter. -3. **Identify polish areas**: - - Visual inconsistencies - - Spacing and alignment issues - - Interaction state gaps - - Copy inconsistencies - - Edge cases and error states - - Loading and transition smoothness - - Information architecture and flow drift (does this feature reveal complexity the way neighboring features do?) +If a prior critique exists, use it as one input: -4. **Pull in any prior critique** (optional signal): If `/impeccable critique` has been run on the same target, its priority issues are a useful prior for what to address first. Resolve the target to a file path or URL, then: - ```bash - slug=$(node .opencode/skills/impeccable/scripts/critique-storage.mjs slug "") - node .opencode/skills/impeccable/scripts/critique-storage.mjs latest "$slug" - ``` - Exit 0 with body = found; fold the P0/P1 items into your polish list and mention the snapshot path so the user sees what you read. Exit 2 = no snapshot, continue without it. The critique is one input among many. Do your own pass either way. +```bash +slug=$(node .opencode/skills/impeccable/scripts/critique-storage.mjs slug "") +node .opencode/skills/impeccable/scripts/critique-storage.mjs latest "$slug" +``` -5. **Triage cosmetic vs functional**: Classify each issue as **cosmetic** (looks off, doesn't impede the user) or **functional** (breaks, blocks, or confuses the experience). When polish time is tight, functional issues ship first; cosmetic ones can land in a follow-up. Quality should be consistent; never perfect one corner while leaving another rough. +Exit 0 returns the latest snapshot; incorporate relevant P0/P1 findings and name the snapshot read. Exit 2 means none exists. Perform an independent pass either way. -**CRITICAL**: Polish is the last step, not the first. Don't polish work that's not functionally complete. +## 3. Triage -## Polish Systematically +Separate functional defects from cosmetic ones and fix in this order: -Work through these dimensions methodically: +1. broken or blocked tasks, data loss, misleading state, and inaccessible paths; +2. missing loading, empty, error, success, disabled, and permission states; +3. flow, hierarchy, responsive, and design-system drift; +4. visual and motion inconsistencies; +5. code and asset cleanup. -### Visual Alignment & Spacing +Do not perfect one corner while leaving the rest below the same quality bar. -- **Pixel-perfect alignment**: Everything lines up to grid -- **Consistent spacing**: All gaps use spacing scale (no random 13px gaps) -- **Optical alignment**: Adjust for visual weight (icons may need offset for optical centering) -- **Responsive consistency**: Spacing and alignment work at all breakpoints -- **Grid adherence**: Elements snap to baseline grid +## 4. Polish the whole path -**Check**: -- Enable grid overlay and verify alignment -- Check spacing with browser inspector -- Test at multiple viewport sizes -- Look for elements that "feel" off +### Flow and hierarchy -### Information Architecture & Flow +- Match neighboring mental models, terminology, disclosure, routing, save behavior, and optimistic or pessimistic patterns. +- Make the primary task and current state obvious without flattening every element to equal weight. +- Ensure arrival, transition, empty, and recovery paths connect instead of behaving as isolated screens. -Visual polish on a misshapen flow is wasted work. Match the *shape* of the experience to the system, not just the surface. +### Layout and type -- **Progressive disclosure**: Match how much is revealed when, compared to neighboring features. A settings page exposing 40 fields when the rest of the app reveals 5 at a time is drift, even if every field is perfectly styled. -- **Established user flows**: Multi-step actions follow the same shape as comparable flows elsewhere: modal vs full-page, inline edit vs separate route, save-on-blur vs explicit submit, optimistic vs pessimistic updates. -- **Hierarchy & complexity**: The same conceptual weight gets the same visual weight throughout. Primary actions don't become tertiary in one corner of the product, and tertiary actions don't shout. -- **Empty, loading, and arrival transitions**: How content arrives, updates, and leaves matches how it does in adjacent features. -- **Naming and mental model**: The feature uses the same nouns and verbs as the rest of the system. A "Workspace" here shouldn't be a "Project" three screens away. +- Align to the project's grid and spacing scale; fix optical as well as mathematical alignment. +- Group related content tightly and separate distinct groups generously. +- Keep same-role typography consistent; test measure, wrapping, localization expansion, zoom, and font loading. +- Verify every supported viewport rather than correcting only the current screenshot. -### Typography Refinement +### Color, imagery, and icons -- **Hierarchy consistency**: Same elements use same sizes/weights throughout -- **Line length**: 45-75 characters for body text -- **Line height**: Appropriate for font size and context -- **Widows & orphans**: No single words on last line -- **Hyphenation**: Appropriate for language and column width -- **Kerning**: Adjust letter spacing where needed (especially headlines) -- **Font loading**: No FOUT/FOIT flashes +- Use semantic tokens and stable color meanings across themes. +- Verify text, control, and focus contrast in every state. +- Keep icon families, stroke/weight, sizing, and optical alignment coherent. +- Prevent image layout shift; use correct aspect ratios, responsive sources, and useful alt text. -### Color & Contrast +### Interaction and state -- **Contrast ratios**: All text meets WCAG standards -- **Consistent token usage**: No hard-coded colors, all use design tokens -- **Theme consistency**: Works in all theme variants -- **Color meaning**: Same colors mean same things throughout -- **Accessible focus**: Focus indicators visible with sufficient contrast -- **Gray on color**: Never put gray text on colored backgrounds; use a shade of that color or transparency +- Every control needs appropriate default, hover, focus, active, disabled, loading, error, and success behavior. +- Preserve visible keyboard focus, logical tab order, labels, and platform-appropriate touch targets. +- Keep motion coherent, interruptible, performant, and reduced-motion aware. Do not add animation merely to make polish visible. +- Validate long, missing, localized, offline, slow, and permission-limited content where the product can encounter it. -### Interaction States +### Content and code -Every interactive element needs all states: +- Keep terminology, capitalization, punctuation, and factual copy consistent. Ask before changing claims. +- Remove debug output, dead code, unused imports, obsolete styles, and polish-created duplication. +- Replace custom implementations with shared components where the system owns the pattern. +- Promote genuinely reusable values to tokens; do not create a system abstraction for one local exception. -- **Default**: Resting state -- **Hover**: Subtle feedback (color, scale, shadow) -- **Focus**: Keyboard focus indicator (never remove without replacement) -- **Active**: Click/tap feedback -- **Disabled**: Clearly non-interactive -- **Loading**: Async action feedback -- **Error**: Validation or error state -- **Success**: Successful completion +## 5. Verify and finish -**Missing states create confusion and broken experiences**. +Walk the complete path again with mouse, keyboard, and touch where applicable. Check: -### Micro-interactions & Transitions +- mobile, intermediate, and wide layouts; +- loading, empty, error, success, disabled, long-content, and missing-content states; +- zoom, contrast, focus, semantics, screen-reader names, and reduced motion; +- console errors, layout shift, interaction latency, image loading, and supported browsers; +- agreement with DESIGN.md, neighboring features, and the user's scope. -- **Smooth transitions**: All state changes animated appropriately (150-300ms) -- **Consistent easing**: Use ease-out-quart/quint/expo for natural deceleration. Never bounce or elastic; they feel dated. -- **No jank**: Smooth animations; use atmospheric blur/filter/mask/shadow effects when they add polish, but bound expensive paint areas and avoid casual layout-property animation -- **Appropriate motion**: Motion serves purpose, not decoration -- **Reduced motion**: Respects `prefers-reduced-motion` +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. -### Content & Copy - -- **Consistent terminology**: Same things called same names throughout -- **Consistent capitalization**: Title Case vs Sentence case applied consistently -- **Grammar & spelling**: No typos -- **Appropriate length**: Not too wordy, not too terse -- **Punctuation consistency**: Periods on sentences, not on labels (unless all labels have them) - -### Icons & Images - -- **Consistent style**: All icons from same family or matching style -- **Appropriate sizing**: Icons sized consistently for context -- **Proper alignment**: Icons align with adjacent text optically -- **Alt text**: All images have descriptive alt text -- **Loading states**: Images don't cause layout shift, proper aspect ratios -- **Retina support**: 2x assets for high-DPI screens - -### Forms & Inputs - -- **Label consistency**: All inputs properly labeled -- **Required indicators**: Clear and consistent -- **Error messages**: Helpful and consistent -- **Tab order**: Logical keyboard navigation -- **Auto-focus**: Appropriate (don't overuse) -- **Validation timing**: Consistent (on blur vs on submit) - -### Edge Cases & Error States - -- **Loading states**: All async actions have loading feedback -- **Empty states**: Helpful empty states, not just blank space -- **Error states**: Clear error messages with recovery paths -- **Success states**: Confirmation of successful actions -- **Long content**: Handles very long names, descriptions, etc. -- **No content**: Handles missing data gracefully -- **Offline**: Appropriate offline handling (if applicable) - -### Responsiveness - -- **All breakpoints**: Test mobile, tablet, desktop -- **Touch targets**: 44x44px minimum on touch devices -- **Readable text**: No text smaller than 14px on mobile -- **No horizontal scroll**: Content fits viewport -- **Appropriate reflow**: Content adapts logically - -### Performance - -- **Fast initial load**: Optimize critical path -- **No layout shift**: Elements don't jump after load (CLS) -- **Smooth interactions**: No lag or jank -- **Optimized images**: Appropriate formats and sizes -- **Lazy loading**: Off-screen content loads lazily - -### Code Quality - -- **Remove console logs**: No debug logging in production -- **Remove commented code**: Clean up dead code -- **Remove unused imports**: Clean up unused dependencies -- **Consistent naming**: Variables and functions follow conventions -- **Type safety**: No TypeScript `any` or ignored errors -- **Accessibility**: Proper ARIA labels and semantic HTML - -## Polish Checklist - -Go through systematically: - -- [ ] Aligned to the design system (drift named and resolved by root cause) -- [ ] Information architecture and flow shape match neighboring features -- [ ] Visual alignment perfect at all breakpoints -- [ ] Spacing uses design tokens consistently -- [ ] Typography hierarchy consistent -- [ ] All interactive states implemented -- [ ] All transitions smooth (60fps) -- [ ] Copy is consistent and polished -- [ ] Icons are consistent and properly sized -- [ ] All forms properly labeled and validated -- [ ] Error states are helpful -- [ ] Loading states are clear -- [ ] Empty states are welcoming -- [ ] Touch targets are 44x44px minimum -- [ ] Contrast ratios meet WCAG AA -- [ ] Keyboard navigation works -- [ ] Focus indicators visible -- [ ] No console errors or warnings -- [ ] No layout shift on load -- [ ] Works in all supported browsers -- [ ] Respects reduced motion preference -- [ ] Code is clean (no TODOs, console.logs, commented code) - -**IMPORTANT**: Polish is about details. Zoom in. Squint at it. Use it yourself. The little things add up. - -Sweat the details. Zoom in until the alignment is right and the spacing reads as deliberate. Then ship. - -**NEVER**: -- Polish before it's functionally complete -- Polish without aligning to the design system; that's decoration on drift -- Guess at design system principles instead of asking when something is ambiguous -- Spend hours on polish if it ships in 30 minutes (triage) -- Introduce bugs while polishing (test thoroughly) -- Ignore systematic issues (if spacing is off everywhere, fix the system, not just one screen) -- Perfect one thing while leaving others rough (consistent quality level) -- Create new one-off components when design system equivalents exist -- Hard-code values that should use design tokens -- Introduce new patterns or flows that diverge from established ones - -## Final Verification - -Before marking as done: - -- **Use it yourself**: Actually interact with the feature. -- **Test on real devices**: Not just browser DevTools. -- **Ask someone else to review**: Fresh eyes catch things. -- **Compare to design**: Match intended design. -- **Check all states**: Don't just test happy path. -- **Treat automation carefully**: Run detector or QA commands when they are available and relevant, fix their defects, but never cite a clean result as proof that the work is polished. - -## Clean Up - -After polishing, ensure code quality: - -- **Replace custom implementations**: If the design system provides a component you reimplemented, switch to the shared version. -- **Remove orphaned code**: Delete unused styles, components, or files made obsolete by polish. -- **Consolidate tokens**: If you introduced new values, check whether they should be tokens. -- **Verify DRYness**: Look for duplication introduced during polishing and consolidate. +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. diff --git a/.opencode/skills/impeccable/reference/quieter.md b/.opencode/skills/impeccable/reference/quieter.md index 06a3c999d..7bc1dd548 100644 --- a/.opencode/skills/impeccable/reference/quieter.md +++ b/.opencode/skills/impeccable/reference/quieter.md @@ -2,7 +2,7 @@ Quiet design is harder than bold design. Subtlety needs precision. Reduce visual --- -## Register +## Visitor mode Persuade + Experience: "quieter" means more restrained palette, more whitespace, more typographic air. Drama is reduced, not eliminated; the POV stays intact. diff --git a/.opencode/skills/impeccable/reference/routing.md b/.opencode/skills/impeccable/reference/routing.md index 42557d348..9d6caa879 100644 --- a/.opencode/skills/impeccable/reference/routing.md +++ b/.opencode/skills/impeccable/reference/routing.md @@ -11,7 +11,7 @@ Reason over the signals; there is no score to obey: - `critique.latest` with a low `score` or non-zero `p0` / `p1` → `polish` (it reads that snapshot as its backlog), or re-run `critique` if the snapshot looks stale. - `git.changedFiles` pointing at one surface → scope `audit` or `polish` to those files specifically, naming them. - `devServer.running` true → `live` is available for in-browser iteration; if false, don't lead with `live`. **`live` and the bundled `detect.mjs` are web-only.** If `setup.platform` is `ios`, `android`, or `adaptive`, don't lead with either; the browser overlay and the HTML rule engine don't apply to native app code. -- Otherwise group by intent exactly as init's "Recommend starting points" step does (build new / improve what's there / iterate visually), tailored to `setup.register`. +- Otherwise group by intent (build new / improve what's there / iterate visually), tailored to the current surface and `setup.platform`. **If `scan.targets` is non-empty and `setup.platform` is not `ios`/`android`/`adaptive`, run `node .opencode/skills/impeccable/scripts/detect.mjs --json ` once** (the bundled detector over local files: no network, no npx; it reads HTML/CSS, so skip it for native projects). `scan.via` tells you what they are: `git-changes` (the markup/style files in your dirty tree, the most relevant set), `source-dir` (e.g. `src`, `app`), `html`, or `root`. Fold the hits into your picks: many quality / contrast hits → `audit` or `polish`; a specific slop family → the matching command (gradient text or eyebrows → `quieter` / `typeset`, flat or gray palette → `colorize`, and so on). It's a real, current signal that beats guessing. If detect errors or the tree is large and slow, skip it and recommend the user run `audit` themselves; never block the suggestion on it. diff --git a/.opencode/skills/impeccable/reference/shape.md b/.opencode/skills/impeccable/reference/shape.md index 9990ca153..acbb26e5b 100644 --- a/.opencode/skills/impeccable/reference/shape.md +++ b/.opencode/skills/impeccable/reference/shape.md @@ -1,167 +1,61 @@ -Shape the UX and UI for a feature before any code is written. This command produces a **design brief**: a structured artifact that guides implementation through discovery, not guesswork. +# Shape -**Scope**: Design planning only. This command does NOT write code. It produces the thinking that makes code good. +Discover what should be made and how it should work, then return a confirmed design brief without code. -**Output**: A design brief that can be handed off to /impeccable craft, or directly to /impeccable for freeform implementation. When visual direction probes are used, the images are supporting artifacts, not the primary output. +**Product gate:** when context reports that PRODUCT.md is missing, load and complete [init.md](init.md) before Phase 1. Do not substitute shape questions for the product interview. Once PRODUCT.md exists, return here; product context does not replace task discovery. -## Philosophy +## Phase 1: Discovery interview -Most AI-generated UIs fail not because of bad code, but because of skipped thinking. They jump to "here's a card grid" without asking "what is the user trying to accomplish?" This command inverts that: understand deeply first, so implementation is precise. +Do not write code or choose visual direction yet. -## Phase 1: Discovery Interview +### Cadence -**Do NOT write any code or make any design decisions during this phase.** Your only job is to understand the feature deeply enough to make excellent design decisions later. +- Use the structured question tool when available; otherwise ask and stop. +- Ask two or three related questions per round, then wait. One round is the default; add a second only when the answers expose a material gap. +- Do not dump a questionnaire, repeat settled facts, or turn obvious facts into menus. Assert the likely reading and invite correction. +- A sparse prompt requires at least one answer round. A precise prompt may need only a compact confirmation. -**Unattended runs:** when no user can respond (a one-shot task, an automated run, or an explicit instruction not to ask questions), answer the interview questions yourself from the brief, record the answers as the design brief, and continue without pausing at any confirmation gate in this file. The thinking is still required; only the waiting is waived. +### Round 1: purpose, people, and outcome -This is a required interaction, not optional guidance. Ask these questions in conversation, adapting based on answers. Don't dump them all at once; have a natural dialogue. STOP and call the `question` tool to clarify. +Choose the two or three questions that most change the result: -### Interview cadence +- What is this surface or feature for, and what problem must it solve? +- Who specifically reaches it, in what situation and state of mind? +- What is the primary thing they must understand or do? What would success look like? +- What is uniquely true here that a neighboring product or generic template could not claim? -Discovery includes at least one user-answer round unless PRODUCT.md, DESIGN.md, or an already-confirmed brief directly answers the needed inputs. With a sparse prompt, do **not** synthesize a complete brief for confirmation on the first response. +### Round 2: material, behavior, and boundaries -- Use the harness's structured question tool when one exists. Otherwise, ask directly in chat and stop. -- Ask **2-3 questions per round**, then wait for answers. -- Treat PRODUCT.md and DESIGN.md as anchors; they reduce repeated questions but do **not** replace shape for craft. Shape is task-specific. -- One round is the default. Add a second only if the first answers leave material gaps. Don't run a second round just to feel thorough. -- Round 1 should clarify purpose, audience/context, content/scope, and (for brand) visual direction. -- Round 2, when needed, fills in whatever's still genuinely missing. +Run only for material unresolved decisions: -**Assert-then-confirm, not menu-with-escape.** When PRODUCT.md and the user's prompt make one option obvious, name it and ask the user to confirm or override. Don't enumerate "Restrained / Committed / Or something else?" as a real choice; "This reads as Restrained, confirm?" beats a four-option menu when the answer is already clear. +- What real content, evidence, data, and assets must the experience carry? What are realistic minimum, typical, and maximum ranges? +- Which states and transitions matter: first-run, empty, loading, error, success, permissions, overflow, or expert use? +- What is the intended fidelity, breadth, and interactivity: exploration, production-ready screen, full flow, or broader surface? +- What must remain untouched? What would make the result feel wrong even if it looked polished? +- Which platform, framework, performance, accessibility, localization, or delivery constraints are binding? -### Purpose & Context -- What is this feature for? What problem does it solve? -- Who specifically will use it? (Not "users"; be specific: role, context, frequency) -- What does success look like? How will you know this feature is working? -- What's the user's state of mind when they reach this feature? (Rushed? Exploring? Anxious? Focused?) +Never ask for CSS values or canned aesthetic lanes. New-work owns visual-world and concept choices. -### Content & Data -- What content or data does this feature display or collect? -- What are the realistic ranges? (Minimum, typical, maximum, e.g., 0 items, 5 items, 500 items) -- What are the edge cases? (Empty state, error state, first-time use, power user) -- Is any content dynamic? What changes and how often? -- What visual assets are real content here? Note required images, product shots, illustrations, maps, textures, diagrams, generated objects, or existing project assets. +## Phase 2: Resolve the design direction -### Design Direction +For new surfaces, brand expansion, or replacement, follow [new-work.md](new-work.md) through visual authority, any world workshop, and concept choice. Reuse discovery, then return before its contract, persistence, or implementation. Inside an established world, use its concept process only when composition or interaction remains materially open. -Force a visual decision on three fronts. Skip anything PRODUCT.md or DESIGN.md already answers; ask only what's missing. +## Phase 3: Write the brief -- **Color strategy for this surface.** Pick one: Restrained / Committed / Full palette / Drenched. Can override the project default if the surface earns it (e.g. a drenched hero inside an otherwise Restrained product). -- **Theme via scene sentence.** Write one sentence of physical context for this surface: who uses it, where, under what ambient light, in what mood. The sentence forces dark vs light. If it doesn't, add detail until it does. -- **Two or three named anchor references.** Specific products, brands, objects. Not adjectives like "modern" or "clean." +Write the smallest useful brief: -### Scope +1. **Job and audience:** who arrives, their context, need, and visitor mode. +2. **Outcome and proof:** primary task/action, success, real evidence, and product-specific truth. +3. **Selected direction:** visual authority, structural/interaction thesis, sequence, focal moment, and implementation consequence. +4. **Scope and boundaries:** fidelity, breadth, interactivity, named target, what remains untouched, and explicit anti-goals. +5. **States and ranges:** realistic content/data ranges and material states. +6. **Interaction and layout:** hierarchy, topology, responsiveness, affordances, feedback, and transitions; intent, not CSS. +7. **Constraints and open decisions:** platform, delivery, accessibility, localization, reusable components, and choices a builder must not invent. -Always ask. Sketch quality and shipped quality are different outputs; don't guess between them. +Use three to five bullets when the task is settled; use the full structure only for ambiguous, multi-screen, or standalone planning. Do not restate the conversation. -- **Fidelity.** Sketch / mid-fi / high-fi / production-ready? -- **Breadth.** One screen / a flow / a whole surface? -- **Interactivity.** Static visual / interactive prototype / shipped-quality component? -- **Time intent.** Quick exploration, or polish until it ships? +## Confirm and stop -Scope answers are task-scoped. Don't write them to PRODUCT.md or DESIGN.md; carry them through the design brief only. +Present the brief for explicit confirmation or one correction round, then stop: shape never writes code or a direction contract. -### Constraints -- Are there technical constraints? (Framework, performance budget, browser support) -- Are there content constraints? (Localization, dynamic text length, user-generated content) -- Mobile/responsive requirements? -- Accessibility requirements beyond WCAG AA? - -### Anti-Goals -- What should this NOT be? What would be a wrong direction? -- What's the biggest risk of getting this wrong? - -## Phase 1.5: Visual Direction Probe (Capability-Gated) - -After the discovery interview, generate a small set of visual direction probes **before** writing the final brief when all of these are true: - -- The work is **net-new** or directionally ambiguous enough that visual exploration will clarify the brief. -- The requested fidelity is **mid-fi, high-fi, or production-ready**. Skip for sketch-only planning. -- The current harness gives you native image generation (Codex's `image_gen`, an equivalent MCP tool, or similar). Don't ask the user to install APIs or tooling. - -When those conditions are met, this step is mandatory. If image generation isn't natively available, do not ask the user to install APIs or tooling. State in one line that the image step is skipped because the harness lacks native image generation, then proceed. The one-line announcement is required, not optional; it forces a conscious decision instead of letting the step quietly evaporate. - -Use probes to explore visual lanes, not to replace the brief. - -Do not skip probes because the final UI will be semantic, editable, code-native, responsive, or accessible. Those are implementation requirements, not reasons to avoid visual exploration. - -### What to generate - -Generate **2 to 4** distinct direction probes based on the discovery answers, especially: - -- Color strategy -- Theme scene sentence -- Named anchor references -- Scope and fidelity - -The probes should differ in primary visual direction (hierarchy, topology, density, typographic voice, or color strategy), not just palette tweaks. - -### How to use the probes - -- Treat them as **direction tests**, not final designs. -- Use them to pressure-test whether the brief is pointing at the right lane. -- Ask the user which direction feels closest, what feels off, and what should carry forward. -- If the probes reveal a mismatch, revise the brief inputs before finalizing the brief. - -### Important limits - -- Do **not** skip discovery because image generation is available. -- Do **not** treat generated imagery as final UX specification, final copy, or final accessibility behavior. -- Do **not** use this step for minor refinements of existing work. It's for shaping a new surface or clarifying a big directional choice. - -If image generation isn't natively available, announce the skip in one line and proceed to the design brief. - -## Phase 2: Design Brief - -After the interview and any required probes, present a brief and **end your response**. The user must confirm before any implementation runs. Do not present a brief and then continue to code in the same response, even if the brief feels obvious to you. The user's confirmation is the gate. - -**Choose the brief shape based on how clear the answers are:** - -- **Compact form (3-5 bullets)** when discovery was crisp and the original prompt + PRODUCT.md already pinned scope, content, and direction. State what you're building, the visual lane, and end with one or two specific questions or a clear "confirm or override?" prompt. This is the default for typical craft requests with a clear prompt. -- **Full structured form (sections below)** when the task is genuinely ambiguous, multi-screen, or when the user asked for shape as a standalone step. Use this when the discipline of structure earns its weight. - -Don't pad a clear brief into a long one to look thorough. A 70-line brief restating answers the user just gave is noise, not rigor. Equally, don't skip the confirmation pause to look efficient: the pause is the point. - -Present the brief, then **stop and wait for explicit confirmation**. You are not the judge of whether the user already approved. Even when the brief feels obviously right, ask once and wait. The pause is what separates shape from premature implementation. - -### Brief Structure - -**1. Feature Summary** (2-3 sentences) -What this is, who it's for, what it needs to accomplish. - -**2. Primary User Action** -The single most important thing a user should do or understand here. - -**3. Design Direction** -Color strategy (Restrained / Committed / Full palette / Drenched) + the theme scene sentence + 2–3 named anchor references. Reference PRODUCT.md and DESIGN.md where they already answer, and note any per-surface overrides. - -If you ran the Visual Direction Probe step, name which probe direction won and what changed in the brief because of it. - -**4. Scope** -Fidelity, breadth, interactivity, and time intent from the Scope section of the interview. Task-scoped; these don't persist beyond the brief. - -**5. Layout Strategy** -High-level spatial approach: what gets emphasis, what's secondary, how information flows. Describe the visual hierarchy and rhythm, not specific CSS. - -**6. Key States** -List every state the feature needs: default, empty, loading, error, success, edge cases. For each, note what the user needs to see and feel. - -**7. Interaction Model** -How users interact with this feature. What happens on click, hover, scroll? What feedback do they get? What's the flow from entry to completion? - -**8. Content Requirements** -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., 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. - ---- - -STOP and call the `question` tool to clarify. Ask for explicit confirmation of the brief before finishing. - -If the user disagrees with any part, revisit the relevant discovery questions. A shape run is incomplete until the user confirms direction. - -Once confirmed, the brief is complete. The user can now hand it to /impeccable, or use it to guide any other implementation approach. (If the user wants the full discovery-then-build flow in one step, they should use /impeccable craft instead, which runs this command internally.) +When no human or structured answer mechanism exists, mark assumptions plainly, return the brief, and stop. diff --git a/.opencode/skills/impeccable/reference/typeset.md b/.opencode/skills/impeccable/reference/typeset.md index 3eca79293..72ce07ccf 100644 --- a/.opencode/skills/impeccable/reference/typeset.md +++ b/.opencode/skills/impeccable/reference/typeset.md @@ -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. --- -## Register +## Visitor mode -New identity work: run the font selection procedure in [new-work.md](new-work.md). Fluid `clamp()` scale, ≥1.25 ratio between steps. +- **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, register, **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 .opencode/skills/impeccable/scripts/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 `/impeccable polish` for the final pass. +When the hierarchy holds, hand off to `/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) * )` 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 - -The tactical selection procedure and the reflex-reject list live in [new-work.md](new-work.md) (loaded for new identity work). 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 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. diff --git a/.opencode/skills/impeccable/scripts/command-metadata.json b/.opencode/skills/impeccable/scripts/command-metadata.json index 83796d54b..dad8ef2e0 100644 --- a/.opencode/skills/impeccable/scripts/command-metadata.json +++ b/.opencode/skills/impeccable/scripts/command-metadata.json @@ -1,6 +1,6 @@ { "craft": { - "description": "Full confirmed-brief-then-build flow. Runs multi-round shape discovery first, resolves visual probe and north-star mock gates when available, then builds and visually iterates. Use when building a new feature end-to-end.", + "description": "Deprecated compatibility alias for an ordinary Impeccable new-work request. It adds no behavior; natural build and redesign requests use the same flow.", "argumentHint": "[feature description]" }, "init": { diff --git a/.opencode/skills/impeccable/scripts/concept-seed.mjs b/.opencode/skills/impeccable/scripts/concept-seed.mjs index 1f141a80f..25f20d9ab 100644 --- a/.opencode/skills/impeccable/scripts/concept-seed.mjs +++ b/.opencode/skills/impeccable/scripts/concept-seed.mjs @@ -1,6 +1,7 @@ #!/usr/bin/env node /** - * Concept-seed picker: the dice half of the new-work concept procedure. + * External concept seed: the dice half of new-work's coupled-direction and + * established-world surface procedures. * * The model derives a grounded shortlist of candidate FORMS from the * audience's world and the subject's cultural home (see @@ -12,10 +13,9 @@ * * This script rolls them from outside, the same trick that made the * palette seed work: - * - BUILD INDEX (2-5): which entry of the model's own resonance-ordered - * shortlist to build. The dice never choose an ungrounded ingredient; - * they only refuse the argmax rut. (Index 1 is excluded: that's the - * concept every run would ship anyway.) + * - PROMOTED INDEX: which entry of the model's own resonance-ordered + * shortlist must be taken seriously beside its favorites. The dice never + * choose an ungrounded ingredient; they only refuse the argmax rut. * - CHALLENGERS (3): outside forms from concept-ingredients.json, weighed * against the derived candidates on exactly two axes — audience * identification and product clarity. They win only when they beat the @@ -23,8 +23,8 @@ * material and win over thin categories, which is the intended shape. * * Usage: - * node scripts/concept-seed.mjs # roll at random - * node scripts/concept-seed.mjs --from # deterministic (hash key) + * node scripts/concept-seed.mjs --scope direction + * node scripts/concept-seed.mjs --scope surface --from * * Env vars: * IMPECCABLE_CONCEPT_SEED — same as --from; for reproducible eval runs. @@ -40,6 +40,12 @@ const pool = JSON.parse(readFileSync(join(here, 'concept-ingredients.json'), 'ut const args = process.argv.slice(2); const fromIdx = args.indexOf('--from'); +const scopeIdx = args.indexOf('--scope'); +const scope = scopeIdx !== -1 ? args[scopeIdx + 1] : 'surface'; +if (scope !== 'surface' && scope !== 'direction') { + process.stderr.write('concept-seed: --scope must be direction or surface\n'); + process.exit(1); +} // When no key is supplied, generate one and print it: a user reporting a // bad outcome can hand us the key and we replay the exact roll. const key = fromIdx !== -1 @@ -47,7 +53,7 @@ const key = fromIdx !== -1 : (process.env.IMPECCABLE_CONCEPT_SEED || crypto.randomBytes(4).toString('hex')); function hashUnit(k, salt) { - const h = crypto.createHash('sha256').update(`${salt}:${k}`).digest(); + const h = crypto.createHash('sha256').update(`${scope}:${salt}:${k}`).digest(); return h.readUInt32BE(0) / 0xffffffff; } const unit = (salt) => hashUnit(key, salt); @@ -67,22 +73,48 @@ for (let i = 0; picks.length < 3 && i < 60; i++) { } } -process.stdout.write(`CONCEPT SEED (key: ${key}; rerun with --from ${key} to reproduce this roll) -BUILD INDEX: ${buildIndex} - After ordering your derived candidates by resonance, build the page whose - form comes from candidate number ${buildIndex}, exactly as if it had ranked - first: full commitment. Your top-ranked candidate is what every run in this - category would ship; the assignment exists to refuse that rut, not to - punish it. -CHALLENGERS (weigh against your derived candidates on the same two axes, -audience identification and product clarity; a challenger wins only when -it beats the grounded list on both): +const promotedInstruction = scope === 'direction' + ? `After ordering the grounded coupled directions by product fit, promote + candidate ${buildIndex} into the serious shortlist. Each candidate must join + a durable visual system to a concrete expression for the requested first + surface; select or revise that pair as one decision. It must survive the + current task plus navigation, quiet and dense content, interaction and state, + and a substantially different future surface.` + : `After ordering the task's grounded structural candidates by resonance, + promote candidate ${buildIndex} into the serious shortlist. In an attended + run, present it beside the strongest materially different candidates and + let the user select or revise the surface concept. In a truly unattended + run, use it when it survives audience identification and product clarity.`; + +const challengerInstruction = scope === 'direction' + ? `A challenger enters only when it can supply both reusable identity grammar + and a strong first-surface structure, not a one-page costume or a style pasted + onto an unrelated layout. Weigh audience identification, product clarity, + current-surface force, and cross-surface system breadth.` + : `A challenger wins only when it beats the grounded list on both audience + identification and product clarity. It may change task topology or + interaction, but never the committed visual identity.`; + +const authorityInstruction = scope === 'direction' + ? `PRODUCT.md and explicit incumbent brand commitments constrain every coupled +direction. The seed never chooses exact colors, fonts, tokens, or a user +preference, and it never permits the world and first surface to be selected +independently.` + : `PRODUCT.md and DESIGN.md constrain every surface candidate's identity +vocabulary; they do not cancel task-level composition. The seed never +authorizes a new palette, type system, material world, or unfamiliar control +behavior.`; + +process.stdout.write(`${scope.toUpperCase()} CONCEPT SEED (key: ${key}; rerun with --scope ${scope} --from ${key} to reproduce this roll) +PROMOTED INDEX: ${buildIndex} + ${promotedInstruction} + The promotion exists to refuse the model's ranking rut, not to outrank the + user or the brief. +CHALLENGERS: 1. ${picks[0]} 2. ${picks[1]} 3. ${picks[2]} -If a challenger wins, it replaces the assigned candidate. If the surface is -an existing world whose incumbent carries a deliberate, ownable idea, the -incumbent IS the chosen candidate: intensify its lineage and ignore the -roll entirely. The same override applies when the user, PRODUCT.md, or -DESIGN.md pins a direction: pinned direction beats the roll, always. +${challengerInstruction} +${authorityInstruction} +A user- or brief-pinned decision beats the roll, always. `); diff --git a/.opencode/skills/impeccable/scripts/context-signals.mjs b/.opencode/skills/impeccable/scripts/context-signals.mjs index 2fc27bea7..d296320d7 100644 --- a/.opencode/skills/impeccable/scripts/context-signals.mjs +++ b/.opencode/skills/impeccable/scripts/context-signals.mjs @@ -11,7 +11,7 @@ * output is always valid JSON. * * Signals: - * - setup: PRODUCT.md / DESIGN.md presence, register, whether code exists + * - setup: PRODUCT.md / DESIGN.md presence and whether code exists * - critique: the latest cached critique score (.impeccable/critique) * - git: branch + files changed vs the default branch (a scope hint) * - devServer: whether a local dev server answers on a common port (gates live) @@ -21,7 +21,7 @@ import net from 'node:net'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { execFileSync } from 'node:child_process'; -import { loadContext, extractRegister, extractPlatform } from './context.mjs'; +import { loadContext, extractPlatform } from './context.mjs'; import { getCritiqueDir } from './lib/impeccable-paths.mjs'; /** Is there code here at all, or just context files / an empty repo? */ @@ -196,7 +196,6 @@ export async function gatherSignals(cwd = process.cwd()) { hasDesign: ctx.hasDesign, designPath: ctx.designPath, hasCode: hasCode(cwd), - register: extractRegister(ctx.product), platform: extractPlatform(ctx.product), }, critique: { latest: latestCritique(cwd) }, diff --git a/.opencode/skills/impeccable/scripts/context.mjs b/.opencode/skills/impeccable/scripts/context.mjs index 2ef2d4ce8..080e7ee3a 100644 --- a/.opencode/skills/impeccable/scripts/context.mjs +++ b/.opencode/skills/impeccable/scripts/context.mjs @@ -1,8 +1,10 @@ /** - * Context loader: prints PRODUCT.md (and DESIGN.md if present) as one - * markdown block on stdout, or prints a `NO_PRODUCT_MD:` message when no + * Context loader: prints PRODUCT.md, DESIGN.md when present, the matching + * persisted surface brief when one can be resolved, and native-platform + * guidance selected from PRODUCT.md. It prints a + * `NO_PRODUCT_MD:` message when no * PRODUCT.md is found anywhere. The skill keys off that message to branch: - * from-scratch build commands (init / teach / craft / shape) and clear + * from-scratch build requests (plus init / teach / shape) and clear * build/shape intent divert into the init flow, while scoped commands proceed * using the existing code as context. * @@ -23,10 +25,13 @@ import os from 'node:os'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { parseTargetOptions } from './lib/target-args.mjs'; -import { IMPECCABLE_COMMAND } from './lib/provider.mjs'; +import { IMPECCABLE_COMMAND, IMPECCABLE_PROVIDER_ID } from './lib/provider.mjs'; +import { renderLlmOnlySlopReview } from './lib/slop-review.mjs'; +import { resolveSurfaceBrief } from './lib/surface-briefs.mjs'; const PRODUCT_NAMES = ['PRODUCT.md', 'Product.md', 'product.md']; const DESIGN_NAMES = ['DESIGN.md', 'Design.md', 'design.md']; +const SKILL_REFERENCE_DIR = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', 'reference'); const FALLBACK_DIRS = ['.agents/context', 'docs']; const MONOREPO_MARKER_FILES = ['pnpm-workspace.yaml', 'turbo.json', 'nx.json', 'lerna.json']; const MONOREPO_FALLBACK_PROJECT_DIRS = ['apps', 'packages']; @@ -41,6 +46,8 @@ const WORKSPACE_DISCOVERY_IGNORED_DIRS = new Set([ '.turbo', '.cache', 'coverage', + 'vendor', + 'vendors', ]); const VISUAL_SOURCE_DIRS = ['src', 'app', 'pages', 'components', 'site', 'public', 'styles']; const STYLE_EXTENSIONS = new Set(['.css', '.scss', '.sass', '.less', '.styl']); @@ -73,6 +80,12 @@ export function loadContext(cwd = process.cwd(), options = {}) { const designPath = resolved.designPath; const product = productPath ? safeRead(productPath) : null; const design = designPath ? safeRead(designPath) : null; + const platform = extractPlatform(product); + const surfaceResolution = resolveSurfaceBrief( + resolved.projectRoot, + hasTargetOption(options) ? options.targetPath : null, + ); + const surfaceBrief = surfaceResolution.brief; return { hasProduct: !!product, product, @@ -83,7 +96,18 @@ export function loadContext(cwd = process.cwd(), options = {}) { contextDir: resolved.contextDir, productContextDir: productPath ? path.dirname(productPath) : null, designContextDir: designPath ? path.dirname(designPath) : null, + hasSurfaceBrief: !!surfaceBrief, + surfaceBrief: surfaceBrief?.text ?? null, + surfaceBriefPath: surfaceBrief?.path ? path.relative(absCwd, surfaceBrief.path) : null, + surfaceBriefReason: surfaceResolution.reason, + surfaceBriefCandidates: surfaceResolution.candidates.map((brief) => ({ + slug: brief.slug, + path: path.relative(absCwd, brief.path), + primaryTarget: brief.primaryTarget, + relatedTargets: brief.relatedTargets, + })), hasVisualImplementation: hasVisualImplementation(resolved.projectRoot), + platform, projectRoot: resolved.projectRoot, repoRoot: resolved.repoRoot, isMonorepo: resolved.isMonorepo, @@ -696,6 +720,19 @@ function safeRead(p) { } } +function loadNativePlatformReferences(platform) { + const names = platform === 'adaptive' + ? ['ios', 'android'] + : platform === 'ios' || platform === 'android' + ? [platform] + : []; + return names.flatMap((name) => { + const filePath = path.join(SKILL_REFERENCE_DIR, `${name}.md`); + const content = safeRead(filePath); + return content ? [{ name, filePath, content }] : []; + }); +} + /** * Best-effort evidence that the project already has an incumbent visual * implementation. DESIGN.md is documentation, not the only source of design @@ -719,9 +756,11 @@ export function hasVisualImplementation(projectRoot) { let styledComponents = 0; const inspectFile = (filePath) => { - if (scannedFiles++ >= VISUAL_SCAN_FILE_LIMIT) return false; const ext = path.extname(filePath).toLowerCase(); if (!STYLE_EXTENSIONS.has(ext) && !UI_EXTENSIONS.has(ext)) return false; + const base = path.basename(filePath).toLowerCase(); + if (/\.min\.[a-z]+$/.test(base)) return false; + if (scannedFiles++ >= VISUAL_SCAN_FILE_LIMIT) return false; let body; try { body = fs.readFileSync(filePath, 'utf-8').slice(0, 64 * 1024); @@ -729,18 +768,28 @@ export function hasVisualImplementation(projectRoot) { return false; } - const base = path.basename(filePath).toLowerCase(); + const evidence = body + .replace(/\/\*[\s\S]*?\*\//g, '') + .replace(//g, '') + .replace(/^\s*\/\/.*$/gm, ''); if (STYLE_EXTENSIONS.has(ext)) { - const customProperties = body.match(/--[a-z0-9_-]+\s*:/gi)?.length ?? 0; - const visualDeclarations = body.match(/\b(?:color|background(?:-color)?|border(?:-color)?|font-family)\s*:/gi)?.length ?? 0; - if (/\b(?:tokens?|theme|design-system)\b/.test(base) && body.trim().length > 80) return true; + const customProperties = evidence.match(/--[a-z0-9_-]+\s*:/gi)?.length ?? 0; + const visualDeclarations = evidence.match(/\b(?:color|background(?:-color)?|border(?:-color)?|font-family)\s*:/gi)?.length ?? 0; + if (/\b(?:tokens?|theme|design-system)\b/.test(base) && evidence.trim().length > 80) return true; if (customProperties >= 3 || visualDeclarations >= 5) return true; } - if ((ext === '.html' || ext === '.htm') && body.length > 600 && /]+stylesheet/i.test(body)) { + if ((ext === '.html' || ext === '.htm') && evidence.length > 600 && /]+stylesheet/i.test(evidence)) { return true; } - if (!['.html', '.htm'].includes(ext) && body.length > 300 && /class(?:Name)?\s*=|style\s*=|styled\(|css`/i.test(body)) { + if (!['.html', '.htm'].includes(ext) && evidence.length > 300) { + const embeddedCustomProperties = evidence.match(/--[a-z0-9_-]+\s*:/gi)?.length ?? 0; + const embeddedVisualDeclarations = evidence.match(/\b(?:color|background(?:-color)?|border(?:-color)?|font-family)\s*:/gi)?.length ?? 0; + const classTokens = [...evidence.matchAll(/class(?:Name)?\s*=\s*["'`]([^"'`]+)["'`]/gi)] + .reduce((count, match) => count + match[1].trim().split(/\s+/).length, 0); + if ((embeddedCustomProperties >= 3 && embeddedVisualDeclarations >= 3) || embeddedVisualDeclarations >= 5 || classTokens >= 12) return true; + } + if (!['.html', '.htm'].includes(ext) && evidence.length > 300 && /class(?:Name)?\s*=|style\s*=|styled\(|css`/i.test(evidence)) { styledComponents += 1; if (styledComponents >= 3) return true; } @@ -781,9 +830,9 @@ function escapeRegExp(value) { /** * Read the first non-empty line under a bare `## ` section of - * PRODUCT.md (e.g. `## Register`, `## Platform`). Returns null when the + * PRODUCT.md (for example `## Platform`). Returns null when the * section is absent. The heading match is exact (`\s*$`) so near-miss - * headings like `## Register guidelines` don't shadow the real field. + * near-miss headings don't shadow the real field. */ export function extractSectionValue(product, heading) { if (!product) return null; @@ -802,16 +851,6 @@ export function extractSectionValue(product, heading) { return null; } -/** - * Pull the register (`brand` or `product`) out of PRODUCT.md by looking - * for a `## Register` section and reading the first non-empty line that - * follows it. Returns null when the file is legacy / register-less. - */ -export function extractRegister(product) { - const word = (extractSectionValue(product, 'Register') || '').toLowerCase(); - return word === 'brand' || word === 'product' ? word : null; -} - /** * Pull the platform (`web`, `ios`, `android`, or `adaptive`) out of PRODUCT.md * by looking for a `## Platform` section and reading the first non-empty line @@ -985,30 +1024,35 @@ async function cli() { const parts = ctx.hasVisualImplementation ? [ 'NO_PRODUCT_MD: This project has no PRODUCT.md yet, but it does have an incumbent visual implementation. ' + - 'For `init` or `teach`, load reference/init.md and create PRODUCT.md. For `craft`, `shape`, or other work ' + - 'against the existing product, read its CSS, tokens, components, and assets first and proceed without blocking; ' + - `offer \`${IMPECCABLE_COMMAND} init\` as a follow-up. Only treat the work as greenfield when the user explicitly ` + - 'asks to discard or replace the current identity.', - 'EXISTING_VISUAL_SYSTEM: Code and assets are the incumbent design authority. Missing DESIGN.md is a documentation ' + - 'gap, not permission to invent a replacement identity. Preserve and extend the implementation unless the user ' + - 'explicitly requests a rebrand, or inspection proves the detected files contain no coherent visual decisions.', + 'For `init`, `teach`, `shape`, or any request to create a new surface or replacement visual world, load reference/init.md and create PRODUCT.md with the user first. ' + + 'After init writes PRODUCT.md, reference/new-work.md preserves and documents the incumbent system for an ' + + 'extension or replaces it with the user for a redesign/rebrand. Other ' + + 'narrow refinement commands may read the CSS, tokens, components, and assets and proceed without blocking, then ' + + `offer \`${IMPECCABLE_COMMAND} init\` as a follow-up.`, + 'BUILD_INIT_REQUIRED: Before shape or any new-surface/redesign flow, init must capture PRODUCT.md with the human or structured ' + + 'simulated user. Init writes product truth only; reference/new-work.md owns every visual decision.', + 'SCOPED_EXISTING_ALLOWED: Narrow refinement commands may use the incumbent implementation as authority without ' + + 'blocking on context setup; they must preserve it and offer init afterward.', + 'EXISTING_VISUAL_SYSTEM: For refinement or extension, code and assets are incumbent design authority and missing ' + + 'DESIGN.md is a documentation gap. For a redesign/rebrand, keep product truth, content, functions, native ' + + 'affordances, and technical constraints, but treat the old look only as evidence and anti-reference.', ] : [ 'NO_PRODUCT_MD: This project has no PRODUCT.md yet. ' + - 'For `init`, `teach`, `craft`, `shape`, ' + + 'For `init`, `teach`, `shape`, ' + 'or wording that clearly maps to a from-scratch build/shape flow, load ' + - 'reference/init.md and write PRODUCT.md first, unless no user can ' + - 'respond (a one-shot or automated run, or the user said not to ask): ' + - 'then write a one-paragraph understanding of the product, audience, ' + - 'and the page\'s job from the brief, and continue. For any other ' + + 'reference/init.md, complete its human or structured simulated-user interview, and write PRODUCT.md before ' + + 'designing. If no answer mechanism truly exists, init may infer only from the explicit brief and must label its ' + + 'assumptions. It never writes DESIGN.md. For any other ' + '(scoped) command against existing code, proceed using the code as ' + `context and offer \`${IMPECCABLE_COMMAND} init\` as a suggestion (do not block).`, - 'NEW_WORK: No committed design context was found. If this task produces ' + - 'new design (a build from scratch, or a redesign that discards the ' + - 'current look), you MUST read reference/new-work.md before making any ' + - 'design decision. Scoped fixes to existing code do not need it.', + 'PRODUCT_INIT_REQUIRED: No product context or visual authority was found. New builds and redesigns ' + + 'must finish reference/init.md for PRODUCT.md, then reference/new-work.md establishes the world and surface. Scoped ' + + 'fixes to existing code do not need the new-surface flow.', ]; + appendSurfaceBriefContext(parts, ctx); parts.push(buildResolvedContextDirective(ctx, cliOptions, { targetExists })); + appendHookFallback(parts, ctx); if (shouldWarnMissingTarget(ctx, targetProvided, targetExists)) { parts.push(buildMissingTargetDirective()); } @@ -1020,37 +1064,29 @@ async function cli() { if (ctx.hasDesign) { parts.push(`# DESIGN.md\n\n${ctx.design.trim()}`); } + appendSurfaceBriefContext(parts, ctx); parts.push(buildResolvedContextDirective(ctx, cliOptions, { targetExists })); + appendHookFallback(parts, ctx); if (shouldWarnMissingTarget(ctx, targetProvided, targetExists)) { parts.push(buildMissingTargetDirective()); } - const register = extractRegister(ctx.product); - // The register field survives as a family hint (brand = Persuade/Experience, - // product = Operate/Read); SKILL.md's mode section carries the essentials - // inline, so no register-file read is mandated here. What IS mandated: - // the new-work playbook when no committed design system exists yet. if (!ctx.hasDesign) { parts.push(ctx.hasVisualImplementation - ? 'EXISTING_VISUAL_SYSTEM: PRODUCT.md exists and DESIGN.md is missing, but code contains incumbent visual decisions. ' + - 'Treat CSS, tokens, components, and assets as design authority. Do not route to new identity work merely because the ' + - 'document is absent; preserve and extend the implementation unless the user explicitly asks to discard it.' - : 'NEW_WORK: PRODUCT.md exists but no DESIGN.md or incumbent visual implementation was found. If this task produces ' + - 'new design or a redesign that discards the current look, you MUST read reference/new-work.md before making any ' + - 'design decision. Scoped fixes to existing code do not need it.'); + ? 'INCUMBENT_WORLD_UNDOCUMENTED: PRODUCT.md exists and DESIGN.md is missing, but code contains incumbent visual decisions. ' + + 'For shape or a new-surface/redesign request, load reference/new-work.md: an extension documents and preserves the code-defined world; ' + + 'a redesign replaces it with the user and uses the old look only as evidence and anti-reference. Narrow refinement ' + + 'commands may proceed using the implementation directly.' + : 'WORLD_DISCOVERY_REQUIRED: PRODUCT.md exists but no DESIGN.md or incumbent visual implementation was found. ' + + 'For a new build or redesign, load reference/new-work.md and establish the visual world with the human or structured ' + + 'simulated user before developing the task concept. Scoped fixes to existing code do not need this flow.'); } - if (register) { - parts.push(`REGISTER: \`${register}\` (family hint: brand = Persuade/Experience surfaces, product = Operate/Read). Derive the visitor's mode per SKILL.md.`); - } - const platform = extractPlatform(ctx.product); - const nativeRefs = - platform === 'adaptive' ? ['ios', 'android'] : platform === 'ios' || platform === 'android' ? [platform] : []; - if (nativeRefs.length) { - const refList = nativeRefs.map(p => `\`reference/${p}.md\``).join(' and '); - const label = platform === 'adaptive' ? '`adaptive` (both iOS and Android)' : `\`${platform}\``; + const platformReferences = loadNativePlatformReferences(ctx.platform); + for (const reference of platformReferences) { parts.push( - `NEXT STEP: This project targets ${label}. Also read ${refList} for native conventions, in addition to SKILL.md's mode guidance.`, + `# NATIVE PLATFORM REFERENCE: ${reference.name.toUpperCase()} (reference/${reference.name}.md)\n\n${reference.content.trim()}`, ); - } else if (!platform) { + } + if (!ctx.platform) { // A `## Platform` section that names something we don't recognize (a // toolchain like `flutter`, a typo) would otherwise silently fall back to // web — the wrong default exactly when the user tried to say "native". @@ -1078,6 +1114,72 @@ function pathExistsForTarget(cwd, targetPath) { return fs.existsSync(abs); } +const HOOK_MANIFESTS_BY_PROVIDER = Object.freeze({ + 'claude-code': ['.claude/settings.local.json', '.claude/settings.json'], + codex: ['.codex/hooks.json'], + agents: ['.codex/hooks.json'], + cursor: ['.cursor/hooks.json'], + github: ['.github/hooks/impeccable.json'], +}); + +function truthyEnv(value) { + return typeof value === 'string' && /^(1|true|yes|on)$/i.test(value.trim()); +} + +function valueHasHookMarker(value) { + if (typeof value === 'string') { + return value.includes('skills/impeccable/scripts/hook.mjs') + || value.includes('skills/impeccable/scripts/hook-before-edit.mjs'); + } + if (Array.isArray(value)) return value.some(valueHasHookMarker); + if (value && typeof value === 'object') return Object.values(value).some(valueHasHookMarker); + return false; +} + +function hookEnabledAt(root) { + if (truthyEnv(process.env.IMPECCABLE_HOOK_DISABLED)) return false; + let enabled = true; + for (const name of ['.impeccable/config.json', '.impeccable/config.local.json']) { + const raw = readJson(path.join(root, name)); + if (raw?.hook && Object.prototype.hasOwnProperty.call(raw.hook, 'enabled')) { + enabled = raw.hook.enabled !== false; + } + } + return enabled; +} + +const STOP_REVIEW_PROVIDERS = new Set(['claude-code', 'codex', 'agents']); + +function automaticHookMode(ctx) { + if (ctx.platform === 'ios' || ctx.platform === 'android' || ctx.platform === 'adaptive') { + return 'none'; + } + const activeRoot = path.resolve(ctx.projectRoot || process.cwd()); + if (!hookEnabledAt(activeRoot)) return 'none'; + const manifests = HOOK_MANIFESTS_BY_PROVIDER[IMPECCABLE_PROVIDER_ID] || []; + const roots = [...new Set([process.cwd(), ctx.projectRoot, ctx.repoRoot].filter(Boolean).map((root) => path.resolve(root)))]; + for (const root of roots) { + for (const rel of manifests) { + const raw = readJson(path.join(root, rel)); + if (raw?.hooks && valueHasHookMarker(raw.hooks)) { + return STOP_REVIEW_PROVIDERS.has(IMPECCABLE_PROVIDER_ID) ? 'stop' : 'per-edit'; + } + } + } + return 'none'; +} + +function appendHookFallback(parts, ctx) { + const hookMode = automaticHookMode(ctx); + if (hookMode === 'stop') return; + const native = ctx.platform === 'ios' || ctx.platform === 'android' || ctx.platform === 'adaptive'; + parts.push(renderLlmOnlySlopReview({ + automaticDetector: hookMode === 'per-edit', + manualDetector: hookMode === 'none' && !native, + scriptsPath: path.dirname(fileURLToPath(import.meta.url)), + })); +} + function buildResolvedContextDirective(ctx, options, { targetExists = null } = {}) { const targetPath = hasTargetOption(options) ? options.targetPath : null; return `RESOLVED_CONTEXT:\n${JSON.stringify({ @@ -1087,10 +1189,29 @@ function buildResolvedContextDirective(ctx, options, { targetExists = null } = { repoRoot: ctx.repoRoot, productPath: ctx.productPath, designPath: ctx.designPath, + surfaceBriefPath: ctx.surfaceBriefPath, + surfaceBriefReason: ctx.surfaceBriefReason, + surfaceBriefCandidates: ctx.surfaceBriefCandidates, hasVisualImplementation: ctx.hasVisualImplementation, + platform: ctx.platform, }, null, 2)}`; } +function appendSurfaceBriefContext(parts, ctx) { + if (ctx.hasSurfaceBrief && ctx.surfaceBrief) { + parts.push(`# SURFACE BRIEF (${ctx.surfaceBriefPath})\n\n${ctx.surfaceBrief.trim()}`); + return; + } + if (!ctx.surfaceBriefCandidates?.length) return; + const helper = path.join(path.dirname(fileURLToPath(import.meta.url)), 'surface-brief.mjs'); + parts.push( + 'SURFACE_CONTEXT_AVAILABLE: Persisted surface briefs exist, but none was selected unambiguously for this invocation. ' + + 'Resolve the requested surface to its concrete primary or related source path, then run ' + + `\`node ${helper} read \` once before changing that surface. Candidates:\n` + + JSON.stringify(ctx.surfaceBriefCandidates, null, 2), + ); +} + function shouldWarnMissingTarget(ctx, targetProvided, targetExists = null) { if (ctx.isMonorepo && targetProvided && targetExists === false) return true; return !!( diff --git a/.opencode/skills/impeccable/scripts/critique-storage.mjs b/.opencode/skills/impeccable/scripts/critique-storage.mjs index 6b4d225cf..38674285a 100644 --- a/.opencode/skills/impeccable/scripts/critique-storage.mjs +++ b/.opencode/skills/impeccable/scripts/critique-storage.mjs @@ -29,8 +29,9 @@ import fs from 'node:fs'; import path from 'node:path'; import { fileURLToPath, pathToFileURL } from 'node:url'; import { getCritiqueDir } from './lib/impeccable-paths.mjs'; +import { slugFromTarget } from './lib/target-slug.mjs'; -const SLUG_MAX = 50; +export { slugFromTarget } from './lib/target-slug.mjs'; /** * Mechanically derive a slug from a resolved target. Returns null if the @@ -40,46 +41,6 @@ const SLUG_MAX = 50; * concrete artifact before calling this — we never slug a natural-language * phrase. */ -export function slugFromTarget(resolved, { cwd = process.cwd() } = {}) { - if (!resolved || typeof resolved !== 'string') return null; - const trimmed = resolved.trim(); - if (!trimmed) return null; - - // URL - if (/^https?:\/\//i.test(trimmed)) { - let url; - try { url = new URL(trimmed); } catch { return null; } - const hostPath = `${url.hostname}${url.pathname}`; - return kebab(hostPath); - } - - // File path. Make it project-relative so two devs critiquing the same - // checkout get the same slug regardless of where their repo is cloned. - const abs = path.isAbsolute(trimmed) ? trimmed : path.resolve(cwd, trimmed); - let rel = path.relative(cwd, abs); - // If the target is outside cwd, fall back to the basename so we still - // produce a stable slug (vs the absolute path, which would include - // home dirs / usernames). - if (rel.startsWith('..') || path.isAbsolute(rel)) { - rel = path.basename(abs); - } - if (!rel || rel === '.' || rel === '') return null; - return kebab(rel); -} - -function kebab(s) { - const slug = s - .toLowerCase() - .replace(/[/\\.]+/g, '-') - .replace(/[^a-z0-9-]+/g, '-') - .replace(/-+/g, '-') - .replace(/^-|-$/g, ''); - if (!slug) return null; - // Cap from the tail — the tail (filename) is more identifying than the - // top-level directory. - return slug.length <= SLUG_MAX ? slug : slug.slice(slug.length - SLUG_MAX).replace(/^-/, ''); -} - /** * Filename-safe UTC ISO timestamp: hyphens for separators, trailing Z. * Plain colons aren't allowed on Windows filesystems. diff --git a/.opencode/skills/impeccable/scripts/detector/detect-antipatterns-browser.js b/.opencode/skills/impeccable/scripts/detector/detect-antipatterns-browser.js index 033573e36..76bf4ddeb 100644 --- a/.opencode/skills/impeccable/scripts/detector/detect-antipatterns-browser.js +++ b/.opencode/skills/impeccable/scripts/detector/detect-antipatterns-browser.js @@ -312,17 +312,6 @@ const ANTIPATTERNS = [ skillSection: 'Layout & Space', skillGuideline: 'numbered section markers', }, - { - id: 'numbered-section-markers', - category: 'slop', - scopes: ['layout'], - severity: 'advisory', - name: 'Numbered section markers (01 / 02 / 03)', - description: - 'Numbered display markers as section labels (01, 02, 03) are the AI editorial scaffold one tier deeper than tracked eyebrow chips. If you find yourself reaching for them, choose a different section cadence.', - skillSection: 'Layout & Space', - skillGuideline: 'numbered section markers', - }, { id: 'em-dash-overuse', category: 'slop', diff --git a/.opencode/skills/impeccable/scripts/detector/engines/regex/detect-text.mjs b/.opencode/skills/impeccable/scripts/detector/engines/regex/detect-text.mjs index 81c30ab99..f641d5917 100644 --- a/.opencode/skills/impeccable/scripts/detector/engines/regex/detect-text.mjs +++ b/.opencode/skills/impeccable/scripts/detector/engines/regex/detect-text.mjs @@ -280,22 +280,6 @@ const REGEX_ANALYZERS = [ if (count === 0) return []; return [finding('marketing-buzzword', filePath, `${count} buzzword phrase${count === 1 ? '' : 's'}: "${firstSample}"`)]; }, - // Numbered section markers (01 / 02 / 03 ...) - (content, filePath) => { - const text = stripHtmlToText(content); - const re = /\b(0[1-9]|1[0-2])\b/g; - const seen = new Set(); - let m; - while ((m = re.exec(text)) !== null) seen.add(m[1]); - if (seen.size < 3) return []; - const sorted = [...seen].sort(); - let sequential = 0; - for (let i = 1; i < sorted.length; i++) { - if (parseInt(sorted[i], 10) === parseInt(sorted[i - 1], 10) + 1) sequential++; - } - if (sequential < 2) return []; - return [finding('numbered-section-markers', filePath, `Sequence: ${sorted.slice(0, 6).join(', ')}`)]; - }, // Aphoristic cadence: manufactured-contrast + short-rebuttal (content, filePath) => { const text = stripHtmlToText(content); @@ -430,21 +414,20 @@ function runRegexMatchers(lines, filePath, lineOffset = 0, blockContext = null, } /** Page-level analyzers that scan rendered text content (em-dash use, - * buzzword phrases, numbered section markers, aphoristic cadence). + * buzzword phrases, aphoristic cadence). * These are detector-agnostic — they work on any HTML/text source * and don't need a parsed DOM. Exported so detectHtml can call them * for `.html` files (which otherwise skip the regex engine). */ const TEXT_CONTENT_ANALYZER_IDS = [ 'em-dash-overuse', 'marketing-buzzword', - 'numbered-section-markers', 'aphoristic-cadence', ]; function runTextContentAnalyzers(content, filePath, options = {}) { const profile = options?.profile; if (!shouldRunPageAnalyzers(content, filePath)) return []; - // The 4 text-content analyzers are at indices 3-6 in REGEX_ANALYZERS. + // The 3 text-content analyzers are at indices 3-5 in REGEX_ANALYZERS. const findings = []; for (let i = 0; i < TEXT_CONTENT_ANALYZER_IDS.length; i++) { const analyzer = REGEX_ANALYZERS[3 + i]; @@ -535,7 +518,6 @@ function detectText(content, filePath, options = {}) { 'monotonous-spacing', 'em-dash-overuse', 'marketing-buzzword', - 'numbered-section-markers', 'aphoristic-cadence', 'dark-glow', ]; diff --git a/.opencode/skills/impeccable/scripts/detector/registry/antipatterns.mjs b/.opencode/skills/impeccable/scripts/detector/registry/antipatterns.mjs index b85f7e62f..84c69b0b8 100644 --- a/.opencode/skills/impeccable/scripts/detector/registry/antipatterns.mjs +++ b/.opencode/skills/impeccable/scripts/detector/registry/antipatterns.mjs @@ -210,17 +210,6 @@ const ANTIPATTERNS = [ skillSection: 'Layout & Space', skillGuideline: 'numbered section markers', }, - { - id: 'numbered-section-markers', - category: 'slop', - scopes: ['layout'], - severity: 'advisory', - name: 'Numbered section markers (01 / 02 / 03)', - description: - 'Numbered display markers as section labels (01, 02, 03) are the AI editorial scaffold one tier deeper than tracked eyebrow chips. If you find yourself reaching for them, choose a different section cadence.', - skillSection: 'Layout & Space', - skillGuideline: 'numbered section markers', - }, { id: 'em-dash-overuse', category: 'slop', diff --git a/.opencode/skills/impeccable/scripts/detector/rules/checks.mjs b/.opencode/skills/impeccable/scripts/detector/rules/checks.mjs index 3fdd6b2dd..9bace11b3 100644 --- a/.opencode/skills/impeccable/scripts/detector/rules/checks.mjs +++ b/.opencode/skills/impeccable/scripts/detector/rules/checks.mjs @@ -352,27 +352,57 @@ function isAccentColor(cssColor) { return false; } +function resolveHeroHeadingSizePx(value) { + const input = String(value || '').trim().toLowerCase(); + if (!input) return 0; + + const simpleLengthPx = (token) => { + const match = /^(-?\d*\.?\d+)\s*(px|rem|em|%)?$/.exec(String(token || '').trim()); + if (!match) return null; + const amount = Number(match[1]); + if (!Number.isFinite(amount)) return null; + if (match[2] === 'rem' || match[2] === 'em') return amount * 16; + if (match[2] === '%') return amount * 0.16; + return amount; + }; + + const direct = simpleLengthPx(input); + if (direct !== null) return direct; + + // Static CSS engines cannot resolve viewport units, but clamp's min/max + // bounds still tell us whether the heading can ever reach hero scale. + const clamp = /^clamp\((.*)\)$/.exec(input); + if (clamp) { + const parts = clamp[1].split(','); + if (parts.length === 3) { + const bounds = [simpleLengthPx(parts[0]), simpleLengthPx(parts[2])] + .filter((candidate) => candidate !== null); + if (bounds.length > 0) return Math.max(...bounds); + } + } + + return 0; +} + // Sibling-relationship rule. Anchor on a hero-scale h1, look at the // previousElementSibling, and gate on EITHER the classic tracked- // uppercase eyebrow OR the modern accent-colored bold eyebrow. function checkHeroEyebrow(opts) { const { headingTag, headingText, headingFontSize, + headingInApplicationContext, siblingTag, siblingText, siblingTextTransform, siblingFontSize, siblingLetterSpacing, siblingFontWeight, siblingColor, siblingHasAccentDashPseudo, } = opts; if (headingTag !== 'h1') return []; - // We previously gated on headingFontSize >= 48 to anchor "hero scale". - // But modern hero h1s use clamp() / vw / var(--text-*), none of which - // jsdom can resolve — the computed value comes back as "2em" or - // "var(--text-9xl)" and parseFloat returns 2 or NaN. The gate fails - // on virtually every Tailwind v4 / framework build. The other gates - // (sibling text 2-60 chars, font-size ≤ 14px, accent-bold OR - // tracked-caps) are tight enough to avoid false positives on non- - // hero h1s — a tiny tan label directly above any h1 is the - // antipattern regardless of how big the h1 ends up. + // This is specifically a marketing-hero cliché, not a ban on compact + // context labels in product UI (for example, a station name inside a tab + // panel). Browser-computed sizes are reliable; the static adapter also + // resolves ordinary px/rem/em and clamp() bounds before reaching here. + if (headingInApplicationContext) return []; + if (!(headingFontSize >= 48)) return []; if (!siblingTag) return []; // An h2 above an h1 is a different anti-pattern (heading hierarchy / dual // headings) — never an eyebrow. @@ -1938,6 +1968,7 @@ function checkElementHeroEyebrowDOM(el) { headingTag: tag, headingText: el.textContent || '', headingFontSize: parseFloat(headStyle.fontSize) || 0, + headingInApplicationContext: !!el.closest('[role="tabpanel"], [role="dialog"], [role="application"], dialog'), siblingTag: sibling.tagName.toLowerCase(), siblingText: sibling.textContent || '', siblingTextTransform: sibStyle.textTransform || '', @@ -3367,7 +3398,8 @@ function checkElementHeroEyebrow(el, style, tag, window, customPropMap) { return checkHeroEyebrow({ headingTag: tag, headingText: el.textContent || '', - headingFontSize: parseFloat(headingFontSizeRaw) || 0, + headingFontSize: resolveHeroHeadingSizePx(headingFontSizeRaw), + headingInApplicationContext: !!el.closest?.('[role="tabpanel"], [role="dialog"], [role="application"], dialog'), siblingTag: sibling.tagName.toLowerCase(), siblingText: sibling.textContent || '', siblingTextTransform: sibStyle.textTransform || '', diff --git a/.opencode/skills/impeccable/scripts/hook-lib.mjs b/.opencode/skills/impeccable/scripts/hook-lib.mjs index e61ea692b..50aa2f8fb 100644 --- a/.opencode/skills/impeccable/scripts/hook-lib.mjs +++ b/.opencode/skills/impeccable/scripts/hook-lib.mjs @@ -46,6 +46,7 @@ import path from 'node:path'; import { pathToFileURL, fileURLToPath } from 'node:url'; import { extractPlatform, loadContext } from './context.mjs'; import { IMPECCABLE_COMMAND } from './lib/provider.mjs'; +import { renderStopSlopReview } from './lib/slop-review.mjs'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); @@ -1868,22 +1869,42 @@ export const CONTRACT_MAX_CHARS = 1800; // Cap contract sections per Stop emission so many touched artifacts cannot // stack an unbounded message. export const CONTRACT_AUDIT_MAX_FILES = 3; +export const CONTRACT_EXTS = new Set(['.html', '.htm', '.astro', '.svelte', '.vue', '.jsx', '.tsx']); +export const CONTRACT_REQUIRED_FIELDS = ['UNIQUE', 'NOT-TEMPLATE', 'OWN-WORLD', 'STORY', 'FIRST VIEWPORT', 'FORM']; /** - * Extract the artifact's own direction-contract comment: the first HTML - * comment in the head of the file, when its opening chars identify it as a - * contract/concept block. Returns the trimmed, length-capped body, or null - * when the file carries none (no comment, unclosed comment, marker missing, - * or the comment starts past the head window). + * Extract the artifact's own direction-contract comment from the head of an + * HTML or component file. Supports HTML-family comments and JSX block + * comments so the contract works in the Astro/Svelte/Vue/React scaffolds the + * skill actually builds. Returns the trimmed, length-capped body, or null + * when the file carries no valid contract block. */ export function extractDirectionContract(content) { if (typeof content !== 'string' || !content) return null; const head = content.slice(0, CONTRACT_HEAD_CHARS); - const m = //.exec(head); - if (!m) return null; - const body = m[1].trim(); - if (!body || !/contract|concept/i.test(body.slice(0, CONTRACT_MARKER_CHARS))) return null; - return body.slice(0, CONTRACT_MAX_CHARS); + const candidates = []; + for (const pattern of [//g, /\{\/\*([\s\S]*?)\*\/\}/g]) { + for (const match of head.matchAll(pattern)) { + const index = match.index ?? 0; + const linePrefix = head.slice(head.lastIndexOf('\n', index - 1) + 1, index).trim(); + if (linePrefix.startsWith('//')) continue; + candidates.push({ index, body: match[1].trim() }); + } + } + candidates.sort((a, b) => a.index - b.index); + for (const candidate of candidates) { + if (!candidate.body || !/direction\s+contract|concept\s+contract/i.test(candidate.body.slice(0, CONTRACT_MARKER_CHARS))) continue; + return candidate.body.slice(0, CONTRACT_MAX_CHARS); + } + return null; +} + +export function missingDirectionContractFields(contract) { + const body = typeof contract === 'string' ? contract : ''; + return CONTRACT_REQUIRED_FIELDS.filter((field) => { + const label = field.replace(/[.*+?^${}()|[\]\\]/g, '\\$&').replace(/\s+/g, '\\s+'); + return !new RegExp(`\\b${label}\\s*:`, 'i').test(body); + }); } /** @@ -1899,7 +1920,11 @@ export function renderContractAudit(entries, opts = {}) { const shown = entries.slice(0, CONTRACT_AUDIT_MAX_FILES); const blocks = shown.map(({ filePath, contract }) => { const display = relativize(filePath, cwd); - return `${display} opens with this direction contract, written when the direction was decided:\n\n${contract}`; + const missing = missingDirectionContractFields(contract); + const integrity = missing.length > 0 + ? `\n\nContract integrity defect: missing ${missing.join(', ')}. Repair the contract and the implementation together.` + : ''; + return `${display} opens with this direction contract, written when the direction was decided:\n\n${contract}${integrity}`; }); return [ `${ENVELOPE_PREFIX} Direction-contract audit. Before finishing, audit the rendered page against the contract it opens with, promise by promise.`, @@ -1984,9 +2009,22 @@ export async function runStopHook({ stdinJson, env = {}, cwd = process.cwd(), no return result({ skipped: 'native-platform', platform, durationMs: Date.now() - started }); } + const session = ensureSession(cache, sessionId); + const needsSlopReview = session.llmSlopReviewed !== true; + const det = detector || await loadDetector(); if (!det || typeof det.detectText !== 'function') { - return result({ skipped: 'detector-missing', durationMs: Date.now() - started }); + if (!needsSlopReview) return result({ skipped: 'detector-missing', durationMs: Date.now() - started }); + session.llmSlopReviewed = true; + session.updatedAt = Date.now(); + persistCache(projectCwd, cache); + const text = renderStopSlopReview(); + return { + exitCode: 0, + stdout: payload(text, 'Stop', harness), + emission: { kind: 'stop-llm-slop-review', llmSlopReview: true }, + audit: { ...audit, emitted: true, detectorMissing: true, llmSlopReview: true, chars: text.length, durationMs: Date.now() - started }, + }; } const scanOptions = designSystemOptions(config, det, projectCwd); @@ -2013,10 +2051,11 @@ export async function runStopHook({ stdinJson, env = {}, cwd = process.cwd(), no ? configuredExt.engine === 'html' : (ext === '.html' || ext === '.htm'); - // Direction-contract audit: HTML artifacts only, at most once per file + // Direction-contract audit: HTML and component artifacts, at most once per file // per session. The flag lives on the same session cache entry the // finding dedupe uses, so a second Stop fire stays quiet about it. - if (useHtmlEngine) { + const contractCapable = useHtmlEngine || CONTRACT_EXTS.has(ext); + if (contractCapable) { const fileEntry = ensureFile(cache, sessionId, filePath); if (!fileEntry.contractAudited) { const contract = extractDirectionContract(content); @@ -2046,10 +2085,15 @@ export async function runStopHook({ stdinJson, env = {}, cwd = process.cwd(), no } audit.scannedFiles = scanned; - if (freshGroups.length === 0 && contractEntries.length === 0) { + if (freshGroups.length === 0 && contractEntries.length === 0 && !needsSlopReview) { return result({ emitted: false, skipped: 'stop-clean', durationMs: Date.now() - started }); } + if (needsSlopReview) { + session.llmSlopReviewed = true; + session.updatedAt = Date.now(); + } + // Fresh findings and first-time contract audits earn the cache write; // both mark this batch as surfaced so the next Stop fire is silent // unless new issues appear. @@ -2064,6 +2108,9 @@ export async function runStopHook({ stdinJson, env = {}, cwd = process.cwd(), no if (contractEntries.length > 0) { parts.push(renderContractAudit(contractEntries, { cwd: projectCwd })); } + if (needsSlopReview) { + parts.push(renderStopSlopReview()); + } const text = appendDesignSystemNote(parts.join('\n\n'), scanOptions); return { exitCode: 0, @@ -2074,6 +2121,7 @@ export async function runStopHook({ stdinJson, env = {}, cwd = process.cwd(), no ...(contractEntries.length > 0 ? { contractFiles: contractEntries.map((entry) => entry.filePath) } : {}), + ...(needsSlopReview ? { llmSlopReview: true } : {}), }, audit: { ...audit, @@ -2081,6 +2129,7 @@ export async function runStopHook({ stdinJson, env = {}, cwd = process.cwd(), no freshFiles: freshGroups.length, freshFindings: freshGroups.reduce((sum, group) => sum + group.findings.length, 0), ...(contractEntries.length > 0 ? { contractAudits: contractEntries.length } : {}), + ...(needsSlopReview ? { llmSlopReview: true } : {}), chars: text.length, durationMs: Date.now() - started, }, diff --git a/.opencode/skills/impeccable/scripts/lib/provider.mjs b/.opencode/skills/impeccable/scripts/lib/provider.mjs index 7fa928a9c..cf50d161c 100644 --- a/.opencode/skills/impeccable/scripts/lib/provider.mjs +++ b/.opencode/skills/impeccable/scripts/lib/provider.mjs @@ -1,4 +1,5 @@ // Source scripts default to slash commands. The provider build replaces only // this exact declaration, avoiding heuristic rewrites across executable code. export const IMPECCABLE_COMMAND_PREFIX = "/"; +export const IMPECCABLE_PROVIDER_ID = "opencode"; export const IMPECCABLE_COMMAND = `${IMPECCABLE_COMMAND_PREFIX}impeccable`; diff --git a/.opencode/skills/impeccable/scripts/lib/slop-review.mjs b/.opencode/skills/impeccable/scripts/lib/slop-review.mjs new file mode 100644 index 000000000..30f03e34d --- /dev/null +++ b/.opencode/skills/impeccable/scripts/lib/slop-review.mjs @@ -0,0 +1,30 @@ +export const LLM_ONLY_SLOP_ITEMS = Object.freeze([ + 'Monospace used merely to signal “technical” or “developer.”', + 'Light or dark mode chosen by category habit rather than the actual use scene.', + 'Everything wrapped in cards, or identical icon-heading-text cards repeated as the page structure.', + 'Hero-metric scaffolds: one oversized number, a small label, supporting stats, and an accent treatment.', + 'Decorative glassmorphism, meaningless sparklines, or generic rounded rectangles with drop shadows.', + 'A modal chosen by reflex when the task does not require interruption or protected focus.', +]); + +export function renderLlmOnlySlopReview({ manualDetector = false, automaticDetector = false, scriptsPath = null } = {}) { + const lines = [ + automaticDetector + ? 'AI_SLOP_REVIEW_REQUIRED: The automatic detector covers mechanical rules, but this harness has no reliable late review. Before finishing changed UI, inspect the authored result for detector-blind model reflexes:' + : 'AI_SLOP_REVIEW_REQUIRED: The automatic Impeccable design hook is not available for this session. Before finishing changed UI, inspect the authored result for detector-blind model reflexes:', + ...LLM_ONLY_SLOP_ITEMS.map((item) => `- ${item}`), + 'Fix reflexes, not intentional choices required by the brief or established visual authority.', + ]; + if (manualDetector && scriptsPath) { + lines.push(`Then run the mechanical detector once over the changed web UI: \`node ${scriptsPath}/detect.mjs --json \`. Do not run it earlier during concept selection.`); + } + return lines.join('\n'); +} + +export function renderStopSlopReview() { + return [ + '[impeccable@1] Detector-blind AI-slop review. The mechanical pass is complete; now inspect the rendered result for model reflexes it cannot reliably detect:', + ...LLM_ONLY_SLOP_ITEMS.map((item) => `- ${item}`), + 'Fix reflexes, not intentional choices required by the brief or established visual authority. Do not rerun the detector; this is the authored judgment layer.', + ].join('\n'); +} diff --git a/.opencode/skills/impeccable/scripts/lib/surface-briefs.mjs b/.opencode/skills/impeccable/scripts/lib/surface-briefs.mjs new file mode 100644 index 000000000..f83416f69 --- /dev/null +++ b/.opencode/skills/impeccable/scripts/lib/surface-briefs.mjs @@ -0,0 +1,151 @@ +import fs from 'node:fs'; +import path from 'node:path'; +import { slugFromTarget } from './target-slug.mjs'; + +export const SURFACE_BRIEF_VERSION = 1; + +export function getSurfaceBriefDir(projectRoot) { + return path.join(projectRoot, '.impeccable', 'surfaces'); +} + +export function normalizeSurfaceTarget(target, { projectRoot = process.cwd() } = {}) { + if (!target || typeof target !== 'string' || !target.trim()) return null; + const trimmed = target.trim(); + if (/^https?:\/\//i.test(trimmed)) { + try { + const url = new URL(trimmed); + url.hash = ''; + url.search = ''; + return url.toString().replace(/\/$/, '') || url.origin; + } catch { + return null; + } + } + if (/^route:/i.test(trimmed)) { + const route = trimmed.slice(trimmed.indexOf(':') + 1).trim(); + if (!route.startsWith('/') || route.includes('..')) return null; + const normalizedRoute = route.split(/[?#]/, 1)[0].replace(/\/{2,}/g, '/').replace(/\/$/, '') || '/'; + return `route:${normalizedRoute}`; + } + if (trimmed === '/') return 'route:/'; + if (trimmed.startsWith('/')) { + const absolute = path.resolve(trimmed); + const relativeToProject = path.relative(projectRoot, absolute); + const isProjectFile = relativeToProject && !relativeToProject.startsWith('..') && !path.isAbsolute(relativeToProject); + if (!isProjectFile && !fs.existsSync(absolute) && !trimmed.includes('..')) { + const normalizedRoute = trimmed.split(/[?#]/, 1)[0].replace(/\/{2,}/g, '/').replace(/\/$/, '') || '/'; + return `route:${normalizedRoute}`; + } + } + const abs = path.isAbsolute(trimmed) ? trimmed : path.resolve(projectRoot, trimmed); + const rel = path.relative(projectRoot, abs); + if (!rel || rel === '.' || rel.startsWith('..') || path.isAbsolute(rel)) return null; + return rel.split(path.sep).join('/'); +} + +export function surfaceBriefPathForTarget(target, { projectRoot = process.cwd() } = {}) { + const normalized = normalizeSurfaceTarget(target, { projectRoot }); + if (!normalized) return null; + const slugInput = normalized.startsWith('route:') ? `route${normalized.slice('route:'.length)}` : normalized; + const slug = slugFromTarget(slugInput, { cwd: projectRoot }); + return slug ? path.join(getSurfaceBriefDir(projectRoot), `${slug}.md`) : null; +} + +export function parseSurfaceBrief(text, filePath = null) { + const match = String(text || '').match(/^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/); + const meta = {}; + if (match) { + for (const line of match[1].split(/\r?\n/)) { + const colon = line.indexOf(':'); + if (colon < 0) continue; + const key = line.slice(0, colon).trim(); + const raw = line.slice(colon + 1).trim(); + if (!key) continue; + if (/^(?:\[|\{|\")/.test(raw) || /^(?:true|false|null|-?\d+(?:\.\d+)?)$/.test(raw)) { + try { meta[key] = JSON.parse(raw); continue; } catch { /* keep string */ } + } + meta[key] = raw.replace(/^['"]|['"]$/g, ''); + } + } + const primaryTarget = typeof meta.primary_target === 'string' ? meta.primary_target : null; + const relatedTargets = Array.isArray(meta.related_targets) + ? meta.related_targets.filter((value) => typeof value === 'string') + : []; + return { + path: filePath, + text: String(text || ''), + body: match ? String(text || '').slice(match[0].length).trim() : String(text || '').trim(), + meta, + slug: typeof meta.slug === 'string' ? meta.slug : filePath ? path.basename(filePath, '.md') : null, + primaryTarget, + relatedTargets, + targets: [primaryTarget, ...relatedTargets].filter(Boolean), + }; +} + +export function listSurfaceBriefs(projectRoot = process.cwd()) { + const dir = getSurfaceBriefDir(projectRoot); + let names; + try { + names = fs.readdirSync(dir).filter((name) => name.endsWith('.md')).sort(); + } catch { + return []; + } + return names.flatMap((name) => { + const filePath = path.join(dir, name); + try { + return [parseSurfaceBrief(fs.readFileSync(filePath, 'utf-8'), filePath)]; + } catch { + return []; + } + }); +} + +export function resolveSurfaceBrief(projectRoot = process.cwd(), target = null) { + const briefs = listSurfaceBriefs(projectRoot); + if (!target) { + return { + brief: briefs.length === 1 ? briefs[0] : null, + candidates: briefs, + reason: briefs.length === 1 ? 'only-brief' : briefs.length > 1 ? 'ambiguous' : 'none', + }; + } + + const normalized = normalizeSurfaceTarget(target, { projectRoot }); + if (!normalized) return { brief: null, candidates: briefs, reason: 'invalid-target' }; + const exactPath = surfaceBriefPathForTarget(normalized, { projectRoot }); + const exact = briefs.find((brief) => brief.path === exactPath && (!brief.targets.length || brief.targets.includes(normalized))); + if (exact) return { brief: exact, candidates: briefs, reason: 'slug' }; + const mapped = briefs.filter((brief) => brief.targets.includes(normalized)); + return { + brief: mapped.length === 1 ? mapped[0] : null, + candidates: mapped.length > 1 ? mapped : briefs, + reason: mapped.length === 1 ? 'mapping' : mapped.length > 1 ? 'ambiguous-target' : 'not-found', + }; +} + +export function writeSurfaceBrief({ + projectRoot = process.cwd(), + primaryTarget, + relatedTargets = [], + body, +}) { + const normalizedPrimary = normalizeSurfaceTarget(primaryTarget, { projectRoot }); + if (!normalizedPrimary) throw new Error('surface brief requires a concrete project-relative primary target or URL'); + const normalizedRelated = [...new Set(relatedTargets + .map((target) => normalizeSurfaceTarget(target, { projectRoot })) + .filter((target) => target && target !== normalizedPrimary))]; + const slug = slugFromTarget(normalizedPrimary, { cwd: projectRoot }); + const filePath = surfaceBriefPathForTarget(normalizedPrimary, { projectRoot }); + fs.mkdirSync(path.dirname(filePath), { recursive: true }); + const frontmatter = [ + '---', + `version: ${SURFACE_BRIEF_VERSION}`, + `slug: ${JSON.stringify(slug)}`, + `primary_target: ${JSON.stringify(normalizedPrimary)}`, + `related_targets: ${JSON.stringify(normalizedRelated)}`, + '---', + ].join('\n'); + fs.writeFileSync(filePath, `${frontmatter}\n\n${String(body || '').trim()}\n`, 'utf-8'); + return filePath; +} diff --git a/.opencode/skills/impeccable/scripts/lib/target-slug.mjs b/.opencode/skills/impeccable/scripts/lib/target-slug.mjs new file mode 100644 index 000000000..025915ad5 --- /dev/null +++ b/.opencode/skills/impeccable/scripts/lib/target-slug.mjs @@ -0,0 +1,33 @@ +import path from 'node:path'; + +const SLUG_MAX = 50; + +/** Derive one clone-stable slug from a concrete file path or URL. */ +export function slugFromTarget(resolved, { cwd = process.cwd() } = {}) { + if (!resolved || typeof resolved !== 'string') return null; + const trimmed = resolved.trim(); + if (!trimmed) return null; + + if (/^https?:\/\//i.test(trimmed)) { + let url; + try { url = new URL(trimmed); } catch { return null; } + return kebab(`${url.hostname}${url.pathname}`); + } + + const abs = path.isAbsolute(trimmed) ? trimmed : path.resolve(cwd, trimmed); + let rel = path.relative(cwd, abs); + if (rel.startsWith('..') || path.isAbsolute(rel)) rel = path.basename(abs); + if (!rel || rel === '.') return null; + return kebab(rel); +} + +export function kebab(value) { + const slug = String(value || '') + .toLowerCase() + .replace(/[/\\.]+/g, '-') + .replace(/[^a-z0-9-]+/g, '-') + .replace(/-+/g, '-') + .replace(/^-|-$/g, ''); + if (!slug) return null; + return slug.length <= SLUG_MAX ? slug : slug.slice(slug.length - SLUG_MAX).replace(/^-/, ''); +} diff --git a/.opencode/skills/impeccable/scripts/live-browser.js b/.opencode/skills/impeccable/scripts/live-browser.js index 616f1290a..47bbfefbf 100644 --- a/.opencode/skills/impeccable/scripts/live-browser.js +++ b/.opencode/skills/impeccable/scripts/live-browser.js @@ -2592,7 +2592,7 @@ minWidth: '16px', height: '16px', padding: '0 4px', borderRadius: '999px', background: tuneOpen ? C.brand : BP.hairline, - color: tuneOpen ? 'oklch(98% 0 0)' : 'inherit', + color: tuneOpen ? C.ink : 'inherit', fontFamily: MONO, fontSize: '9.5px', fontWeight: '600', lineHeight: '1', boxSizing: 'border-box', @@ -3131,7 +3131,7 @@ position: 'absolute', top: '2px', left: initial ? '18px' : '2px', width: '16px', height: '16px', borderRadius: '50%', - background: 'oklch(98% 0 0)', + background: C.ink, transition: 'left 0.18s ' + EASE, boxShadow: '0 1px 2px oklch(0% 0 0 / 0.2)', }); @@ -3165,7 +3165,7 @@ const b = el('button', { padding: '5px 4px', border: 'none', borderRadius: '3px', background: active ? C.brand : 'transparent', - color: active ? 'oklch(98% 0 0)' : P.text, + color: active ? C.ink : P.text, fontFamily: FONT, fontSize: '10.5px', fontWeight: '500', cursor: 'pointer', whiteSpace: 'nowrap', transition: 'background 0.1s ease, color 0.1s ease', @@ -3178,7 +3178,7 @@ segBtns.forEach(({ btn, val }) => { const on = val === o.value; btn.style.background = on ? C.brand : 'transparent'; - btn.style.color = on ? 'oklch(98% 0 0)' : P.text; + btn.style.color = on ? C.ink : P.text; }); applyParamValue(variantEl, p, o.value); queueCheckpoint('param_changed'); diff --git a/.opencode/skills/impeccable/scripts/surface-brief.mjs b/.opencode/skills/impeccable/scripts/surface-brief.mjs new file mode 100644 index 000000000..723f7c1b4 --- /dev/null +++ b/.opencode/skills/impeccable/scripts/surface-brief.mjs @@ -0,0 +1,74 @@ +#!/usr/bin/env node +import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath, pathToFileURL } from 'node:url'; +import { resolveProjectRoot } from './context.mjs'; +import { + listSurfaceBriefs, + resolveSurfaceBrief, + surfaceBriefPathForTarget, + writeSurfaceBrief, +} from './lib/surface-briefs.mjs'; + +function summary(brief, projectRoot) { + return { + slug: brief.slug, + path: path.relative(projectRoot, brief.path).split(path.sep).join('/'), + primaryTarget: brief.primaryTarget, + relatedTargets: brief.relatedTargets, + }; +} + +function main(argv) { + const [command, target, bodyFile, ...relatedTargets] = argv; + const projectRoot = resolveProjectRoot(process.cwd(), target ? { targetPath: target } : {}); + if (command === 'path') { + const filePath = surfaceBriefPathForTarget(target, { projectRoot }); + if (!filePath) throw new Error('surface brief path requires a concrete target'); + process.stdout.write(`${path.relative(process.cwd(), filePath) || filePath}\n`); + return; + } + if (command === 'list') { + process.stdout.write(`${JSON.stringify(listSurfaceBriefs(projectRoot).map((brief) => summary(brief, projectRoot)), null, 2)}\n`); + return; + } + if (command === 'read') { + const result = resolveSurfaceBrief(projectRoot, target || null); + if (result.brief) { + process.stdout.write(result.brief.text); + return; + } + if (result.candidates.length) process.stderr.write(`${JSON.stringify(result.candidates.map((brief) => summary(brief, projectRoot)), null, 2)}\n`); + process.exit(2); + } + if (command === 'write') { + if (!target || !bodyFile) throw new Error('usage: surface-brief.mjs write '); + const filePath = writeSurfaceBrief({ + projectRoot, + primaryTarget: target, + relatedTargets, + body: fs.readFileSync(bodyFile, 'utf-8'), + }); + process.stdout.write(`${path.relative(process.cwd(), filePath) || filePath}\n`); + return; + } + throw new Error('usage: surface-brief.mjs [target] [body-file] [related-target ...]'); +} + +function isMainModule() { + if (!process.argv[1]) return false; + try { + return fs.realpathSync(fileURLToPath(import.meta.url)) === fs.realpathSync(process.argv[1]); + } catch { + return import.meta.url === pathToFileURL(process.argv[1]).href; + } +} + +if (isMainModule()) { + try { + main(process.argv.slice(2)); + } catch (error) { + process.stderr.write(`${error?.message || error}\n`); + process.exit(1); + } +} diff --git a/.pi/skills/impeccable/SKILL.md b/.pi/skills/impeccable/SKILL.md index 5da3885a7..1fa69631f 100644 --- a/.pi/skills/impeccable/SKILL.md +++ b/.pi/skills/impeccable/SKILL.md @@ -1,63 +1,62 @@ --- name: impeccable description: Use when the user wants to design, redesign, shape, critique, audit, polish, clarify, distill, harden, optimize, adapt, animate, colorize, extract, or otherwise improve a frontend interface. Covers websites, landing pages, dashboards, product UI, app shells, components, forms, settings, onboarding, and empty states. Handles UX review, visual hierarchy, information architecture, cognitive load, accessibility, performance, responsive behavior, theming, anti-patterns, typography, fonts, spacing, layout, alignment, color, motion, micro-interactions, UX copy, error states, edge cases, i18n, and reusable design systems or tokens. Also use for bland designs that need to become bolder or more delightful, loud designs that should become quieter, live browser iteration on UI elements, or ambitious visual effects that should feel technically extraordinary. Not for backend-only or non-UI tasks. -version: 4.0.0-alpha.9 +version: 4.0.0-alpha.10 license: Apache 2.0 allowed-tools: - Bash(npx impeccable *) - Bash(node .pi/skills/impeccable/scripts/*) --- -Designs and iterates production-grade frontend interfaces. Real working code, committed design choices, exceptional craft. +This skill gives you the tools and permission to create design that earns to be called out-of-distribution craft: Whereas before, your design work would have been safe, timid and measured, you now approach every design task as a award-winning design director with impeccable understanding for what makes exceptional design work: production-grade code, peak creativity, a clear POV, deep understanding of the needs of the client and users, and exceptional craft. -Approach every design task as the design lead at a small studio known for giving every client a visual identity that could not be mistaken for anyone else's. The client has already rejected work that felt templated; they are paying for a point of view. the model is capable of extraordinary work. Don't hold back. +Core principles: +- Go all out. No hedging, no shortcuts. The deliverable must be complete (except assets the user must provide). +- Dream big and bold. Distinct, beautiful, outstanding and highly inspiring work. +- Iterate with tools available to you (e.g. visual understanding, browser screenshots) until you think this meets the bar. ## Setup -1. Run `node .pi/skills/impeccable/scripts/context.mjs` once per session (if the runtime shows this skill's loaded base directory, run `node /scripts/context.mjs`; keep cwd at the user's project). It prints the project's context and its directives; follow what it prints. Once its output is in the conversation, never rerun it on a later turn. -2. `craft` and `shape` are build-path exceptions: the new-work gate below owns their flow, and on unattended runs its checkpoints resolve without pausing. For any other invoked sub-command (`audit`, `polish`, `live`, ...), immediately read **`reference/.md`** after `context.mjs` (the `.native` variant from the Commands table when the platform is `ios`/`android`/`adaptive`) and follow it. This read is a hard gate: do not inspect the target, run command-specific scripts, or edit files until the reference is loaded. -3. Read at least one project file (CSS / tokens / theme / a representative component) to learn what world you're in. If PRODUCT.md's `## Platform` is `ios` or `android`, also read `reference/.md` (`adaptive` reads both). +1. Run `node .pi/skills/impeccable/scripts/context.mjs` once per session (if the runtime shows this skill's loaded base directory, run `node /scripts/context.mjs`; keep cwd at the user's project). Pass a named source file or route as `--target `. It loads PRODUCT.md, DESIGN.md, the matching surface brief, and native-platform guidance when applicable; follow its directives and do not rerun it. +2. Before acting, load the one playbook that owns the request: the Commands table's reference for an explicit or clearly implied sub-command, or [reference/new-work.md](reference/new-work.md) for a new surface or replacement visual world. Then inspect the target and at least one representative source of incumbent visual truth (tokens, theme, CSS, component, or asset) before editing. ## How to design -**The brief wins.** Where the brief pins down a direction (a named aesthetic, an era, a place, a material, a specific font or palette), follow it exactly, including when it asks for a look this skill warns is saturated. Redirecting a pinned direction toward your own taste is a failure, not a save. - -**Existing worlds are sacred.** Most of impeccable's work happens inside a site or app that already exists. When the surface has a committed design system (real tokens, deliberately chosen faces, a palette the brand owns), work inside that world: extend it, sharpen it, leave it unmistakably the same brand, and never degrade a working page's performance. Inventing parallel colors, fonts, or styles on an existing surface is a defect, not creativity. A scoped refinement keeps the named scope's content and media footprint unless the brief explicitly changes them; build the added emphasis by recomposing what is already there through hierarchy, proportion, rhythm, and the system's own motifs. - -**New identity work reads the playbook first.** When nothing committed exists (greenfield, or a codebase with no real tokens or chosen faces), or the user explicitly asks for a redesign that discards the current look, you MUST read [reference/new-work.md](reference/new-work.md) before making any design decision. Missing DESIGN.md alone does not make work greenfield: code, tokens, chosen type, components, and assets are incumbent design authority. Preserve and extend them unless the user asked to replace them. Producing a genuinely new identity without the playbook yields the generic default this skill exists to prevent. Scoped fixes inside an existing world don't need it; the craft floor below governs them. `context.mjs` prints the appropriate authority directive. +- **The brief wins.** Honor pinned aesthetics, eras, materials, fonts, and palettes even when they conflict with a saturated-pattern warning. Redirecting a clear brief toward your taste is failure. +- **Refinement preserves; redesign replaces.** Refinement keeps the incumbent identity, behavior, copy, and everything outside scope. Ask before replacing factual copy or adding claims. Redesign keeps product truth, content, function, native affordances, and constraints, but treats the old look as evidence and anti-reference; choose a replacement world in new-work and replace DESIGN.md. Never split the difference into polish on the discarded look. +- **Visual authority is evidence, not a filename.** Missing DESIGN.md alone does not make a project greenfield; new-work decides whether to preserve, expand, or replace the incumbent world. ## Modes -Name the visitor's mode before designing; the page's grammar follows from it, and most ruined pages are one mode wearing another mode's grammar. **The mode belongs to the surface, not the subject**: a landing page for a dense technical tool is still Persuade, with Persuade's full permission to be striking; a docs page for a fashion house is still Read. Deciding a page can be plain because its subject is workmanlike is the same category error in reverse. The brief and the surface decide the mode; PRODUCT.md's `register` field survives only as a family hint (`brand` covers Persuade and Experience, `product` covers Operate and Read). Depth beyond the paragraphs below: [reference/new-work.md](reference/new-work.md) when inventing identity, [reference/operate.md](reference/operate.md) for substantial Operate and Read work. +Choose the mode from the requested surface, not the product, and persist it only in that surface brief. A tool's landing page is still Persuade; a fashion house's documentation is still Read. See [new-work.md](reference/new-work.md) for new surfaces and [operate.md](reference/operate.md) for deeper Operate/Read guidance. -**Persuade** (the surface exists to win someone over; design IS the product). The deliverable is an impression that stops the scroll, earns the click, converts. Spans every genre; don't collapse them into one look. On new surfaces, briefs that imply imagery must ship real, verified imagery; a colored rectangle where a photo belongs reads as incomplete. New Persuade surfaces take their typeface procedure and reject list from [reference/new-work.md](reference/new-work.md). - -**Operate** (the surface is a tool someone works in; design SERVES the task). A person getting something done: scanability and consistency outrank expressiveness. These surfaces earn trust by feeling native to their platform: system font stacks and workhorse UI faces are legitimate and often correct here (the Persuade reject list does not apply). The brand lives in the details: focus states, empty states, microcopy, one owned accent. The usage scene is part of the spec: an interface read outdoors, in motion, or at a glance must survive its real ambient light, and the theme follows the scene, not the category's habit. - -**Read** (the surface exists to be understood; long-form, reference, guidance). The deliverable is comprehension, and comprehension is earned twice: a structure the reader can hold in their head with nothing standing between them and the answer, and a reading experience good enough to stay in, through typographic quality and whatever visual or interactive support genuinely helps the reader follow. The brand lives in type, spacing, and small accents. - -**Experience** (the surface presents a body of work; the page IS the work). The artifact leads, the interface recedes, and the visitor meets the work itself in the first viewport at every screen size. Boldness here means trusting the work. +- **Persuade:** win someone over; design is the product. Earn attention and action. Ship real imagery when the brief needs it; follow the committed world, not category habit. +- **Operate:** help someone do work. Scanability, consistency, native expectations, and the real usage scene outrank expression. Brand lives in precise details. +- **Read:** make something understood. Structure for comprehension, then make the reading experience worth staying in. +- **Experience:** present a body of work. Let the artifact lead from the first viewport; the interface recedes. ## Craft floor -Build to this floor without announcing it. The design detector (the project hook, `node .pi/skills/impeccable/scripts/detect.mjs --json `, or `audit`) verifies most of it mechanically; resolve every finding before finalizing. Fix real defects, but use context judgment rather than distorting intentional design to appease a false positive. Classify any intentional exception explicitly and use the hook system's narrowest appropriate waiver when it must persist. +Build to this floor without announcing it. -- Contrast: body text ≥4.5:1 against its background (placeholders too); large text ≥3:1. Gray text on a colored background looks washed out: use a darker shade of the background's own hue, or a transparency of the text color. -- Shadows describe real light: an offset and a soft blur. A zero-offset colored halo is decoration announcing itself. -- Spacing has rhythm: generous separations, tight groupings; cramped padding reads as broken; the space above a heading exceeds the space below it. Verify computed spacing, not intended spacing. -- Type: body line length 65-75ch; display clamp() max ≤6rem; letter-spacing ≥-0.04em; `text-wrap: balance` on headings; modular scale ≥1.25 between steps; light-on-dark adds 0.05-0.1 line-height. Pair faces on a contrast axis, never two similar-but-not-identical ones; one family with committed weight contrast beats a timid pair. Test headings at every breakpoint; overflow means reduce the clamp or rewrite the copy. -- Motion is part of the build: one orchestrated moment beats scattered effects; ease-out exponential curves; reveals enhance an already-visible default (content gated on a class-triggered transition ships blank in hidden tabs and headless renderers). Responsive down to mobile and visible keyboard focus are part of the floor. -- Ship real content (no placeholders, dead links, or fake controls) and cover the interaction states people will actually hit (hover, focus, disabled, loading, error, empty). -- Copy is design material: name things the way the page's own people speak, make every control say what it does, and make every error say what happened and what to do next. -- Before finishing, re-read the brief: every requirement it names must exist on the page, findable in seconds. A beautiful page missing an asked-for feature is unfinished. +- **Contrast:** body and placeholder text ≥4.5:1; large text ≥3:1. On colored surfaces, tint secondary text from that hue or the foreground instead of using gray. +- **Depth:** shadows describe light with offset and soft blur; zero-offset colored halos are decoration. +- **Spacing:** tight groups, generous separation, no cramped containers; space above a heading exceeds space below. Verify computed values. +- **Type:** body measure 65–75ch; display max 6rem and tracking floor -0.04em; balance headings; use clear scale/weight contrast; test overflow at every breakpoint. +- **Motion:** author one coherent moment instead of scattered effects. Use exponential ease-out and an already-visible default. Premium motion is not transform/opacity alone: it may add focus, depth, masks, light, or material change through blur/filter, backdrop-filter, clip-path/masks, or shadow when smooth. Always provide reduced motion. +- **Shipping:** real content, working controls, responsive composition, keyboard focus, and the states users hit: hover, disabled, loading, error, and empty. +- **Copy:** use the product's language; controls name their action, errors name the problem and recovery. +- **Coverage:** every brief requirement must exist and be findable within seconds. + +Before finishing changed UI, follow the quality guidance supplied by `context.mjs` and hooks. Context requests a manual scan only when no automatic detector is active; never add a second detector pass. ## Commands | Command | Category | Description | Reference | |---|---|---|---| -| `craft [feature]` | Build | The standard build flow with attended checkpoints | [reference/new-work.md](reference/new-work.md) | +| `craft [feature]` | Build | Deprecated alias for an ordinary new-work request | [reference/craft.md](reference/craft.md) | | `shape [feature]` | Build | Plan UX/UI before writing code | [reference/shape.md](reference/shape.md) | -| `init` | Build | Set up project context: PRODUCT.md, DESIGN.md, live config, next steps | [reference/init.md](reference/init.md) | +| `init` | Build | Capture durable product context in PRODUCT.md | [reference/init.md](reference/init.md) | | `document` | Build | Generate DESIGN.md from existing project code | [reference/document.md](reference/document.md) | | `extract [target]` | Build | Pull reusable tokens and components into design system | [reference/extract.md](reference/extract.md) | | `critique [target]` | Evaluate | UX design review with heuristic scoring | [reference/critique.md](reference/critique.md) | @@ -79,7 +78,14 @@ Build to this floor without announcing it. The design detector (the project hook | `optimize [target]` | Fix | Diagnose and fix UI performance | [reference/optimize.md](reference/optimize.md) | | `live` | Iterate | Visual variant mode: pick elements in the browser, generate alternatives | [reference/live.md](reference/live.md) | -Routing: **no argument** → read [reference/routing.md](reference/routing.md) and present the context-aware menu (never auto-run a command). **First word matches a command** (or `pin` / `unpin` / `hooks`) → load its reference (native variant on native platforms) and follow it; everything after the command name is the target. **Intent clearly maps to one command** ("fix the spacing" → `layout`, "rewrite this error" → `clarify`) → same; if two fit, ask once. **Otherwise** → general design invocation: apply Setup and this file's guidance; builds flow through the new-work gate above, whose playbook carries the direction checkpoint and the finishing pass. `teach` routes to `init`, and `craft` routes to the standard build flow with attended checkpoints. If setup diverted into `init` for a build request, finish init, refresh context, then resume. +Routing: + +- **No argument:** read [routing.md](reference/routing.md) and present its context-aware menu; never auto-run a command. +- **Explicit or clearly implied command:** load its reference (native variant on native platforms) and follow it. Ask once if two commands fit. +- **Otherwise:** treat the request as general design work. Missing PRODUCT.md routes through init; new surfaces and replacement worlds use new-work. +- `teach` aliases `init`. `craft` is a deprecated alias for ordinary new-work and adds nothing. `shape` owns task discovery, then enters new-work only for visual-world and surface-concept decisions. + +After init writes PRODUCT.md, resume without rerunning `context.mjs`. **Pin / Unpin:** `node .pi/skills/impeccable/scripts/pin.mjs ` creates or removes a standalone `/` shortcut. Report the script's result concisely; relay stderr verbatim on error. diff --git a/.pi/skills/impeccable/reference/android.md b/.pi/skills/impeccable/reference/android.md index 3575c31fb..6337b9018 100644 --- a/.pi/skills/impeccable/reference/android.md +++ b/.pi/skills/impeccable/reference/android.md @@ -2,7 +2,7 @@ For native Android apps: Jetpack Compose, Android Views, React Native, Expo, Flutter shipping to Android hardware. -On native, register narrows. Material Design 3 governs structure, navigation, and interaction whatever the register; brand expresses through Material's theming (color roles, type scale, shape, motion). A Material-everywhere cross-platform app that also ships to iPhone still owes iOS its OS guarantees on that hardware: safe-area insets, Reduce Motion, edge-swipe back. +On native, the visitor mode narrows what expression may override. Material Design 3 governs structure, navigation, and interaction in every mode; brand expresses through Material's theming (color roles, type scale, shape, motion). A Material-everywhere cross-platform app that also ships to iPhone still owes iOS its OS guarantees on that hardware: safe-area insets, Reduce Motion, edge-swipe back. ## The Android slop test diff --git a/.pi/skills/impeccable/reference/animate.md b/.pi/skills/impeccable/reference/animate.md index 1a1098a5b..d51bb6d63 100644 --- a/.pi/skills/impeccable/reference/animate.md +++ b/.pi/skills/impeccable/reference/animate.md @@ -1,203 +1,88 @@ > **Additional context needed**: performance constraints. -Add motion that conveys state, gives feedback, and clarifies hierarchy. Cut motion that exists only for decoration. Animation fatigue is a real cost; spend the budget on the moments that need it. +Use motion to explain state, relationship, and hierarchy, or to create one authored moment the surface has earned. Decoration without purpose is animation debt. --- -## Register +## Visitor mode -Persuade + Experience: 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. +- **Persuade + Experience:** motion may carry the voice. Prefer one rehearsed focal sequence to repeated section reveals. +- **Operate + Read:** motion serves feedback, state, and continuity. Keep routine transitions fast and do not make users wait through page-load choreography. +- **Native (`ios` / `android` / `adaptive`):** follow the Motion section of [ios.md](ios.md) or [android.md](android.md), including the platform's Reduce Motion behavior. Do not apply the web tooling below. -Operate + Read: 150–250 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. +## Find the job -Native (`ios` / `android` / `adaptive`): implementation follows the Motion section of [ios.md](ios.md) / [android.md](android.md) (read it first if Setup hasn't already): system transitions and OS Reduce Motion, never the web tooling below. +Inspect the existing motion language, interaction states, target devices, and performance budget. Find only the places where motion would: ---- +- acknowledge an action; +- make a state change or spatial relationship legible; +- preserve continuity through navigation or layout change; +- direct attention at a meaningful moment; +- embody the selected visual world. -## Assess Animation Opportunities +Ask only when a material constraint cannot be inferred. Do not animate a static area merely because it exists. -Analyze where motion would improve the experience: +## Set the motion thesis -1. **Identify static areas**: - - **Missing feedback**: Actions without visual acknowledgment (button clicks, form submission, etc.) - - **Jarring transitions**: Instant state changes that feel abrupt (show/hide, page loads, route changes) - - **Unclear relationships**: Spatial or hierarchical relationships that aren't obvious - - **Lack of delight**: Functional but joyless interactions - - **Missed guidance**: Opportunities to direct attention or explain behavior +Write a short plan before implementation: -2. **Understand the context**: - - What's the personality? (Playful vs serious, energetic vs calm) - - What's the performance budget? (Mobile-first? Complex page?) - - Who's the audience? (Motion-sensitive users? Power users who want speed?) - - What matters most? (One hero animation vs many micro-interactions?) +- **Focal moment:** the one sequence or interaction that deserves authorship, if any. +- **Continuity:** the state, layout, or navigation changes that need explanation. +- **Feedback:** the controls and outcomes that need acknowledgment. +- **Budget:** which effects may be expensive, how often they run, and the reduced-motion equivalent. -If any of these are unclear from the codebase, ask the user directly to clarify what you cannot infer. +The focal moment must come from this product and surface concept. A generic fade-and-rise, hover lift, parallax layer, or scroll reveal is not a thesis. -**CRITICAL**: Respect `prefers-reduced-motion`. Always provide non-animated alternatives for users who need them. +## Choose material by meaning -## Plan Animation Strategy +Transform and opacity are reliable foundations, not the entire palette. Choose properties for what the transition communicates: -Create a purposeful animation plan: +- **Continuity and relationship:** shared-element motion, FLIP-style transforms, view transitions, or deliberate spatial movement. +- **Focus and depth:** bounded blur, filter, backdrop, light, or shadow changes. +- **Reveal and composition:** masks, clip paths, cropping, or controlled occlusion. +- **Material and energy:** color, gradient position, texture, distortion, or shader effects when the world and runtime support them. +- **State and feedback:** the smallest change that makes cause and result unmistakable. -- **Feedback layer**: Which interactions need acknowledgment? -- **Transition layer**: Which state changes need smoothing? -- **Entrance moment**: The ONE entrance worth rehearsing, where the mode invites it. Not every surface wants one. -- **Delight layer**: Where can we surprise and delight? +Do not stack techniques for spectacle. One strong material idea, carried through the focal sequence and quiet supporting states, is usually enough. -**IMPORTANT**: One well-orchestrated experience beats scattered animations everywhere. Focus on high-impact moments. +Sibling stagger is appropriate when a list appears as a list. Cap the total delay, and never reinterpret every scrolled section as a staggered list. -## Implement Animations +## Timing and easing -Add motion systematically across these categories: +Timing should express distance and consequence: -### Micro-interactions -- **Button feedback**: - - Hover: Subtle scale (1.02-1.05), color shift, shadow increase - - Click: Quick scale down then up (0.95 → 1), ripple effect - - Loading: Spinner or pulse state -- **Form interactions**: - - Input focus: Border color transition, slight scale or glow - - Validation: Shake on error, check mark on success, smooth color transitions -- **Toggle switches**: Smooth slide + color transition (200-300ms) -- **Checkboxes/radio**: Check mark animation, ripple effect -- **Like/favorite**: Scale + rotation, particle effects, color transition +| Duration | Typical use | +|---|---| +| 100–150 ms | immediate feedback | +| 150–300 ms | routine state change | +| 300–500 ms | layout, overlay, or view transition | +| 500–800 ms | a deliberately authored focal entrance | -### State Transitions -- **Show/hide**: Fade + slide (not instant), appropriate timing (200-300ms) -- **Expand/collapse**: Height transition with overflow handling, icon rotation -- **Loading states**: Skeleton screen fades, spinner animations, progress bars -- **Success/error**: Color transitions, icon animations, gentle scale pulse -- **Enable/disable**: Opacity transitions, cursor changes +Exit faster than entrance. Use natural deceleration such as `cubic-bezier(0.16, 1, 0.3, 1)` for confident arrivals; do not use bounce or elastic curves by reflex. Long feedback feels like latency. -### Entrance Animations -- **The one entrance moment** (where the mode invites it): a committed entrance for primary content (scale, parallax, or a creative effect) -- **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. +## Implement to the runtime - 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. +- Use CSS transitions and keyframes for declarative state and bounded sequences. +- Use Web Animations API or the project's existing motion library for interruption, sequencing, and dynamic values. +- Use View Transitions or shared-element techniques when continuity across states is the point. +- Use scroll-driven motion only when the scroll relationship itself carries meaning, with a robust fallback. +- Do not add a dependency for an effect the existing stack can express cleanly. -### Navigation & Flow -- **Page transitions**: Crossfade between routes, shared element transitions -- **Tab switching**: Slide indicator, content fade/slide -- **Carousel/slider**: Smooth transforms, snap points, momentum -- **Scroll effects**: Parallax layers, sticky headers with state changes, scroll progress indicators +Keep content visible in the default state so failed scripts do not hide the page. Avoid casually animating layout-driving properties such as `width`, `height`, `top`, `left`, and margins; use FLIP, transforms, or grid techniques when appropriate. Bound blur, filter, shadow, canvas, and shader work to isolated regions. Apply `will-change` only during known animation. Measure on target viewports and devices rather than assuming transform means fast. -### Feedback & Guidance -- **Hover hints**: Tooltip fade-ins, cursor changes, element highlights -- **Drag & drop**: Lift effect (shadow + scale), drop zone highlights, smooth repositioning -- **Copy/paste**: Brief highlight flash on paste, "copied" confirmation -- **Focus flow**: Highlight path through form or workflow +## Accessibility and control -### Delight Moments -- **Empty states**: Subtle floating animations on illustrations -- **Completed actions**: Confetti, check mark flourish, success celebrations -- **Easter eggs**: Hidden interactions for discovery -- **Contextual animation**: Weather effects, time-of-day themes, seasonal touches +Honor `prefers-reduced-motion` with an intentional alternative. Preserve state change and hierarchy while removing travel, parallax, flashing, or prolonged sequences. Do not replace every animation globally with `0.01ms` if that destroys useful feedback. Motion must never block focus, interaction, reading, or task completion. -## Technical Implementation +Respect autoplay and sound preferences. Any nonessential loop must stop when offscreen or hidden. -Use appropriate techniques for each animation: +## Verify -### Timing & Easing +- The focal motion is specific to the selected world and surface. +- Every supporting animation explains feedback, state, or relationship. +- Interruption and repeated use behave correctly. +- Desktop, mobile, keyboard, and reduced-motion paths remain usable. +- Expensive effects stay smooth on the target device. +- Removing an animation would lose meaning or authored character, not merely decoration. -**Duration: the 100/300/500 rule.** Timing matters more than easing for "feels right": - -| Duration | Use Case | Examples | -|----------|----------|----------| -| **100–150ms** | Instant feedback | Button press, toggle, color change | -| **200–300ms** | State changes | Menu open, tooltip, hover state | -| **300–500ms** | Layout changes | Accordion, modal, drawer | -| **500–800ms** | Entrance animations | Page load, hero reveal | - -**Easing curves (use these, not CSS defaults):** -```css -/* Recommended: natural deceleration */ ---ease-out-quart: cubic-bezier(0.25, 1, 0.5, 1); /* Smooth */ ---ease-out-quint: cubic-bezier(0.22, 1, 0.36, 1); /* Slightly snappier */ ---ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1); /* Confident, decisive */ - -/* AVOID: feel dated and tacky */ -/* bounce: cubic-bezier(0.34, 1.56, 0.64, 1); */ -/* elastic: cubic-bezier(0.68, -0.6, 0.32, 1.6); */ -``` - -**Exit animations are faster than entrances.** Use ~75% of enter duration. - -### CSS Animations -```css -/* Prefer for simple, declarative animations */ -- transitions for state changes -- @keyframes for complex sequences -- transform and opacity for reliable movement -- blur, filters, masks, clip paths, shadows, and color shifts for premium atmospheric effects when verified smooth -``` - -### JavaScript Animation -```javascript -/* Use for complex, interactive animations */ -- Web Animations API for programmatic control -- Framer Motion for React -- 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 -- **Layout safety**: Avoid casual animation of layout-driving properties (`width`, `height`, `top`, `left`, margins) -- **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) { - * { - animation-duration: 0.01ms !important; - animation-iteration-count: 1 !important; - transition-duration: 0.01ms !important; - } -} -``` - -**NEVER**: -- Use bounce or elastic easing curves; they feel dated and draw attention to the animation itself -- Animate layout properties casually (`width`, `height`, `top`, `left`, margins) when transform, FLIP, or grid-based techniques would work -- Use durations over 500ms for feedback (it feels laggy) -- Animate without purpose (every animation needs a reason) -- Ignore `prefers-reduced-motion` (this is an accessibility violation) -- Animate everything (animation fatigue makes interfaces feel exhausting) -- Block interaction during animations unless intentional - -## Verify Quality - -Test animations thoroughly: - -- **Smooth at 60fps**: No jank on target devices -- **Feels natural**: Easing curves feel organic, not robotic -- **Appropriate timing**: Not too fast (jarring) or too slow (laggy) -- **Reduced motion works**: Animations disabled or simplified appropriately -- **Doesn't block**: Users can interact during/after animations -- **Adds value**: Makes interface clearer or more delightful - -When the motion clarifies state instead of decorating it, hand off to `/impeccable polish` for the final pass. +When motion earns its place, hand off to `/impeccable polish` for the final pass. diff --git a/.pi/skills/impeccable/reference/audit.md b/.pi/skills/impeccable/reference/audit.md index 72815af69..70d80771a 100644 --- a/.pi/skills/impeccable/reference/audit.md +++ b/.pi/skills/impeccable/reference/audit.md @@ -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 - diff --git a/.pi/skills/impeccable/reference/audit.native.md b/.pi/skills/impeccable/reference/audit.native.md index 737d50e4f..0126fa157 100644 --- a/.pi/skills/impeccable/reference/audit.native.md +++ b/.pi/skills/impeccable/reference/audit.native.md @@ -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) diff --git a/.pi/skills/impeccable/reference/bolder.md b/.pi/skills/impeccable/reference/bolder.md index 6b44f0e38..78f5e4811 100644 --- a/.pi/skills/impeccable/reference/bolder.md +++ b/.pi/skills/impeccable/reference/bolder.md @@ -8,12 +8,12 @@ ## Why it reads flat -A section usually reads flat for reasons its neighbors have already solved. Look at what the rest of the page does that this section does not: the display type at full strength, the structural devices that carry meaning, the signature motif, the density and pacing, the specificity of the copy. A flat section is typically one that quietly opts out of the system's own strongest moves. The most reliable bolder pass brings the target up to the expressive level its neighbors already reach, in the system's own vocabulary rather than a new one. +A section usually reads flat for reasons its neighbors have already solved. Look at what the rest of the page does that this section does not: the display type at full strength, the structural devices that carry meaning, the signature motif, the density and pacing. A flat section is typically one that quietly opts out of the system's own strongest moves. The most reliable bolder pass brings the target up to the expressive level its neighbors already reach, in the system's own vocabulary rather than a new one. ## The amplification - **Amplify what the system already owns.** Reuse its motif and its type scale at full strength, turned up for this section rather than invented for it. The bolder version should look more like the same brand, not less. -- **Let the content carry the weight.** Flat copy makes a flat design, and no amount of size fixes a generic claim. Make every word specific enough to earn its place, and let the section's real evidence do the work that a decorative addition would otherwise fake. +- **Keep content true.** Existing claims are part of the scope: preserve them unless the user supplies replacements. If real evidence is essential to the direction but absent, ask for it. - **Commit, then clarify.** Half-measures read as noise. Make the one decisive move completely, then quiet everything around it so the move is legible. If every element got louder, the section got flatter. - **Give it its own rhythm.** The target should read as a peak in the scroll, a shift in density or pace from what surrounds it, not simply more of the same. diff --git a/.pi/skills/impeccable/reference/clarify.md b/.pi/skills/impeccable/reference/clarify.md index 00ee978c3..3047a9d82 100644 --- a/.pi/skills/impeccable/reference/clarify.md +++ b/.pi/skills/impeccable/reference/clarify.md @@ -1,288 +1,94 @@ -> **Additional context needed**: audience technical level and users' mental state in context. +> **Additional context needed**: audience knowledge and emotional state. -Find the unclear, confusing, or poorly written interface text and rewrite it. Vague copy creates support tickets and abandonment; specific copy gets users through the task. +Rewrite unclear interface text so users understand what happened, what matters, and what to do next. Preserve factual meaning, product terminology, and brand voice. +## Audit the language ---- +Read the entire interaction path, not isolated strings. Identify: -## Assess Current Copy +- ambiguous nouns, verbs, and actions; +- internal jargon or assumed knowledge; +- vague labels, outcomes, and system states; +- missing consequences, recovery, or timing; +- inconsistent terminology and capitalization; +- redundant headings, intros, helper text, and confirmations; +- text that breaks at realistic widths or in translation; +- tone that ignores stress, risk, success, or urgency. -Identify what makes the text unclear or ineffective: +Infer audience and task from product context and surrounding UI. Ask before changing factual claims, legal meaning, or a term that may be domain-specific. -1. **Find clarity problems**: - - **Jargon**: Technical terms users won't understand - - **Ambiguity**: Multiple interpretations possible - - **Passive voice**: "Your file has been uploaded" vs "We uploaded your file" - - **Length**: Too wordy or too terse - - **Assumptions**: Assuming user knowledge they don't have - - **Missing context**: Users don't know what to do or why - - **Tone mismatch**: Too formal, too casual, or inappropriate for situation +## Set the message hierarchy -2. **Understand the context**: - - Who's the audience? (Technical? General? First-time users?) - - What's the user's mental state? (Stressed during error? Confident during success?) - - What's the action? (What do we want users to do?) - - What's the constraint? (Character limits? Space limitations?) +For each state, decide: -**CRITICAL**: Clear copy helps users succeed. Unclear copy creates frustration, errors, and support tickets. +1. the one fact the user needs now; +2. the action available next; +3. supporting context that changes the decision; +4. the appropriate tone for this moment. -## Plan Copy Improvements +Say each idea once. If the heading already explains the state, the introduction should add new information or disappear. -Create a strategy for clearer communication: +## Rewrite by function -- **Primary message**: What's the ONE thing users need to know? -- **Action needed**: What should users do next (if anything)? -- **Tone**: How should this feel? (Helpful? Apologetic? Encouraging?) -- **Constraints**: Length limits, brand voice, localization considerations +### Actions and navigation -**IMPORTANT**: Good UX writing is invisible. Users should understand immediately without noticing the words. +Use a specific verb and object when the outcome is not already obvious. Labels should describe what will happen, not the gesture used to trigger it. Keep the same noun and verb for the same concept throughout the product. -## Improve Copy Systematically +For destructive actions, name the object and consequence. Prefer undo over confirmation when recovery is safe. When confirmation is necessary, name the action on both the message and button instead of using `Yes`, `No`, `OK`, or `Submit`. -Refine text across these common areas: +### Forms -### Error Messages -**Bad**: "Error 403: Forbidden" -**Good**: "You don't have permission to view this page. Contact your admin for access." +Use persistent labels; placeholders are examples, not labels. Put format and eligibility requirements before submission. Explain why information is requested only when it is not obvious. Required and optional treatment should be consistent. -**Bad**: "Invalid input" -**Good**: "Email addresses need an @ symbol. Try: name@example.com" +Validation says what needs attention and how to correct it without blaming the user. Keep related instructions near the field and announce errors accessibly. -**Principles**: -- Explain what went wrong in plain language -- Suggest how to fix it -- Don't blame the user -- Include examples when helpful -- Link to help/support if applicable - -### Form Labels & Instructions -**Bad**: "DOB (MM/DD/YYYY)" -**Good**: "Date of birth" (with placeholder showing format) - -**Bad**: "Enter value here" -**Good**: "Your email address" or "Company name" - -**Principles**: -- Use clear, specific labels (not generic placeholders) -- Show format expectations with examples -- Explain why you're asking (when not obvious) -- Put instructions before the field, not after -- Keep required field indicators clear - -### Button & CTA Text -**Bad**: "Click here" | "Submit" | "OK" -**Good**: "Create account" | "Save changes" | "Got it, thanks" +### Errors and permissions -**Principles**: -- Describe the action specifically -- Use active voice (verb + noun) -- Match user's mental model -- Be specific ("Save" is better than "OK") +An actionable error answers: -### Help Text & Tooltips -**Bad**: "This is the username field" -**Good**: "Choose a username. You can change this later in Settings." +1. what failed; +2. why, when known and useful; +3. how to recover or what alternative remains. -**Principles**: -- Add value (don't just repeat the label) -- Answer the implicit question ("What is this?" or "Why do you need this?") -- Keep it brief but complete -- Link to detailed docs if needed +Do not expose internal codes as the primary message. Do not promise a cause or resolution the system cannot know. Treat privacy, payment, deletion, access loss, and blocked work seriously; warmth is welcome, jokes are not. -### Empty States -**Bad**: "No items" -**Good**: "No projects yet. Create your first project to get started." +### Loading, empty, and success states -**Principles**: -- Explain why it's empty (if not obvious) -- Show next action clearly -- Make it welcoming, not dead-end +Loading text names the real operation and sets an honest expectation when the wait is meaningful. Show determinate progress when available; never invent progress. -### Success Messages -**Bad**: "Success" -**Good**: "Settings saved! Your changes will take effect immediately." +An empty state distinguishes first use, no results, filters, permissions, and failure. Explain the state and provide the next useful action. -**Principles**: -- Confirm what happened -- Explain what happens next (if relevant) -- Be brief but complete -- Match the user's emotional moment (celebrate big wins) - -### Loading States -**Bad**: "Loading..." (for 30+ seconds) -**Good**: "Analyzing your data... this usually takes 30-60 seconds" - -**Principles**: -- Set expectations (how long?) -- Explain what's happening (when it's not obvious) -- Show progress when possible -- Offer escape hatch if appropriate ("Cancel") +Success confirms the completed outcome and mentions the next consequence only when it changes what the user should do. Routine success should be brief. -### Confirmation Dialogs -**Bad**: "Are you sure?" -**Good**: "Delete 'Project Alpha'? This can't be undone." +### Help and instructional text -**Principles**: -- State the specific action -- Explain consequences (especially for destructive actions) -- Use clear button labels ("Delete project" not "Yes") -- Don't overuse confirmations (only for risky actions) +Helper text answers an implicit question instead of restating the control. Use progressive disclosure for uncommon detail. Link text must make sense out of context; icon-only controls need accessible names. -### Navigation & Wayfinding -**Bad**: Generic labels like "Items" | "Things" | "Stuff" -**Good**: Specific labels like "Your projects" | "Team members" | "Settings" +## Voice, accessibility, and localization -**Principles**: -- Be specific and descriptive -- Use language users understand (not internal jargon) -- Make hierarchy clear -- Consider information scent (breadcrumbs, current location) +Voice stays consistent; tone adapts to the moment. Use plain language without flattening terminology the audience genuinely knows. -## Apply Clarity Principles +- Write complete translatable messages rather than concatenated fragments. +- Keep variables and numbers structured so translators can reorder them. +- Allow expansion instead of abbreviating prematurely. +- Make alt text convey the image's information; use empty alt for decoration. +- Keep screen-reader names aligned with visible labels and outcomes. +- Do not rely on punctuation, color, or iconography to carry the message alone. -Every piece of copy should follow these rules: +Maintain a short terminology glossary when inconsistency spans the product. Do not vary words for literary effect in an interface. -1. **Be specific**: "Enter email" not "Enter value" -2. **Be concise**: Cut unnecessary words (but don't sacrifice clarity) -3. **Be active**: "Save changes" not "Changes will be saved" -4. **Be human**: "Oops, something went wrong" not "System error encountered" -5. **Tell users what to do**, not just what happened -6. **Be consistent**: Use same terms throughout (don't vary for variety) +## Verify -**NEVER**: -- Use jargon without explanation -- Blame users ("You made an error" → "This field is required") -- Be vague ("Something went wrong" without explanation) -- Use passive voice unnecessarily -- Write overly long explanations (be concise) -- Use humor for errors (be empathetic instead) -- Assume technical knowledge -- Vary terminology (pick one term and stick with it) -- Repeat information (headers restating intros, redundant explanations) -- Use placeholders as the only labels (they disappear when users type) +Read the flow in context and test: -## Verify Improvements +- comprehension without hidden product knowledge; +- actionability at errors, empty states, and decision points; +- factual accuracy and consistent terminology; +- scanability at target widths and 200% zoom; +- long names, localization expansion, pluralization, and dynamic values; +- accessible names and announced state changes; +- tone appropriate to consequence and emotional context. -Test that copy improvements work: +The final copy is as short as it can be without removing meaning or recovery. -- **Comprehension**: Can users understand without context? -- **Actionability**: Do users know what to do next? -- **Brevity**: Is it as short as possible while remaining clear? -- **Consistency**: Does it match terminology elsewhere? -- **Tone**: Is it appropriate for the situation? - -When the copy reads cleanly, hand off to `/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. +When the language reads cleanly, hand off to `/impeccable polish` for the final pass. diff --git a/.pi/skills/impeccable/reference/codex.md b/.pi/skills/impeccable/reference/codex.md index 18a924a91..6c550f1dc 100644 --- a/.pi/skills/impeccable/reference/codex.md +++ b/.pi/skills/impeccable/reference/codex.md @@ -1,105 +1,38 @@ -# Codex: Visual Direction & Asset Production +# Codex: Surface Probes & Asset Production -This file is loaded by `/impeccable craft` when the harness has native image generation (currently Codex via `image_gen`). Other harnesses skip it. It covers the two craft steps that depend on real image generation: landing the visual direction, and producing the raster assets the implementation will compose. +Load this from [new-work.md](new-work.md) only when the harness has native image generation and a substantial, high-fidelity surface would benefit from seeing the shortlisted concept before code. PRODUCT.md and DESIGN.md are preconditions. New-work has already resolved the visual world; this file must not reopen it. -Read this *before* generating any images. The order matters, and the per-step user pauses are what keep generated imagery from drifting away from the brief. +The purpose of a probe is to test composition, narrative, hierarchy, density, focal moment, signature use, and image requirements. It is not a second identity workshop. Keep DESIGN.md's palette, typography direction, material language, component character, imagery stance, and motion grammar fixed. -### Four stop points before code +## Generate the smallest useful probe set -Steps A through D each end with the user. Do not advance past any of them on your own read of the situation. +Generate one to three high-fidelity north-star comps using the native image-generation capability. Base them on the real content and the surface concepts already developed with the user. -1. **STOP after Step A questions.** Wait for answers. -2. **STOP after Step B palette generation.** Wait for "confirm palette." -3. **STOP after Step C mocks.** Wait for direction approval or delegation. -4. **Only after Step D approves a direction** do you return to the build (new-work.md's plan, then code). +- When the user shortlisted multiple concepts, show one clear expression of each. +- When one concept is already selected, vary only the structural uncertainty that the image can resolve: topology, sequence, density, hierarchy, focal composition, or interaction framing. +- Show enough beyond the opening moment to prove the concept can govern the whole requested surface. +- Do not generate a palette artifact, ask new atmosphere questions, introduce a different type voice, or invent a new motif. If the committed world cannot support the concept, return to the concept shortlist rather than changing the world. -Prior shape approval does **not** satisfy any of these. Shape's "confirm or override" advances you into Step A; it is not a substitute for it. +Treat each comp as a direction test, not a screenshot specification. Core UI text, responsive behavior, accessibility, semantics, and interaction states remain implementation responsibilities. -## Step A: Explore Directions with the User +## One approval point -Before generating anything, run a brief direction conversation grounded in the shape brief. +Show the probes together and ask what should carry forward, what feels false to the world, and whether the selected surface concept should be approved, combined, revised, or rejected. Then stop and wait. A structured simulated user counts as attended and receives the same question. -**Step A is required even when shape just produced a confirmed brief.** The shape questions and Step A questions cover different ground: shape pins purpose, content, scope; Step A pins palette, atmosphere, and named visual references for the comps you're about to generate. The only time you can skip Step A is when the user has already answered these exact palette/atmosphere/reference questions in the same session. +Do not begin code until the user approves a direction or explicitly delegates the choice. If they delegate, choose using the task brief, PRODUCT.md, and DESIGN.md, and state the evidence. Approval refines the task concept; it does not modify DESIGN.md. -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: +After approval, summarize the composition and the parts of the comp that must not be literalized. Return to new-work.md and write the direction contract from the approved surface concept before code. -- "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.)" +## Inventory implementation fidelity -**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. +Before building, inventory the approved comp's major visible ingredients and choose an implementation medium for each: semantic HTML/CSS/SVG, existing project asset, generated raster, sourced raster, icon library, canvas/WebGL, or accepted omission. -## Step B: Generate the Brand Palette First +Pay special attention to the dominant composition, signature use, image-native content, second-fold system, and any interaction the still image only implies. If the concept depends on a photograph, architectural scene, product object, portrait, or other raster-native material, do not silently replace it with generic CSS scenery. -Generate **one** palette artifact before any mocks. This is a small, focused image: typography pairing on the chosen background, primary + accent color swatches, one signature ornament or motif. Single image, single pass. +Treat the comp as a north star, not something to trace. Do not rasterize core UI text or controls. Do not substitute a different visual driver after approval without asking. -Why palette first: mocks generated against a vague color sense produce noise that drowns out the structural decisions. A confirmed palette is the first concrete contract for everything downstream. +## Produce only the assets the build needs -Show the palette to the user. Ask one question: "This is the palette I'm locking in for the mocks. Confirm, or call out what to shift?" +When clean raster ingredients are required and a scoped subagent is available and authorized, use `impeccable_asset_producer`. Give it the approved comp, output paths, required dimensions and formats, transparency needs, crop notes, and what must remain semantic code. Otherwise produce the minimum required assets with the native image-generation capability in the current thread. -**STOP and wait for confirmation.** Do not generate mocks against an unconfirmed palette. "Probably good enough" is the wrong call here; the palette is the contract for everything downstream. - -## Step C: Generate 1-3 Visual Mocks Against the Palette - -Once the palette is confirmed, generate **1 to 3** high-fidelity north-star comps. Each mock must use the confirmed palette and typography. Mocks differ in *structural* direction (hierarchy, topology, density, composition), not in color or motif. - -- Brand work: push visual identity, composition, mood, and signature motifs. -- Product work: push hierarchy, topology, density, tone, grounded in realistic product structure. -- Landing pages and long-form brand surfaces: show enough of the second fold to establish the system beyond the hero. - -Use the `image_gen` tool directly (or via the imagegen skill when available). Don't ask the user to install anything. - -## Step D: Approval Loop - -Show the comps. Ask what carries forward. Iterate until **one direction is approved** or the user explicitly delegates. - -**STOP and wait for the approval or the delegation.** Do not begin Step E or return to the build until a single direction is named. If the user delegates, pick the strongest direction and explain it from the brief, not personal taste. - -Before moving to assets, summarize what to carry into code and what *not* to literalize from the mock. This is the handoff between visual exploration and semantic implementation. - -## Step E: Mock Fidelity Inventory - -Inventory the approved mock's major visible ingredients. For each, decide implementation: semantic HTML/CSS/SVG, generated raster, sourced raster, icon library, canvas/WebGL, or accepted omission. - -Common ingredients to inventory: - -- Hero silhouette and dominant composition -- Signature motifs (planets, devices, portraits, charts, route lines, insets, badges, etc.) -- Nav and primary-action treatment (when the surface has one) -- Section sequence, especially the second fold -- Image-native content the concept depends on -- Typography, density, color/material treatment, motion cues - -Treat the mock as a north star, not a screenshot to trace. Don't rasterize core UI text. But if the live result lacks the mock's major ingredients, the implementation is wrong. - -If a photographic, architectural, product, or place-led mock becomes generic CSS scenery, decorative diagrams, bullets, or copy, stop and fix it. That's a broken implementation, not a harmless interpretation. - -Don't substitute a different hero composition or visual driver post-approval without user sign-off. - -## Step F: Asset Slicing via the Asset Producer - -Raster ingredients identified in Step E need clean production assets. Use the bundled `impeccable_asset_producer` subagent rather than producing inline. - -Spawn it as a scoped subagent. If you do not have explicit permission to use agents, stop and ask: - -```text -Asset production will work better as a scoped subagent job. Should I spawn the Impeccable asset producer subagent for this step? -``` - -Pass to the agent: - -- Approved mock path or screenshot reference -- Crop paths or a contact sheet with crop ids -- Output directory -- Required dimensions, format, transparency needs -- Avoid list -- Notes on what should remain semantic HTML/CSS/SVG instead of raster - -Attach image generation capability to the spawned agent when the harness supports it. Do **not** load image-generation reference material into the parent thread. - -Inline asset production is allowed only if the user declines subagents, the harness cannot spawn the authorized agent, or the user explicitly asks for single-thread mode. - -Prefer HTML/CSS/SVG/canvas when they can credibly reproduce an ingredient; reach for real, generated, or stock imagery when the mock or subject matter calls for actual visual content. - -## After This File - -Once Steps A through F are complete, return to the build: implement per new-work.md and SKILL.md's craft floor. The implementation builds against the confirmed palette, approved mock, and the assets the producer wrote. +Return to [new-work.md](new-work.md) for the direction contract, implementation, and finishing pass. diff --git a/.pi/skills/impeccable/reference/colorize.md b/.pi/skills/impeccable/reference/colorize.md index abee1acf8..dc45f88c5 100644 --- a/.pi/skills/impeccable/reference/colorize.md +++ b/.pi/skills/impeccable/reference/colorize.md @@ -1,257 +1,86 @@ > **Additional context needed**: existing brand colors. -Replace timid grayscale or single-accent designs with a strategic palette: pick a color strategy, choose a hue family that fits the brand, then apply color with intent. More color ≠ better. Strategic color beats rainbow vomit. +Introduce color as hierarchy, meaning, and atmosphere. Preserve confirmed brand and semantic conventions; do not replace a visual world under the guise of colorizing it. --- -## Register +## Visitor mode -Persuade + Experience: palette IS voice. Pick a color strategy first per SKILL.md (Restrained / Committed / Full palette / Drenched) and follow its dosage. Committed, Full palette, and Drenched deliberately exceed the ≤10% rule; that rule is Restrained only. Unexpected combinations are allowed; a dominant color can own the page when the chosen strategy calls for it. +- **Persuade + Experience:** color may carry the voice and own large regions when the selected world calls for it. +- **Operate + Read:** color primarily encodes action, selection, status, wayfinding, and reading hierarchy. Rarity gives an accent force. -Operate + Read: semantic-first and almost always Restrained. Accent color is reserved for primary action, current selection, and state indicators. Not decoration. Every color has a consistent meaning across every screen. +## Audit before choosing ---- +Read DESIGN.md, tokens, assets, current themes, and representative states. Identify: -## Assess Color Opportunity +- which colors are confirmed brand commitments; +- current surface, text, action, and semantic roles; +- places where grayscale obscures hierarchy or state; +- contrast failures and color-only communication; +- light/dark or data-visualization requirements; +- whether the task asks for more color or a new identity. -Analyze the current state and identify opportunities: +If a new identity is required, use [new-work.md](new-work.md). Ask only when a binding brand decision cannot be inferred. -1. **Understand current state**: - - **Color absence**: Pure grayscale? Limited neutrals? One timid accent? - - **Missed opportunities**: Where could color add meaning, hierarchy, or delight? - - **Context**: What's appropriate for this domain and audience? - - **Brand**: Are there existing brand colors we should use? +## Choose a strategy -2. **Identify where color adds value**: - - **Semantic meaning**: Success (green), error (red), warning (yellow/orange), info (blue) - - **Hierarchy**: Drawing attention to important elements - - **Categorization**: Different sections, types, or states - - **Emotional tone**: Warmth, energy, trust, creativity - - **Wayfinding**: Helping users navigate and understand structure - - **Delight**: Moments of visual interest and personality +Name the intended emotional temperature, dominant relationship, contrast range, and color dosage before editing. The strategy may be restrained or immersive; it must follow the brief and selected world rather than a fixed percentage rule. -If any of these are unclear from the codebase, ask the user directly to clarify what you cannot infer. +Build roles, not a bag of swatches: -**CRITICAL**: More color ≠ better. Strategic color beats rainbow vomit every time. Every color should have a purpose. +- canvas and elevated surfaces; +- primary and secondary text; +- action, focus, and selection; +- borders and separators; +- success, warning, error, and information; +- data categories or scales when needed. -## Plan Color Strategy +Use the project's existing color space. For a new web palette, prefer OKLCH because lightness and chroma can be adjusted predictably. Choose hue from product meaning and visual direction, never from a default category association. -Create a purposeful color introduction plan: +## Apply at system scale -- **Color palette**: What colors match the brand/context? (Choose 2-4 colors max beyond neutrals) -- **Dominant color**: Which color owns 60% of colored elements? -- **Accent colors**: Which colors provide contrast and highlights? (30% and 10%) -- **Application strategy**: Where does each color appear and why? +- Let the strongest color own a deliberate region or role instead of scattering tiny accents. +- Keep the primary action easy to find; do not spend its color on decoration. +- Tint neutrals only when the brand hue genuinely creates cohesion. Neutral gray is valid when it serves the world. +- On colored surfaces, derive secondary text from the foreground or surface hue rather than using washed-out generic gray. +- Keep semantic meanings consistent, but respect platform and domain conventions instead of assuming fixed hues. +- For data, use distinct lightness, chroma, shape, label, or pattern so color is not the only code. +- 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. -**IMPORTANT**: Color should enhance hierarchy and meaning, not create chaos. Less is more when it matters more. +Decoration without a relationship to hierarchy, state, content, or the visual world is not a color strategy. -## Introduce Color Strategically +## Contrast and perception -Add color systematically across these dimensions: +Verify computed foreground/background pairs: -### Semantic Color -- **State indicators**: - - Success: Green tones (emerald, forest, mint) - - Error: Red/pink tones (rose, crimson, coral) - - Warning: Orange/amber tones - - Info: Blue tones (sky, ocean, indigo) - - Neutral: Gray/slate for inactive states +| Content | WCAG AA minimum | +|---|---| +| body text | 4.5:1 | +| large text | 3:1 | +| controls, icons, focus indicators | 3:1 | -- **Status badges**: Colored backgrounds or borders for states (active, pending, completed, etc.) -- **Progress indicators**: Colored bars, rings, or charts showing completion or health +Do not rely on eyesight alone. Check interactive states, overlays, text on images, disabled content, and both themes. Simulate common vision deficiencies. Information conveyed by color also needs text, shape, iconography, or position. -### Accent Color Application -- **Primary actions**: Color the most important buttons/CTAs -- **Links**: Add color to clickable text (maintain accessibility) -- **Icons**: Colorize key icons for recognition and personality -- **Headers/titles**: Add color to section headers or key labels -- **Hover states**: Introduce color on interaction +When deriving OKLCH ramps, vary lightness and reduce chroma near white and black. Do not keep high chroma at extreme lightness merely to make the math uniform. Prefer explicit colors over chains of translucent overlays when alpha would make contrast context-dependent. -### Background & Surfaces -- **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 toward the brand, not "for warmth" by reflex +## Verify -**Use OKLCH for color**: It's perceptually uniform, meaning equal steps in lightness *look* equal. Great for generating harmonious scales. - -### Data Visualization -- **Charts & graphs**: Use color to encode categories or values -- **Heatmaps**: Color intensity shows density or importance -- **Comparison**: Color coding for different datasets or timeframes - -### Borders & Accents -- **Hairline borders**: 1px colored borders on full perimeter (not side-stripes; see the absolute ban on `border-left/right > 1px`) -- **Underlines**: Color underlines for emphasis or active states -- **Dividers**: Subtle colored dividers instead of gray lines -- **Focus rings**: Colored focus indicators matching brand -- **Surface tints**: A 4-8% background wash of the accent color instead of a stripe - -**NEVER**: `border-left` or `border-right` greater than 1px as a colored accent stripe. This is one of the three absolute bans in the parent skill. If you want to mark a card as "active" or "warning", use a full hairline border, a background tint, a leading glyph, or a numbered prefix. Not a side stripe. - -### Typography Color -- **Colored headings**: Use brand colors for section headings (maintain contrast) -- **Highlight text**: Color for emphasis or categories -- **Labels & tags**: Small colored labels for metadata or categories - -### Decorative Elements -- **Illustrations**: Add colored illustrations or icons -- **Shapes**: Geometric shapes in brand colors as background elements -- **Gradients**: Colorful gradient overlays or mesh backgrounds -- **Blobs/organic shapes**: Soft colored shapes for visual interest - -## Balance & Refinement - -Ensure color addition improves rather than overwhelms: - -### Maintain Hierarchy -- **Dominant color** (60%): Primary brand color or most used accent -- **Secondary color** (30%): Supporting color for variety -- **Accent color** (10%): High contrast for key moments -- **Neutrals** (remaining): Gray/black/white for structure - -### Accessibility -- **Contrast ratios**: Ensure WCAG compliance (4.5:1 for text, 3:1 for UI components) -- **Don't rely on color alone**: Use icons, labels, or patterns alongside color -- **Test for color blindness**: Verify red/green combinations work for all users - -### Cohesion -- **Consistent palette**: Use colors from defined palette, not arbitrary choices -- **Systematic application**: Same color meanings throughout (green always = success) -- **Temperature consistency**: Warm palette stays warm, cool stays cool - -**NEVER**: -- 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 -- Violate WCAG contrast requirements -- Use color as the only indicator (accessibility issue) -- Make everything colorful (defeats the purpose) -- Default to purple-blue gradients (AI slop aesthetic) - -## Verify Color Addition - -Test that colorization improves the experience: - -- **Better hierarchy**: Does color guide attention appropriately? -- **Clearer meaning**: Does color help users understand states/categories? -- **More engaging**: Does the interface feel warmer and more inviting? -- **Still accessible**: Do all color combinations meet WCAG standards? -- **Not overwhelming**: Is color balanced and purposeful? +- Every color has a stable role or a world-specific atmospheric purpose. +- Attention lands on the intended action, content, or state. +- The palette works across quiet, dense, interactive, error, and empty states. +- Light and dark themes are each composed, not mechanically inverted. +- Contrast and non-color cues pass in all relevant states. +- The result is recognizably this product, not a generic “colorful” treatment. When the palette earns its place, hand off to `/impeccable polish` for the final pass. ## Live-mode signature params -When invoked from live mode, each variant MUST declare a `color-amount` param so the user can dial between a restrained accent and a drenched surface without regeneration. Author the variant's CSS against `var(--p-color-amount, 0.5)`, typically as the alpha multiplier on backgrounds, or as a scaling factor on the chroma axis in an OKLCH expression. 0 = neutral/monochrome, 1 = full saturation / dominant coverage. +When invoked from live mode, every variant declares a `color-amount` parameter. Author CSS against `var(--p-color-amount, 0.5)` so the user can move from neutral to the variant's full color strategy without regeneration. ```json {"id":"color-amount","kind":"range","min":0,"max":1,"step":0.05,"default":0.5,"label":"Color amount"} ``` -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). +Add at most two variant-specific parameters, such as palette, temperature, or tint behavior. Follow [live.md](live.md)'s parameter contract. diff --git a/.pi/skills/impeccable/reference/craft.md b/.pi/skills/impeccable/reference/craft.md index cc7f98d37..dbbc9402c 100644 --- a/.pi/skills/impeccable/reference/craft.md +++ b/.pi/skills/impeccable/reference/craft.md @@ -1,3 +1,5 @@ # Craft (deprecated alias) -`craft` is a deprecated alias for the standard build flow. Builds route through SKILL.md's new-work gate; [new-work.md](new-work.md) carries the direction checkpoint ("Decide, then build"), the identity playbook, and the finishing pass. Invoking `craft` explicitly forces the attended checkpoints: pause at the stated direction for confirmation, and follow [codex.md](codex.md)'s mock flow when the harness generates images. Nothing else differs from a plain build request. +`craft` is a deprecated alias for an ordinary request to make new visual work. It adds no setup, interview, checkpoint, tool, or quality behavior. Apply SKILL.md's normal routing: create missing PRODUCT.md through [init.md](init.md), then follow [new-work.md](new-work.md) for visual authority, world and surface decisions, implementation, and finish. + +Do not tell users they need to invoke `craft`. Natural requests such as “build this feature,” “make a landing page,” or “redesign this screen” use the same flow. diff --git a/.pi/skills/impeccable/reference/critique.md b/.pi/skills/impeccable/reference/critique.md index 1dfae73b1..b07023e94 100644 --- a/.pi/skills/impeccable/reference/critique.md +++ b/.pi/skills/impeccable/reference/critique.md @@ -43,13 +43,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 @@ -115,11 +115,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. @@ -176,7 +176,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. 2. **Pass the structured metadata** through `IMPECCABLE_CRITIQUE_META` (JSON), then run the write command: ```bash diff --git a/.pi/skills/impeccable/reference/delight.md b/.pi/skills/impeccable/reference/delight.md index 596cff09e..40aaddc34 100644 --- a/.pi/skills/impeccable/reference/delight.md +++ b/.pi/skills/impeccable/reference/delight.md @@ -1,302 +1,70 @@ -> **Additional context needed**: what's appropriate for the domain (playful vs professional vs quirky vs elegant). +> **Additional context needed**: the brand's emotional range. -Find the moments where personality and unexpected polish would turn a functional interface into one users remember and tell other people about. Add only where the moment earns it; delight everywhere reads as noise. +Make the experience memorable at moments that earn it. Delight is not a layer of generic whimsy; it is product character revealed through a useful interaction, a humane response, or an unexpectedly considered detail. --- -## Register +## Visitor mode -Persuade + Experience: delight can be distributed across copy voice, section transitions, discovery rewards, seasonal touches, personality across the whole surface. +- **Persuade + Experience:** personality may run through voice, composition, motion, and discovery, provided the artifact remains the focus. +- **Operate + Read:** concentrate delight at meaningful moments such as first use, completion, recovery, or mastery. Reliability carries everything else. -Operate + Read: delight at specific moments, not pages. Completion, first-time actions, error recovery, milestone crossings. Reliability and consistency carry the rest of the experience; delight pushed everywhere reads as noise. +## Find the opportunity ---- +Inspect the target, DESIGN.md, product voice, repeated-use frequency, and emotional context. Look for: -## Assess Delight Opportunities +- effort worth acknowledging; +- waiting that can become informative; +- an empty or first-use state that can orient; +- an error or recovery moment that needs empathy; +- an interaction whose physical or verbal response could express the brand; +- a useful capability people might enjoy discovering. -Identify where delight would enhance (not distract from) the experience: +Do not manufacture a celebration for an ordinary click. Ask only when the brand's emotional range or the stakes cannot be inferred. -1. **Find natural delight moments**: - - **Success states**: Completed actions (save, send, publish) - - **Empty states**: First-time experiences, onboarding - - **Loading states**: Waiting periods that could be entertaining - - **Achievements**: Milestones, streaks, completions - - **Interactions**: Hover states, clicks, drags - - **Errors**: Softening frustrating moments - - **Easter eggs**: Hidden discoveries for curious users +## Define one delight thesis -2. **Understand the context**: - - What's the brand personality? (Playful? Professional? Quirky? Elegant?) - - Who's the audience? (Tech-savvy? Creative? Corporate?) - - What's the emotional context? (Accomplishment? Exploration? Frustration?) - - What's appropriate? (Banking app ≠ gaming app) +State in one sentence what the user should feel and why that feeling belongs to this product. Then choose the smallest system that can deliver it: -3. **Define delight strategy**: - - **Subtle sophistication**: Refined micro-interactions (luxury brands) - - **Playful personality**: Whimsical illustrations and copy (consumer apps) - - **Helpful surprises**: Anticipating needs before users ask (productivity tools) - - **Sensory richness**: Satisfying sounds, smooth animations (creative tools) +- a distinctive response to a meaningful action; +- product-specific language that clarifies while carrying voice; +- an interaction or transition with a recognizable material behavior; +- an illustration, sound, haptic, or environmental detail grounded in the product world; +- a discovery reward that reveals real utility. -If any of these are unclear from the codebase, ask the user directly to clarify what you cannot infer. +Derive the treatment from product mechanism and visual world, not a stock catalog. -**CRITICAL**: Delight should enhance usability, never obscure it. If users notice the delight more than accomplishing their goal, you've gone too far. +## Build for the emotional moment -## Delight Principles +- **Success:** match the response to the effort and consequence. Major milestones can expand; routine saves should simply feel certain. +- **Waiting:** show truthful progress, useful context, or product-specific activity. Never fake work or delay completion to stage a flourish. +- **Empty and first use:** make the next action clear before adding personality. +- **Error and recovery:** lead with the problem and recovery. Warmth may reduce stress; jokes must not trivialize loss, money, privacy, or blocked work. +- **Repeated interaction:** keep the response satisfying after the hundredth use. Variation is useful only when it remains coherent and predictable enough to trust. +- **Discovery:** reward curiosity without hiding required functionality. -Follow these guidelines: +Copy must use the product's language. Generic whimsy is worse than neutral clarity. -### Delight Amplifies, Never Blocks -- Delight moments should be quick (< 1 second) -- Never delay core functionality for delight -- Make delight skippable or subtle -- Respect user's time and task focus +## Protect the experience -### Surprise and Discovery -- Hide delightful details for users to discover -- Reward exploration and curiosity -- Don't announce every delight moment -- Let users share discoveries with others +Delight must not: -### Appropriate to Context -- Match delight to emotional moment (celebrate success, empathize with errors) -- Respect the user's state (don't be playful during critical errors) -- Match brand personality and audience expectations -- Cultural sensitivity (what's delightful varies by culture) +- delay, block, or obscure the primary task; +- override platform conventions or accessibility; +- add unrequested factual claims; +- play sound without consent or ignore mute settings; +- become mandatory, unskippable, or exhausting on repeat; +- add a dependency or asset cost disproportionate to the moment. -### Compound Over Time -- Delight should remain fresh with repeated use -- Vary responses (not same animation every time) -- Reveal deeper layers with continued use -- Build anticipation through patterns +For authored motion, load [animate.md](animate.md). Respect reduced motion, screen readers, keyboard use, touch, localization, and cultural context. Nonessential loops stop when hidden. Make celebration intensity proportional to frequency and consequence. -## Delight Techniques +## Verify -Add personality and joy through these methods: +- The moment is specific enough that a neighboring product could not use it unchanged. +- It improves comprehension, confidence, motivation, or emotional recovery. +- The interface remains fast and obvious without the flourish. +- Repetition does not turn charm into friction. +- Reduced-motion, muted, keyboard, touch, and localized paths work. +- The result feels like the selected world, not a generic “delight” treatment. -### Micro-interactions & Animation - -**Button delight**: -```css -/* Satisfying button press */ -.button { - transition: transform 0.1s, box-shadow 0.1s; -} -.button:active { - transform: translateY(2px); - box-shadow: 0 2px 4px rgba(0,0,0,0.2); -} - -/* Ripple effect on click */ -/* Smooth lift on hover */ -.button:hover { - transform: translateY(-2px); - transition: transform 0.2s cubic-bezier(0.25, 1, 0.5, 1); /* ease-out-quart */ -} -``` - -**Loading delight**: -- Playful loading animations (not just spinners) -- Personality in loading messages (write product-specific ones, not generic AI filler) -- Progress indication with encouraging messages -- Skeleton screens with subtle animations - -**Success animations**: -- Checkmark draw animation -- Confetti burst for major achievements -- Gentle scale + fade for confirmation -- Satisfying sound effects (subtle) - -**Hover surprises**: -- Icons that animate on hover -- Color shifts or glow effects -- Tooltip reveals with personality -- Cursor changes (custom cursors for branded experiences) - -### Personality in Copy - -**Playful error messages**: -``` -"Error 404" -"This page is playing hide and seek. (And winning)" - -"Connection failed" -"Looks like the internet took a coffee break. Want to retry?" -``` - -**Encouraging empty states**: -``` -"No projects" -"Your canvas awaits. Create something amazing." - -"No saved articles" -"Nothing on the nightstand yet. Save a story for later." -``` - -**Playful labels & tooltips**: -``` -"Delete" -"Send to void" (for playful brand) - -"Help" -"Rescue me" (tooltip) -``` - -**IMPORTANT**: Match copy personality to brand. Banks shouldn't be wacky, but they can be warm. - -### Illustrations & Visual Personality - -**Custom illustrations**: -- Empty state illustrations (not stock icons) -- Error state illustrations (friendly monsters, quirky characters) -- Loading state illustrations (animated characters) -- Success state illustrations (celebrations) - -**Icon personality**: -- Custom icon set matching brand personality -- Animated icons (subtle motion on hover/click) -- Illustrative icons (more detailed than generic) -- Consistent style across all icons - -**Background effects**: -- Subtle particle effects -- Gradient mesh backgrounds -- Geometric patterns -- Parallax depth -- Time-of-day themes (morning vs night) - -### Satisfying Interactions - -**Drag and drop delight**: -- Lift effect on drag (shadow, scale) -- Snap animation when dropped -- Satisfying placement sound -- Undo toast ("Dropped in wrong place? [Undo]") - -**Toggle switches**: -- Smooth slide with spring physics -- Color transition -- Haptic feedback on mobile -- Optional sound effect - -**Progress & achievements** (Operate surfaces with recurring tasks only; gamification on editorial, portfolio, or one-visit surfaces reads as noise): -- Streak counters with celebratory milestones -- Progress bars that "celebrate" at 100% -- Badge unlocks with animation -- Playful stats ("You're on fire! 5 days in a row") - -**Form interactions**: -- Input fields that animate on focus -- Checkboxes with a satisfying scale pulse when checked -- Success state that celebrates valid input -- Auto-grow textareas - -### Sound Design - -**Subtle audio cues** (when appropriate): -- Notification sounds (distinctive but not annoying) -- Success sounds (satisfying "ding") -- Error sounds (empathetic, not harsh) -- Typing sounds for chat/messaging -- Ambient background audio (very subtle) - -**IMPORTANT**: -- Respect system sound settings -- Provide mute option -- Keep volumes quiet (subtle cues, not alarms) -- Don't play on every interaction (sound fatigue is real) - -### Easter Eggs & Hidden Delights - -**Discovery rewards**: -- Konami code unlocks special theme -- Hidden keyboard shortcuts (Cmd+K for special features) -- Hover reveals on logos or illustrations -- Alt text jokes on images (for screen reader users too!) -- A view-source or console colophon (a note on the typefaces, a thank-you, a hiring line) - -**Seasonal touches**: -- Holiday themes (subtle, tasteful) -- Seasonal color shifts -- Weather-based variations -- Time-based changes (dark at night, light during day) - -**Contextual personality**: -- Different messages based on time of day -- Responses to specific user actions -- Randomized variations (not same every time) -- Progressive reveals with continued use - -### Loading & Waiting States - -**Make waiting engaging**: -- Interesting loading messages that rotate -- Progress bars with personality -- Mini-games during long loads -- Fun facts or tips while waiting -- Countdown with encouraging messages - -``` -Loading messages: write ones specific to your product, not generic AI filler: -- "Syncing with your team's changes..." -- "Fetching this week's issue..." -- "Developing your photos..." -- "Checking tomorrow's tide tables..." -``` - -**WARNING**: Avoid cliched loading messages like "Herding pixels", "Teaching robots to dance", "Consulting the magic 8-ball", "Counting backwards from infinity". These are AI-slop copy, instantly recognizable as machine-generated. Write messages that are specific to what your product actually does. - -### Celebration Moments - -**Success celebrations**: -- Confetti for major milestones -- Animated checkmarks for completions -- Progress bar celebrations at 100% -- "Achievement unlocked" style notifications -- Personalized messages ("You published your 10th article!") - -**Milestone recognition** (same scoping as Progress & achievements: Operate surfaces with recurring tasks): -- First-time actions get special treatment -- Streak tracking and celebration -- Progress toward goals -- Anniversary celebrations - -## Implementation Patterns - -**Animation libraries**: -- Framer Motion (React) -- GSAP (universal) -- Lottie (After Effects animations) -- Canvas confetti (party effects) - -**Sound libraries**: -- Howler.js (audio management) -- Use-sound (React hook) - -**Physics libraries**: -- React Spring (spring physics) -- Popmotion (animation primitives) - -**IMPORTANT**: File size matters. Compress images, optimize animations, lazy load delight features. - -**NEVER**: -- Delay core functionality for delight -- Force users through delightful moments (make skippable) -- Use delight to hide poor UX -- Overdo it (less is more) -- Ignore accessibility (animate responsibly, provide alternatives) -- Make every interaction delightful (special moments should be special) -- Sacrifice performance for delight -- Be inappropriate for context (read the room) - -## Verify Delight Quality - -Test that delight actually delights: - -- **User reactions**: Do users smile? Share screenshots? -- **Doesn't annoy**: Still pleasant after 100th time? -- **Doesn't block**: Can users opt out or skip? -- **Performant**: No jank, no slowdown -- **Appropriate**: Matches brand and context -- **Accessible**: Works with reduced motion, screen readers - -When the moments feel earned, hand off to `/impeccable polish` for the final pass. +When the personality feels earned, hand off to `/impeccable polish` for the final pass. diff --git a/.pi/skills/impeccable/reference/document.md b/.pi/skills/impeccable/reference/document.md index 0d38eb568..121a00ab1 100644 --- a/.pi/skills/impeccable/reference/document.md +++ b/.pi/skills/impeccable/reference/document.md @@ -1,6 +1,6 @@ Generate a `DESIGN.md` file at the project root that captures the current visual design system, so AI agents generating new screens stay on-brand. -DESIGN.md follows the [official DESIGN.md format spec](https://raw.githubusercontent.com/google-labs-code/design.md/main/docs/spec.md): YAML frontmatter carrying machine-readable design tokens, followed by a markdown body with exactly six sections in a fixed order. **Tokens are normative; prose provides context for how to apply them.** Sections may be omitted when not relevant, but **do not reorder them and do not rename them**. Section headers must match the spec character-for-character so the file stays parseable by other DESIGN.md-aware tools (Stitch itself, awesome-design-md, skill-rest, etc.). +DESIGN.md follows the [official DESIGN.md format spec](https://raw.githubusercontent.com/google-labs-code/design.md/main/docs/spec.md): optional YAML frontmatter carrying machine-readable design tokens, followed by up to eight markdown sections in a fixed order. **Tokens are normative; prose provides context for how to apply them.** Sections may be omitted when not relevant, but those present stay in the specified order. Use the canonical headings below so the file remains portable across DESIGN.md-aware tools. ## The frontmatter: token schema @@ -43,26 +43,28 @@ components: 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. +- **Colors accept any valid CSS color string.** Hex is the recommended default for portability, but preserve an incumbent `rgb()`, `hsl()`, `oklch()`, wide-gamut, or mixed-color value when it is the project's normative source. 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 (`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) +## The markdown body: eight sections (canonical order) 1. `## Overview` 2. `## Colors` 3. `## Typography` -4. `## Elevation` -5. `## Components` -6. `## Do's and Don'ts` +4. `## Layout` +5. `## Elevation & Depth` +6. `## Shapes` +7. `## Components` +8. `## Do's and Don'ts` -Optional evocative subtitles are allowed in the form `## 2. Colors: The [Name] Palette` (Stitch's own outputs do this), but the literal word in each header (Overview, Colors, Typography, Elevation, Components, Do's and Don'ts) must be present. Do NOT add extra top-level sections (Layout Principles, Responsive Behavior, Motion, Agent Prompt Guide). Fold that content into the six spec sections where it naturally belongs. +Omit irrelevant sections rather than filling them with invented rules. Put responsive layout in Layout, depth in Elevation & Depth, radius and form language in Shapes, and per-component behavior in Components. Unknown sections are preserved by the format, but new visual guidance should use the canonical structure whenever it fits. ## When to run -- 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. +- New-work found a coherent incumbent visual system but no `DESIGN.md`. +- The first implementation of a new world is complete and its provisional decisions need to be carbonized. - An existing `DESIGN.md` is stale (the design has drifted). - Before a large redesign, to capture the current state as a reference. @@ -71,9 +73,9 @@ 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 init, nothing built yet). Interview for five high-level answers, write a minimal DESIGN.md marked ``. Re-run in scan mode once there's code. +- **Seed mode**: the project is pre-implementation. Ensure PRODUCT.md exists, then reuse new-work's visual-world workshop and write its directional DESIGN.md 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. +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` requests new-work's world workshop, but it does not authorize replacing coherent code: when an incumbent system exists, offer scan mode or route an explicit identity-replacement request through new-work. ## Scan mode (approach C: auto-extract, then confirm descriptive language) @@ -97,7 +99,8 @@ Build a structured draft from the discovered tokens. For each token class: - **Typography**: Map observed sizes and weights to the Material hierarchy (display / headline / title / body / label). Note font-family stacks and the scale ratio. - **Elevation**: Catalogue the shadow vocabulary. If the project is flat and uses tonal layering instead, that's a valid answer; state it explicitly. - **Components**: For each common component (button, card, input, chip, list item, tooltip, nav), extract shape (radius), color assignment, hover/focus treatment, internal padding. -- **Spacing + layout**: Fold into Overview or relevant Components. The spec does NOT have a Layout section. +- **Layout + spacing**: Extract grid, container, breakpoint, rhythm, and density behavior into Layout. +- **Shapes**: Extract radius, corner, border, clipping, and recurring form behavior into Shapes. ### Step 2b: Stage the frontmatter @@ -112,19 +115,19 @@ Skip anything the project doesn't have. Empty scale keys or fabricated tokens po ### Step 3: Ask the user for qualitative language -The following require creative input that cannot be auto-extracted. Group them into one `AskUserQuestion` interaction: +The following require creative input that cannot be auto-extracted. Ask them in two structured rounds of no more than three questions each (or the harness's lower limit), waiting between rounds: - **Creative North Star**: a single named metaphor for the whole system ("The Editorial Sanctuary", "The Golden State Curator", "The Lab Notebook"). Offer 2-3 options that honor PRODUCT.md's brand personality. -- **Overview voice**: mood adjectives, aesthetic philosophy in 2-3 sentences, anti-references (what the system should not feel like). +- **Overview voice**: mood adjectives, aesthetic philosophy in 2-3 sentences, and any confirmed visual anti-reference. - **Color character** (for auto-extracted colors): descriptive names ("Deep Muted Teal-Navy", not "blue-800"). Suggest 2-3 options per key color based on hue/saturation. - **Elevation philosophy**: flat/layered/lifted. If shadows exist, is their role ambient or structural? - **Component philosophy**: the feel of buttons, cards, inputs in one phrase ("tactile and confident" vs. "refined and restrained"). -Quote a line from PRODUCT.md when possible so the user sees their own strategic language carry forward. +Carry a line from PRODUCT.md only when it is a durable brand commitment that actually constrains the visual system. Page strategy and surface concepts do not belong here. ### Step 4: Write DESIGN.md -The file opens with the YAML frontmatter staged in Step 2b (schema documented at the top of this reference), then the markdown body using the structure below. Headers must match character-for-character. Optional evocative subtitles (e.g. `## 2. Colors: The Coastal Palette`) are allowed. +The file opens with the YAML frontmatter staged in Step 2b (schema documented at the top of this reference), then the markdown body using the canonical structure below. ```markdown --- @@ -136,13 +139,13 @@ colors: # Design System: [Project Title] -## 1. Overview +## Overview **Creative North Star: "[Named metaphor in quotes]"** -[2-3 paragraph holistic description: personality, density, aesthetic philosophy. Start from the North Star and work outward. State what this system explicitly rejects (pulled from PRODUCT.md's anti-references). End with a short **Key Characteristics:** bullet list.] +[2-3 paragraph holistic description: personality, density, and aesthetic philosophy. Start from the North Star and work outward. State only confirmed visual rejections. End with a short **Key Characteristics:** bullet list.] -## 2. Colors +## Colors [Describe the palette character in one sentence.] @@ -162,7 +165,7 @@ colors: ### Named Rules (optional, powerful) **The [Rule Name] Rule.** [Short, forceful prohibition or doctrine, e.g. "The One Voice Rule. The primary accent is used on ≤10% of any given screen. Its rarity is the point."] -## 3. Typography +## Typography **Display Font:** [Family] (with [fallback]) **Body Font:** [Family] (with [fallback]) @@ -180,7 +183,11 @@ colors: ### Named Rules (optional) **The [Rule Name] Rule.** [Short doctrine about type use.] -## 4. Elevation +## Layout + +[Describe the grid or spatial model, container behavior, density, responsive changes, and the spacing rhythm. Include exact values only when observed.] + +## Elevation & Depth [One paragraph: does this system use shadows, tonal layering, or a hybrid? If "no shadows", say so explicitly and describe how depth is conveyed instead.] @@ -191,7 +198,11 @@ colors: ### Named Rules (optional) **The [Rule Name] Rule.** [e.g. "The Flat-By-Default Rule. Surfaces are flat at rest. Shadows appear only as a response to state (hover, elevation, focus)."] -## 5. Components +## Shapes + +[Describe the form language: corner/radius strategy, borders, clipping, and any recurring silhouette or geometry.] + +## Components For each component, lead with a short character line, then specify shape, color assignment, states, and any distinctive behavior. @@ -223,16 +234,16 @@ For each component, lead with a short character line, then specify shape, color ### [Signature Component] (optional; if the project has a distinctive custom component worth documenting) [Description.] -## 6. Do's and Don'ts +## Do's and Don'ts -Concrete, forceful guardrails. Lead each with "Do" or "Don't". Be specific: include exact colors, pixel values, and named anti-patterns the user mentioned in PRODUCT.md. **Every anti-reference in PRODUCT.md should show up here as a "Don't" with the same language**, so the visual spec carries the strategic line through. Quote PRODUCT.md directly where possible: if PRODUCT.md says *"avoid dark mode with purple gradients, neon accents, glassmorphism"*, the Don'ts here should repeat that by name. +Concrete visual guardrails grounded in the incumbent implementation or the user's chosen world. Lead each with "Do" or "Don't" and include exact values only when established. Do not turn a task-specific concept or surface strategy into a system-wide prohibition. ### Do: - **Do** [specific prescription with exact values / named rule]. - **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** [...] ``` @@ -307,7 +318,7 @@ The `html` and `css` fields must be **self-contained, drop-in snippets** that re Aim for a tight set of **5-10 components** that best represent the visual system: - **Canonical primitives (always include if the project has them):** button (each variant as a separate component entry), input/text field, navigation, chip/tag, card. -- **Signature components (include if distinctive):** hero CTA, featured card, filter pill, a distinctive table or list-row treatment, any custom pattern the user mentioned as important in PRODUCT.md. +- **Signature components (include if distinctive):** the recurring custom patterns that actually define the implemented system. - **Skip the rest.** Utility components, form building blocks, wrapper layouts: not worth documenting unless visually distinctive. If the project has **no component library yet** (bare landing page, new project), synthesize canonical primitives from the tokens using best-practice defaults consistent with the DESIGN.md's rules. Every `.impeccable/design.json` has *something* to render, even on day zero. @@ -338,64 +349,40 @@ Your own write is the freshest source; subsequent commands in this session don't ## Seed mode -For projects with no visual system to extract yet. Produces a minimal scaffold, not a full spec. +For projects with no visual system to extract yet. Produces a user-chosen visual-world scaffold, not a fabricated token spec. -### Step 1: Confirm seed mode +### Step 1: Route through new-work's workshop -Before interviewing: "There's no existing visual system to scan. I'll ask five quick questions to seed a starter DESIGN.md. You can re-run `/impeccable document` once there's code, to capture the real tokens and components. OK?" +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 the user prefers to skip, stop. No file. +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. -### Step 2: Five questions +If new-work already completed the workshop in this session, use its chosen direction directly. Do not ask again. -Group into one `AskUserQuestion` interaction. Options must be concrete. +### Step 2: Write seed DESIGN.md -1. **Color strategy.** Pick one: - - Restrained: tinted neutrals + one accent ≤10% - - Committed: one saturated color carries 30–60% of the surface - - Full palette: 3–4 named color roles, each deliberate - - Drenched: the surface IS the color - - Then: one hue family or anchor reference ("deep teal", "mustard", "Klim #ff4500 orange"). - -2. **Typography direction.** Pick one (specific fonts come later): - - Serif display + sans body - - Single sans (warm / technical / geometric / humanist; pick a feel) - - Display + mono - - Mono-forward - - Editorial script + sans - -3. **Motion energy.** Pick one: - - Restrained: state changes only - - Responsive: feedback + transitions, no choreography - - Choreographed: orchestrated entrances, scroll-driven sequences - -4. **Three named references.** Brands, products, printed objects. Not adjectives. - -5. **One anti-reference.** What it should NOT feel like. Also named. - -### Step 3: Write seed DESIGN.md - -Use the six-section spec from Scan mode. Populate what the interview answers; leave the rest as honest placeholders. The seed is a scaffold, not a fabricated spec. +Use the canonical section order from Scan mode. Populate the selected workshop direction and leave unresolved implementation facts as honest placeholders. The seed commits a world and its invariants; it does not pretend implementation tokens already exist. Lead the file with: ```markdown - + ``` Per-section guidance in seed mode: -- **Overview**: Creative North Star and philosophy phrased from the answers (color strategy + motion energy + references). Reference the user's anti-reference directly. -- **Colors**: Color strategy as a Named Rule (e.g. *"The Drenched Rule. The surface IS the color."*). Hue family or anchor reference. No hex values; mark as `[to be resolved during implementation]`. -- **Typography**: the direction the user picked (e.g. "Serif display + sans body"). No font names yet: `[font pairing to be chosen at implementation]`. -- **Elevation**: inferred from motion energy. Restrained/Responsive → flat by default; Choreographed → layered. One sentence. +- **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. +- **Elevation & Depth**: the selected material and depth behavior, stated as an invariant rather than inferred from a generic preset. +- **Shapes**: the selected form and corner language. - **Components**: omit entirely; no components exist yet. -- **Do's and Don'ts**: carry PRODUCT.md's anti-references directly plus the anti-reference named in Q5. +- **Do's and Don'ts**: record the durable guardrails confirmed during the world choice, not task-local refusals. 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 +### Step 3: 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." @@ -405,15 +392,15 @@ Your own write is the freshest source; no reload needed. ## Style guidelines - **Frontmatter first, prose second.** Tokens go in the YAML frontmatter; prose contextualizes them. Don't redefine a token value in two places; the frontmatter is normative. -- **Cite PRODUCT.md anti-references by name** in the Do's and Don'ts section. If PRODUCT.md lists "SaaS landing-page clichés" or "generic AI tool marketing" as anti-references, the DESIGN.md Don'ts should repeat those phrases verbatim so the visual spec enforces the strategic line. -- **Match the spec, don't invent new sections.** The six section names are fixed. If you have Layout/Motion/Responsive content to document, fold it into Overview (philosophy-level rules) or Components (per-component behavior). +- **Carry only durable product constraints.** A binding logo, identity asset, accessibility need, or brand commitment from PRODUCT.md may constrain DESIGN.md. Surface strategy stays in its surface brief. +- **Match the spec.** Use its eight canonical sections in order and omit any that are irrelevant. Put motion guidance with the world or component it affects rather than creating a token group the schema does not support. - **Descriptive > technical**: "Gently curved edges (8px radius)" > "rounded-lg". Include the technical value in parens, lead with the description. - **Functional > decorative**: for each token, explain WHERE and WHY it's used, not just WHAT it is. - **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 forceful**. The voice of a design director. "Prohibited", "forbidden", "never", "always", not "consider", "might", "prefer". Match PRODUCT.md's tone. -- **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. -- **Reference PRODUCT.md**. The anti-references section of PRODUCT.md should directly inform the Do's and Don'ts section here. Quote or paraphrase. +- **Be decisive where evidence is decisive.** Use hard language for actual invariants and softer language for provisional guidance. +- **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. ## Pitfalls @@ -423,7 +410,7 @@ Your own write is the freshest source; no reload needed. - Don't invent components that don't exist. If the project only has buttons and cards, only document those. - Don't overwrite an existing DESIGN.md without asking. - Don't duplicate content from PRODUCT.md. DESIGN.md is strictly visual. -- Don't add a "Layout Principles" or "Motion" or "Responsive Behavior" top-level section. The spec has six, not nine. Fold that content where it belongs. +- Don't replace canonical sections with near-synonyms. Put layout and responsive behavior in `Layout`; put motion with the affected world or component. - Don't rename sections even slightly. "Colors" not "Color Palette & Roles". "Typography" not "Typography Rules". Tooling parsing depends on exact headers. - Don't duplicate token values between frontmatter and prose. If a color is in `colors.primary` as hex, the prose can name it and describe its role but should not reassert a different hex. The frontmatter is normative. - Don't invent frontmatter token groups outside Stitch's schema (no `motion:`, `breakpoints:`, `shadows:` at the top level). Stitch's Zod schema only accepts `colors`, `typography`, `rounded`, `spacing`, `components`. Anything else belongs in the sidecar's `extensions`. diff --git a/.pi/skills/impeccable/reference/hooks.md b/.pi/skills/impeccable/reference/hooks.md index c6b202c93..2c4f4fbe5 100644 --- a/.pi/skills/impeccable/reference/hooks.md +++ b/.pi/skills/impeccable/reference/hooks.md @@ -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. diff --git a/.pi/skills/impeccable/reference/init.md b/.pi/skills/impeccable/reference/init.md index e3562734f..d3e3a1fbd 100644 --- a/.pi/skills/impeccable/reference/init.md +++ b/.pi/skills/impeccable/reference/init.md @@ -1,224 +1,114 @@ -# Init Flow +# Init flow -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 [DESIGN.md format spec](https://raw.githubusercontent.com/google-labs-code/design.md/main/docs/spec.md). Answers "how it looks". -- **`.impeccable/live/config.json`** (live mode): pre-configured so `/impeccable live` boots straight into variant mode with no first-time detour. - -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. +`init` captures durable product truth in PRODUCT.md. It does not invent a visual world and does not write DESIGN.md; [new-work.md](new-work.md) creates or expands one, and [document.md](document.md) records an incumbent one. Existing runnable web projects may also receive `.impeccable/live/config.json`. ## Step 1: Load current state -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). +Use the PRODUCT.md path resolved by context.mjs. Update it instead of creating a competing authority. In a child app inheriting root context, confirm shared versus app-specific scope before writing. -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. -- **PRODUCT.md exists, DESIGN.md missing**: skip to Step 5 and offer to run `/impeccable document` for DESIGN.md. -- **PRODUCT.md exists but has no `## Register` section (legacy)**: add it. Infer a hypothesis from the codebase (see Step 2), confirm with the user, write the field. -- **PRODUCT.md exists but has no `## Platform` section (legacy)**: add it the same way, but only when the project is native (`ios` / `android` / `adaptive`) or the user wants it explicit; a missing field already means `web`. -- **Both exist**: ask the user directly to clarify what you cannot infer. Ask which file to refresh. Skip the one the user doesn't want changed. -- **Just DESIGN.md exists (unusual)**: do Steps 2-4 to produce PRODUCT.md. +- **No PRODUCT.md:** explore, interview, and write it. +- **PRODUCT.md exists:** ask what product knowledge is stale or missing; do not reopen confirmed fields without a reason. +- **Legacy PRODUCT.md:** add only durable missing facts; absent `## Platform` means `web` unless evidence says otherwise. +- **Only DESIGN.md exists:** leave it untouched and create PRODUCT.md. +- **Redesign/rebrand request:** preserve confirmed product truth unless the user changes it. Visual replacement happens later in new-work, not here. -Never silently overwrite an existing file. Always confirm first. +Never silently overwrite an existing file or offer DESIGN.md during init. If another request invoked init, finish PRODUCT.md and resume it. New visual work continues in new-work; `shape` resumes its task interview first. -If init was invoked as a setup blocker by another command, such as `/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 project -## Step 2: Explore the codebase +Before asking, scan enough to avoid making the user repeat known facts: product docs and copy; package/config and app boundaries; features, workflows, routes, and roles; names, logos, legal/proof assets, and brand commitments; platform/accessibility signals; and the dev command/entry when live mode applies. -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: +Treat repository evidence as a hypothesis, not user approval. Note visual maturity without documenting, extending, or replacing the world. -- **README and docs**: Project purpose, target audience, any stated goals -- **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 -- **Any style guides or brand documentation** +Form a platform hypothesis: `web`, `ios`, `android`, or `adaptive` (one product that genuinely adapts its design language per OS). Mobile web remains `web`; a native wrapper around a website does not make its design language native. -Also form a **register hypothesis** from what you find: +## Step 3: Interview for product truth -- Brand signals: `/`, `/about`, `/pricing`, `/blog/*`, `/docs/*`, hero sections, big typography, scroll-driven sections, landing-page-shaped content. -- Product signals: `/app/*`, `/dashboard`, `/settings`, `/(auth)`, forms, data tables, side/top nav, app-shell components. +ask the user directly to clarify what you cannot infer. Ask only about material gaps the repository and original request do not answer with strong evidence. -Register is a hypothesis at this point, not a decision; Step 3 confirms it. +Use the structured question tool when available; otherwise ask and wait. Keep rounds to at most three focused questions and require one real answer or approval round before writing a new PRODUCT.md. Confirm inferences. -Also form a **platform hypothesis**: +Start with the unknowns that most change future product decisions: -- Native signals: React Native / Expo (`react-native`, `expo`), Flutter (`pubspec.yaml`, `flutter`), SwiftUI / UIKit (`.swift`, `.xcodeproj`, an `ios/` app target), Jetpack Compose / Android (`build.gradle`, an `android/` app module, `AndroidManifest.xml`). An `ios/` and/or `android/` directory that is a real app target, not just a Capacitor/Cordova wrapper around a website. -- Web signals (the default): a web framework (Vite, Next, Nuxt, SvelteKit, Astro), an HTML entry, a CSS/Tailwind setup, no native app target. +1. Who is the primary user, in what situation, and what job are they doing? +2. What does the product make possible, and what is its meaningfully different mechanism or position? +3. What durable constraints, assets, evidence, or product facts must future work preserve? -Values: `web` / `ios` / `android` / `adaptive` (one codebase, ships both, adapts per OS). Mobile web is still `web`. Like register, this is a hypothesis; Step 3 confirms it. +Confirm ambiguous platform separately. Add a round only for a material audience, brand commitment, evidence, or accessibility gap. Record undecided facts instead of inventing them. -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. +Do not ask for an aesthetic direction, emotional feel, visual references, colors, typography, or style during init. If the user volunteers a binding visual constraint, record it without expanding it. -## Step 3: Ask strategic questions (for PRODUCT.md) +### What belongs here -ask the user directly to clarify what you cannot infer. Ask about anything the codebase doesn't answer with strong, explicit evidence. +- users, jobs, workflows, purpose, success, positioning, and operating context; +- capabilities, constraints, terminology, evidence, platform, and accessibility; +- confirmed voice, assets, and brand commitments. -### Interview mode, not confirmation mode +### What does not belong here -If the repo is empty or the user's brief is sparse, run a short interview before proposing PRODUCT.md. Do **not** turn a one-sentence request into a complete inferred PRODUCT.md and ask for blanket confirmation. - -- Use the harness's structured question tool when one exists. Otherwise, ask directly in chat and stop: one question at a time, with lettered options where the crawl suggests likely answers, waiting for each answer before the next. -- Keep skill vocabulary (register, belief ladder, anti-references) out of question text; ask for the thing in words the user would use. For the brand register, ask like a magazine editor profiling the brand: curious and narrative, drawing out the story, the feel, and what a visitor should come to believe. -- Ask in focused rounds and wait for answers between them. Keep **one topic per question**; add rounds rather than fold several topics into one either-or choice. Options obey the same rule: an option answers only the question asked; never write a compound option that bundles a feeling with a business outcome or names an additional audience. -- Use inferred answers as hypotheses or options, not as finished facts. -- Complete at least one real user-answer round before drafting PRODUCT.md, unless every required answer is directly discoverable from repo docs. -- Round 1 should establish register, platform, users, purpose, positioning, and desired outcome. -- Round 2 should establish brand personality or references, anti-references, and accessibility needs, plus conversion & proof for Persuade surfaces. - -### Minimum viable interview - -Ask enough to complete PRODUCT.md. At minimum, cover register confirmation, **platform confirmation** (`web` / `ios` / `android` / `adaptive`), users, purpose, positioning, brand personality, anti-references, and accessibility needs (plus conversion & proof for Persuade surfaces) unless each answer is directly discoverable from repo context. Never let the template's default `web` stand unconfirmed for a native or cross-platform repo. After at least one interview round, you may propose inferred answers, but the user must confirm them before you write PRODUCT.md. Never synthesize PRODUCT.md from the original task prompt alone. - -### Register (ask first; it shapes everything below) - -Every design task serves one of four visitor modes (see SKILL.md's Registers): **Persuade** (marketing, landing, campaigns), **Experience** (portfolios, albums, bodies of work), **Operate** (app UI, admin, dashboards, tools), and **Read** (documentation, editorial, long-form). PRODUCT.md stores the two-value family that covers them: `brand` covers Persuade and Experience, `product` covers Operate and Read. The brief decides the exact mode per task; the stored register only sets the default family. - -If Step 2 produced a clear hypothesis, lead with it: *"From the codebase, this looks like a [brand / product] surface. Does that match your intent, or should we treat it differently?"* - -If the signal is genuinely split (e.g. a product with a big marketing landing), ask the user directly to clarify what you cannot infer. Ask which register describes the **primary** surface. The register can be overridden per task later, but PRODUCT.md carries one default. Settle the default before drafting any register-dependent questions; never batch Persuade-only questions (Conversion & proof) into the same round as the question that decides the register. - -### Platform (ask right after register) - -Every project targets **web** (includes responsive mobile web), **ios**, **android**, or **adaptive** (one codebase, ships both, adapts per OS: Flutter, React Native, KMP). Platform picks the native rulebook: HIG for `ios`, Material 3 for `android`, both for `adaptive`, none for `web`. - -If Step 2 produced a clear hypothesis, lead with it: *"From the codebase, this looks like a [web / ios / android / adaptive] project. Does that match?"* For cross-platform apps, decide by the **design language the app renders**, not the toolchain: one look on both platforms (Flutter's Material-everywhere default) takes that platform's value; genuine per-OS adaptation (Cupertino on iOS, Material on Android) is `adaptive`. When in doubt, `web`. - -A monorepo shipping both a website and a native app gets a PRODUCT.md per app, each with its own `## Platform`; the root PRODUCT.md carries the primary surface's platform. - -### Users & Purpose -- Who uses this? What's their context when using it? -- What job are they trying to get done? -- What is this for? A purpose stated in README or docs is a hypothesis, not strong evidence; confirm it, don't transcribe it. -- What does success look like? -- If more than one kind of user is plausible, confirm a primary and secondary audience; don't manufacture a split that isn't there. An audience implied by another answer (a success metric, a CTA) is still unconfirmed; ask before writing it as secondary. -- If the surface speaks to a different audience than the people who use the product, ask the user to name both. -- For brand: what emotions should the interface evoke? (confidence, delight, calm, urgency) Ask this standalone; don't fold emotions into the success question. -- For product: what workflow are they in? What's the primary task on any given screen? - -### Positioning -- In one line, what does this do that nothing else does? The single strategic claim every screen reinforces. - -### 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? - - 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? - -### Conversion & proof (Persuade surfaces only) - -Ask these only when the primary surface is a Persuade one (marketing, landing, campaigns). Experience and Read surfaces (portfolios, albums, long-form writing, documentation) get no CTA, belief-ladder, or proof questions even when the stored register is `brand`; skip this section and its PRODUCT.md counterpart. - -- What's the primary CTA? -- What's the secondary fallback, for visitors not ready for the primary? -- The one line a visitor should remember after 10 seconds. -- What must the visitor believe, in order, before taking the primary CTA? (The template's belief ladder.) -- What proof is on hand? Ask the user to hand over any testimonials, case studies, press, or client/partner logos they already have. If you can receive files directly, collect them; otherwise create `.impeccable/assets/proof/` and ask the user to add files there. Reference supplied files by path; record text proof inline. - -### Accessibility & Inclusion -- Specific accessibility requirements? (WCAG level, known user needs) -- Considerations for reduced motion, color blindness, or other accommodations? - -Skip questions where the answer is already clear. **Do NOT ask about colors, fonts, radii, or visual styling here.** Those belong in DESIGN.md, not PRODUCT.md. +- visual worlds, palettes, typography, components, or page concepts; +- visitor mode, narrative, CTA/proof sequence, or other surface strategy; +- invented testimonials, customers, benchmarks, pricing, licensing, or deployment claims; +- a requirement to decide every optional field. ## Step 4: Write PRODUCT.md -Write PRODUCT.md only after the user has confirmed the strategic answers from Step 3. If an inferred answer is uncertain or unconfirmed, ask before writing. Confirmed means what the user actually said yes to; do not pad a confirmed answer with extras they never picked (additional anti-references, audiences, roadmap claims, a WCAG level), whether drawn from the crawl, another answer, or your own option text. If an extra belongs in the doc, ask about it first. - -Synthesize into a strategic document: +Write only confirmed facts and explicitly marked open decisions. Omit irrelevant sections rather than filling them with generic prose. ```markdown # Product -## Register - -product - ## Platform web ## Users -[Who they are, their context, the job to be done. Primary audience; a secondary audience or a surface-vs-user split only when they apply.] +[Primary users, their situation, and job. Add other audiences only when confirmed.] ## Product Purpose -[What this product does, why it exists, what success looks like] +[What the product does, why it exists, and what success means.] ## Positioning -[The single strategic claim every screen reinforces. Not a visual rule, not an anti-reference.] +[The product mechanism or claim a neighboring product could not truthfully copy.] -## Conversion & proof -[Persuade surfaces only (marketing, landing, campaigns). Omit this section entirely, heading included, for the product register and for Experience or Read surfaces such as portfolios, albums, or long-form writing.] -- Primary and secondary CTA: [...] -- The line a visitor remembers after 10 seconds: [...] -- Belief ladder: [...] -- Proof on hand: [testimonials, case studies, press, or logos, referenced by path] +## Operating Context +[Workflows, environments, tools, documents, materials, and rituals that are factual parts of using or evaluating the product.] -## Brand Personality -[Voice, tone, 3-word personality, emotional goals] +## Capabilities and Constraints +[Confirmed functionality, technical constraints, terminology, and explicitly undecided product facts.] -## Anti-references -[What this should NOT look like. Specific bad-example sites or patterns to avoid.] +## Brand Commitments +[Existing name, voice, assets, personality, identity constraints, and references the user explicitly made binding. Omit when none exist.] -## Design Principles -[3-5 strategic principles derived from the conversation. Principles like "practice what you preach", "show, don't tell", "expert confidence". NOT visual rules like "use OKLCH" or "magenta accent".] +## Evidence on Hand +[Real content, data, demonstrations, testimonials, case studies, press, or assets, with paths where applicable. State absences that future work must not fabricate.] + +## Product Principles +[Three to five durable strategic principles derived from confirmed answers; no visual recipes.] ## Accessibility & Inclusion -[WCAG level, known user needs, considerations] +[Known user needs or required standard. Omit when no product-specific requirement was established.] ``` -Register is either `brand` or `product` as a bare value. No prose, no commentary. Platform is `web`, `ios`, `android`, or `adaptive`, also a bare value; omit the section only on legacy files you're leaving untouched, otherwise write `web` explicitly. +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. -Write fields as prose, and use bold sparingly: only where a word carries a decision, never as a label lead-in on every line. +### Completion gate -Write to `PROJECT_ROOT/PRODUCT.md`. If `.impeccable.md` existed, the loader already renamed it; merge into that content rather than starting from scratch. +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: Decide on DESIGN.md +## Step 5: Configure live mode when useful -Offer `/impeccable document` either way. Two paths: +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. -- **Code exists** (CSS tokens, components, a running site): "I can generate a DESIGN.md that captures your visual system (colors, typography, components) so variants stay on-brand. Want to do that now?" -- **Pre-implementation** (empty project): "I can seed a starter DESIGN.md from five quick questions about color strategy, type direction, motion energy, and references. You can re-run once there's code, to capture the real tokens. Want to do that now?" +## Step 6: Wrap up or resume -If the user agrees, delegate to `/impeccable document` (it auto-detects scan vs seed). Load its reference and follow that flow. +Summarize captured and deliberately undecided facts. Do not offer DESIGN.md merely because it is missing. -If the user prefers to skip, mention they can run `/impeccable document` any time later. +Recommend the next action from the actual project state: -## Step 6: Configure live mode (when code exists) +- Empty or early project: ask naturally for the surface to be built, or use `/impeccable shape ` when the user wants a confirmed brief without implementation. New-work will establish a visual world only when the requested work needs one. +- Existing coherent interface without DESIGN.md: `/impeccable document` if the user wants the incumbent system recorded independently of a new build. +- Existing surface needing work: name the most relevant scoped command. +- Web project ready for visual iteration: `/impeccable live` when configured. -**Skip this step when the platform is native** (`ios` / `android` / `adaptive`): live mode drives a browser overlay. A hybrid wrapper or Expo web target serving HTML doesn't change that. - -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 .pi/skills/impeccable/scripts/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) and platform captured (web / ios / android / adaptive) -- 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 or live config is pending, one line on how to set it up later - -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 platform**, offer the 2-4 most relevant (not a menu dump), and give the exact command to type. Group by intent: - -- **Build something new**: `/impeccable craft ` (shape, then build end-to-end) or `/impeccable shape ` (plan first). Lead with this for empty or early-stage projects. -- **Improve what's there**: name the specific surface. `/impeccable critique ` for a scored UX review; `/impeccable audit ` for a11y / perf / responsive checks; `/impeccable polish ` 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** (web only): `/impeccable live` (configured in Step 6) to pick elements in the browser and generate variants in place. **Skip this group for native platforms.** - -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 the user directly to clarify what you cannot infer. Ask whether they'd like a brief summary of PRODUCT.md appended to AGENTS.md for easier agent reference. If yes, append a short **Design Context** pointer section there. +If init was invoked by another request, resume without rerunning context.mjs; new-work owns later visual decisions. diff --git a/.pi/skills/impeccable/reference/ios.md b/.pi/skills/impeccable/reference/ios.md index ef10ee0d2..ccef5d2c4 100644 --- a/.pi/skills/impeccable/reference/ios.md +++ b/.pi/skills/impeccable/reference/ios.md @@ -2,7 +2,7 @@ For native iOS / iPadOS apps: SwiftUI, UIKit, React Native, Expo, Flutter shipping to Apple hardware. -On native, register narrows. HIG conformance governs structure, navigation, and interaction whatever the register; brand expresses through the expressive layer the platform provides (tint, type, motion, content). Calm, Duolingo, and Spotify carry strong identity entirely inside HIG conventions. +On native, the visitor mode narrows what expression may override. HIG conformance governs structure, navigation, and interaction in every mode; brand expresses through the layer the platform leaves open (tint, type, motion, content). ## The iOS slop test diff --git a/.pi/skills/impeccable/reference/layout.md b/.pi/skills/impeccable/reference/layout.md index 590a57c90..bb8eeab07 100644 --- a/.pi/skills/impeccable/reference/layout.md +++ b/.pi/skills/impeccable/reference/layout.md @@ -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. --- -## Register +## 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, register, 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 .pi/skills/impeccable/scripts/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 `/impeccable polish` for the final pass. +When the structure holds, hand off to `/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) * )`: 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. diff --git a/.pi/skills/impeccable/reference/live.md b/.pi/skills/impeccable/reference/live.md index e8e3e1921..191159766 100644 --- a/.pi/skills/impeccable/reference/live.md +++ b/.pi/skills/impeccable/reference/live.md @@ -33,7 +33,7 @@ Chat is overhead. No recap, no tutorial output, no pasting PRODUCT / DESIGN bodi node .pi/skills/impeccable/scripts/live.mjs ``` -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. +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, DESIGN.md, and any surface brief already loaded by Setup in mind for variant generation: **DESIGN.md wins on visual decisions; PRODUCT.md wins on durable product and voice decisions; the surface brief wins on this surface's strategy.** 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 requires the user's explicit redesign/replacement intent. `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). @@ -187,7 +187,7 @@ All three carry `fallback: "agent-driven"`. Follow **Handle fallback** below. ### 3. Load the action's reference -If `event.action` is `impeccable` (the default freeform action), use SKILL.md's shared laws plus the loaded register reference (`brand.md` or `product.md`). Do not load a sub-command reference. **Freeform is not a pass to skip parameters:** you still follow the composition budget and the freeform bias in **§7 Parameters** below. Sub-command files list MUST-have signature knobs; freeform has no such file, so sizing knobs from surface weight and primary axes is entirely on you. +If `event.action` is `impeccable` (the default freeform action), use SKILL.md's shared laws and decide the visitor mode from the selected surface. Do not load a sub-command reference. **Freeform is not a pass to skip parameters:** you still follow the composition budget and the freeform bias in **§7 Parameters** below. Sub-command files list MUST-have signature knobs; freeform has no such file, so sizing knobs from surface weight and primary axes is entirely on you. Any other `event.action` (`bolder`, `quieter`, `distill`, `polish`, `typeset`, `colorize`, `layout`, `adapt`, `animate`, `delight`, `overdrive`): Read `reference/.md` before planning. Each sub-command encodes a specific discipline; skipping its reference produces generic output. Those files may require specific params; layer them on top of the §7 budget, not instead of it. @@ -216,7 +216,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. @@ -224,10 +224,7 @@ This sentence is the **identity lock**. Every variant must be readable as the sa **Default mode**: the existing identity is preserved. Variants vary expression axes within it. *This is the right mode for ~90% of live sessions.* The user picked an element on a real product they're shipping; they expect variants of *their* hero, not three different brands' heroes. -**Departure mode**: the existing identity is rejected. Variants propose alternatives consistent with PRODUCT.md voice. Trigger only when at least one is true: - -- PRODUCT.md anti-references explicitly call out the current surface ("the current `index.html` is itself an example"; "diffuse away from this"; "the page on screen is the failure"). Generic anti-references that describe what to avoid in general do **not** trigger departure mode; only ones that point at *this* surface specifically. -- The user's freeform prompt explicitly asks for departure ("rebuild this from scratch", "what if it weren't editorial at all", "show me something completely different"). +**Departure mode**: the existing identity is rejected. Variants propose alternatives consistent with durable product and brand truth. Trigger only when the user explicitly asks for departure in the current request or freeform prompt ("redesign this", "rebuild this from scratch", "what if it weren't editorial at all", "show me something completely different"). A stale page critique or an old task note is not replacement authorization. If you're unsure, you're in default mode. The cost of being wrong about default is "three on-brand variants with similar feel": recoverable, the user picks none. The cost of being wrong about departure is "three off-brand variants": unrecoverable, the user is annoyed. @@ -246,13 +243,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 the brand's stated voice and register in PRODUCT.md. Do NOT pick from a fixed catalog of lane categories. The right three directions for this brand are not the same as the right three for another brand, 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. @@ -262,7 +259,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. @@ -289,7 +286,7 @@ In **default mode**, the prompt narrows the axes you choose, not the identity. * In **departure mode**, the prompt narrows the lanes you draw from, not the families. *"Make it feel like a newspaper front page"* would itself be a departure-mode prompt; honor it but pick three meaningfully different newspaper-adjacent lanes (broadsheet vs. tabloid vs. trade journal), and run the family pass to confirm they don't collapse into one. -When the prompt and PRODUCT.md anti-references conflict (the prompt asks for X, the anti-references ban X), the anti-references win; they describe the brand's standing position, the prompt is one moment. +When the prompt conflicts with a confirmed binding brand commitment or DESIGN.md invariant, preserve the invariant unless the user explicitly revokes or replaces it. Task-local strategy from the matching surface brief may change when the user changes that surface's goal. ### 6. Write all variants in a single edit diff --git a/.pi/skills/impeccable/reference/new-work.md b/.pi/skills/impeccable/reference/new-work.md index 4b58a70a0..3a56bd2fb 100644 --- a/.pi/skills/impeccable/reference/new-work.md +++ b/.pi/skills/impeccable/reference/new-work.md @@ -1,47 +1,178 @@ -# New identity work +# New visual work -Read this only when the project has no committed visual identity, or the user explicitly asked to replace the current one. A missing DESIGN.md is not enough: code, tokens, chosen type, components, and assets are incumbent design authority. If they exist, preserve and extend them unless the brief says to discard them. +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. -This playbook is task-scoped. PRODUCT.md owns durable product facts and DESIGN.md owns durable visual invariants; the direction for one page or feature belongs in the current task, not in either document. +## 1. Name the intent -## Choose the authority +- **Greenfield:** no coherent visual implementation. +- **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. -Classify the work before inventing anything: +A plain “redesign this page/site” authorizes replacement. “Redesign this within the current brand/system” means extension or refinement. Ask once only when the wording is genuinely ambiguous. -- **Extend**: an incumbent system exists and the task adds or improves a surface. Preserve its lineage, semantics, and affordances. -- **Surface redesign**: the current identity remains, but this surface may change its topology, hierarchy, and expression. -- **New identity**: nothing committed exists, or the user explicitly authorized a rebrand. +## 2. Resolve visual authority -When the evidence is mixed, preserve. Novelty is not permission to erase authored decisions. +Read DESIGN.md and representative code, tokens, components, and assets. Choose one path: -## Find the concept +### A. Explicit redesign -Name the subject, audience, page job, and visitor mode from SKILL.md. Then identify the product's unique mechanism: what it does, proves, or enables that a neighboring product cannot truthfully claim. +The old DESIGN.md and implementation are not authority. Keep only unrevoked product facts, content, function, native expectations, constraints, and brand commitments. Establish a replacement world. -Derive the concept from that mechanism through one primary transformation: information topology, evidence, temporal behavior, or spatial mapping. The transformation must change how the surface is organized or behaves, not merely how it is decorated. This produces stranger and more defensible work than imitating a culturally specific document or object. Borrowing a literal cultural form is allowed only when it is genuinely native to the product and audience, and never as costume. +### B. DESIGN.md covers this kind of surface -For **Operate** and **Read**, start with the task and information structure. Native controls remain native controls; expression comes from hierarchy, density, rhythm, state, and the system around them. For **Persuade** and **Experience** creating a new identity, explore more freely, but keep the product legible and the primary action obvious. +Use its invariants and normative tokens. Skip world-building and discover the surface. -Run `node .pi/skills/impeccable/scripts/concept-seed.mjs` only for Persuade or Experience new-identity work when two or more directions remain equally credible. It is an entropy source, not an authority: never let the draw overrule the strongest fit. When color is genuinely unconstrained, `node .pi/skills/impeccable/scripts/palette.mjs` may break a reflex palette; subject evidence and incumbent color still win. +### C. A coherent implementation exists but DESIGN.md does not -## State the direction +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. -Before code, write a task-scoped direction contract of at most 120 words in your reasoning: +### D. The brand exists, but a whole-surface family is unresolved -- the thesis this surface owns; -- the structural or behavioral transformation; -- what authority is preserved; -- the first viewport's composition and primary action; -- the signature element the surface will be remembered by. +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. -Do not embed this contract as a comment in the production artifact or turn it into a second design system. In an attended substantial build, confirm it once. In an unattended run, record it and continue. +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. -## Build once, commit fully +### E. No confirmed visual authority exists -Plan one compact token system and one layout source: the concept or the content's real structure. Build the strongest coherent direction once. Commitment means the transformation governs the whole surface; it does not mean every control must be rebuilt as a metaphor. +Establish a world. Scaffolds, framework defaults, and stray utilities are not identity. -Make the first viewport demonstrate the product's mechanism. Pace long surfaces through contrast in density and scale, and cut sections that only repeat claims. Briefs that depend on imagery ship real, verified imagery. Preserve semantic HTML, familiar interaction behavior, accessibility, and the project's technical conventions. +## 3. Discover the requested surface -## Finish proportionally +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. -Inspect desktop and mobile, write one honest critique against the brief and direction, patch material defects, then run the detector once. Repeat only when a real defect remains. A separate reviewer is optional when the harness already provides one and the extra cost is earned by the risk; it is not a default step and its presence is not evidence of quality. +Ask one attended round of at most three material questions without repeating durable facts. CTA hierarchy, proof sequence, content gaps, and interaction outcomes belong here, not PRODUCT.md. For a fully specified narrow request, state the interpretation and invite correction. + +When `shape` has already completed its discovery interview, reuse those confirmed answers and do not ask this round again. + +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. Each candidate joins a durable visual system (identity thesis, information/layout grammar, material and type behavior, color strategy, imagery, motion, reusable signature) to a concrete expression for this surface (topology, sequence, focal moment, and implementation consequence). Do not rank yet. +3. **Break the ranking rut once.** Run `node .pi/skills/impeccable/scripts/concept-seed.mjs --scope direction`. Promote the assigned grounded pair and weigh its challengers only when they can become both a coherent system and a strong solution to this task. +4. **Test coupling and breadth.** Reject a pair if its surface could swap into another candidate unchanged, or if its world is merely the surface motif repeated. Test each survivor on the requested surface plus navigation, quiet and dense content, interaction/state, and one unlike future surface. Compare it with both the category habit and predictable contrarian response. +5. **Offer coupled choices.** Present two or three materially different pairs without recommendation cues. For each, show the world rules, this surface's 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. 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 .pi/skills/impeccable/scripts/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 promoted candidate when a roll ran; otherwise use the strongest grounded structure. It must survive both tests. + +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). + +### Write or update DESIGN.md + +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: + +`` + +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 .pi/skills/impeccable/scripts/surface-brief.mjs read ` + +Exit 0: preserve still-valid decisions and change only what the user changed. For redesign, retain valid product strategy, content, function, and open decisions; replace visual direction and contract. Exit 2: no brief. After the contract, write with `node .pi/skills/impeccable/scripts/surface-brief.mjs write [related-target ...]`. + +The body is concise and contains: + +```markdown +# Surface brief: [name] + +## Scope +[Primary/related route or artifact, visitor mode, and what this surface owns.] + +## Product strategy +[Surface-specific audience and job, desired outcome, primary/secondary action or task, content and proof sequence, factual constraints, and explicitly open decisions.] + +## Selected direction +[Reference to the applicable DESIGN.md world or expression range, selected surface concept, focal moment, narrative/interaction sequence, and implementation consequence.] + +## Direction contract +[The six contract blocks below.] + +## Open decisions +[Only unresolved items that later work must not silently invent. Omit when empty.] +``` + +Commit `.impeccable/surfaces/.md` as stable later-work context. Exclude global truth, exact tokens, transient notes, and work logs. + +## 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. + +Before code, write a direction contract of at most 150 words in an opening HTML or framework comment. The first 200 characters must name `DIRECTION CONTRACT`. + +- `UNIQUE`: the task thesis tied to the product mechanism; +- `NOT-TEMPLATE`: the category-default arrangement this structure refuses; +- `OWN-WORLD`: the confirmed DESIGN.md invariants, tokens, and materials it uses; +- `STORY`: what the visitor understands, believes, and does; +- `FIRST VIEWPORT`: exact composition, hierarchy, action, and where the concept exceeds competent convention; +- `FORM`: chosen structure or behavior, signature, implementation consequence, and seed key. + +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. + +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. + +## 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. + +Build the strongest coherent direction once. Its grammar governs navigation, actions, controls, content, and transitions without disguising affordances. Give the focal form the scale that gives it force; do not trap it inside a standard hero panel. + +**Make the opening a thesis.** Demonstrate the mechanism immediately; leave an idea, interaction, or evidence, not merely mood. + +**Commit before correcting.** Land the hard move at full strength before refining it. In unattended work, safety is the known risk. + +**Commit at page scale.** Let color, material, image, or type own a region when the world calls for it. Scattered signature decoration is not commitment. + +**Pace the whole surface.** Vary density, scale, image, motion, and quiet inside one grammar. Cut repeated claims; prove the mechanism with real artifact, interaction, data, transformation, or content. + +**Author motion as material.** Motion expresses the world and task. Premium moments go beyond repeated transform/opacity through earned focus, depth, masks, continuity, light, or material change. Bound expensive effects, test in-browser, keep content visible by default, and design reduced motion. + +Preserve semantics, affordances, accessibility, performance, responsiveness, and project conventions. Operate/Read express through topology, hierarchy, density, rhythm, and state; Persuade/Experience may earn drama. + +## 8. Solidify the visual record + +After first implementation of a new/replacement world or approved expansion, refresh DESIGN.md from the build: + +- replace provisional direction with the exact type, color roles, tokens, spacing/radii, components, states, and motion that survived; +- add normative YAML tokens only for values the implementation actually uses; +- remove the directional-seed status once the record and implementation agree; +- preserve broader world invariants and expression ranges; +- do not promote the task's story, hero composition, or one-off motif into a global rule unless it is intentionally reusable. + +Ordinary extension does not rewrite DESIGN.md; only approved durable changes do. + +## 9. Finish like a 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. diff --git a/.pi/skills/impeccable/reference/operate.md b/.pi/skills/impeccable/reference/operate.md index 04ee9d7d2..f420cbaa2 100644 --- a/.pi/skills/impeccable/reference/operate.md +++ b/.pi/skills/impeccable/reference/operate.md @@ -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. - No orchestrated page-load sequences. Product loads into a task; users don't want to watch it load. -## Product bans (on top of the shared absolute bans) +## Product constraints - Decorative motion that doesn't convey state. - Inconsistent component vocabulary across screens. If the "save" button looks different in two places, one is wrong. @@ -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. diff --git a/.pi/skills/impeccable/reference/polish.md b/.pi/skills/impeccable/reference/polish.md index 11b5ad91e..b0ca7e753 100644 --- a/.pi/skills/impeccable/reference/polish.md +++ b/.pi/skills/impeccable/reference/polish.md @@ -1,241 +1,98 @@ -> **Additional context needed**: quality bar (MVP vs flagship). +> **Additional context needed**: quality bar and shipping constraints. -Perform a meticulous final pass to catch all the small details that separate good work from great work. The difference between shipped and polished. +Polish is refinement, never concealed redesign. Preserve the incumbent visual world, content, behavior, and everything outside scope. If the concept itself is wrong, say so and recommend redesign or `bolder` instead of smuggling in a replacement. -Detector and automated QA output are defect evidence only. A clean script result is never proof that the design is strong; gather browser evidence and inspect the real interaction path. +A detector result is defect evidence, not proof of quality. Inspect the rendered experience and real interaction path. -## Design System Discovery +## 1. Establish the system -Aligning the feature to the design system is **not optional**. Polish without alignment is decoration on top of drift, and it makes the next person's job harder. Discovery comes before any other polish work. +Read DESIGN.md and representative tokens, shared components, patterns, and neighboring flows. If no formal system exists, use coherent project conventions. -1. **Find the design system**: Search for design system documentation, component libraries, style guides, or token definitions. Study the core patterns: design principles, target audience, color tokens, spacing scale, typography styles, component API, motion conventions. -2. **Note the conventions**: How are shared components imported? What spacing scale is used? Which colors come from tokens vs hard-coded values? What motion and interaction patterns are established? What flow shapes are used for comparable actions (modal vs full-page, inline vs route, save-on-blur vs explicit submit)? -3. **Identify drift, then name the root cause**: For every deviation, classify it as a **missing token** (the value should exist in the system but doesn't), a **one-off implementation** (a shared component already exists but wasn't used), or a **conceptual misalignment** (the feature's flow, IA, or hierarchy doesn't match neighboring features). The fix differs by category: patch the value, swap to the shared component, or rework the flow. Fixing the symptom without naming the cause is how drift compounds. +Classify each drift before fixing it: -If a design system exists, polish **must** align the feature with it. If none exists, polish against the conventions visible in the codebase. **If anything about the system is ambiguous, ask. Never guess at design system principles.** +- **missing token:** the system needs a reusable value; +- **one-off implementation:** an existing shared component or pattern should replace it; +- **conceptual mismatch:** the flow, information architecture, or hierarchy differs from comparable product areas; +- **local defect:** the implementation is simply incomplete or inconsistent. -## Pre-Polish Assessment +Fix the cause at the narrowest correct level. Ask when a binding system principle cannot be inferred. -Understand the current state and goals before touching anything: +## 2. Gather the evidence -1. **Review completeness**: - - Is it functionally complete? - - Are there known issues to preserve (mark with TODOs)? - - What's the quality bar? (MVP vs flagship feature?) - - When does it ship? (How much time for polish?) +Use the feature yourself at representative desktop and mobile sizes. Determine: -2. **Think experience-first**: Who actually uses this, and what's the best possible experience for them? Effective design beats decorative polish; a feature that looks beautiful but fights the user's flow is not polished. Walk the path from their perspective before opening DevTools. +- whether the path is functionally complete; +- the intended quality bar and time available; +- known constraints or deliberately unfinished work; +- the states, content lengths, roles, and input methods users will actually encounter. -3. **Identify polish areas**: - - Visual inconsistencies - - Spacing and alignment issues - - Interaction state gaps - - Copy inconsistencies - - Edge cases and error states - - Loading and transition smoothness - - Information architecture and flow drift (does this feature reveal complexity the way neighboring features do?) +If a prior critique exists, use it as one input: -4. **Pull in any prior critique** (optional signal): If `/impeccable critique` has been run on the same target, its priority issues are a useful prior for what to address first. Resolve the target to a file path or URL, then: - ```bash - slug=$(node .pi/skills/impeccable/scripts/critique-storage.mjs slug "") - node .pi/skills/impeccable/scripts/critique-storage.mjs latest "$slug" - ``` - Exit 0 with body = found; fold the P0/P1 items into your polish list and mention the snapshot path so the user sees what you read. Exit 2 = no snapshot, continue without it. The critique is one input among many. Do your own pass either way. +```bash +slug=$(node .pi/skills/impeccable/scripts/critique-storage.mjs slug "") +node .pi/skills/impeccable/scripts/critique-storage.mjs latest "$slug" +``` -5. **Triage cosmetic vs functional**: Classify each issue as **cosmetic** (looks off, doesn't impede the user) or **functional** (breaks, blocks, or confuses the experience). When polish time is tight, functional issues ship first; cosmetic ones can land in a follow-up. Quality should be consistent; never perfect one corner while leaving another rough. +Exit 0 returns the latest snapshot; incorporate relevant P0/P1 findings and name the snapshot read. Exit 2 means none exists. Perform an independent pass either way. -**CRITICAL**: Polish is the last step, not the first. Don't polish work that's not functionally complete. +## 3. Triage -## Polish Systematically +Separate functional defects from cosmetic ones and fix in this order: -Work through these dimensions methodically: +1. broken or blocked tasks, data loss, misleading state, and inaccessible paths; +2. missing loading, empty, error, success, disabled, and permission states; +3. flow, hierarchy, responsive, and design-system drift; +4. visual and motion inconsistencies; +5. code and asset cleanup. -### Visual Alignment & Spacing +Do not perfect one corner while leaving the rest below the same quality bar. -- **Pixel-perfect alignment**: Everything lines up to grid -- **Consistent spacing**: All gaps use spacing scale (no random 13px gaps) -- **Optical alignment**: Adjust for visual weight (icons may need offset for optical centering) -- **Responsive consistency**: Spacing and alignment work at all breakpoints -- **Grid adherence**: Elements snap to baseline grid +## 4. Polish the whole path -**Check**: -- Enable grid overlay and verify alignment -- Check spacing with browser inspector -- Test at multiple viewport sizes -- Look for elements that "feel" off +### Flow and hierarchy -### Information Architecture & Flow +- Match neighboring mental models, terminology, disclosure, routing, save behavior, and optimistic or pessimistic patterns. +- Make the primary task and current state obvious without flattening every element to equal weight. +- Ensure arrival, transition, empty, and recovery paths connect instead of behaving as isolated screens. -Visual polish on a misshapen flow is wasted work. Match the *shape* of the experience to the system, not just the surface. +### Layout and type -- **Progressive disclosure**: Match how much is revealed when, compared to neighboring features. A settings page exposing 40 fields when the rest of the app reveals 5 at a time is drift, even if every field is perfectly styled. -- **Established user flows**: Multi-step actions follow the same shape as comparable flows elsewhere: modal vs full-page, inline edit vs separate route, save-on-blur vs explicit submit, optimistic vs pessimistic updates. -- **Hierarchy & complexity**: The same conceptual weight gets the same visual weight throughout. Primary actions don't become tertiary in one corner of the product, and tertiary actions don't shout. -- **Empty, loading, and arrival transitions**: How content arrives, updates, and leaves matches how it does in adjacent features. -- **Naming and mental model**: The feature uses the same nouns and verbs as the rest of the system. A "Workspace" here shouldn't be a "Project" three screens away. +- Align to the project's grid and spacing scale; fix optical as well as mathematical alignment. +- Group related content tightly and separate distinct groups generously. +- Keep same-role typography consistent; test measure, wrapping, localization expansion, zoom, and font loading. +- Verify every supported viewport rather than correcting only the current screenshot. -### Typography Refinement +### Color, imagery, and icons -- **Hierarchy consistency**: Same elements use same sizes/weights throughout -- **Line length**: 45-75 characters for body text -- **Line height**: Appropriate for font size and context -- **Widows & orphans**: No single words on last line -- **Hyphenation**: Appropriate for language and column width -- **Kerning**: Adjust letter spacing where needed (especially headlines) -- **Font loading**: No FOUT/FOIT flashes +- Use semantic tokens and stable color meanings across themes. +- Verify text, control, and focus contrast in every state. +- Keep icon families, stroke/weight, sizing, and optical alignment coherent. +- Prevent image layout shift; use correct aspect ratios, responsive sources, and useful alt text. -### Color & Contrast +### Interaction and state -- **Contrast ratios**: All text meets WCAG standards -- **Consistent token usage**: No hard-coded colors, all use design tokens -- **Theme consistency**: Works in all theme variants -- **Color meaning**: Same colors mean same things throughout -- **Accessible focus**: Focus indicators visible with sufficient contrast -- **Gray on color**: Never put gray text on colored backgrounds; use a shade of that color or transparency +- Every control needs appropriate default, hover, focus, active, disabled, loading, error, and success behavior. +- Preserve visible keyboard focus, logical tab order, labels, and platform-appropriate touch targets. +- Keep motion coherent, interruptible, performant, and reduced-motion aware. Do not add animation merely to make polish visible. +- Validate long, missing, localized, offline, slow, and permission-limited content where the product can encounter it. -### Interaction States +### Content and code -Every interactive element needs all states: +- Keep terminology, capitalization, punctuation, and factual copy consistent. Ask before changing claims. +- Remove debug output, dead code, unused imports, obsolete styles, and polish-created duplication. +- Replace custom implementations with shared components where the system owns the pattern. +- Promote genuinely reusable values to tokens; do not create a system abstraction for one local exception. -- **Default**: Resting state -- **Hover**: Subtle feedback (color, scale, shadow) -- **Focus**: Keyboard focus indicator (never remove without replacement) -- **Active**: Click/tap feedback -- **Disabled**: Clearly non-interactive -- **Loading**: Async action feedback -- **Error**: Validation or error state -- **Success**: Successful completion +## 5. Verify and finish -**Missing states create confusion and broken experiences**. +Walk the complete path again with mouse, keyboard, and touch where applicable. Check: -### Micro-interactions & Transitions +- mobile, intermediate, and wide layouts; +- loading, empty, error, success, disabled, long-content, and missing-content states; +- zoom, contrast, focus, semantics, screen-reader names, and reduced motion; +- console errors, layout shift, interaction latency, image loading, and supported browsers; +- agreement with DESIGN.md, neighboring features, and the user's scope. -- **Smooth transitions**: All state changes animated appropriately (150-300ms) -- **Consistent easing**: Use ease-out-quart/quint/expo for natural deceleration. Never bounce or elastic; they feel dated. -- **No jank**: Smooth animations; use atmospheric blur/filter/mask/shadow effects when they add polish, but bound expensive paint areas and avoid casual layout-property animation -- **Appropriate motion**: Motion serves purpose, not decoration -- **Reduced motion**: Respects `prefers-reduced-motion` +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. -### Content & Copy - -- **Consistent terminology**: Same things called same names throughout -- **Consistent capitalization**: Title Case vs Sentence case applied consistently -- **Grammar & spelling**: No typos -- **Appropriate length**: Not too wordy, not too terse -- **Punctuation consistency**: Periods on sentences, not on labels (unless all labels have them) - -### Icons & Images - -- **Consistent style**: All icons from same family or matching style -- **Appropriate sizing**: Icons sized consistently for context -- **Proper alignment**: Icons align with adjacent text optically -- **Alt text**: All images have descriptive alt text -- **Loading states**: Images don't cause layout shift, proper aspect ratios -- **Retina support**: 2x assets for high-DPI screens - -### Forms & Inputs - -- **Label consistency**: All inputs properly labeled -- **Required indicators**: Clear and consistent -- **Error messages**: Helpful and consistent -- **Tab order**: Logical keyboard navigation -- **Auto-focus**: Appropriate (don't overuse) -- **Validation timing**: Consistent (on blur vs on submit) - -### Edge Cases & Error States - -- **Loading states**: All async actions have loading feedback -- **Empty states**: Helpful empty states, not just blank space -- **Error states**: Clear error messages with recovery paths -- **Success states**: Confirmation of successful actions -- **Long content**: Handles very long names, descriptions, etc. -- **No content**: Handles missing data gracefully -- **Offline**: Appropriate offline handling (if applicable) - -### Responsiveness - -- **All breakpoints**: Test mobile, tablet, desktop -- **Touch targets**: 44x44px minimum on touch devices -- **Readable text**: No text smaller than 14px on mobile -- **No horizontal scroll**: Content fits viewport -- **Appropriate reflow**: Content adapts logically - -### Performance - -- **Fast initial load**: Optimize critical path -- **No layout shift**: Elements don't jump after load (CLS) -- **Smooth interactions**: No lag or jank -- **Optimized images**: Appropriate formats and sizes -- **Lazy loading**: Off-screen content loads lazily - -### Code Quality - -- **Remove console logs**: No debug logging in production -- **Remove commented code**: Clean up dead code -- **Remove unused imports**: Clean up unused dependencies -- **Consistent naming**: Variables and functions follow conventions -- **Type safety**: No TypeScript `any` or ignored errors -- **Accessibility**: Proper ARIA labels and semantic HTML - -## Polish Checklist - -Go through systematically: - -- [ ] Aligned to the design system (drift named and resolved by root cause) -- [ ] Information architecture and flow shape match neighboring features -- [ ] Visual alignment perfect at all breakpoints -- [ ] Spacing uses design tokens consistently -- [ ] Typography hierarchy consistent -- [ ] All interactive states implemented -- [ ] All transitions smooth (60fps) -- [ ] Copy is consistent and polished -- [ ] Icons are consistent and properly sized -- [ ] All forms properly labeled and validated -- [ ] Error states are helpful -- [ ] Loading states are clear -- [ ] Empty states are welcoming -- [ ] Touch targets are 44x44px minimum -- [ ] Contrast ratios meet WCAG AA -- [ ] Keyboard navigation works -- [ ] Focus indicators visible -- [ ] No console errors or warnings -- [ ] No layout shift on load -- [ ] Works in all supported browsers -- [ ] Respects reduced motion preference -- [ ] Code is clean (no TODOs, console.logs, commented code) - -**IMPORTANT**: Polish is about details. Zoom in. Squint at it. Use it yourself. The little things add up. - -Sweat the details. Zoom in until the alignment is right and the spacing reads as deliberate. Then ship. - -**NEVER**: -- Polish before it's functionally complete -- Polish without aligning to the design system; that's decoration on drift -- Guess at design system principles instead of asking when something is ambiguous -- Spend hours on polish if it ships in 30 minutes (triage) -- Introduce bugs while polishing (test thoroughly) -- Ignore systematic issues (if spacing is off everywhere, fix the system, not just one screen) -- Perfect one thing while leaving others rough (consistent quality level) -- Create new one-off components when design system equivalents exist -- Hard-code values that should use design tokens -- Introduce new patterns or flows that diverge from established ones - -## Final Verification - -Before marking as done: - -- **Use it yourself**: Actually interact with the feature. -- **Test on real devices**: Not just browser DevTools. -- **Ask someone else to review**: Fresh eyes catch things. -- **Compare to design**: Match intended design. -- **Check all states**: Don't just test happy path. -- **Treat automation carefully**: Run detector or QA commands when they are available and relevant, fix their defects, but never cite a clean result as proof that the work is polished. - -## Clean Up - -After polishing, ensure code quality: - -- **Replace custom implementations**: If the design system provides a component you reimplemented, switch to the shared version. -- **Remove orphaned code**: Delete unused styles, components, or files made obsolete by polish. -- **Consolidate tokens**: If you introduced new values, check whether they should be tokens. -- **Verify DRYness**: Look for duplication introduced during polishing and consolidate. +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. diff --git a/.pi/skills/impeccable/reference/quieter.md b/.pi/skills/impeccable/reference/quieter.md index 55b00b6f3..bb163e40f 100644 --- a/.pi/skills/impeccable/reference/quieter.md +++ b/.pi/skills/impeccable/reference/quieter.md @@ -2,7 +2,7 @@ Quiet design is harder than bold design. Subtlety needs precision. Reduce visual --- -## Register +## Visitor mode Persuade + Experience: "quieter" means more restrained palette, more whitespace, more typographic air. Drama is reduced, not eliminated; the POV stays intact. diff --git a/.pi/skills/impeccable/reference/routing.md b/.pi/skills/impeccable/reference/routing.md index 77aeb7982..2125286b4 100644 --- a/.pi/skills/impeccable/reference/routing.md +++ b/.pi/skills/impeccable/reference/routing.md @@ -11,7 +11,7 @@ Reason over the signals; there is no score to obey: - `critique.latest` with a low `score` or non-zero `p0` / `p1` → `polish` (it reads that snapshot as its backlog), or re-run `critique` if the snapshot looks stale. - `git.changedFiles` pointing at one surface → scope `audit` or `polish` to those files specifically, naming them. - `devServer.running` true → `live` is available for in-browser iteration; if false, don't lead with `live`. **`live` and the bundled `detect.mjs` are web-only.** If `setup.platform` is `ios`, `android`, or `adaptive`, don't lead with either; the browser overlay and the HTML rule engine don't apply to native app code. -- Otherwise group by intent exactly as init's "Recommend starting points" step does (build new / improve what's there / iterate visually), tailored to `setup.register`. +- Otherwise group by intent (build new / improve what's there / iterate visually), tailored to the current surface and `setup.platform`. **If `scan.targets` is non-empty and `setup.platform` is not `ios`/`android`/`adaptive`, run `node .pi/skills/impeccable/scripts/detect.mjs --json ` once** (the bundled detector over local files: no network, no npx; it reads HTML/CSS, so skip it for native projects). `scan.via` tells you what they are: `git-changes` (the markup/style files in your dirty tree, the most relevant set), `source-dir` (e.g. `src`, `app`), `html`, or `root`. Fold the hits into your picks: many quality / contrast hits → `audit` or `polish`; a specific slop family → the matching command (gradient text or eyebrows → `quieter` / `typeset`, flat or gray palette → `colorize`, and so on). It's a real, current signal that beats guessing. If detect errors or the tree is large and slow, skip it and recommend the user run `audit` themselves; never block the suggestion on it. diff --git a/.pi/skills/impeccable/reference/shape.md b/.pi/skills/impeccable/reference/shape.md index 40804f6e4..acbb26e5b 100644 --- a/.pi/skills/impeccable/reference/shape.md +++ b/.pi/skills/impeccable/reference/shape.md @@ -1,167 +1,61 @@ -Shape the UX and UI for a feature before any code is written. This command produces a **design brief**: a structured artifact that guides implementation through discovery, not guesswork. +# Shape -**Scope**: Design planning only. This command does NOT write code. It produces the thinking that makes code good. +Discover what should be made and how it should work, then return a confirmed design brief without code. -**Output**: A design brief that can be handed off to /impeccable craft, or directly to /impeccable for freeform implementation. When visual direction probes are used, the images are supporting artifacts, not the primary output. +**Product gate:** when context reports that PRODUCT.md is missing, load and complete [init.md](init.md) before Phase 1. Do not substitute shape questions for the product interview. Once PRODUCT.md exists, return here; product context does not replace task discovery. -## Philosophy +## Phase 1: Discovery interview -Most AI-generated UIs fail not because of bad code, but because of skipped thinking. They jump to "here's a card grid" without asking "what is the user trying to accomplish?" This command inverts that: understand deeply first, so implementation is precise. +Do not write code or choose visual direction yet. -## Phase 1: Discovery Interview +### Cadence -**Do NOT write any code or make any design decisions during this phase.** Your only job is to understand the feature deeply enough to make excellent design decisions later. +- Use the structured question tool when available; otherwise ask and stop. +- Ask two or three related questions per round, then wait. One round is the default; add a second only when the answers expose a material gap. +- Do not dump a questionnaire, repeat settled facts, or turn obvious facts into menus. Assert the likely reading and invite correction. +- A sparse prompt requires at least one answer round. A precise prompt may need only a compact confirmation. -**Unattended runs:** when no user can respond (a one-shot task, an automated run, or an explicit instruction not to ask questions), answer the interview questions yourself from the brief, record the answers as the design brief, and continue without pausing at any confirmation gate in this file. The thinking is still required; only the waiting is waived. +### Round 1: purpose, people, and outcome -This is a required interaction, not optional guidance. Ask these questions in conversation, adapting based on answers. Don't dump them all at once; have a natural dialogue. ask the user directly to clarify what you cannot infer. +Choose the two or three questions that most change the result: -### Interview cadence +- What is this surface or feature for, and what problem must it solve? +- Who specifically reaches it, in what situation and state of mind? +- What is the primary thing they must understand or do? What would success look like? +- What is uniquely true here that a neighboring product or generic template could not claim? -Discovery includes at least one user-answer round unless PRODUCT.md, DESIGN.md, or an already-confirmed brief directly answers the needed inputs. With a sparse prompt, do **not** synthesize a complete brief for confirmation on the first response. +### Round 2: material, behavior, and boundaries -- Use the harness's structured question tool when one exists. Otherwise, ask directly in chat and stop. -- Ask **2-3 questions per round**, then wait for answers. -- Treat PRODUCT.md and DESIGN.md as anchors; they reduce repeated questions but do **not** replace shape for craft. Shape is task-specific. -- One round is the default. Add a second only if the first answers leave material gaps. Don't run a second round just to feel thorough. -- Round 1 should clarify purpose, audience/context, content/scope, and (for brand) visual direction. -- Round 2, when needed, fills in whatever's still genuinely missing. +Run only for material unresolved decisions: -**Assert-then-confirm, not menu-with-escape.** When PRODUCT.md and the user's prompt make one option obvious, name it and ask the user to confirm or override. Don't enumerate "Restrained / Committed / Or something else?" as a real choice; "This reads as Restrained, confirm?" beats a four-option menu when the answer is already clear. +- What real content, evidence, data, and assets must the experience carry? What are realistic minimum, typical, and maximum ranges? +- Which states and transitions matter: first-run, empty, loading, error, success, permissions, overflow, or expert use? +- What is the intended fidelity, breadth, and interactivity: exploration, production-ready screen, full flow, or broader surface? +- What must remain untouched? What would make the result feel wrong even if it looked polished? +- Which platform, framework, performance, accessibility, localization, or delivery constraints are binding? -### Purpose & Context -- What is this feature for? What problem does it solve? -- Who specifically will use it? (Not "users"; be specific: role, context, frequency) -- What does success look like? How will you know this feature is working? -- What's the user's state of mind when they reach this feature? (Rushed? Exploring? Anxious? Focused?) +Never ask for CSS values or canned aesthetic lanes. New-work owns visual-world and concept choices. -### Content & Data -- What content or data does this feature display or collect? -- What are the realistic ranges? (Minimum, typical, maximum, e.g., 0 items, 5 items, 500 items) -- What are the edge cases? (Empty state, error state, first-time use, power user) -- Is any content dynamic? What changes and how often? -- What visual assets are real content here? Note required images, product shots, illustrations, maps, textures, diagrams, generated objects, or existing project assets. +## Phase 2: Resolve the design direction -### Design Direction +For new surfaces, brand expansion, or replacement, follow [new-work.md](new-work.md) through visual authority, any world workshop, and concept choice. Reuse discovery, then return before its contract, persistence, or implementation. Inside an established world, use its concept process only when composition or interaction remains materially open. -Force a visual decision on three fronts. Skip anything PRODUCT.md or DESIGN.md already answers; ask only what's missing. +## Phase 3: Write the brief -- **Color strategy for this surface.** Pick one: Restrained / Committed / Full palette / Drenched. Can override the project default if the surface earns it (e.g. a drenched hero inside an otherwise Restrained product). -- **Theme via scene sentence.** Write one sentence of physical context for this surface: who uses it, where, under what ambient light, in what mood. The sentence forces dark vs light. If it doesn't, add detail until it does. -- **Two or three named anchor references.** Specific products, brands, objects. Not adjectives like "modern" or "clean." +Write the smallest useful brief: -### Scope +1. **Job and audience:** who arrives, their context, need, and visitor mode. +2. **Outcome and proof:** primary task/action, success, real evidence, and product-specific truth. +3. **Selected direction:** visual authority, structural/interaction thesis, sequence, focal moment, and implementation consequence. +4. **Scope and boundaries:** fidelity, breadth, interactivity, named target, what remains untouched, and explicit anti-goals. +5. **States and ranges:** realistic content/data ranges and material states. +6. **Interaction and layout:** hierarchy, topology, responsiveness, affordances, feedback, and transitions; intent, not CSS. +7. **Constraints and open decisions:** platform, delivery, accessibility, localization, reusable components, and choices a builder must not invent. -Always ask. Sketch quality and shipped quality are different outputs; don't guess between them. +Use three to five bullets when the task is settled; use the full structure only for ambiguous, multi-screen, or standalone planning. Do not restate the conversation. -- **Fidelity.** Sketch / mid-fi / high-fi / production-ready? -- **Breadth.** One screen / a flow / a whole surface? -- **Interactivity.** Static visual / interactive prototype / shipped-quality component? -- **Time intent.** Quick exploration, or polish until it ships? +## Confirm and stop -Scope answers are task-scoped. Don't write them to PRODUCT.md or DESIGN.md; carry them through the design brief only. +Present the brief for explicit confirmation or one correction round, then stop: shape never writes code or a direction contract. -### Constraints -- Are there technical constraints? (Framework, performance budget, browser support) -- Are there content constraints? (Localization, dynamic text length, user-generated content) -- Mobile/responsive requirements? -- Accessibility requirements beyond WCAG AA? - -### Anti-Goals -- What should this NOT be? What would be a wrong direction? -- What's the biggest risk of getting this wrong? - -## Phase 1.5: Visual Direction Probe (Capability-Gated) - -After the discovery interview, generate a small set of visual direction probes **before** writing the final brief when all of these are true: - -- The work is **net-new** or directionally ambiguous enough that visual exploration will clarify the brief. -- The requested fidelity is **mid-fi, high-fi, or production-ready**. Skip for sketch-only planning. -- The current harness gives you native image generation (Codex's `image_gen`, an equivalent MCP tool, or similar). Don't ask the user to install APIs or tooling. - -When those conditions are met, this step is mandatory. If image generation isn't natively available, do not ask the user to install APIs or tooling. State in one line that the image step is skipped because the harness lacks native image generation, then proceed. The one-line announcement is required, not optional; it forces a conscious decision instead of letting the step quietly evaporate. - -Use probes to explore visual lanes, not to replace the brief. - -Do not skip probes because the final UI will be semantic, editable, code-native, responsive, or accessible. Those are implementation requirements, not reasons to avoid visual exploration. - -### What to generate - -Generate **2 to 4** distinct direction probes based on the discovery answers, especially: - -- Color strategy -- Theme scene sentence -- Named anchor references -- Scope and fidelity - -The probes should differ in primary visual direction (hierarchy, topology, density, typographic voice, or color strategy), not just palette tweaks. - -### How to use the probes - -- Treat them as **direction tests**, not final designs. -- Use them to pressure-test whether the brief is pointing at the right lane. -- Ask the user which direction feels closest, what feels off, and what should carry forward. -- If the probes reveal a mismatch, revise the brief inputs before finalizing the brief. - -### Important limits - -- Do **not** skip discovery because image generation is available. -- Do **not** treat generated imagery as final UX specification, final copy, or final accessibility behavior. -- Do **not** use this step for minor refinements of existing work. It's for shaping a new surface or clarifying a big directional choice. - -If image generation isn't natively available, announce the skip in one line and proceed to the design brief. - -## Phase 2: Design Brief - -After the interview and any required probes, present a brief and **end your response**. The user must confirm before any implementation runs. Do not present a brief and then continue to code in the same response, even if the brief feels obvious to you. The user's confirmation is the gate. - -**Choose the brief shape based on how clear the answers are:** - -- **Compact form (3-5 bullets)** when discovery was crisp and the original prompt + PRODUCT.md already pinned scope, content, and direction. State what you're building, the visual lane, and end with one or two specific questions or a clear "confirm or override?" prompt. This is the default for typical craft requests with a clear prompt. -- **Full structured form (sections below)** when the task is genuinely ambiguous, multi-screen, or when the user asked for shape as a standalone step. Use this when the discipline of structure earns its weight. - -Don't pad a clear brief into a long one to look thorough. A 70-line brief restating answers the user just gave is noise, not rigor. Equally, don't skip the confirmation pause to look efficient: the pause is the point. - -Present the brief, then **stop and wait for explicit confirmation**. You are not the judge of whether the user already approved. Even when the brief feels obviously right, ask once and wait. The pause is what separates shape from premature implementation. - -### Brief Structure - -**1. Feature Summary** (2-3 sentences) -What this is, who it's for, what it needs to accomplish. - -**2. Primary User Action** -The single most important thing a user should do or understand here. - -**3. Design Direction** -Color strategy (Restrained / Committed / Full palette / Drenched) + the theme scene sentence + 2–3 named anchor references. Reference PRODUCT.md and DESIGN.md where they already answer, and note any per-surface overrides. - -If you ran the Visual Direction Probe step, name which probe direction won and what changed in the brief because of it. - -**4. Scope** -Fidelity, breadth, interactivity, and time intent from the Scope section of the interview. Task-scoped; these don't persist beyond the brief. - -**5. Layout Strategy** -High-level spatial approach: what gets emphasis, what's secondary, how information flows. Describe the visual hierarchy and rhythm, not specific CSS. - -**6. Key States** -List every state the feature needs: default, empty, loading, error, success, edge cases. For each, note what the user needs to see and feel. - -**7. Interaction Model** -How users interact with this feature. What happens on click, hover, scroll? What feedback do they get? What's the flow from entry to completion? - -**8. Content Requirements** -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., 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. - ---- - -ask the user directly to clarify what you cannot infer. Ask for explicit confirmation of the brief before finishing. - -If the user disagrees with any part, revisit the relevant discovery questions. A shape run is incomplete until the user confirms direction. - -Once confirmed, the brief is complete. The user can now hand it to /impeccable, or use it to guide any other implementation approach. (If the user wants the full discovery-then-build flow in one step, they should use /impeccable craft instead, which runs this command internally.) +When no human or structured answer mechanism exists, mark assumptions plainly, return the brief, and stop. diff --git a/.pi/skills/impeccable/reference/typeset.md b/.pi/skills/impeccable/reference/typeset.md index 8d11752ff..5f1dd2783 100644 --- a/.pi/skills/impeccable/reference/typeset.md +++ b/.pi/skills/impeccable/reference/typeset.md @@ -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. --- -## Register +## Visitor mode -New identity work: run the font selection procedure in [new-work.md](new-work.md). Fluid `clamp()` scale, ≥1.25 ratio between steps. +- **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, register, **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 .pi/skills/impeccable/scripts/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 `/impeccable polish` for the final pass. +When the hierarchy holds, hand off to `/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) * )` 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 - -The tactical selection procedure and the reflex-reject list live in [new-work.md](new-work.md) (loaded for new identity work). 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 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. diff --git a/.pi/skills/impeccable/scripts/command-metadata.json b/.pi/skills/impeccable/scripts/command-metadata.json index 83796d54b..dad8ef2e0 100644 --- a/.pi/skills/impeccable/scripts/command-metadata.json +++ b/.pi/skills/impeccable/scripts/command-metadata.json @@ -1,6 +1,6 @@ { "craft": { - "description": "Full confirmed-brief-then-build flow. Runs multi-round shape discovery first, resolves visual probe and north-star mock gates when available, then builds and visually iterates. Use when building a new feature end-to-end.", + "description": "Deprecated compatibility alias for an ordinary Impeccable new-work request. It adds no behavior; natural build and redesign requests use the same flow.", "argumentHint": "[feature description]" }, "init": { diff --git a/.pi/skills/impeccable/scripts/concept-seed.mjs b/.pi/skills/impeccable/scripts/concept-seed.mjs index 1f141a80f..25f20d9ab 100644 --- a/.pi/skills/impeccable/scripts/concept-seed.mjs +++ b/.pi/skills/impeccable/scripts/concept-seed.mjs @@ -1,6 +1,7 @@ #!/usr/bin/env node /** - * Concept-seed picker: the dice half of the new-work concept procedure. + * External concept seed: the dice half of new-work's coupled-direction and + * established-world surface procedures. * * The model derives a grounded shortlist of candidate FORMS from the * audience's world and the subject's cultural home (see @@ -12,10 +13,9 @@ * * This script rolls them from outside, the same trick that made the * palette seed work: - * - BUILD INDEX (2-5): which entry of the model's own resonance-ordered - * shortlist to build. The dice never choose an ungrounded ingredient; - * they only refuse the argmax rut. (Index 1 is excluded: that's the - * concept every run would ship anyway.) + * - PROMOTED INDEX: which entry of the model's own resonance-ordered + * shortlist must be taken seriously beside its favorites. The dice never + * choose an ungrounded ingredient; they only refuse the argmax rut. * - CHALLENGERS (3): outside forms from concept-ingredients.json, weighed * against the derived candidates on exactly two axes — audience * identification and product clarity. They win only when they beat the @@ -23,8 +23,8 @@ * material and win over thin categories, which is the intended shape. * * Usage: - * node scripts/concept-seed.mjs # roll at random - * node scripts/concept-seed.mjs --from # deterministic (hash key) + * node scripts/concept-seed.mjs --scope direction + * node scripts/concept-seed.mjs --scope surface --from * * Env vars: * IMPECCABLE_CONCEPT_SEED — same as --from; for reproducible eval runs. @@ -40,6 +40,12 @@ const pool = JSON.parse(readFileSync(join(here, 'concept-ingredients.json'), 'ut const args = process.argv.slice(2); const fromIdx = args.indexOf('--from'); +const scopeIdx = args.indexOf('--scope'); +const scope = scopeIdx !== -1 ? args[scopeIdx + 1] : 'surface'; +if (scope !== 'surface' && scope !== 'direction') { + process.stderr.write('concept-seed: --scope must be direction or surface\n'); + process.exit(1); +} // When no key is supplied, generate one and print it: a user reporting a // bad outcome can hand us the key and we replay the exact roll. const key = fromIdx !== -1 @@ -47,7 +53,7 @@ const key = fromIdx !== -1 : (process.env.IMPECCABLE_CONCEPT_SEED || crypto.randomBytes(4).toString('hex')); function hashUnit(k, salt) { - const h = crypto.createHash('sha256').update(`${salt}:${k}`).digest(); + const h = crypto.createHash('sha256').update(`${scope}:${salt}:${k}`).digest(); return h.readUInt32BE(0) / 0xffffffff; } const unit = (salt) => hashUnit(key, salt); @@ -67,22 +73,48 @@ for (let i = 0; picks.length < 3 && i < 60; i++) { } } -process.stdout.write(`CONCEPT SEED (key: ${key}; rerun with --from ${key} to reproduce this roll) -BUILD INDEX: ${buildIndex} - After ordering your derived candidates by resonance, build the page whose - form comes from candidate number ${buildIndex}, exactly as if it had ranked - first: full commitment. Your top-ranked candidate is what every run in this - category would ship; the assignment exists to refuse that rut, not to - punish it. -CHALLENGERS (weigh against your derived candidates on the same two axes, -audience identification and product clarity; a challenger wins only when -it beats the grounded list on both): +const promotedInstruction = scope === 'direction' + ? `After ordering the grounded coupled directions by product fit, promote + candidate ${buildIndex} into the serious shortlist. Each candidate must join + a durable visual system to a concrete expression for the requested first + surface; select or revise that pair as one decision. It must survive the + current task plus navigation, quiet and dense content, interaction and state, + and a substantially different future surface.` + : `After ordering the task's grounded structural candidates by resonance, + promote candidate ${buildIndex} into the serious shortlist. In an attended + run, present it beside the strongest materially different candidates and + let the user select or revise the surface concept. In a truly unattended + run, use it when it survives audience identification and product clarity.`; + +const challengerInstruction = scope === 'direction' + ? `A challenger enters only when it can supply both reusable identity grammar + and a strong first-surface structure, not a one-page costume or a style pasted + onto an unrelated layout. Weigh audience identification, product clarity, + current-surface force, and cross-surface system breadth.` + : `A challenger wins only when it beats the grounded list on both audience + identification and product clarity. It may change task topology or + interaction, but never the committed visual identity.`; + +const authorityInstruction = scope === 'direction' + ? `PRODUCT.md and explicit incumbent brand commitments constrain every coupled +direction. The seed never chooses exact colors, fonts, tokens, or a user +preference, and it never permits the world and first surface to be selected +independently.` + : `PRODUCT.md and DESIGN.md constrain every surface candidate's identity +vocabulary; they do not cancel task-level composition. The seed never +authorizes a new palette, type system, material world, or unfamiliar control +behavior.`; + +process.stdout.write(`${scope.toUpperCase()} CONCEPT SEED (key: ${key}; rerun with --scope ${scope} --from ${key} to reproduce this roll) +PROMOTED INDEX: ${buildIndex} + ${promotedInstruction} + The promotion exists to refuse the model's ranking rut, not to outrank the + user or the brief. +CHALLENGERS: 1. ${picks[0]} 2. ${picks[1]} 3. ${picks[2]} -If a challenger wins, it replaces the assigned candidate. If the surface is -an existing world whose incumbent carries a deliberate, ownable idea, the -incumbent IS the chosen candidate: intensify its lineage and ignore the -roll entirely. The same override applies when the user, PRODUCT.md, or -DESIGN.md pins a direction: pinned direction beats the roll, always. +${challengerInstruction} +${authorityInstruction} +A user- or brief-pinned decision beats the roll, always. `); diff --git a/.pi/skills/impeccable/scripts/context-signals.mjs b/.pi/skills/impeccable/scripts/context-signals.mjs index 2fc27bea7..d296320d7 100644 --- a/.pi/skills/impeccable/scripts/context-signals.mjs +++ b/.pi/skills/impeccable/scripts/context-signals.mjs @@ -11,7 +11,7 @@ * output is always valid JSON. * * Signals: - * - setup: PRODUCT.md / DESIGN.md presence, register, whether code exists + * - setup: PRODUCT.md / DESIGN.md presence and whether code exists * - critique: the latest cached critique score (.impeccable/critique) * - git: branch + files changed vs the default branch (a scope hint) * - devServer: whether a local dev server answers on a common port (gates live) @@ -21,7 +21,7 @@ import net from 'node:net'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { execFileSync } from 'node:child_process'; -import { loadContext, extractRegister, extractPlatform } from './context.mjs'; +import { loadContext, extractPlatform } from './context.mjs'; import { getCritiqueDir } from './lib/impeccable-paths.mjs'; /** Is there code here at all, or just context files / an empty repo? */ @@ -196,7 +196,6 @@ export async function gatherSignals(cwd = process.cwd()) { hasDesign: ctx.hasDesign, designPath: ctx.designPath, hasCode: hasCode(cwd), - register: extractRegister(ctx.product), platform: extractPlatform(ctx.product), }, critique: { latest: latestCritique(cwd) }, diff --git a/.pi/skills/impeccable/scripts/context.mjs b/.pi/skills/impeccable/scripts/context.mjs index 2ef2d4ce8..080e7ee3a 100644 --- a/.pi/skills/impeccable/scripts/context.mjs +++ b/.pi/skills/impeccable/scripts/context.mjs @@ -1,8 +1,10 @@ /** - * Context loader: prints PRODUCT.md (and DESIGN.md if present) as one - * markdown block on stdout, or prints a `NO_PRODUCT_MD:` message when no + * Context loader: prints PRODUCT.md, DESIGN.md when present, the matching + * persisted surface brief when one can be resolved, and native-platform + * guidance selected from PRODUCT.md. It prints a + * `NO_PRODUCT_MD:` message when no * PRODUCT.md is found anywhere. The skill keys off that message to branch: - * from-scratch build commands (init / teach / craft / shape) and clear + * from-scratch build requests (plus init / teach / shape) and clear * build/shape intent divert into the init flow, while scoped commands proceed * using the existing code as context. * @@ -23,10 +25,13 @@ import os from 'node:os'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { parseTargetOptions } from './lib/target-args.mjs'; -import { IMPECCABLE_COMMAND } from './lib/provider.mjs'; +import { IMPECCABLE_COMMAND, IMPECCABLE_PROVIDER_ID } from './lib/provider.mjs'; +import { renderLlmOnlySlopReview } from './lib/slop-review.mjs'; +import { resolveSurfaceBrief } from './lib/surface-briefs.mjs'; const PRODUCT_NAMES = ['PRODUCT.md', 'Product.md', 'product.md']; const DESIGN_NAMES = ['DESIGN.md', 'Design.md', 'design.md']; +const SKILL_REFERENCE_DIR = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', 'reference'); const FALLBACK_DIRS = ['.agents/context', 'docs']; const MONOREPO_MARKER_FILES = ['pnpm-workspace.yaml', 'turbo.json', 'nx.json', 'lerna.json']; const MONOREPO_FALLBACK_PROJECT_DIRS = ['apps', 'packages']; @@ -41,6 +46,8 @@ const WORKSPACE_DISCOVERY_IGNORED_DIRS = new Set([ '.turbo', '.cache', 'coverage', + 'vendor', + 'vendors', ]); const VISUAL_SOURCE_DIRS = ['src', 'app', 'pages', 'components', 'site', 'public', 'styles']; const STYLE_EXTENSIONS = new Set(['.css', '.scss', '.sass', '.less', '.styl']); @@ -73,6 +80,12 @@ export function loadContext(cwd = process.cwd(), options = {}) { const designPath = resolved.designPath; const product = productPath ? safeRead(productPath) : null; const design = designPath ? safeRead(designPath) : null; + const platform = extractPlatform(product); + const surfaceResolution = resolveSurfaceBrief( + resolved.projectRoot, + hasTargetOption(options) ? options.targetPath : null, + ); + const surfaceBrief = surfaceResolution.brief; return { hasProduct: !!product, product, @@ -83,7 +96,18 @@ export function loadContext(cwd = process.cwd(), options = {}) { contextDir: resolved.contextDir, productContextDir: productPath ? path.dirname(productPath) : null, designContextDir: designPath ? path.dirname(designPath) : null, + hasSurfaceBrief: !!surfaceBrief, + surfaceBrief: surfaceBrief?.text ?? null, + surfaceBriefPath: surfaceBrief?.path ? path.relative(absCwd, surfaceBrief.path) : null, + surfaceBriefReason: surfaceResolution.reason, + surfaceBriefCandidates: surfaceResolution.candidates.map((brief) => ({ + slug: brief.slug, + path: path.relative(absCwd, brief.path), + primaryTarget: brief.primaryTarget, + relatedTargets: brief.relatedTargets, + })), hasVisualImplementation: hasVisualImplementation(resolved.projectRoot), + platform, projectRoot: resolved.projectRoot, repoRoot: resolved.repoRoot, isMonorepo: resolved.isMonorepo, @@ -696,6 +720,19 @@ function safeRead(p) { } } +function loadNativePlatformReferences(platform) { + const names = platform === 'adaptive' + ? ['ios', 'android'] + : platform === 'ios' || platform === 'android' + ? [platform] + : []; + return names.flatMap((name) => { + const filePath = path.join(SKILL_REFERENCE_DIR, `${name}.md`); + const content = safeRead(filePath); + return content ? [{ name, filePath, content }] : []; + }); +} + /** * Best-effort evidence that the project already has an incumbent visual * implementation. DESIGN.md is documentation, not the only source of design @@ -719,9 +756,11 @@ export function hasVisualImplementation(projectRoot) { let styledComponents = 0; const inspectFile = (filePath) => { - if (scannedFiles++ >= VISUAL_SCAN_FILE_LIMIT) return false; const ext = path.extname(filePath).toLowerCase(); if (!STYLE_EXTENSIONS.has(ext) && !UI_EXTENSIONS.has(ext)) return false; + const base = path.basename(filePath).toLowerCase(); + if (/\.min\.[a-z]+$/.test(base)) return false; + if (scannedFiles++ >= VISUAL_SCAN_FILE_LIMIT) return false; let body; try { body = fs.readFileSync(filePath, 'utf-8').slice(0, 64 * 1024); @@ -729,18 +768,28 @@ export function hasVisualImplementation(projectRoot) { return false; } - const base = path.basename(filePath).toLowerCase(); + const evidence = body + .replace(/\/\*[\s\S]*?\*\//g, '') + .replace(//g, '') + .replace(/^\s*\/\/.*$/gm, ''); if (STYLE_EXTENSIONS.has(ext)) { - const customProperties = body.match(/--[a-z0-9_-]+\s*:/gi)?.length ?? 0; - const visualDeclarations = body.match(/\b(?:color|background(?:-color)?|border(?:-color)?|font-family)\s*:/gi)?.length ?? 0; - if (/\b(?:tokens?|theme|design-system)\b/.test(base) && body.trim().length > 80) return true; + const customProperties = evidence.match(/--[a-z0-9_-]+\s*:/gi)?.length ?? 0; + const visualDeclarations = evidence.match(/\b(?:color|background(?:-color)?|border(?:-color)?|font-family)\s*:/gi)?.length ?? 0; + if (/\b(?:tokens?|theme|design-system)\b/.test(base) && evidence.trim().length > 80) return true; if (customProperties >= 3 || visualDeclarations >= 5) return true; } - if ((ext === '.html' || ext === '.htm') && body.length > 600 && /]+stylesheet/i.test(body)) { + if ((ext === '.html' || ext === '.htm') && evidence.length > 600 && /]+stylesheet/i.test(evidence)) { return true; } - if (!['.html', '.htm'].includes(ext) && body.length > 300 && /class(?:Name)?\s*=|style\s*=|styled\(|css`/i.test(body)) { + if (!['.html', '.htm'].includes(ext) && evidence.length > 300) { + const embeddedCustomProperties = evidence.match(/--[a-z0-9_-]+\s*:/gi)?.length ?? 0; + const embeddedVisualDeclarations = evidence.match(/\b(?:color|background(?:-color)?|border(?:-color)?|font-family)\s*:/gi)?.length ?? 0; + const classTokens = [...evidence.matchAll(/class(?:Name)?\s*=\s*["'`]([^"'`]+)["'`]/gi)] + .reduce((count, match) => count + match[1].trim().split(/\s+/).length, 0); + if ((embeddedCustomProperties >= 3 && embeddedVisualDeclarations >= 3) || embeddedVisualDeclarations >= 5 || classTokens >= 12) return true; + } + if (!['.html', '.htm'].includes(ext) && evidence.length > 300 && /class(?:Name)?\s*=|style\s*=|styled\(|css`/i.test(evidence)) { styledComponents += 1; if (styledComponents >= 3) return true; } @@ -781,9 +830,9 @@ function escapeRegExp(value) { /** * Read the first non-empty line under a bare `## ` section of - * PRODUCT.md (e.g. `## Register`, `## Platform`). Returns null when the + * PRODUCT.md (for example `## Platform`). Returns null when the * section is absent. The heading match is exact (`\s*$`) so near-miss - * headings like `## Register guidelines` don't shadow the real field. + * near-miss headings don't shadow the real field. */ export function extractSectionValue(product, heading) { if (!product) return null; @@ -802,16 +851,6 @@ export function extractSectionValue(product, heading) { return null; } -/** - * Pull the register (`brand` or `product`) out of PRODUCT.md by looking - * for a `## Register` section and reading the first non-empty line that - * follows it. Returns null when the file is legacy / register-less. - */ -export function extractRegister(product) { - const word = (extractSectionValue(product, 'Register') || '').toLowerCase(); - return word === 'brand' || word === 'product' ? word : null; -} - /** * Pull the platform (`web`, `ios`, `android`, or `adaptive`) out of PRODUCT.md * by looking for a `## Platform` section and reading the first non-empty line @@ -985,30 +1024,35 @@ async function cli() { const parts = ctx.hasVisualImplementation ? [ 'NO_PRODUCT_MD: This project has no PRODUCT.md yet, but it does have an incumbent visual implementation. ' + - 'For `init` or `teach`, load reference/init.md and create PRODUCT.md. For `craft`, `shape`, or other work ' + - 'against the existing product, read its CSS, tokens, components, and assets first and proceed without blocking; ' + - `offer \`${IMPECCABLE_COMMAND} init\` as a follow-up. Only treat the work as greenfield when the user explicitly ` + - 'asks to discard or replace the current identity.', - 'EXISTING_VISUAL_SYSTEM: Code and assets are the incumbent design authority. Missing DESIGN.md is a documentation ' + - 'gap, not permission to invent a replacement identity. Preserve and extend the implementation unless the user ' + - 'explicitly requests a rebrand, or inspection proves the detected files contain no coherent visual decisions.', + 'For `init`, `teach`, `shape`, or any request to create a new surface or replacement visual world, load reference/init.md and create PRODUCT.md with the user first. ' + + 'After init writes PRODUCT.md, reference/new-work.md preserves and documents the incumbent system for an ' + + 'extension or replaces it with the user for a redesign/rebrand. Other ' + + 'narrow refinement commands may read the CSS, tokens, components, and assets and proceed without blocking, then ' + + `offer \`${IMPECCABLE_COMMAND} init\` as a follow-up.`, + 'BUILD_INIT_REQUIRED: Before shape or any new-surface/redesign flow, init must capture PRODUCT.md with the human or structured ' + + 'simulated user. Init writes product truth only; reference/new-work.md owns every visual decision.', + 'SCOPED_EXISTING_ALLOWED: Narrow refinement commands may use the incumbent implementation as authority without ' + + 'blocking on context setup; they must preserve it and offer init afterward.', + 'EXISTING_VISUAL_SYSTEM: For refinement or extension, code and assets are incumbent design authority and missing ' + + 'DESIGN.md is a documentation gap. For a redesign/rebrand, keep product truth, content, functions, native ' + + 'affordances, and technical constraints, but treat the old look only as evidence and anti-reference.', ] : [ 'NO_PRODUCT_MD: This project has no PRODUCT.md yet. ' + - 'For `init`, `teach`, `craft`, `shape`, ' + + 'For `init`, `teach`, `shape`, ' + 'or wording that clearly maps to a from-scratch build/shape flow, load ' + - 'reference/init.md and write PRODUCT.md first, unless no user can ' + - 'respond (a one-shot or automated run, or the user said not to ask): ' + - 'then write a one-paragraph understanding of the product, audience, ' + - 'and the page\'s job from the brief, and continue. For any other ' + + 'reference/init.md, complete its human or structured simulated-user interview, and write PRODUCT.md before ' + + 'designing. If no answer mechanism truly exists, init may infer only from the explicit brief and must label its ' + + 'assumptions. It never writes DESIGN.md. For any other ' + '(scoped) command against existing code, proceed using the code as ' + `context and offer \`${IMPECCABLE_COMMAND} init\` as a suggestion (do not block).`, - 'NEW_WORK: No committed design context was found. If this task produces ' + - 'new design (a build from scratch, or a redesign that discards the ' + - 'current look), you MUST read reference/new-work.md before making any ' + - 'design decision. Scoped fixes to existing code do not need it.', + 'PRODUCT_INIT_REQUIRED: No product context or visual authority was found. New builds and redesigns ' + + 'must finish reference/init.md for PRODUCT.md, then reference/new-work.md establishes the world and surface. Scoped ' + + 'fixes to existing code do not need the new-surface flow.', ]; + appendSurfaceBriefContext(parts, ctx); parts.push(buildResolvedContextDirective(ctx, cliOptions, { targetExists })); + appendHookFallback(parts, ctx); if (shouldWarnMissingTarget(ctx, targetProvided, targetExists)) { parts.push(buildMissingTargetDirective()); } @@ -1020,37 +1064,29 @@ async function cli() { if (ctx.hasDesign) { parts.push(`# DESIGN.md\n\n${ctx.design.trim()}`); } + appendSurfaceBriefContext(parts, ctx); parts.push(buildResolvedContextDirective(ctx, cliOptions, { targetExists })); + appendHookFallback(parts, ctx); if (shouldWarnMissingTarget(ctx, targetProvided, targetExists)) { parts.push(buildMissingTargetDirective()); } - const register = extractRegister(ctx.product); - // The register field survives as a family hint (brand = Persuade/Experience, - // product = Operate/Read); SKILL.md's mode section carries the essentials - // inline, so no register-file read is mandated here. What IS mandated: - // the new-work playbook when no committed design system exists yet. if (!ctx.hasDesign) { parts.push(ctx.hasVisualImplementation - ? 'EXISTING_VISUAL_SYSTEM: PRODUCT.md exists and DESIGN.md is missing, but code contains incumbent visual decisions. ' + - 'Treat CSS, tokens, components, and assets as design authority. Do not route to new identity work merely because the ' + - 'document is absent; preserve and extend the implementation unless the user explicitly asks to discard it.' - : 'NEW_WORK: PRODUCT.md exists but no DESIGN.md or incumbent visual implementation was found. If this task produces ' + - 'new design or a redesign that discards the current look, you MUST read reference/new-work.md before making any ' + - 'design decision. Scoped fixes to existing code do not need it.'); + ? 'INCUMBENT_WORLD_UNDOCUMENTED: PRODUCT.md exists and DESIGN.md is missing, but code contains incumbent visual decisions. ' + + 'For shape or a new-surface/redesign request, load reference/new-work.md: an extension documents and preserves the code-defined world; ' + + 'a redesign replaces it with the user and uses the old look only as evidence and anti-reference. Narrow refinement ' + + 'commands may proceed using the implementation directly.' + : 'WORLD_DISCOVERY_REQUIRED: PRODUCT.md exists but no DESIGN.md or incumbent visual implementation was found. ' + + 'For a new build or redesign, load reference/new-work.md and establish the visual world with the human or structured ' + + 'simulated user before developing the task concept. Scoped fixes to existing code do not need this flow.'); } - if (register) { - parts.push(`REGISTER: \`${register}\` (family hint: brand = Persuade/Experience surfaces, product = Operate/Read). Derive the visitor's mode per SKILL.md.`); - } - const platform = extractPlatform(ctx.product); - const nativeRefs = - platform === 'adaptive' ? ['ios', 'android'] : platform === 'ios' || platform === 'android' ? [platform] : []; - if (nativeRefs.length) { - const refList = nativeRefs.map(p => `\`reference/${p}.md\``).join(' and '); - const label = platform === 'adaptive' ? '`adaptive` (both iOS and Android)' : `\`${platform}\``; + const platformReferences = loadNativePlatformReferences(ctx.platform); + for (const reference of platformReferences) { parts.push( - `NEXT STEP: This project targets ${label}. Also read ${refList} for native conventions, in addition to SKILL.md's mode guidance.`, + `# NATIVE PLATFORM REFERENCE: ${reference.name.toUpperCase()} (reference/${reference.name}.md)\n\n${reference.content.trim()}`, ); - } else if (!platform) { + } + if (!ctx.platform) { // A `## Platform` section that names something we don't recognize (a // toolchain like `flutter`, a typo) would otherwise silently fall back to // web — the wrong default exactly when the user tried to say "native". @@ -1078,6 +1114,72 @@ function pathExistsForTarget(cwd, targetPath) { return fs.existsSync(abs); } +const HOOK_MANIFESTS_BY_PROVIDER = Object.freeze({ + 'claude-code': ['.claude/settings.local.json', '.claude/settings.json'], + codex: ['.codex/hooks.json'], + agents: ['.codex/hooks.json'], + cursor: ['.cursor/hooks.json'], + github: ['.github/hooks/impeccable.json'], +}); + +function truthyEnv(value) { + return typeof value === 'string' && /^(1|true|yes|on)$/i.test(value.trim()); +} + +function valueHasHookMarker(value) { + if (typeof value === 'string') { + return value.includes('skills/impeccable/scripts/hook.mjs') + || value.includes('skills/impeccable/scripts/hook-before-edit.mjs'); + } + if (Array.isArray(value)) return value.some(valueHasHookMarker); + if (value && typeof value === 'object') return Object.values(value).some(valueHasHookMarker); + return false; +} + +function hookEnabledAt(root) { + if (truthyEnv(process.env.IMPECCABLE_HOOK_DISABLED)) return false; + let enabled = true; + for (const name of ['.impeccable/config.json', '.impeccable/config.local.json']) { + const raw = readJson(path.join(root, name)); + if (raw?.hook && Object.prototype.hasOwnProperty.call(raw.hook, 'enabled')) { + enabled = raw.hook.enabled !== false; + } + } + return enabled; +} + +const STOP_REVIEW_PROVIDERS = new Set(['claude-code', 'codex', 'agents']); + +function automaticHookMode(ctx) { + if (ctx.platform === 'ios' || ctx.platform === 'android' || ctx.platform === 'adaptive') { + return 'none'; + } + const activeRoot = path.resolve(ctx.projectRoot || process.cwd()); + if (!hookEnabledAt(activeRoot)) return 'none'; + const manifests = HOOK_MANIFESTS_BY_PROVIDER[IMPECCABLE_PROVIDER_ID] || []; + const roots = [...new Set([process.cwd(), ctx.projectRoot, ctx.repoRoot].filter(Boolean).map((root) => path.resolve(root)))]; + for (const root of roots) { + for (const rel of manifests) { + const raw = readJson(path.join(root, rel)); + if (raw?.hooks && valueHasHookMarker(raw.hooks)) { + return STOP_REVIEW_PROVIDERS.has(IMPECCABLE_PROVIDER_ID) ? 'stop' : 'per-edit'; + } + } + } + return 'none'; +} + +function appendHookFallback(parts, ctx) { + const hookMode = automaticHookMode(ctx); + if (hookMode === 'stop') return; + const native = ctx.platform === 'ios' || ctx.platform === 'android' || ctx.platform === 'adaptive'; + parts.push(renderLlmOnlySlopReview({ + automaticDetector: hookMode === 'per-edit', + manualDetector: hookMode === 'none' && !native, + scriptsPath: path.dirname(fileURLToPath(import.meta.url)), + })); +} + function buildResolvedContextDirective(ctx, options, { targetExists = null } = {}) { const targetPath = hasTargetOption(options) ? options.targetPath : null; return `RESOLVED_CONTEXT:\n${JSON.stringify({ @@ -1087,10 +1189,29 @@ function buildResolvedContextDirective(ctx, options, { targetExists = null } = { repoRoot: ctx.repoRoot, productPath: ctx.productPath, designPath: ctx.designPath, + surfaceBriefPath: ctx.surfaceBriefPath, + surfaceBriefReason: ctx.surfaceBriefReason, + surfaceBriefCandidates: ctx.surfaceBriefCandidates, hasVisualImplementation: ctx.hasVisualImplementation, + platform: ctx.platform, }, null, 2)}`; } +function appendSurfaceBriefContext(parts, ctx) { + if (ctx.hasSurfaceBrief && ctx.surfaceBrief) { + parts.push(`# SURFACE BRIEF (${ctx.surfaceBriefPath})\n\n${ctx.surfaceBrief.trim()}`); + return; + } + if (!ctx.surfaceBriefCandidates?.length) return; + const helper = path.join(path.dirname(fileURLToPath(import.meta.url)), 'surface-brief.mjs'); + parts.push( + 'SURFACE_CONTEXT_AVAILABLE: Persisted surface briefs exist, but none was selected unambiguously for this invocation. ' + + 'Resolve the requested surface to its concrete primary or related source path, then run ' + + `\`node ${helper} read \` once before changing that surface. Candidates:\n` + + JSON.stringify(ctx.surfaceBriefCandidates, null, 2), + ); +} + function shouldWarnMissingTarget(ctx, targetProvided, targetExists = null) { if (ctx.isMonorepo && targetProvided && targetExists === false) return true; return !!( diff --git a/.pi/skills/impeccable/scripts/critique-storage.mjs b/.pi/skills/impeccable/scripts/critique-storage.mjs index 6b4d225cf..38674285a 100644 --- a/.pi/skills/impeccable/scripts/critique-storage.mjs +++ b/.pi/skills/impeccable/scripts/critique-storage.mjs @@ -29,8 +29,9 @@ import fs from 'node:fs'; import path from 'node:path'; import { fileURLToPath, pathToFileURL } from 'node:url'; import { getCritiqueDir } from './lib/impeccable-paths.mjs'; +import { slugFromTarget } from './lib/target-slug.mjs'; -const SLUG_MAX = 50; +export { slugFromTarget } from './lib/target-slug.mjs'; /** * Mechanically derive a slug from a resolved target. Returns null if the @@ -40,46 +41,6 @@ const SLUG_MAX = 50; * concrete artifact before calling this — we never slug a natural-language * phrase. */ -export function slugFromTarget(resolved, { cwd = process.cwd() } = {}) { - if (!resolved || typeof resolved !== 'string') return null; - const trimmed = resolved.trim(); - if (!trimmed) return null; - - // URL - if (/^https?:\/\//i.test(trimmed)) { - let url; - try { url = new URL(trimmed); } catch { return null; } - const hostPath = `${url.hostname}${url.pathname}`; - return kebab(hostPath); - } - - // File path. Make it project-relative so two devs critiquing the same - // checkout get the same slug regardless of where their repo is cloned. - const abs = path.isAbsolute(trimmed) ? trimmed : path.resolve(cwd, trimmed); - let rel = path.relative(cwd, abs); - // If the target is outside cwd, fall back to the basename so we still - // produce a stable slug (vs the absolute path, which would include - // home dirs / usernames). - if (rel.startsWith('..') || path.isAbsolute(rel)) { - rel = path.basename(abs); - } - if (!rel || rel === '.' || rel === '') return null; - return kebab(rel); -} - -function kebab(s) { - const slug = s - .toLowerCase() - .replace(/[/\\.]+/g, '-') - .replace(/[^a-z0-9-]+/g, '-') - .replace(/-+/g, '-') - .replace(/^-|-$/g, ''); - if (!slug) return null; - // Cap from the tail — the tail (filename) is more identifying than the - // top-level directory. - return slug.length <= SLUG_MAX ? slug : slug.slice(slug.length - SLUG_MAX).replace(/^-/, ''); -} - /** * Filename-safe UTC ISO timestamp: hyphens for separators, trailing Z. * Plain colons aren't allowed on Windows filesystems. diff --git a/.pi/skills/impeccable/scripts/detector/detect-antipatterns-browser.js b/.pi/skills/impeccable/scripts/detector/detect-antipatterns-browser.js index 033573e36..76bf4ddeb 100644 --- a/.pi/skills/impeccable/scripts/detector/detect-antipatterns-browser.js +++ b/.pi/skills/impeccable/scripts/detector/detect-antipatterns-browser.js @@ -312,17 +312,6 @@ const ANTIPATTERNS = [ skillSection: 'Layout & Space', skillGuideline: 'numbered section markers', }, - { - id: 'numbered-section-markers', - category: 'slop', - scopes: ['layout'], - severity: 'advisory', - name: 'Numbered section markers (01 / 02 / 03)', - description: - 'Numbered display markers as section labels (01, 02, 03) are the AI editorial scaffold one tier deeper than tracked eyebrow chips. If you find yourself reaching for them, choose a different section cadence.', - skillSection: 'Layout & Space', - skillGuideline: 'numbered section markers', - }, { id: 'em-dash-overuse', category: 'slop', diff --git a/.pi/skills/impeccable/scripts/detector/engines/regex/detect-text.mjs b/.pi/skills/impeccable/scripts/detector/engines/regex/detect-text.mjs index 81c30ab99..f641d5917 100644 --- a/.pi/skills/impeccable/scripts/detector/engines/regex/detect-text.mjs +++ b/.pi/skills/impeccable/scripts/detector/engines/regex/detect-text.mjs @@ -280,22 +280,6 @@ const REGEX_ANALYZERS = [ if (count === 0) return []; return [finding('marketing-buzzword', filePath, `${count} buzzword phrase${count === 1 ? '' : 's'}: "${firstSample}"`)]; }, - // Numbered section markers (01 / 02 / 03 ...) - (content, filePath) => { - const text = stripHtmlToText(content); - const re = /\b(0[1-9]|1[0-2])\b/g; - const seen = new Set(); - let m; - while ((m = re.exec(text)) !== null) seen.add(m[1]); - if (seen.size < 3) return []; - const sorted = [...seen].sort(); - let sequential = 0; - for (let i = 1; i < sorted.length; i++) { - if (parseInt(sorted[i], 10) === parseInt(sorted[i - 1], 10) + 1) sequential++; - } - if (sequential < 2) return []; - return [finding('numbered-section-markers', filePath, `Sequence: ${sorted.slice(0, 6).join(', ')}`)]; - }, // Aphoristic cadence: manufactured-contrast + short-rebuttal (content, filePath) => { const text = stripHtmlToText(content); @@ -430,21 +414,20 @@ function runRegexMatchers(lines, filePath, lineOffset = 0, blockContext = null, } /** Page-level analyzers that scan rendered text content (em-dash use, - * buzzword phrases, numbered section markers, aphoristic cadence). + * buzzword phrases, aphoristic cadence). * These are detector-agnostic — they work on any HTML/text source * and don't need a parsed DOM. Exported so detectHtml can call them * for `.html` files (which otherwise skip the regex engine). */ const TEXT_CONTENT_ANALYZER_IDS = [ 'em-dash-overuse', 'marketing-buzzword', - 'numbered-section-markers', 'aphoristic-cadence', ]; function runTextContentAnalyzers(content, filePath, options = {}) { const profile = options?.profile; if (!shouldRunPageAnalyzers(content, filePath)) return []; - // The 4 text-content analyzers are at indices 3-6 in REGEX_ANALYZERS. + // The 3 text-content analyzers are at indices 3-5 in REGEX_ANALYZERS. const findings = []; for (let i = 0; i < TEXT_CONTENT_ANALYZER_IDS.length; i++) { const analyzer = REGEX_ANALYZERS[3 + i]; @@ -535,7 +518,6 @@ function detectText(content, filePath, options = {}) { 'monotonous-spacing', 'em-dash-overuse', 'marketing-buzzword', - 'numbered-section-markers', 'aphoristic-cadence', 'dark-glow', ]; diff --git a/.pi/skills/impeccable/scripts/detector/registry/antipatterns.mjs b/.pi/skills/impeccable/scripts/detector/registry/antipatterns.mjs index b85f7e62f..84c69b0b8 100644 --- a/.pi/skills/impeccable/scripts/detector/registry/antipatterns.mjs +++ b/.pi/skills/impeccable/scripts/detector/registry/antipatterns.mjs @@ -210,17 +210,6 @@ const ANTIPATTERNS = [ skillSection: 'Layout & Space', skillGuideline: 'numbered section markers', }, - { - id: 'numbered-section-markers', - category: 'slop', - scopes: ['layout'], - severity: 'advisory', - name: 'Numbered section markers (01 / 02 / 03)', - description: - 'Numbered display markers as section labels (01, 02, 03) are the AI editorial scaffold one tier deeper than tracked eyebrow chips. If you find yourself reaching for them, choose a different section cadence.', - skillSection: 'Layout & Space', - skillGuideline: 'numbered section markers', - }, { id: 'em-dash-overuse', category: 'slop', diff --git a/.pi/skills/impeccable/scripts/detector/rules/checks.mjs b/.pi/skills/impeccable/scripts/detector/rules/checks.mjs index 3fdd6b2dd..9bace11b3 100644 --- a/.pi/skills/impeccable/scripts/detector/rules/checks.mjs +++ b/.pi/skills/impeccable/scripts/detector/rules/checks.mjs @@ -352,27 +352,57 @@ function isAccentColor(cssColor) { return false; } +function resolveHeroHeadingSizePx(value) { + const input = String(value || '').trim().toLowerCase(); + if (!input) return 0; + + const simpleLengthPx = (token) => { + const match = /^(-?\d*\.?\d+)\s*(px|rem|em|%)?$/.exec(String(token || '').trim()); + if (!match) return null; + const amount = Number(match[1]); + if (!Number.isFinite(amount)) return null; + if (match[2] === 'rem' || match[2] === 'em') return amount * 16; + if (match[2] === '%') return amount * 0.16; + return amount; + }; + + const direct = simpleLengthPx(input); + if (direct !== null) return direct; + + // Static CSS engines cannot resolve viewport units, but clamp's min/max + // bounds still tell us whether the heading can ever reach hero scale. + const clamp = /^clamp\((.*)\)$/.exec(input); + if (clamp) { + const parts = clamp[1].split(','); + if (parts.length === 3) { + const bounds = [simpleLengthPx(parts[0]), simpleLengthPx(parts[2])] + .filter((candidate) => candidate !== null); + if (bounds.length > 0) return Math.max(...bounds); + } + } + + return 0; +} + // Sibling-relationship rule. Anchor on a hero-scale h1, look at the // previousElementSibling, and gate on EITHER the classic tracked- // uppercase eyebrow OR the modern accent-colored bold eyebrow. function checkHeroEyebrow(opts) { const { headingTag, headingText, headingFontSize, + headingInApplicationContext, siblingTag, siblingText, siblingTextTransform, siblingFontSize, siblingLetterSpacing, siblingFontWeight, siblingColor, siblingHasAccentDashPseudo, } = opts; if (headingTag !== 'h1') return []; - // We previously gated on headingFontSize >= 48 to anchor "hero scale". - // But modern hero h1s use clamp() / vw / var(--text-*), none of which - // jsdom can resolve — the computed value comes back as "2em" or - // "var(--text-9xl)" and parseFloat returns 2 or NaN. The gate fails - // on virtually every Tailwind v4 / framework build. The other gates - // (sibling text 2-60 chars, font-size ≤ 14px, accent-bold OR - // tracked-caps) are tight enough to avoid false positives on non- - // hero h1s — a tiny tan label directly above any h1 is the - // antipattern regardless of how big the h1 ends up. + // This is specifically a marketing-hero cliché, not a ban on compact + // context labels in product UI (for example, a station name inside a tab + // panel). Browser-computed sizes are reliable; the static adapter also + // resolves ordinary px/rem/em and clamp() bounds before reaching here. + if (headingInApplicationContext) return []; + if (!(headingFontSize >= 48)) return []; if (!siblingTag) return []; // An h2 above an h1 is a different anti-pattern (heading hierarchy / dual // headings) — never an eyebrow. @@ -1938,6 +1968,7 @@ function checkElementHeroEyebrowDOM(el) { headingTag: tag, headingText: el.textContent || '', headingFontSize: parseFloat(headStyle.fontSize) || 0, + headingInApplicationContext: !!el.closest('[role="tabpanel"], [role="dialog"], [role="application"], dialog'), siblingTag: sibling.tagName.toLowerCase(), siblingText: sibling.textContent || '', siblingTextTransform: sibStyle.textTransform || '', @@ -3367,7 +3398,8 @@ function checkElementHeroEyebrow(el, style, tag, window, customPropMap) { return checkHeroEyebrow({ headingTag: tag, headingText: el.textContent || '', - headingFontSize: parseFloat(headingFontSizeRaw) || 0, + headingFontSize: resolveHeroHeadingSizePx(headingFontSizeRaw), + headingInApplicationContext: !!el.closest?.('[role="tabpanel"], [role="dialog"], [role="application"], dialog'), siblingTag: sibling.tagName.toLowerCase(), siblingText: sibling.textContent || '', siblingTextTransform: sibStyle.textTransform || '', diff --git a/.pi/skills/impeccable/scripts/hook-lib.mjs b/.pi/skills/impeccable/scripts/hook-lib.mjs index e61ea692b..50aa2f8fb 100644 --- a/.pi/skills/impeccable/scripts/hook-lib.mjs +++ b/.pi/skills/impeccable/scripts/hook-lib.mjs @@ -46,6 +46,7 @@ import path from 'node:path'; import { pathToFileURL, fileURLToPath } from 'node:url'; import { extractPlatform, loadContext } from './context.mjs'; import { IMPECCABLE_COMMAND } from './lib/provider.mjs'; +import { renderStopSlopReview } from './lib/slop-review.mjs'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); @@ -1868,22 +1869,42 @@ export const CONTRACT_MAX_CHARS = 1800; // Cap contract sections per Stop emission so many touched artifacts cannot // stack an unbounded message. export const CONTRACT_AUDIT_MAX_FILES = 3; +export const CONTRACT_EXTS = new Set(['.html', '.htm', '.astro', '.svelte', '.vue', '.jsx', '.tsx']); +export const CONTRACT_REQUIRED_FIELDS = ['UNIQUE', 'NOT-TEMPLATE', 'OWN-WORLD', 'STORY', 'FIRST VIEWPORT', 'FORM']; /** - * Extract the artifact's own direction-contract comment: the first HTML - * comment in the head of the file, when its opening chars identify it as a - * contract/concept block. Returns the trimmed, length-capped body, or null - * when the file carries none (no comment, unclosed comment, marker missing, - * or the comment starts past the head window). + * Extract the artifact's own direction-contract comment from the head of an + * HTML or component file. Supports HTML-family comments and JSX block + * comments so the contract works in the Astro/Svelte/Vue/React scaffolds the + * skill actually builds. Returns the trimmed, length-capped body, or null + * when the file carries no valid contract block. */ export function extractDirectionContract(content) { if (typeof content !== 'string' || !content) return null; const head = content.slice(0, CONTRACT_HEAD_CHARS); - const m = //.exec(head); - if (!m) return null; - const body = m[1].trim(); - if (!body || !/contract|concept/i.test(body.slice(0, CONTRACT_MARKER_CHARS))) return null; - return body.slice(0, CONTRACT_MAX_CHARS); + const candidates = []; + for (const pattern of [//g, /\{\/\*([\s\S]*?)\*\/\}/g]) { + for (const match of head.matchAll(pattern)) { + const index = match.index ?? 0; + const linePrefix = head.slice(head.lastIndexOf('\n', index - 1) + 1, index).trim(); + if (linePrefix.startsWith('//')) continue; + candidates.push({ index, body: match[1].trim() }); + } + } + candidates.sort((a, b) => a.index - b.index); + for (const candidate of candidates) { + if (!candidate.body || !/direction\s+contract|concept\s+contract/i.test(candidate.body.slice(0, CONTRACT_MARKER_CHARS))) continue; + return candidate.body.slice(0, CONTRACT_MAX_CHARS); + } + return null; +} + +export function missingDirectionContractFields(contract) { + const body = typeof contract === 'string' ? contract : ''; + return CONTRACT_REQUIRED_FIELDS.filter((field) => { + const label = field.replace(/[.*+?^${}()|[\]\\]/g, '\\$&').replace(/\s+/g, '\\s+'); + return !new RegExp(`\\b${label}\\s*:`, 'i').test(body); + }); } /** @@ -1899,7 +1920,11 @@ export function renderContractAudit(entries, opts = {}) { const shown = entries.slice(0, CONTRACT_AUDIT_MAX_FILES); const blocks = shown.map(({ filePath, contract }) => { const display = relativize(filePath, cwd); - return `${display} opens with this direction contract, written when the direction was decided:\n\n${contract}`; + const missing = missingDirectionContractFields(contract); + const integrity = missing.length > 0 + ? `\n\nContract integrity defect: missing ${missing.join(', ')}. Repair the contract and the implementation together.` + : ''; + return `${display} opens with this direction contract, written when the direction was decided:\n\n${contract}${integrity}`; }); return [ `${ENVELOPE_PREFIX} Direction-contract audit. Before finishing, audit the rendered page against the contract it opens with, promise by promise.`, @@ -1984,9 +2009,22 @@ export async function runStopHook({ stdinJson, env = {}, cwd = process.cwd(), no return result({ skipped: 'native-platform', platform, durationMs: Date.now() - started }); } + const session = ensureSession(cache, sessionId); + const needsSlopReview = session.llmSlopReviewed !== true; + const det = detector || await loadDetector(); if (!det || typeof det.detectText !== 'function') { - return result({ skipped: 'detector-missing', durationMs: Date.now() - started }); + if (!needsSlopReview) return result({ skipped: 'detector-missing', durationMs: Date.now() - started }); + session.llmSlopReviewed = true; + session.updatedAt = Date.now(); + persistCache(projectCwd, cache); + const text = renderStopSlopReview(); + return { + exitCode: 0, + stdout: payload(text, 'Stop', harness), + emission: { kind: 'stop-llm-slop-review', llmSlopReview: true }, + audit: { ...audit, emitted: true, detectorMissing: true, llmSlopReview: true, chars: text.length, durationMs: Date.now() - started }, + }; } const scanOptions = designSystemOptions(config, det, projectCwd); @@ -2013,10 +2051,11 @@ export async function runStopHook({ stdinJson, env = {}, cwd = process.cwd(), no ? configuredExt.engine === 'html' : (ext === '.html' || ext === '.htm'); - // Direction-contract audit: HTML artifacts only, at most once per file + // Direction-contract audit: HTML and component artifacts, at most once per file // per session. The flag lives on the same session cache entry the // finding dedupe uses, so a second Stop fire stays quiet about it. - if (useHtmlEngine) { + const contractCapable = useHtmlEngine || CONTRACT_EXTS.has(ext); + if (contractCapable) { const fileEntry = ensureFile(cache, sessionId, filePath); if (!fileEntry.contractAudited) { const contract = extractDirectionContract(content); @@ -2046,10 +2085,15 @@ export async function runStopHook({ stdinJson, env = {}, cwd = process.cwd(), no } audit.scannedFiles = scanned; - if (freshGroups.length === 0 && contractEntries.length === 0) { + if (freshGroups.length === 0 && contractEntries.length === 0 && !needsSlopReview) { return result({ emitted: false, skipped: 'stop-clean', durationMs: Date.now() - started }); } + if (needsSlopReview) { + session.llmSlopReviewed = true; + session.updatedAt = Date.now(); + } + // Fresh findings and first-time contract audits earn the cache write; // both mark this batch as surfaced so the next Stop fire is silent // unless new issues appear. @@ -2064,6 +2108,9 @@ export async function runStopHook({ stdinJson, env = {}, cwd = process.cwd(), no if (contractEntries.length > 0) { parts.push(renderContractAudit(contractEntries, { cwd: projectCwd })); } + if (needsSlopReview) { + parts.push(renderStopSlopReview()); + } const text = appendDesignSystemNote(parts.join('\n\n'), scanOptions); return { exitCode: 0, @@ -2074,6 +2121,7 @@ export async function runStopHook({ stdinJson, env = {}, cwd = process.cwd(), no ...(contractEntries.length > 0 ? { contractFiles: contractEntries.map((entry) => entry.filePath) } : {}), + ...(needsSlopReview ? { llmSlopReview: true } : {}), }, audit: { ...audit, @@ -2081,6 +2129,7 @@ export async function runStopHook({ stdinJson, env = {}, cwd = process.cwd(), no freshFiles: freshGroups.length, freshFindings: freshGroups.reduce((sum, group) => sum + group.findings.length, 0), ...(contractEntries.length > 0 ? { contractAudits: contractEntries.length } : {}), + ...(needsSlopReview ? { llmSlopReview: true } : {}), chars: text.length, durationMs: Date.now() - started, }, diff --git a/.pi/skills/impeccable/scripts/lib/provider.mjs b/.pi/skills/impeccable/scripts/lib/provider.mjs index 7fa928a9c..d55bc1216 100644 --- a/.pi/skills/impeccable/scripts/lib/provider.mjs +++ b/.pi/skills/impeccable/scripts/lib/provider.mjs @@ -1,4 +1,5 @@ // Source scripts default to slash commands. The provider build replaces only // this exact declaration, avoiding heuristic rewrites across executable code. export const IMPECCABLE_COMMAND_PREFIX = "/"; +export const IMPECCABLE_PROVIDER_ID = "pi"; export const IMPECCABLE_COMMAND = `${IMPECCABLE_COMMAND_PREFIX}impeccable`; diff --git a/.pi/skills/impeccable/scripts/lib/slop-review.mjs b/.pi/skills/impeccable/scripts/lib/slop-review.mjs new file mode 100644 index 000000000..30f03e34d --- /dev/null +++ b/.pi/skills/impeccable/scripts/lib/slop-review.mjs @@ -0,0 +1,30 @@ +export const LLM_ONLY_SLOP_ITEMS = Object.freeze([ + 'Monospace used merely to signal “technical” or “developer.”', + 'Light or dark mode chosen by category habit rather than the actual use scene.', + 'Everything wrapped in cards, or identical icon-heading-text cards repeated as the page structure.', + 'Hero-metric scaffolds: one oversized number, a small label, supporting stats, and an accent treatment.', + 'Decorative glassmorphism, meaningless sparklines, or generic rounded rectangles with drop shadows.', + 'A modal chosen by reflex when the task does not require interruption or protected focus.', +]); + +export function renderLlmOnlySlopReview({ manualDetector = false, automaticDetector = false, scriptsPath = null } = {}) { + const lines = [ + automaticDetector + ? 'AI_SLOP_REVIEW_REQUIRED: The automatic detector covers mechanical rules, but this harness has no reliable late review. Before finishing changed UI, inspect the authored result for detector-blind model reflexes:' + : 'AI_SLOP_REVIEW_REQUIRED: The automatic Impeccable design hook is not available for this session. Before finishing changed UI, inspect the authored result for detector-blind model reflexes:', + ...LLM_ONLY_SLOP_ITEMS.map((item) => `- ${item}`), + 'Fix reflexes, not intentional choices required by the brief or established visual authority.', + ]; + if (manualDetector && scriptsPath) { + lines.push(`Then run the mechanical detector once over the changed web UI: \`node ${scriptsPath}/detect.mjs --json \`. Do not run it earlier during concept selection.`); + } + return lines.join('\n'); +} + +export function renderStopSlopReview() { + return [ + '[impeccable@1] Detector-blind AI-slop review. The mechanical pass is complete; now inspect the rendered result for model reflexes it cannot reliably detect:', + ...LLM_ONLY_SLOP_ITEMS.map((item) => `- ${item}`), + 'Fix reflexes, not intentional choices required by the brief or established visual authority. Do not rerun the detector; this is the authored judgment layer.', + ].join('\n'); +} diff --git a/.pi/skills/impeccable/scripts/lib/surface-briefs.mjs b/.pi/skills/impeccable/scripts/lib/surface-briefs.mjs new file mode 100644 index 000000000..f83416f69 --- /dev/null +++ b/.pi/skills/impeccable/scripts/lib/surface-briefs.mjs @@ -0,0 +1,151 @@ +import fs from 'node:fs'; +import path from 'node:path'; +import { slugFromTarget } from './target-slug.mjs'; + +export const SURFACE_BRIEF_VERSION = 1; + +export function getSurfaceBriefDir(projectRoot) { + return path.join(projectRoot, '.impeccable', 'surfaces'); +} + +export function normalizeSurfaceTarget(target, { projectRoot = process.cwd() } = {}) { + if (!target || typeof target !== 'string' || !target.trim()) return null; + const trimmed = target.trim(); + if (/^https?:\/\//i.test(trimmed)) { + try { + const url = new URL(trimmed); + url.hash = ''; + url.search = ''; + return url.toString().replace(/\/$/, '') || url.origin; + } catch { + return null; + } + } + if (/^route:/i.test(trimmed)) { + const route = trimmed.slice(trimmed.indexOf(':') + 1).trim(); + if (!route.startsWith('/') || route.includes('..')) return null; + const normalizedRoute = route.split(/[?#]/, 1)[0].replace(/\/{2,}/g, '/').replace(/\/$/, '') || '/'; + return `route:${normalizedRoute}`; + } + if (trimmed === '/') return 'route:/'; + if (trimmed.startsWith('/')) { + const absolute = path.resolve(trimmed); + const relativeToProject = path.relative(projectRoot, absolute); + const isProjectFile = relativeToProject && !relativeToProject.startsWith('..') && !path.isAbsolute(relativeToProject); + if (!isProjectFile && !fs.existsSync(absolute) && !trimmed.includes('..')) { + const normalizedRoute = trimmed.split(/[?#]/, 1)[0].replace(/\/{2,}/g, '/').replace(/\/$/, '') || '/'; + return `route:${normalizedRoute}`; + } + } + const abs = path.isAbsolute(trimmed) ? trimmed : path.resolve(projectRoot, trimmed); + const rel = path.relative(projectRoot, abs); + if (!rel || rel === '.' || rel.startsWith('..') || path.isAbsolute(rel)) return null; + return rel.split(path.sep).join('/'); +} + +export function surfaceBriefPathForTarget(target, { projectRoot = process.cwd() } = {}) { + const normalized = normalizeSurfaceTarget(target, { projectRoot }); + if (!normalized) return null; + const slugInput = normalized.startsWith('route:') ? `route${normalized.slice('route:'.length)}` : normalized; + const slug = slugFromTarget(slugInput, { cwd: projectRoot }); + return slug ? path.join(getSurfaceBriefDir(projectRoot), `${slug}.md`) : null; +} + +export function parseSurfaceBrief(text, filePath = null) { + const match = String(text || '').match(/^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/); + const meta = {}; + if (match) { + for (const line of match[1].split(/\r?\n/)) { + const colon = line.indexOf(':'); + if (colon < 0) continue; + const key = line.slice(0, colon).trim(); + const raw = line.slice(colon + 1).trim(); + if (!key) continue; + if (/^(?:\[|\{|\")/.test(raw) || /^(?:true|false|null|-?\d+(?:\.\d+)?)$/.test(raw)) { + try { meta[key] = JSON.parse(raw); continue; } catch { /* keep string */ } + } + meta[key] = raw.replace(/^['"]|['"]$/g, ''); + } + } + const primaryTarget = typeof meta.primary_target === 'string' ? meta.primary_target : null; + const relatedTargets = Array.isArray(meta.related_targets) + ? meta.related_targets.filter((value) => typeof value === 'string') + : []; + return { + path: filePath, + text: String(text || ''), + body: match ? String(text || '').slice(match[0].length).trim() : String(text || '').trim(), + meta, + slug: typeof meta.slug === 'string' ? meta.slug : filePath ? path.basename(filePath, '.md') : null, + primaryTarget, + relatedTargets, + targets: [primaryTarget, ...relatedTargets].filter(Boolean), + }; +} + +export function listSurfaceBriefs(projectRoot = process.cwd()) { + const dir = getSurfaceBriefDir(projectRoot); + let names; + try { + names = fs.readdirSync(dir).filter((name) => name.endsWith('.md')).sort(); + } catch { + return []; + } + return names.flatMap((name) => { + const filePath = path.join(dir, name); + try { + return [parseSurfaceBrief(fs.readFileSync(filePath, 'utf-8'), filePath)]; + } catch { + return []; + } + }); +} + +export function resolveSurfaceBrief(projectRoot = process.cwd(), target = null) { + const briefs = listSurfaceBriefs(projectRoot); + if (!target) { + return { + brief: briefs.length === 1 ? briefs[0] : null, + candidates: briefs, + reason: briefs.length === 1 ? 'only-brief' : briefs.length > 1 ? 'ambiguous' : 'none', + }; + } + + const normalized = normalizeSurfaceTarget(target, { projectRoot }); + if (!normalized) return { brief: null, candidates: briefs, reason: 'invalid-target' }; + const exactPath = surfaceBriefPathForTarget(normalized, { projectRoot }); + const exact = briefs.find((brief) => brief.path === exactPath && (!brief.targets.length || brief.targets.includes(normalized))); + if (exact) return { brief: exact, candidates: briefs, reason: 'slug' }; + const mapped = briefs.filter((brief) => brief.targets.includes(normalized)); + return { + brief: mapped.length === 1 ? mapped[0] : null, + candidates: mapped.length > 1 ? mapped : briefs, + reason: mapped.length === 1 ? 'mapping' : mapped.length > 1 ? 'ambiguous-target' : 'not-found', + }; +} + +export function writeSurfaceBrief({ + projectRoot = process.cwd(), + primaryTarget, + relatedTargets = [], + body, +}) { + const normalizedPrimary = normalizeSurfaceTarget(primaryTarget, { projectRoot }); + if (!normalizedPrimary) throw new Error('surface brief requires a concrete project-relative primary target or URL'); + const normalizedRelated = [...new Set(relatedTargets + .map((target) => normalizeSurfaceTarget(target, { projectRoot })) + .filter((target) => target && target !== normalizedPrimary))]; + const slug = slugFromTarget(normalizedPrimary, { cwd: projectRoot }); + const filePath = surfaceBriefPathForTarget(normalizedPrimary, { projectRoot }); + fs.mkdirSync(path.dirname(filePath), { recursive: true }); + const frontmatter = [ + '---', + `version: ${SURFACE_BRIEF_VERSION}`, + `slug: ${JSON.stringify(slug)}`, + `primary_target: ${JSON.stringify(normalizedPrimary)}`, + `related_targets: ${JSON.stringify(normalizedRelated)}`, + '---', + ].join('\n'); + fs.writeFileSync(filePath, `${frontmatter}\n\n${String(body || '').trim()}\n`, 'utf-8'); + return filePath; +} diff --git a/.pi/skills/impeccable/scripts/lib/target-slug.mjs b/.pi/skills/impeccable/scripts/lib/target-slug.mjs new file mode 100644 index 000000000..025915ad5 --- /dev/null +++ b/.pi/skills/impeccable/scripts/lib/target-slug.mjs @@ -0,0 +1,33 @@ +import path from 'node:path'; + +const SLUG_MAX = 50; + +/** Derive one clone-stable slug from a concrete file path or URL. */ +export function slugFromTarget(resolved, { cwd = process.cwd() } = {}) { + if (!resolved || typeof resolved !== 'string') return null; + const trimmed = resolved.trim(); + if (!trimmed) return null; + + if (/^https?:\/\//i.test(trimmed)) { + let url; + try { url = new URL(trimmed); } catch { return null; } + return kebab(`${url.hostname}${url.pathname}`); + } + + const abs = path.isAbsolute(trimmed) ? trimmed : path.resolve(cwd, trimmed); + let rel = path.relative(cwd, abs); + if (rel.startsWith('..') || path.isAbsolute(rel)) rel = path.basename(abs); + if (!rel || rel === '.') return null; + return kebab(rel); +} + +export function kebab(value) { + const slug = String(value || '') + .toLowerCase() + .replace(/[/\\.]+/g, '-') + .replace(/[^a-z0-9-]+/g, '-') + .replace(/-+/g, '-') + .replace(/^-|-$/g, ''); + if (!slug) return null; + return slug.length <= SLUG_MAX ? slug : slug.slice(slug.length - SLUG_MAX).replace(/^-/, ''); +} diff --git a/.pi/skills/impeccable/scripts/live-browser.js b/.pi/skills/impeccable/scripts/live-browser.js index 616f1290a..47bbfefbf 100644 --- a/.pi/skills/impeccable/scripts/live-browser.js +++ b/.pi/skills/impeccable/scripts/live-browser.js @@ -2592,7 +2592,7 @@ minWidth: '16px', height: '16px', padding: '0 4px', borderRadius: '999px', background: tuneOpen ? C.brand : BP.hairline, - color: tuneOpen ? 'oklch(98% 0 0)' : 'inherit', + color: tuneOpen ? C.ink : 'inherit', fontFamily: MONO, fontSize: '9.5px', fontWeight: '600', lineHeight: '1', boxSizing: 'border-box', @@ -3131,7 +3131,7 @@ position: 'absolute', top: '2px', left: initial ? '18px' : '2px', width: '16px', height: '16px', borderRadius: '50%', - background: 'oklch(98% 0 0)', + background: C.ink, transition: 'left 0.18s ' + EASE, boxShadow: '0 1px 2px oklch(0% 0 0 / 0.2)', }); @@ -3165,7 +3165,7 @@ const b = el('button', { padding: '5px 4px', border: 'none', borderRadius: '3px', background: active ? C.brand : 'transparent', - color: active ? 'oklch(98% 0 0)' : P.text, + color: active ? C.ink : P.text, fontFamily: FONT, fontSize: '10.5px', fontWeight: '500', cursor: 'pointer', whiteSpace: 'nowrap', transition: 'background 0.1s ease, color 0.1s ease', @@ -3178,7 +3178,7 @@ segBtns.forEach(({ btn, val }) => { const on = val === o.value; btn.style.background = on ? C.brand : 'transparent'; - btn.style.color = on ? 'oklch(98% 0 0)' : P.text; + btn.style.color = on ? C.ink : P.text; }); applyParamValue(variantEl, p, o.value); queueCheckpoint('param_changed'); diff --git a/.pi/skills/impeccable/scripts/surface-brief.mjs b/.pi/skills/impeccable/scripts/surface-brief.mjs new file mode 100644 index 000000000..723f7c1b4 --- /dev/null +++ b/.pi/skills/impeccable/scripts/surface-brief.mjs @@ -0,0 +1,74 @@ +#!/usr/bin/env node +import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath, pathToFileURL } from 'node:url'; +import { resolveProjectRoot } from './context.mjs'; +import { + listSurfaceBriefs, + resolveSurfaceBrief, + surfaceBriefPathForTarget, + writeSurfaceBrief, +} from './lib/surface-briefs.mjs'; + +function summary(brief, projectRoot) { + return { + slug: brief.slug, + path: path.relative(projectRoot, brief.path).split(path.sep).join('/'), + primaryTarget: brief.primaryTarget, + relatedTargets: brief.relatedTargets, + }; +} + +function main(argv) { + const [command, target, bodyFile, ...relatedTargets] = argv; + const projectRoot = resolveProjectRoot(process.cwd(), target ? { targetPath: target } : {}); + if (command === 'path') { + const filePath = surfaceBriefPathForTarget(target, { projectRoot }); + if (!filePath) throw new Error('surface brief path requires a concrete target'); + process.stdout.write(`${path.relative(process.cwd(), filePath) || filePath}\n`); + return; + } + if (command === 'list') { + process.stdout.write(`${JSON.stringify(listSurfaceBriefs(projectRoot).map((brief) => summary(brief, projectRoot)), null, 2)}\n`); + return; + } + if (command === 'read') { + const result = resolveSurfaceBrief(projectRoot, target || null); + if (result.brief) { + process.stdout.write(result.brief.text); + return; + } + if (result.candidates.length) process.stderr.write(`${JSON.stringify(result.candidates.map((brief) => summary(brief, projectRoot)), null, 2)}\n`); + process.exit(2); + } + if (command === 'write') { + if (!target || !bodyFile) throw new Error('usage: surface-brief.mjs write '); + const filePath = writeSurfaceBrief({ + projectRoot, + primaryTarget: target, + relatedTargets, + body: fs.readFileSync(bodyFile, 'utf-8'), + }); + process.stdout.write(`${path.relative(process.cwd(), filePath) || filePath}\n`); + return; + } + throw new Error('usage: surface-brief.mjs [target] [body-file] [related-target ...]'); +} + +function isMainModule() { + if (!process.argv[1]) return false; + try { + return fs.realpathSync(fileURLToPath(import.meta.url)) === fs.realpathSync(process.argv[1]); + } catch { + return import.meta.url === pathToFileURL(process.argv[1]).href; + } +} + +if (isMainModule()) { + try { + main(process.argv.slice(2)); + } catch (error) { + process.stderr.write(`${error?.message || error}\n`); + process.exit(1); + } +} diff --git a/.qoder/skills/impeccable/SKILL.md b/.qoder/skills/impeccable/SKILL.md index 601db784d..bb25ce67d 100644 --- a/.qoder/skills/impeccable/SKILL.md +++ b/.qoder/skills/impeccable/SKILL.md @@ -1,7 +1,7 @@ --- name: impeccable description: Use when the user wants to design, redesign, shape, critique, audit, polish, clarify, distill, harden, optimize, adapt, animate, colorize, extract, or otherwise improve a frontend interface. Covers websites, landing pages, dashboards, product UI, app shells, components, forms, settings, onboarding, and empty states. Handles UX review, visual hierarchy, information architecture, cognitive load, accessibility, performance, responsive behavior, theming, anti-patterns, typography, fonts, spacing, layout, alignment, color, motion, micro-interactions, UX copy, error states, edge cases, i18n, and reusable design systems or tokens. Also use for bland designs that need to become bolder or more delightful, loud designs that should become quieter, live browser iteration on UI elements, or ambitious visual effects that should feel technically extraordinary. Not for backend-only or non-UI tasks. -version: 4.0.0-alpha.9 +version: 4.0.0-alpha.10 user-invocable: true argument-hint: "[craft|shape · audit|critique · animate|bolder|colorize|delight|layout|overdrive|quieter|typeset · adapt|clarify|distill · harden|onboard|optimize|polish · init|document|extract|live] [target]" license: Apache 2.0 @@ -10,56 +10,55 @@ allowed-tools: - Bash(node .qoder/skills/impeccable/scripts/*) --- -Designs and iterates production-grade frontend interfaces. Real working code, committed design choices, exceptional craft. +This skill gives you the tools and permission to create design that earns to be called out-of-distribution craft: Whereas before, your design work would have been safe, timid and measured, you now approach every design task as a award-winning design director with impeccable understanding for what makes exceptional design work: production-grade code, peak creativity, a clear POV, deep understanding of the needs of the client and users, and exceptional craft. -Approach every design task as the design lead at a small studio known for giving every client a visual identity that could not be mistaken for anyone else's. The client has already rejected work that felt templated; they are paying for a point of view. the model is capable of extraordinary work. Don't hold back. +Core principles: +- Go all out. No hedging, no shortcuts. The deliverable must be complete (except assets the user must provide). +- Dream big and bold. Distinct, beautiful, outstanding and highly inspiring work. +- Iterate with tools available to you (e.g. visual understanding, browser screenshots) until you think this meets the bar. ## Setup -1. Run `node .qoder/skills/impeccable/scripts/context.mjs` once per session (if the runtime shows this skill's loaded base directory, run `node /scripts/context.mjs`; keep cwd at the user's project). It prints the project's context and its directives; follow what it prints. Once its output is in the conversation, never rerun it on a later turn. -2. `craft` and `shape` are build-path exceptions: the new-work gate below owns their flow, and on unattended runs its checkpoints resolve without pausing. For any other invoked sub-command (`audit`, `polish`, `live`, ...), immediately read **`reference/.md`** after `context.mjs` (the `.native` variant from the Commands table when the platform is `ios`/`android`/`adaptive`) and follow it. This read is a hard gate: do not inspect the target, run command-specific scripts, or edit files until the reference is loaded. -3. Read at least one project file (CSS / tokens / theme / a representative component) to learn what world you're in. If PRODUCT.md's `## Platform` is `ios` or `android`, also read `reference/.md` (`adaptive` reads both). +1. Run `node .qoder/skills/impeccable/scripts/context.mjs` once per session (if the runtime shows this skill's loaded base directory, run `node /scripts/context.mjs`; keep cwd at the user's project). Pass a named source file or route as `--target `. It loads PRODUCT.md, DESIGN.md, the matching surface brief, and native-platform guidance when applicable; follow its directives and do not rerun it. +2. Before acting, load the one playbook that owns the request: the Commands table's reference for an explicit or clearly implied sub-command, or [reference/new-work.md](reference/new-work.md) for a new surface or replacement visual world. Then inspect the target and at least one representative source of incumbent visual truth (tokens, theme, CSS, component, or asset) before editing. ## How to design -**The brief wins.** Where the brief pins down a direction (a named aesthetic, an era, a place, a material, a specific font or palette), follow it exactly, including when it asks for a look this skill warns is saturated. Redirecting a pinned direction toward your own taste is a failure, not a save. - -**Existing worlds are sacred.** Most of impeccable's work happens inside a site or app that already exists. When the surface has a committed design system (real tokens, deliberately chosen faces, a palette the brand owns), work inside that world: extend it, sharpen it, leave it unmistakably the same brand, and never degrade a working page's performance. Inventing parallel colors, fonts, or styles on an existing surface is a defect, not creativity. A scoped refinement keeps the named scope's content and media footprint unless the brief explicitly changes them; build the added emphasis by recomposing what is already there through hierarchy, proportion, rhythm, and the system's own motifs. - -**New identity work reads the playbook first.** When nothing committed exists (greenfield, or a codebase with no real tokens or chosen faces), or the user explicitly asks for a redesign that discards the current look, you MUST read [reference/new-work.md](reference/new-work.md) before making any design decision. Missing DESIGN.md alone does not make work greenfield: code, tokens, chosen type, components, and assets are incumbent design authority. Preserve and extend them unless the user asked to replace them. Producing a genuinely new identity without the playbook yields the generic default this skill exists to prevent. Scoped fixes inside an existing world don't need it; the craft floor below governs them. `context.mjs` prints the appropriate authority directive. +- **The brief wins.** Honor pinned aesthetics, eras, materials, fonts, and palettes even when they conflict with a saturated-pattern warning. Redirecting a clear brief toward your taste is failure. +- **Refinement preserves; redesign replaces.** Refinement keeps the incumbent identity, behavior, copy, and everything outside scope. Ask before replacing factual copy or adding claims. Redesign keeps product truth, content, function, native affordances, and constraints, but treats the old look as evidence and anti-reference; choose a replacement world in new-work and replace DESIGN.md. Never split the difference into polish on the discarded look. +- **Visual authority is evidence, not a filename.** Missing DESIGN.md alone does not make a project greenfield; new-work decides whether to preserve, expand, or replace the incumbent world. ## Modes -Name the visitor's mode before designing; the page's grammar follows from it, and most ruined pages are one mode wearing another mode's grammar. **The mode belongs to the surface, not the subject**: a landing page for a dense technical tool is still Persuade, with Persuade's full permission to be striking; a docs page for a fashion house is still Read. Deciding a page can be plain because its subject is workmanlike is the same category error in reverse. The brief and the surface decide the mode; PRODUCT.md's `register` field survives only as a family hint (`brand` covers Persuade and Experience, `product` covers Operate and Read). Depth beyond the paragraphs below: [reference/new-work.md](reference/new-work.md) when inventing identity, [reference/operate.md](reference/operate.md) for substantial Operate and Read work. +Choose the mode from the requested surface, not the product, and persist it only in that surface brief. A tool's landing page is still Persuade; a fashion house's documentation is still Read. See [new-work.md](reference/new-work.md) for new surfaces and [operate.md](reference/operate.md) for deeper Operate/Read guidance. -**Persuade** (the surface exists to win someone over; design IS the product). The deliverable is an impression that stops the scroll, earns the click, converts. Spans every genre; don't collapse them into one look. On new surfaces, briefs that imply imagery must ship real, verified imagery; a colored rectangle where a photo belongs reads as incomplete. New Persuade surfaces take their typeface procedure and reject list from [reference/new-work.md](reference/new-work.md). - -**Operate** (the surface is a tool someone works in; design SERVES the task). A person getting something done: scanability and consistency outrank expressiveness. These surfaces earn trust by feeling native to their platform: system font stacks and workhorse UI faces are legitimate and often correct here (the Persuade reject list does not apply). The brand lives in the details: focus states, empty states, microcopy, one owned accent. The usage scene is part of the spec: an interface read outdoors, in motion, or at a glance must survive its real ambient light, and the theme follows the scene, not the category's habit. - -**Read** (the surface exists to be understood; long-form, reference, guidance). The deliverable is comprehension, and comprehension is earned twice: a structure the reader can hold in their head with nothing standing between them and the answer, and a reading experience good enough to stay in, through typographic quality and whatever visual or interactive support genuinely helps the reader follow. The brand lives in type, spacing, and small accents. - -**Experience** (the surface presents a body of work; the page IS the work). The artifact leads, the interface recedes, and the visitor meets the work itself in the first viewport at every screen size. Boldness here means trusting the work. +- **Persuade:** win someone over; design is the product. Earn attention and action. Ship real imagery when the brief needs it; follow the committed world, not category habit. +- **Operate:** help someone do work. Scanability, consistency, native expectations, and the real usage scene outrank expression. Brand lives in precise details. +- **Read:** make something understood. Structure for comprehension, then make the reading experience worth staying in. +- **Experience:** present a body of work. Let the artifact lead from the first viewport; the interface recedes. ## Craft floor -Build to this floor without announcing it. The design detector (the project hook, `node .qoder/skills/impeccable/scripts/detect.mjs --json `, or `audit`) verifies most of it mechanically; resolve every finding before finalizing. Fix real defects, but use context judgment rather than distorting intentional design to appease a false positive. Classify any intentional exception explicitly and use the hook system's narrowest appropriate waiver when it must persist. +Build to this floor without announcing it. -- Contrast: body text ≥4.5:1 against its background (placeholders too); large text ≥3:1. Gray text on a colored background looks washed out: use a darker shade of the background's own hue, or a transparency of the text color. -- Shadows describe real light: an offset and a soft blur. A zero-offset colored halo is decoration announcing itself. -- Spacing has rhythm: generous separations, tight groupings; cramped padding reads as broken; the space above a heading exceeds the space below it. Verify computed spacing, not intended spacing. -- Type: body line length 65-75ch; display clamp() max ≤6rem; letter-spacing ≥-0.04em; `text-wrap: balance` on headings; modular scale ≥1.25 between steps; light-on-dark adds 0.05-0.1 line-height. Pair faces on a contrast axis, never two similar-but-not-identical ones; one family with committed weight contrast beats a timid pair. Test headings at every breakpoint; overflow means reduce the clamp or rewrite the copy. -- Motion is part of the build: one orchestrated moment beats scattered effects; ease-out exponential curves; reveals enhance an already-visible default (content gated on a class-triggered transition ships blank in hidden tabs and headless renderers). Responsive down to mobile and visible keyboard focus are part of the floor. -- Ship real content (no placeholders, dead links, or fake controls) and cover the interaction states people will actually hit (hover, focus, disabled, loading, error, empty). -- Copy is design material: name things the way the page's own people speak, make every control say what it does, and make every error say what happened and what to do next. -- Before finishing, re-read the brief: every requirement it names must exist on the page, findable in seconds. A beautiful page missing an asked-for feature is unfinished. +- **Contrast:** body and placeholder text ≥4.5:1; large text ≥3:1. On colored surfaces, tint secondary text from that hue or the foreground instead of using gray. +- **Depth:** shadows describe light with offset and soft blur; zero-offset colored halos are decoration. +- **Spacing:** tight groups, generous separation, no cramped containers; space above a heading exceeds space below. Verify computed values. +- **Type:** body measure 65–75ch; display max 6rem and tracking floor -0.04em; balance headings; use clear scale/weight contrast; test overflow at every breakpoint. +- **Motion:** author one coherent moment instead of scattered effects. Use exponential ease-out and an already-visible default. Premium motion is not transform/opacity alone: it may add focus, depth, masks, light, or material change through blur/filter, backdrop-filter, clip-path/masks, or shadow when smooth. Always provide reduced motion. +- **Shipping:** real content, working controls, responsive composition, keyboard focus, and the states users hit: hover, disabled, loading, error, and empty. +- **Copy:** use the product's language; controls name their action, errors name the problem and recovery. +- **Coverage:** every brief requirement must exist and be findable within seconds. + +Before finishing changed UI, follow the quality guidance supplied by `context.mjs` and hooks. Context requests a manual scan only when no automatic detector is active; never add a second detector pass. ## Commands | Command | Category | Description | Reference | |---|---|---|---| -| `craft [feature]` | Build | The standard build flow with attended checkpoints | [reference/new-work.md](reference/new-work.md) | +| `craft [feature]` | Build | Deprecated alias for an ordinary new-work request | [reference/craft.md](reference/craft.md) | | `shape [feature]` | Build | Plan UX/UI before writing code | [reference/shape.md](reference/shape.md) | -| `init` | Build | Set up project context: PRODUCT.md, DESIGN.md, live config, next steps | [reference/init.md](reference/init.md) | +| `init` | Build | Capture durable product context in PRODUCT.md | [reference/init.md](reference/init.md) | | `document` | Build | Generate DESIGN.md from existing project code | [reference/document.md](reference/document.md) | | `extract [target]` | Build | Pull reusable tokens and components into design system | [reference/extract.md](reference/extract.md) | | `critique [target]` | Evaluate | UX design review with heuristic scoring | [reference/critique.md](reference/critique.md) | @@ -81,7 +80,14 @@ Build to this floor without announcing it. The design detector (the project hook | `optimize [target]` | Fix | Diagnose and fix UI performance | [reference/optimize.md](reference/optimize.md) | | `live` | Iterate | Visual variant mode: pick elements in the browser, generate alternatives | [reference/live.md](reference/live.md) | -Routing: **no argument** → read [reference/routing.md](reference/routing.md) and present the context-aware menu (never auto-run a command). **First word matches a command** (or `pin` / `unpin` / `hooks`) → load its reference (native variant on native platforms) and follow it; everything after the command name is the target. **Intent clearly maps to one command** ("fix the spacing" → `layout`, "rewrite this error" → `clarify`) → same; if two fit, ask once. **Otherwise** → general design invocation: apply Setup and this file's guidance; builds flow through the new-work gate above, whose playbook carries the direction checkpoint and the finishing pass. `teach` routes to `init`, and `craft` routes to the standard build flow with attended checkpoints. If setup diverted into `init` for a build request, finish init, refresh context, then resume. +Routing: + +- **No argument:** read [routing.md](reference/routing.md) and present its context-aware menu; never auto-run a command. +- **Explicit or clearly implied command:** load its reference (native variant on native platforms) and follow it. Ask once if two commands fit. +- **Otherwise:** treat the request as general design work. Missing PRODUCT.md routes through init; new surfaces and replacement worlds use new-work. +- `teach` aliases `init`. `craft` is a deprecated alias for ordinary new-work and adds nothing. `shape` owns task discovery, then enters new-work only for visual-world and surface-concept decisions. + +After init writes PRODUCT.md, resume without rerunning `context.mjs`. **Pin / Unpin:** `node .qoder/skills/impeccable/scripts/pin.mjs ` creates or removes a standalone `/` shortcut. Report the script's result concisely; relay stderr verbatim on error. diff --git a/.qoder/skills/impeccable/reference/android.md b/.qoder/skills/impeccable/reference/android.md index 3575c31fb..6337b9018 100644 --- a/.qoder/skills/impeccable/reference/android.md +++ b/.qoder/skills/impeccable/reference/android.md @@ -2,7 +2,7 @@ For native Android apps: Jetpack Compose, Android Views, React Native, Expo, Flutter shipping to Android hardware. -On native, register narrows. Material Design 3 governs structure, navigation, and interaction whatever the register; brand expresses through Material's theming (color roles, type scale, shape, motion). A Material-everywhere cross-platform app that also ships to iPhone still owes iOS its OS guarantees on that hardware: safe-area insets, Reduce Motion, edge-swipe back. +On native, the visitor mode narrows what expression may override. Material Design 3 governs structure, navigation, and interaction in every mode; brand expresses through Material's theming (color roles, type scale, shape, motion). A Material-everywhere cross-platform app that also ships to iPhone still owes iOS its OS guarantees on that hardware: safe-area insets, Reduce Motion, edge-swipe back. ## The Android slop test diff --git a/.qoder/skills/impeccable/reference/animate.md b/.qoder/skills/impeccable/reference/animate.md index 1a1098a5b..d51bb6d63 100644 --- a/.qoder/skills/impeccable/reference/animate.md +++ b/.qoder/skills/impeccable/reference/animate.md @@ -1,203 +1,88 @@ > **Additional context needed**: performance constraints. -Add motion that conveys state, gives feedback, and clarifies hierarchy. Cut motion that exists only for decoration. Animation fatigue is a real cost; spend the budget on the moments that need it. +Use motion to explain state, relationship, and hierarchy, or to create one authored moment the surface has earned. Decoration without purpose is animation debt. --- -## Register +## Visitor mode -Persuade + Experience: 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. +- **Persuade + Experience:** motion may carry the voice. Prefer one rehearsed focal sequence to repeated section reveals. +- **Operate + Read:** motion serves feedback, state, and continuity. Keep routine transitions fast and do not make users wait through page-load choreography. +- **Native (`ios` / `android` / `adaptive`):** follow the Motion section of [ios.md](ios.md) or [android.md](android.md), including the platform's Reduce Motion behavior. Do not apply the web tooling below. -Operate + Read: 150–250 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. +## Find the job -Native (`ios` / `android` / `adaptive`): implementation follows the Motion section of [ios.md](ios.md) / [android.md](android.md) (read it first if Setup hasn't already): system transitions and OS Reduce Motion, never the web tooling below. +Inspect the existing motion language, interaction states, target devices, and performance budget. Find only the places where motion would: ---- +- acknowledge an action; +- make a state change or spatial relationship legible; +- preserve continuity through navigation or layout change; +- direct attention at a meaningful moment; +- embody the selected visual world. -## Assess Animation Opportunities +Ask only when a material constraint cannot be inferred. Do not animate a static area merely because it exists. -Analyze where motion would improve the experience: +## Set the motion thesis -1. **Identify static areas**: - - **Missing feedback**: Actions without visual acknowledgment (button clicks, form submission, etc.) - - **Jarring transitions**: Instant state changes that feel abrupt (show/hide, page loads, route changes) - - **Unclear relationships**: Spatial or hierarchical relationships that aren't obvious - - **Lack of delight**: Functional but joyless interactions - - **Missed guidance**: Opportunities to direct attention or explain behavior +Write a short plan before implementation: -2. **Understand the context**: - - What's the personality? (Playful vs serious, energetic vs calm) - - What's the performance budget? (Mobile-first? Complex page?) - - Who's the audience? (Motion-sensitive users? Power users who want speed?) - - What matters most? (One hero animation vs many micro-interactions?) +- **Focal moment:** the one sequence or interaction that deserves authorship, if any. +- **Continuity:** the state, layout, or navigation changes that need explanation. +- **Feedback:** the controls and outcomes that need acknowledgment. +- **Budget:** which effects may be expensive, how often they run, and the reduced-motion equivalent. -If any of these are unclear from the codebase, ask the user directly to clarify what you cannot infer. +The focal moment must come from this product and surface concept. A generic fade-and-rise, hover lift, parallax layer, or scroll reveal is not a thesis. -**CRITICAL**: Respect `prefers-reduced-motion`. Always provide non-animated alternatives for users who need them. +## Choose material by meaning -## Plan Animation Strategy +Transform and opacity are reliable foundations, not the entire palette. Choose properties for what the transition communicates: -Create a purposeful animation plan: +- **Continuity and relationship:** shared-element motion, FLIP-style transforms, view transitions, or deliberate spatial movement. +- **Focus and depth:** bounded blur, filter, backdrop, light, or shadow changes. +- **Reveal and composition:** masks, clip paths, cropping, or controlled occlusion. +- **Material and energy:** color, gradient position, texture, distortion, or shader effects when the world and runtime support them. +- **State and feedback:** the smallest change that makes cause and result unmistakable. -- **Feedback layer**: Which interactions need acknowledgment? -- **Transition layer**: Which state changes need smoothing? -- **Entrance moment**: The ONE entrance worth rehearsing, where the mode invites it. Not every surface wants one. -- **Delight layer**: Where can we surprise and delight? +Do not stack techniques for spectacle. One strong material idea, carried through the focal sequence and quiet supporting states, is usually enough. -**IMPORTANT**: One well-orchestrated experience beats scattered animations everywhere. Focus on high-impact moments. +Sibling stagger is appropriate when a list appears as a list. Cap the total delay, and never reinterpret every scrolled section as a staggered list. -## Implement Animations +## Timing and easing -Add motion systematically across these categories: +Timing should express distance and consequence: -### Micro-interactions -- **Button feedback**: - - Hover: Subtle scale (1.02-1.05), color shift, shadow increase - - Click: Quick scale down then up (0.95 → 1), ripple effect - - Loading: Spinner or pulse state -- **Form interactions**: - - Input focus: Border color transition, slight scale or glow - - Validation: Shake on error, check mark on success, smooth color transitions -- **Toggle switches**: Smooth slide + color transition (200-300ms) -- **Checkboxes/radio**: Check mark animation, ripple effect -- **Like/favorite**: Scale + rotation, particle effects, color transition +| Duration | Typical use | +|---|---| +| 100–150 ms | immediate feedback | +| 150–300 ms | routine state change | +| 300–500 ms | layout, overlay, or view transition | +| 500–800 ms | a deliberately authored focal entrance | -### State Transitions -- **Show/hide**: Fade + slide (not instant), appropriate timing (200-300ms) -- **Expand/collapse**: Height transition with overflow handling, icon rotation -- **Loading states**: Skeleton screen fades, spinner animations, progress bars -- **Success/error**: Color transitions, icon animations, gentle scale pulse -- **Enable/disable**: Opacity transitions, cursor changes +Exit faster than entrance. Use natural deceleration such as `cubic-bezier(0.16, 1, 0.3, 1)` for confident arrivals; do not use bounce or elastic curves by reflex. Long feedback feels like latency. -### Entrance Animations -- **The one entrance moment** (where the mode invites it): a committed entrance for primary content (scale, parallax, or a creative effect) -- **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. +## Implement to the runtime - 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. +- Use CSS transitions and keyframes for declarative state and bounded sequences. +- Use Web Animations API or the project's existing motion library for interruption, sequencing, and dynamic values. +- Use View Transitions or shared-element techniques when continuity across states is the point. +- Use scroll-driven motion only when the scroll relationship itself carries meaning, with a robust fallback. +- Do not add a dependency for an effect the existing stack can express cleanly. -### Navigation & Flow -- **Page transitions**: Crossfade between routes, shared element transitions -- **Tab switching**: Slide indicator, content fade/slide -- **Carousel/slider**: Smooth transforms, snap points, momentum -- **Scroll effects**: Parallax layers, sticky headers with state changes, scroll progress indicators +Keep content visible in the default state so failed scripts do not hide the page. Avoid casually animating layout-driving properties such as `width`, `height`, `top`, `left`, and margins; use FLIP, transforms, or grid techniques when appropriate. Bound blur, filter, shadow, canvas, and shader work to isolated regions. Apply `will-change` only during known animation. Measure on target viewports and devices rather than assuming transform means fast. -### Feedback & Guidance -- **Hover hints**: Tooltip fade-ins, cursor changes, element highlights -- **Drag & drop**: Lift effect (shadow + scale), drop zone highlights, smooth repositioning -- **Copy/paste**: Brief highlight flash on paste, "copied" confirmation -- **Focus flow**: Highlight path through form or workflow +## Accessibility and control -### Delight Moments -- **Empty states**: Subtle floating animations on illustrations -- **Completed actions**: Confetti, check mark flourish, success celebrations -- **Easter eggs**: Hidden interactions for discovery -- **Contextual animation**: Weather effects, time-of-day themes, seasonal touches +Honor `prefers-reduced-motion` with an intentional alternative. Preserve state change and hierarchy while removing travel, parallax, flashing, or prolonged sequences. Do not replace every animation globally with `0.01ms` if that destroys useful feedback. Motion must never block focus, interaction, reading, or task completion. -## Technical Implementation +Respect autoplay and sound preferences. Any nonessential loop must stop when offscreen or hidden. -Use appropriate techniques for each animation: +## Verify -### Timing & Easing +- The focal motion is specific to the selected world and surface. +- Every supporting animation explains feedback, state, or relationship. +- Interruption and repeated use behave correctly. +- Desktop, mobile, keyboard, and reduced-motion paths remain usable. +- Expensive effects stay smooth on the target device. +- Removing an animation would lose meaning or authored character, not merely decoration. -**Duration: the 100/300/500 rule.** Timing matters more than easing for "feels right": - -| Duration | Use Case | Examples | -|----------|----------|----------| -| **100–150ms** | Instant feedback | Button press, toggle, color change | -| **200–300ms** | State changes | Menu open, tooltip, hover state | -| **300–500ms** | Layout changes | Accordion, modal, drawer | -| **500–800ms** | Entrance animations | Page load, hero reveal | - -**Easing curves (use these, not CSS defaults):** -```css -/* Recommended: natural deceleration */ ---ease-out-quart: cubic-bezier(0.25, 1, 0.5, 1); /* Smooth */ ---ease-out-quint: cubic-bezier(0.22, 1, 0.36, 1); /* Slightly snappier */ ---ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1); /* Confident, decisive */ - -/* AVOID: feel dated and tacky */ -/* bounce: cubic-bezier(0.34, 1.56, 0.64, 1); */ -/* elastic: cubic-bezier(0.68, -0.6, 0.32, 1.6); */ -``` - -**Exit animations are faster than entrances.** Use ~75% of enter duration. - -### CSS Animations -```css -/* Prefer for simple, declarative animations */ -- transitions for state changes -- @keyframes for complex sequences -- transform and opacity for reliable movement -- blur, filters, masks, clip paths, shadows, and color shifts for premium atmospheric effects when verified smooth -``` - -### JavaScript Animation -```javascript -/* Use for complex, interactive animations */ -- Web Animations API for programmatic control -- Framer Motion for React -- 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 -- **Layout safety**: Avoid casual animation of layout-driving properties (`width`, `height`, `top`, `left`, margins) -- **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) { - * { - animation-duration: 0.01ms !important; - animation-iteration-count: 1 !important; - transition-duration: 0.01ms !important; - } -} -``` - -**NEVER**: -- Use bounce or elastic easing curves; they feel dated and draw attention to the animation itself -- Animate layout properties casually (`width`, `height`, `top`, `left`, margins) when transform, FLIP, or grid-based techniques would work -- Use durations over 500ms for feedback (it feels laggy) -- Animate without purpose (every animation needs a reason) -- Ignore `prefers-reduced-motion` (this is an accessibility violation) -- Animate everything (animation fatigue makes interfaces feel exhausting) -- Block interaction during animations unless intentional - -## Verify Quality - -Test animations thoroughly: - -- **Smooth at 60fps**: No jank on target devices -- **Feels natural**: Easing curves feel organic, not robotic -- **Appropriate timing**: Not too fast (jarring) or too slow (laggy) -- **Reduced motion works**: Animations disabled or simplified appropriately -- **Doesn't block**: Users can interact during/after animations -- **Adds value**: Makes interface clearer or more delightful - -When the motion clarifies state instead of decorating it, hand off to `/impeccable polish` for the final pass. +When motion earns its place, hand off to `/impeccable polish` for the final pass. diff --git a/.qoder/skills/impeccable/reference/audit.md b/.qoder/skills/impeccable/reference/audit.md index 72815af69..70d80771a 100644 --- a/.qoder/skills/impeccable/reference/audit.md +++ b/.qoder/skills/impeccable/reference/audit.md @@ -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 - diff --git a/.qoder/skills/impeccable/reference/audit.native.md b/.qoder/skills/impeccable/reference/audit.native.md index 737d50e4f..0126fa157 100644 --- a/.qoder/skills/impeccable/reference/audit.native.md +++ b/.qoder/skills/impeccable/reference/audit.native.md @@ -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) diff --git a/.qoder/skills/impeccable/reference/bolder.md b/.qoder/skills/impeccable/reference/bolder.md index 6b44f0e38..78f5e4811 100644 --- a/.qoder/skills/impeccable/reference/bolder.md +++ b/.qoder/skills/impeccable/reference/bolder.md @@ -8,12 +8,12 @@ ## Why it reads flat -A section usually reads flat for reasons its neighbors have already solved. Look at what the rest of the page does that this section does not: the display type at full strength, the structural devices that carry meaning, the signature motif, the density and pacing, the specificity of the copy. A flat section is typically one that quietly opts out of the system's own strongest moves. The most reliable bolder pass brings the target up to the expressive level its neighbors already reach, in the system's own vocabulary rather than a new one. +A section usually reads flat for reasons its neighbors have already solved. Look at what the rest of the page does that this section does not: the display type at full strength, the structural devices that carry meaning, the signature motif, the density and pacing. A flat section is typically one that quietly opts out of the system's own strongest moves. The most reliable bolder pass brings the target up to the expressive level its neighbors already reach, in the system's own vocabulary rather than a new one. ## The amplification - **Amplify what the system already owns.** Reuse its motif and its type scale at full strength, turned up for this section rather than invented for it. The bolder version should look more like the same brand, not less. -- **Let the content carry the weight.** Flat copy makes a flat design, and no amount of size fixes a generic claim. Make every word specific enough to earn its place, and let the section's real evidence do the work that a decorative addition would otherwise fake. +- **Keep content true.** Existing claims are part of the scope: preserve them unless the user supplies replacements. If real evidence is essential to the direction but absent, ask for it. - **Commit, then clarify.** Half-measures read as noise. Make the one decisive move completely, then quiet everything around it so the move is legible. If every element got louder, the section got flatter. - **Give it its own rhythm.** The target should read as a peak in the scroll, a shift in density or pace from what surrounds it, not simply more of the same. diff --git a/.qoder/skills/impeccable/reference/clarify.md b/.qoder/skills/impeccable/reference/clarify.md index 00ee978c3..3047a9d82 100644 --- a/.qoder/skills/impeccable/reference/clarify.md +++ b/.qoder/skills/impeccable/reference/clarify.md @@ -1,288 +1,94 @@ -> **Additional context needed**: audience technical level and users' mental state in context. +> **Additional context needed**: audience knowledge and emotional state. -Find the unclear, confusing, or poorly written interface text and rewrite it. Vague copy creates support tickets and abandonment; specific copy gets users through the task. +Rewrite unclear interface text so users understand what happened, what matters, and what to do next. Preserve factual meaning, product terminology, and brand voice. +## Audit the language ---- +Read the entire interaction path, not isolated strings. Identify: -## Assess Current Copy +- ambiguous nouns, verbs, and actions; +- internal jargon or assumed knowledge; +- vague labels, outcomes, and system states; +- missing consequences, recovery, or timing; +- inconsistent terminology and capitalization; +- redundant headings, intros, helper text, and confirmations; +- text that breaks at realistic widths or in translation; +- tone that ignores stress, risk, success, or urgency. -Identify what makes the text unclear or ineffective: +Infer audience and task from product context and surrounding UI. Ask before changing factual claims, legal meaning, or a term that may be domain-specific. -1. **Find clarity problems**: - - **Jargon**: Technical terms users won't understand - - **Ambiguity**: Multiple interpretations possible - - **Passive voice**: "Your file has been uploaded" vs "We uploaded your file" - - **Length**: Too wordy or too terse - - **Assumptions**: Assuming user knowledge they don't have - - **Missing context**: Users don't know what to do or why - - **Tone mismatch**: Too formal, too casual, or inappropriate for situation +## Set the message hierarchy -2. **Understand the context**: - - Who's the audience? (Technical? General? First-time users?) - - What's the user's mental state? (Stressed during error? Confident during success?) - - What's the action? (What do we want users to do?) - - What's the constraint? (Character limits? Space limitations?) +For each state, decide: -**CRITICAL**: Clear copy helps users succeed. Unclear copy creates frustration, errors, and support tickets. +1. the one fact the user needs now; +2. the action available next; +3. supporting context that changes the decision; +4. the appropriate tone for this moment. -## Plan Copy Improvements +Say each idea once. If the heading already explains the state, the introduction should add new information or disappear. -Create a strategy for clearer communication: +## Rewrite by function -- **Primary message**: What's the ONE thing users need to know? -- **Action needed**: What should users do next (if anything)? -- **Tone**: How should this feel? (Helpful? Apologetic? Encouraging?) -- **Constraints**: Length limits, brand voice, localization considerations +### Actions and navigation -**IMPORTANT**: Good UX writing is invisible. Users should understand immediately without noticing the words. +Use a specific verb and object when the outcome is not already obvious. Labels should describe what will happen, not the gesture used to trigger it. Keep the same noun and verb for the same concept throughout the product. -## Improve Copy Systematically +For destructive actions, name the object and consequence. Prefer undo over confirmation when recovery is safe. When confirmation is necessary, name the action on both the message and button instead of using `Yes`, `No`, `OK`, or `Submit`. -Refine text across these common areas: +### Forms -### Error Messages -**Bad**: "Error 403: Forbidden" -**Good**: "You don't have permission to view this page. Contact your admin for access." +Use persistent labels; placeholders are examples, not labels. Put format and eligibility requirements before submission. Explain why information is requested only when it is not obvious. Required and optional treatment should be consistent. -**Bad**: "Invalid input" -**Good**: "Email addresses need an @ symbol. Try: name@example.com" +Validation says what needs attention and how to correct it without blaming the user. Keep related instructions near the field and announce errors accessibly. -**Principles**: -- Explain what went wrong in plain language -- Suggest how to fix it -- Don't blame the user -- Include examples when helpful -- Link to help/support if applicable - -### Form Labels & Instructions -**Bad**: "DOB (MM/DD/YYYY)" -**Good**: "Date of birth" (with placeholder showing format) - -**Bad**: "Enter value here" -**Good**: "Your email address" or "Company name" - -**Principles**: -- Use clear, specific labels (not generic placeholders) -- Show format expectations with examples -- Explain why you're asking (when not obvious) -- Put instructions before the field, not after -- Keep required field indicators clear - -### Button & CTA Text -**Bad**: "Click here" | "Submit" | "OK" -**Good**: "Create account" | "Save changes" | "Got it, thanks" +### Errors and permissions -**Principles**: -- Describe the action specifically -- Use active voice (verb + noun) -- Match user's mental model -- Be specific ("Save" is better than "OK") +An actionable error answers: -### Help Text & Tooltips -**Bad**: "This is the username field" -**Good**: "Choose a username. You can change this later in Settings." +1. what failed; +2. why, when known and useful; +3. how to recover or what alternative remains. -**Principles**: -- Add value (don't just repeat the label) -- Answer the implicit question ("What is this?" or "Why do you need this?") -- Keep it brief but complete -- Link to detailed docs if needed +Do not expose internal codes as the primary message. Do not promise a cause or resolution the system cannot know. Treat privacy, payment, deletion, access loss, and blocked work seriously; warmth is welcome, jokes are not. -### Empty States -**Bad**: "No items" -**Good**: "No projects yet. Create your first project to get started." +### Loading, empty, and success states -**Principles**: -- Explain why it's empty (if not obvious) -- Show next action clearly -- Make it welcoming, not dead-end +Loading text names the real operation and sets an honest expectation when the wait is meaningful. Show determinate progress when available; never invent progress. -### Success Messages -**Bad**: "Success" -**Good**: "Settings saved! Your changes will take effect immediately." +An empty state distinguishes first use, no results, filters, permissions, and failure. Explain the state and provide the next useful action. -**Principles**: -- Confirm what happened -- Explain what happens next (if relevant) -- Be brief but complete -- Match the user's emotional moment (celebrate big wins) - -### Loading States -**Bad**: "Loading..." (for 30+ seconds) -**Good**: "Analyzing your data... this usually takes 30-60 seconds" - -**Principles**: -- Set expectations (how long?) -- Explain what's happening (when it's not obvious) -- Show progress when possible -- Offer escape hatch if appropriate ("Cancel") +Success confirms the completed outcome and mentions the next consequence only when it changes what the user should do. Routine success should be brief. -### Confirmation Dialogs -**Bad**: "Are you sure?" -**Good**: "Delete 'Project Alpha'? This can't be undone." +### Help and instructional text -**Principles**: -- State the specific action -- Explain consequences (especially for destructive actions) -- Use clear button labels ("Delete project" not "Yes") -- Don't overuse confirmations (only for risky actions) +Helper text answers an implicit question instead of restating the control. Use progressive disclosure for uncommon detail. Link text must make sense out of context; icon-only controls need accessible names. -### Navigation & Wayfinding -**Bad**: Generic labels like "Items" | "Things" | "Stuff" -**Good**: Specific labels like "Your projects" | "Team members" | "Settings" +## Voice, accessibility, and localization -**Principles**: -- Be specific and descriptive -- Use language users understand (not internal jargon) -- Make hierarchy clear -- Consider information scent (breadcrumbs, current location) +Voice stays consistent; tone adapts to the moment. Use plain language without flattening terminology the audience genuinely knows. -## Apply Clarity Principles +- Write complete translatable messages rather than concatenated fragments. +- Keep variables and numbers structured so translators can reorder them. +- Allow expansion instead of abbreviating prematurely. +- Make alt text convey the image's information; use empty alt for decoration. +- Keep screen-reader names aligned with visible labels and outcomes. +- Do not rely on punctuation, color, or iconography to carry the message alone. -Every piece of copy should follow these rules: +Maintain a short terminology glossary when inconsistency spans the product. Do not vary words for literary effect in an interface. -1. **Be specific**: "Enter email" not "Enter value" -2. **Be concise**: Cut unnecessary words (but don't sacrifice clarity) -3. **Be active**: "Save changes" not "Changes will be saved" -4. **Be human**: "Oops, something went wrong" not "System error encountered" -5. **Tell users what to do**, not just what happened -6. **Be consistent**: Use same terms throughout (don't vary for variety) +## Verify -**NEVER**: -- Use jargon without explanation -- Blame users ("You made an error" → "This field is required") -- Be vague ("Something went wrong" without explanation) -- Use passive voice unnecessarily -- Write overly long explanations (be concise) -- Use humor for errors (be empathetic instead) -- Assume technical knowledge -- Vary terminology (pick one term and stick with it) -- Repeat information (headers restating intros, redundant explanations) -- Use placeholders as the only labels (they disappear when users type) +Read the flow in context and test: -## Verify Improvements +- comprehension without hidden product knowledge; +- actionability at errors, empty states, and decision points; +- factual accuracy and consistent terminology; +- scanability at target widths and 200% zoom; +- long names, localization expansion, pluralization, and dynamic values; +- accessible names and announced state changes; +- tone appropriate to consequence and emotional context. -Test that copy improvements work: +The final copy is as short as it can be without removing meaning or recovery. -- **Comprehension**: Can users understand without context? -- **Actionability**: Do users know what to do next? -- **Brevity**: Is it as short as possible while remaining clear? -- **Consistency**: Does it match terminology elsewhere? -- **Tone**: Is it appropriate for the situation? - -When the copy reads cleanly, hand off to `/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. +When the language reads cleanly, hand off to `/impeccable polish` for the final pass. diff --git a/.qoder/skills/impeccable/reference/codex.md b/.qoder/skills/impeccable/reference/codex.md index 18a924a91..6c550f1dc 100644 --- a/.qoder/skills/impeccable/reference/codex.md +++ b/.qoder/skills/impeccable/reference/codex.md @@ -1,105 +1,38 @@ -# Codex: Visual Direction & Asset Production +# Codex: Surface Probes & Asset Production -This file is loaded by `/impeccable craft` when the harness has native image generation (currently Codex via `image_gen`). Other harnesses skip it. It covers the two craft steps that depend on real image generation: landing the visual direction, and producing the raster assets the implementation will compose. +Load this from [new-work.md](new-work.md) only when the harness has native image generation and a substantial, high-fidelity surface would benefit from seeing the shortlisted concept before code. PRODUCT.md and DESIGN.md are preconditions. New-work has already resolved the visual world; this file must not reopen it. -Read this *before* generating any images. The order matters, and the per-step user pauses are what keep generated imagery from drifting away from the brief. +The purpose of a probe is to test composition, narrative, hierarchy, density, focal moment, signature use, and image requirements. It is not a second identity workshop. Keep DESIGN.md's palette, typography direction, material language, component character, imagery stance, and motion grammar fixed. -### Four stop points before code +## Generate the smallest useful probe set -Steps A through D each end with the user. Do not advance past any of them on your own read of the situation. +Generate one to three high-fidelity north-star comps using the native image-generation capability. Base them on the real content and the surface concepts already developed with the user. -1. **STOP after Step A questions.** Wait for answers. -2. **STOP after Step B palette generation.** Wait for "confirm palette." -3. **STOP after Step C mocks.** Wait for direction approval or delegation. -4. **Only after Step D approves a direction** do you return to the build (new-work.md's plan, then code). +- When the user shortlisted multiple concepts, show one clear expression of each. +- When one concept is already selected, vary only the structural uncertainty that the image can resolve: topology, sequence, density, hierarchy, focal composition, or interaction framing. +- Show enough beyond the opening moment to prove the concept can govern the whole requested surface. +- Do not generate a palette artifact, ask new atmosphere questions, introduce a different type voice, or invent a new motif. If the committed world cannot support the concept, return to the concept shortlist rather than changing the world. -Prior shape approval does **not** satisfy any of these. Shape's "confirm or override" advances you into Step A; it is not a substitute for it. +Treat each comp as a direction test, not a screenshot specification. Core UI text, responsive behavior, accessibility, semantics, and interaction states remain implementation responsibilities. -## Step A: Explore Directions with the User +## One approval point -Before generating anything, run a brief direction conversation grounded in the shape brief. +Show the probes together and ask what should carry forward, what feels false to the world, and whether the selected surface concept should be approved, combined, revised, or rejected. Then stop and wait. A structured simulated user counts as attended and receives the same question. -**Step A is required even when shape just produced a confirmed brief.** The shape questions and Step A questions cover different ground: shape pins purpose, content, scope; Step A pins palette, atmosphere, and named visual references for the comps you're about to generate. The only time you can skip Step A is when the user has already answered these exact palette/atmosphere/reference questions in the same session. +Do not begin code until the user approves a direction or explicitly delegates the choice. If they delegate, choose using the task brief, PRODUCT.md, and DESIGN.md, and state the evidence. Approval refines the task concept; it does not modify DESIGN.md. -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: +After approval, summarize the composition and the parts of the comp that must not be literalized. Return to new-work.md and write the direction contract from the approved surface concept before code. -- "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.)" +## Inventory implementation fidelity -**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. +Before building, inventory the approved comp's major visible ingredients and choose an implementation medium for each: semantic HTML/CSS/SVG, existing project asset, generated raster, sourced raster, icon library, canvas/WebGL, or accepted omission. -## Step B: Generate the Brand Palette First +Pay special attention to the dominant composition, signature use, image-native content, second-fold system, and any interaction the still image only implies. If the concept depends on a photograph, architectural scene, product object, portrait, or other raster-native material, do not silently replace it with generic CSS scenery. -Generate **one** palette artifact before any mocks. This is a small, focused image: typography pairing on the chosen background, primary + accent color swatches, one signature ornament or motif. Single image, single pass. +Treat the comp as a north star, not something to trace. Do not rasterize core UI text or controls. Do not substitute a different visual driver after approval without asking. -Why palette first: mocks generated against a vague color sense produce noise that drowns out the structural decisions. A confirmed palette is the first concrete contract for everything downstream. +## Produce only the assets the build needs -Show the palette to the user. Ask one question: "This is the palette I'm locking in for the mocks. Confirm, or call out what to shift?" +When clean raster ingredients are required and a scoped subagent is available and authorized, use `impeccable_asset_producer`. Give it the approved comp, output paths, required dimensions and formats, transparency needs, crop notes, and what must remain semantic code. Otherwise produce the minimum required assets with the native image-generation capability in the current thread. -**STOP and wait for confirmation.** Do not generate mocks against an unconfirmed palette. "Probably good enough" is the wrong call here; the palette is the contract for everything downstream. - -## Step C: Generate 1-3 Visual Mocks Against the Palette - -Once the palette is confirmed, generate **1 to 3** high-fidelity north-star comps. Each mock must use the confirmed palette and typography. Mocks differ in *structural* direction (hierarchy, topology, density, composition), not in color or motif. - -- Brand work: push visual identity, composition, mood, and signature motifs. -- Product work: push hierarchy, topology, density, tone, grounded in realistic product structure. -- Landing pages and long-form brand surfaces: show enough of the second fold to establish the system beyond the hero. - -Use the `image_gen` tool directly (or via the imagegen skill when available). Don't ask the user to install anything. - -## Step D: Approval Loop - -Show the comps. Ask what carries forward. Iterate until **one direction is approved** or the user explicitly delegates. - -**STOP and wait for the approval or the delegation.** Do not begin Step E or return to the build until a single direction is named. If the user delegates, pick the strongest direction and explain it from the brief, not personal taste. - -Before moving to assets, summarize what to carry into code and what *not* to literalize from the mock. This is the handoff between visual exploration and semantic implementation. - -## Step E: Mock Fidelity Inventory - -Inventory the approved mock's major visible ingredients. For each, decide implementation: semantic HTML/CSS/SVG, generated raster, sourced raster, icon library, canvas/WebGL, or accepted omission. - -Common ingredients to inventory: - -- Hero silhouette and dominant composition -- Signature motifs (planets, devices, portraits, charts, route lines, insets, badges, etc.) -- Nav and primary-action treatment (when the surface has one) -- Section sequence, especially the second fold -- Image-native content the concept depends on -- Typography, density, color/material treatment, motion cues - -Treat the mock as a north star, not a screenshot to trace. Don't rasterize core UI text. But if the live result lacks the mock's major ingredients, the implementation is wrong. - -If a photographic, architectural, product, or place-led mock becomes generic CSS scenery, decorative diagrams, bullets, or copy, stop and fix it. That's a broken implementation, not a harmless interpretation. - -Don't substitute a different hero composition or visual driver post-approval without user sign-off. - -## Step F: Asset Slicing via the Asset Producer - -Raster ingredients identified in Step E need clean production assets. Use the bundled `impeccable_asset_producer` subagent rather than producing inline. - -Spawn it as a scoped subagent. If you do not have explicit permission to use agents, stop and ask: - -```text -Asset production will work better as a scoped subagent job. Should I spawn the Impeccable asset producer subagent for this step? -``` - -Pass to the agent: - -- Approved mock path or screenshot reference -- Crop paths or a contact sheet with crop ids -- Output directory -- Required dimensions, format, transparency needs -- Avoid list -- Notes on what should remain semantic HTML/CSS/SVG instead of raster - -Attach image generation capability to the spawned agent when the harness supports it. Do **not** load image-generation reference material into the parent thread. - -Inline asset production is allowed only if the user declines subagents, the harness cannot spawn the authorized agent, or the user explicitly asks for single-thread mode. - -Prefer HTML/CSS/SVG/canvas when they can credibly reproduce an ingredient; reach for real, generated, or stock imagery when the mock or subject matter calls for actual visual content. - -## After This File - -Once Steps A through F are complete, return to the build: implement per new-work.md and SKILL.md's craft floor. The implementation builds against the confirmed palette, approved mock, and the assets the producer wrote. +Return to [new-work.md](new-work.md) for the direction contract, implementation, and finishing pass. diff --git a/.qoder/skills/impeccable/reference/colorize.md b/.qoder/skills/impeccable/reference/colorize.md index abee1acf8..dc45f88c5 100644 --- a/.qoder/skills/impeccable/reference/colorize.md +++ b/.qoder/skills/impeccable/reference/colorize.md @@ -1,257 +1,86 @@ > **Additional context needed**: existing brand colors. -Replace timid grayscale or single-accent designs with a strategic palette: pick a color strategy, choose a hue family that fits the brand, then apply color with intent. More color ≠ better. Strategic color beats rainbow vomit. +Introduce color as hierarchy, meaning, and atmosphere. Preserve confirmed brand and semantic conventions; do not replace a visual world under the guise of colorizing it. --- -## Register +## Visitor mode -Persuade + Experience: palette IS voice. Pick a color strategy first per SKILL.md (Restrained / Committed / Full palette / Drenched) and follow its dosage. Committed, Full palette, and Drenched deliberately exceed the ≤10% rule; that rule is Restrained only. Unexpected combinations are allowed; a dominant color can own the page when the chosen strategy calls for it. +- **Persuade + Experience:** color may carry the voice and own large regions when the selected world calls for it. +- **Operate + Read:** color primarily encodes action, selection, status, wayfinding, and reading hierarchy. Rarity gives an accent force. -Operate + Read: semantic-first and almost always Restrained. Accent color is reserved for primary action, current selection, and state indicators. Not decoration. Every color has a consistent meaning across every screen. +## Audit before choosing ---- +Read DESIGN.md, tokens, assets, current themes, and representative states. Identify: -## Assess Color Opportunity +- which colors are confirmed brand commitments; +- current surface, text, action, and semantic roles; +- places where grayscale obscures hierarchy or state; +- contrast failures and color-only communication; +- light/dark or data-visualization requirements; +- whether the task asks for more color or a new identity. -Analyze the current state and identify opportunities: +If a new identity is required, use [new-work.md](new-work.md). Ask only when a binding brand decision cannot be inferred. -1. **Understand current state**: - - **Color absence**: Pure grayscale? Limited neutrals? One timid accent? - - **Missed opportunities**: Where could color add meaning, hierarchy, or delight? - - **Context**: What's appropriate for this domain and audience? - - **Brand**: Are there existing brand colors we should use? +## Choose a strategy -2. **Identify where color adds value**: - - **Semantic meaning**: Success (green), error (red), warning (yellow/orange), info (blue) - - **Hierarchy**: Drawing attention to important elements - - **Categorization**: Different sections, types, or states - - **Emotional tone**: Warmth, energy, trust, creativity - - **Wayfinding**: Helping users navigate and understand structure - - **Delight**: Moments of visual interest and personality +Name the intended emotional temperature, dominant relationship, contrast range, and color dosage before editing. The strategy may be restrained or immersive; it must follow the brief and selected world rather than a fixed percentage rule. -If any of these are unclear from the codebase, ask the user directly to clarify what you cannot infer. +Build roles, not a bag of swatches: -**CRITICAL**: More color ≠ better. Strategic color beats rainbow vomit every time. Every color should have a purpose. +- canvas and elevated surfaces; +- primary and secondary text; +- action, focus, and selection; +- borders and separators; +- success, warning, error, and information; +- data categories or scales when needed. -## Plan Color Strategy +Use the project's existing color space. For a new web palette, prefer OKLCH because lightness and chroma can be adjusted predictably. Choose hue from product meaning and visual direction, never from a default category association. -Create a purposeful color introduction plan: +## Apply at system scale -- **Color palette**: What colors match the brand/context? (Choose 2-4 colors max beyond neutrals) -- **Dominant color**: Which color owns 60% of colored elements? -- **Accent colors**: Which colors provide contrast and highlights? (30% and 10%) -- **Application strategy**: Where does each color appear and why? +- Let the strongest color own a deliberate region or role instead of scattering tiny accents. +- Keep the primary action easy to find; do not spend its color on decoration. +- Tint neutrals only when the brand hue genuinely creates cohesion. Neutral gray is valid when it serves the world. +- On colored surfaces, derive secondary text from the foreground or surface hue rather than using washed-out generic gray. +- Keep semantic meanings consistent, but respect platform and domain conventions instead of assuming fixed hues. +- For data, use distinct lightness, chroma, shape, label, or pattern so color is not the only code. +- 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. -**IMPORTANT**: Color should enhance hierarchy and meaning, not create chaos. Less is more when it matters more. +Decoration without a relationship to hierarchy, state, content, or the visual world is not a color strategy. -## Introduce Color Strategically +## Contrast and perception -Add color systematically across these dimensions: +Verify computed foreground/background pairs: -### Semantic Color -- **State indicators**: - - Success: Green tones (emerald, forest, mint) - - Error: Red/pink tones (rose, crimson, coral) - - Warning: Orange/amber tones - - Info: Blue tones (sky, ocean, indigo) - - Neutral: Gray/slate for inactive states +| Content | WCAG AA minimum | +|---|---| +| body text | 4.5:1 | +| large text | 3:1 | +| controls, icons, focus indicators | 3:1 | -- **Status badges**: Colored backgrounds or borders for states (active, pending, completed, etc.) -- **Progress indicators**: Colored bars, rings, or charts showing completion or health +Do not rely on eyesight alone. Check interactive states, overlays, text on images, disabled content, and both themes. Simulate common vision deficiencies. Information conveyed by color also needs text, shape, iconography, or position. -### Accent Color Application -- **Primary actions**: Color the most important buttons/CTAs -- **Links**: Add color to clickable text (maintain accessibility) -- **Icons**: Colorize key icons for recognition and personality -- **Headers/titles**: Add color to section headers or key labels -- **Hover states**: Introduce color on interaction +When deriving OKLCH ramps, vary lightness and reduce chroma near white and black. Do not keep high chroma at extreme lightness merely to make the math uniform. Prefer explicit colors over chains of translucent overlays when alpha would make contrast context-dependent. -### Background & Surfaces -- **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 toward the brand, not "for warmth" by reflex +## Verify -**Use OKLCH for color**: It's perceptually uniform, meaning equal steps in lightness *look* equal. Great for generating harmonious scales. - -### Data Visualization -- **Charts & graphs**: Use color to encode categories or values -- **Heatmaps**: Color intensity shows density or importance -- **Comparison**: Color coding for different datasets or timeframes - -### Borders & Accents -- **Hairline borders**: 1px colored borders on full perimeter (not side-stripes; see the absolute ban on `border-left/right > 1px`) -- **Underlines**: Color underlines for emphasis or active states -- **Dividers**: Subtle colored dividers instead of gray lines -- **Focus rings**: Colored focus indicators matching brand -- **Surface tints**: A 4-8% background wash of the accent color instead of a stripe - -**NEVER**: `border-left` or `border-right` greater than 1px as a colored accent stripe. This is one of the three absolute bans in the parent skill. If you want to mark a card as "active" or "warning", use a full hairline border, a background tint, a leading glyph, or a numbered prefix. Not a side stripe. - -### Typography Color -- **Colored headings**: Use brand colors for section headings (maintain contrast) -- **Highlight text**: Color for emphasis or categories -- **Labels & tags**: Small colored labels for metadata or categories - -### Decorative Elements -- **Illustrations**: Add colored illustrations or icons -- **Shapes**: Geometric shapes in brand colors as background elements -- **Gradients**: Colorful gradient overlays or mesh backgrounds -- **Blobs/organic shapes**: Soft colored shapes for visual interest - -## Balance & Refinement - -Ensure color addition improves rather than overwhelms: - -### Maintain Hierarchy -- **Dominant color** (60%): Primary brand color or most used accent -- **Secondary color** (30%): Supporting color for variety -- **Accent color** (10%): High contrast for key moments -- **Neutrals** (remaining): Gray/black/white for structure - -### Accessibility -- **Contrast ratios**: Ensure WCAG compliance (4.5:1 for text, 3:1 for UI components) -- **Don't rely on color alone**: Use icons, labels, or patterns alongside color -- **Test for color blindness**: Verify red/green combinations work for all users - -### Cohesion -- **Consistent palette**: Use colors from defined palette, not arbitrary choices -- **Systematic application**: Same color meanings throughout (green always = success) -- **Temperature consistency**: Warm palette stays warm, cool stays cool - -**NEVER**: -- 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 -- Violate WCAG contrast requirements -- Use color as the only indicator (accessibility issue) -- Make everything colorful (defeats the purpose) -- Default to purple-blue gradients (AI slop aesthetic) - -## Verify Color Addition - -Test that colorization improves the experience: - -- **Better hierarchy**: Does color guide attention appropriately? -- **Clearer meaning**: Does color help users understand states/categories? -- **More engaging**: Does the interface feel warmer and more inviting? -- **Still accessible**: Do all color combinations meet WCAG standards? -- **Not overwhelming**: Is color balanced and purposeful? +- Every color has a stable role or a world-specific atmospheric purpose. +- Attention lands on the intended action, content, or state. +- The palette works across quiet, dense, interactive, error, and empty states. +- Light and dark themes are each composed, not mechanically inverted. +- Contrast and non-color cues pass in all relevant states. +- The result is recognizably this product, not a generic “colorful” treatment. When the palette earns its place, hand off to `/impeccable polish` for the final pass. ## Live-mode signature params -When invoked from live mode, each variant MUST declare a `color-amount` param so the user can dial between a restrained accent and a drenched surface without regeneration. Author the variant's CSS against `var(--p-color-amount, 0.5)`, typically as the alpha multiplier on backgrounds, or as a scaling factor on the chroma axis in an OKLCH expression. 0 = neutral/monochrome, 1 = full saturation / dominant coverage. +When invoked from live mode, every variant declares a `color-amount` parameter. Author CSS against `var(--p-color-amount, 0.5)` so the user can move from neutral to the variant's full color strategy without regeneration. ```json {"id":"color-amount","kind":"range","min":0,"max":1,"step":0.05,"default":0.5,"label":"Color amount"} ``` -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). +Add at most two variant-specific parameters, such as palette, temperature, or tint behavior. Follow [live.md](live.md)'s parameter contract. diff --git a/.qoder/skills/impeccable/reference/craft.md b/.qoder/skills/impeccable/reference/craft.md index cc7f98d37..dbbc9402c 100644 --- a/.qoder/skills/impeccable/reference/craft.md +++ b/.qoder/skills/impeccable/reference/craft.md @@ -1,3 +1,5 @@ # Craft (deprecated alias) -`craft` is a deprecated alias for the standard build flow. Builds route through SKILL.md's new-work gate; [new-work.md](new-work.md) carries the direction checkpoint ("Decide, then build"), the identity playbook, and the finishing pass. Invoking `craft` explicitly forces the attended checkpoints: pause at the stated direction for confirmation, and follow [codex.md](codex.md)'s mock flow when the harness generates images. Nothing else differs from a plain build request. +`craft` is a deprecated alias for an ordinary request to make new visual work. It adds no setup, interview, checkpoint, tool, or quality behavior. Apply SKILL.md's normal routing: create missing PRODUCT.md through [init.md](init.md), then follow [new-work.md](new-work.md) for visual authority, world and surface decisions, implementation, and finish. + +Do not tell users they need to invoke `craft`. Natural requests such as “build this feature,” “make a landing page,” or “redesign this screen” use the same flow. diff --git a/.qoder/skills/impeccable/reference/critique.md b/.qoder/skills/impeccable/reference/critique.md index e89900ee7..f3b7579ba 100644 --- a/.qoder/skills/impeccable/reference/critique.md +++ b/.qoder/skills/impeccable/reference/critique.md @@ -43,13 +43,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 @@ -115,11 +115,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. @@ -176,7 +176,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. 2. **Pass the structured metadata** through `IMPECCABLE_CRITIQUE_META` (JSON), then run the write command: ```bash diff --git a/.qoder/skills/impeccable/reference/delight.md b/.qoder/skills/impeccable/reference/delight.md index 596cff09e..40aaddc34 100644 --- a/.qoder/skills/impeccable/reference/delight.md +++ b/.qoder/skills/impeccable/reference/delight.md @@ -1,302 +1,70 @@ -> **Additional context needed**: what's appropriate for the domain (playful vs professional vs quirky vs elegant). +> **Additional context needed**: the brand's emotional range. -Find the moments where personality and unexpected polish would turn a functional interface into one users remember and tell other people about. Add only where the moment earns it; delight everywhere reads as noise. +Make the experience memorable at moments that earn it. Delight is not a layer of generic whimsy; it is product character revealed through a useful interaction, a humane response, or an unexpectedly considered detail. --- -## Register +## Visitor mode -Persuade + Experience: delight can be distributed across copy voice, section transitions, discovery rewards, seasonal touches, personality across the whole surface. +- **Persuade + Experience:** personality may run through voice, composition, motion, and discovery, provided the artifact remains the focus. +- **Operate + Read:** concentrate delight at meaningful moments such as first use, completion, recovery, or mastery. Reliability carries everything else. -Operate + Read: delight at specific moments, not pages. Completion, first-time actions, error recovery, milestone crossings. Reliability and consistency carry the rest of the experience; delight pushed everywhere reads as noise. +## Find the opportunity ---- +Inspect the target, DESIGN.md, product voice, repeated-use frequency, and emotional context. Look for: -## Assess Delight Opportunities +- effort worth acknowledging; +- waiting that can become informative; +- an empty or first-use state that can orient; +- an error or recovery moment that needs empathy; +- an interaction whose physical or verbal response could express the brand; +- a useful capability people might enjoy discovering. -Identify where delight would enhance (not distract from) the experience: +Do not manufacture a celebration for an ordinary click. Ask only when the brand's emotional range or the stakes cannot be inferred. -1. **Find natural delight moments**: - - **Success states**: Completed actions (save, send, publish) - - **Empty states**: First-time experiences, onboarding - - **Loading states**: Waiting periods that could be entertaining - - **Achievements**: Milestones, streaks, completions - - **Interactions**: Hover states, clicks, drags - - **Errors**: Softening frustrating moments - - **Easter eggs**: Hidden discoveries for curious users +## Define one delight thesis -2. **Understand the context**: - - What's the brand personality? (Playful? Professional? Quirky? Elegant?) - - Who's the audience? (Tech-savvy? Creative? Corporate?) - - What's the emotional context? (Accomplishment? Exploration? Frustration?) - - What's appropriate? (Banking app ≠ gaming app) +State in one sentence what the user should feel and why that feeling belongs to this product. Then choose the smallest system that can deliver it: -3. **Define delight strategy**: - - **Subtle sophistication**: Refined micro-interactions (luxury brands) - - **Playful personality**: Whimsical illustrations and copy (consumer apps) - - **Helpful surprises**: Anticipating needs before users ask (productivity tools) - - **Sensory richness**: Satisfying sounds, smooth animations (creative tools) +- a distinctive response to a meaningful action; +- product-specific language that clarifies while carrying voice; +- an interaction or transition with a recognizable material behavior; +- an illustration, sound, haptic, or environmental detail grounded in the product world; +- a discovery reward that reveals real utility. -If any of these are unclear from the codebase, ask the user directly to clarify what you cannot infer. +Derive the treatment from product mechanism and visual world, not a stock catalog. -**CRITICAL**: Delight should enhance usability, never obscure it. If users notice the delight more than accomplishing their goal, you've gone too far. +## Build for the emotional moment -## Delight Principles +- **Success:** match the response to the effort and consequence. Major milestones can expand; routine saves should simply feel certain. +- **Waiting:** show truthful progress, useful context, or product-specific activity. Never fake work or delay completion to stage a flourish. +- **Empty and first use:** make the next action clear before adding personality. +- **Error and recovery:** lead with the problem and recovery. Warmth may reduce stress; jokes must not trivialize loss, money, privacy, or blocked work. +- **Repeated interaction:** keep the response satisfying after the hundredth use. Variation is useful only when it remains coherent and predictable enough to trust. +- **Discovery:** reward curiosity without hiding required functionality. -Follow these guidelines: +Copy must use the product's language. Generic whimsy is worse than neutral clarity. -### Delight Amplifies, Never Blocks -- Delight moments should be quick (< 1 second) -- Never delay core functionality for delight -- Make delight skippable or subtle -- Respect user's time and task focus +## Protect the experience -### Surprise and Discovery -- Hide delightful details for users to discover -- Reward exploration and curiosity -- Don't announce every delight moment -- Let users share discoveries with others +Delight must not: -### Appropriate to Context -- Match delight to emotional moment (celebrate success, empathize with errors) -- Respect the user's state (don't be playful during critical errors) -- Match brand personality and audience expectations -- Cultural sensitivity (what's delightful varies by culture) +- delay, block, or obscure the primary task; +- override platform conventions or accessibility; +- add unrequested factual claims; +- play sound without consent or ignore mute settings; +- become mandatory, unskippable, or exhausting on repeat; +- add a dependency or asset cost disproportionate to the moment. -### Compound Over Time -- Delight should remain fresh with repeated use -- Vary responses (not same animation every time) -- Reveal deeper layers with continued use -- Build anticipation through patterns +For authored motion, load [animate.md](animate.md). Respect reduced motion, screen readers, keyboard use, touch, localization, and cultural context. Nonessential loops stop when hidden. Make celebration intensity proportional to frequency and consequence. -## Delight Techniques +## Verify -Add personality and joy through these methods: +- The moment is specific enough that a neighboring product could not use it unchanged. +- It improves comprehension, confidence, motivation, or emotional recovery. +- The interface remains fast and obvious without the flourish. +- Repetition does not turn charm into friction. +- Reduced-motion, muted, keyboard, touch, and localized paths work. +- The result feels like the selected world, not a generic “delight” treatment. -### Micro-interactions & Animation - -**Button delight**: -```css -/* Satisfying button press */ -.button { - transition: transform 0.1s, box-shadow 0.1s; -} -.button:active { - transform: translateY(2px); - box-shadow: 0 2px 4px rgba(0,0,0,0.2); -} - -/* Ripple effect on click */ -/* Smooth lift on hover */ -.button:hover { - transform: translateY(-2px); - transition: transform 0.2s cubic-bezier(0.25, 1, 0.5, 1); /* ease-out-quart */ -} -``` - -**Loading delight**: -- Playful loading animations (not just spinners) -- Personality in loading messages (write product-specific ones, not generic AI filler) -- Progress indication with encouraging messages -- Skeleton screens with subtle animations - -**Success animations**: -- Checkmark draw animation -- Confetti burst for major achievements -- Gentle scale + fade for confirmation -- Satisfying sound effects (subtle) - -**Hover surprises**: -- Icons that animate on hover -- Color shifts or glow effects -- Tooltip reveals with personality -- Cursor changes (custom cursors for branded experiences) - -### Personality in Copy - -**Playful error messages**: -``` -"Error 404" -"This page is playing hide and seek. (And winning)" - -"Connection failed" -"Looks like the internet took a coffee break. Want to retry?" -``` - -**Encouraging empty states**: -``` -"No projects" -"Your canvas awaits. Create something amazing." - -"No saved articles" -"Nothing on the nightstand yet. Save a story for later." -``` - -**Playful labels & tooltips**: -``` -"Delete" -"Send to void" (for playful brand) - -"Help" -"Rescue me" (tooltip) -``` - -**IMPORTANT**: Match copy personality to brand. Banks shouldn't be wacky, but they can be warm. - -### Illustrations & Visual Personality - -**Custom illustrations**: -- Empty state illustrations (not stock icons) -- Error state illustrations (friendly monsters, quirky characters) -- Loading state illustrations (animated characters) -- Success state illustrations (celebrations) - -**Icon personality**: -- Custom icon set matching brand personality -- Animated icons (subtle motion on hover/click) -- Illustrative icons (more detailed than generic) -- Consistent style across all icons - -**Background effects**: -- Subtle particle effects -- Gradient mesh backgrounds -- Geometric patterns -- Parallax depth -- Time-of-day themes (morning vs night) - -### Satisfying Interactions - -**Drag and drop delight**: -- Lift effect on drag (shadow, scale) -- Snap animation when dropped -- Satisfying placement sound -- Undo toast ("Dropped in wrong place? [Undo]") - -**Toggle switches**: -- Smooth slide with spring physics -- Color transition -- Haptic feedback on mobile -- Optional sound effect - -**Progress & achievements** (Operate surfaces with recurring tasks only; gamification on editorial, portfolio, or one-visit surfaces reads as noise): -- Streak counters with celebratory milestones -- Progress bars that "celebrate" at 100% -- Badge unlocks with animation -- Playful stats ("You're on fire! 5 days in a row") - -**Form interactions**: -- Input fields that animate on focus -- Checkboxes with a satisfying scale pulse when checked -- Success state that celebrates valid input -- Auto-grow textareas - -### Sound Design - -**Subtle audio cues** (when appropriate): -- Notification sounds (distinctive but not annoying) -- Success sounds (satisfying "ding") -- Error sounds (empathetic, not harsh) -- Typing sounds for chat/messaging -- Ambient background audio (very subtle) - -**IMPORTANT**: -- Respect system sound settings -- Provide mute option -- Keep volumes quiet (subtle cues, not alarms) -- Don't play on every interaction (sound fatigue is real) - -### Easter Eggs & Hidden Delights - -**Discovery rewards**: -- Konami code unlocks special theme -- Hidden keyboard shortcuts (Cmd+K for special features) -- Hover reveals on logos or illustrations -- Alt text jokes on images (for screen reader users too!) -- A view-source or console colophon (a note on the typefaces, a thank-you, a hiring line) - -**Seasonal touches**: -- Holiday themes (subtle, tasteful) -- Seasonal color shifts -- Weather-based variations -- Time-based changes (dark at night, light during day) - -**Contextual personality**: -- Different messages based on time of day -- Responses to specific user actions -- Randomized variations (not same every time) -- Progressive reveals with continued use - -### Loading & Waiting States - -**Make waiting engaging**: -- Interesting loading messages that rotate -- Progress bars with personality -- Mini-games during long loads -- Fun facts or tips while waiting -- Countdown with encouraging messages - -``` -Loading messages: write ones specific to your product, not generic AI filler: -- "Syncing with your team's changes..." -- "Fetching this week's issue..." -- "Developing your photos..." -- "Checking tomorrow's tide tables..." -``` - -**WARNING**: Avoid cliched loading messages like "Herding pixels", "Teaching robots to dance", "Consulting the magic 8-ball", "Counting backwards from infinity". These are AI-slop copy, instantly recognizable as machine-generated. Write messages that are specific to what your product actually does. - -### Celebration Moments - -**Success celebrations**: -- Confetti for major milestones -- Animated checkmarks for completions -- Progress bar celebrations at 100% -- "Achievement unlocked" style notifications -- Personalized messages ("You published your 10th article!") - -**Milestone recognition** (same scoping as Progress & achievements: Operate surfaces with recurring tasks): -- First-time actions get special treatment -- Streak tracking and celebration -- Progress toward goals -- Anniversary celebrations - -## Implementation Patterns - -**Animation libraries**: -- Framer Motion (React) -- GSAP (universal) -- Lottie (After Effects animations) -- Canvas confetti (party effects) - -**Sound libraries**: -- Howler.js (audio management) -- Use-sound (React hook) - -**Physics libraries**: -- React Spring (spring physics) -- Popmotion (animation primitives) - -**IMPORTANT**: File size matters. Compress images, optimize animations, lazy load delight features. - -**NEVER**: -- Delay core functionality for delight -- Force users through delightful moments (make skippable) -- Use delight to hide poor UX -- Overdo it (less is more) -- Ignore accessibility (animate responsibly, provide alternatives) -- Make every interaction delightful (special moments should be special) -- Sacrifice performance for delight -- Be inappropriate for context (read the room) - -## Verify Delight Quality - -Test that delight actually delights: - -- **User reactions**: Do users smile? Share screenshots? -- **Doesn't annoy**: Still pleasant after 100th time? -- **Doesn't block**: Can users opt out or skip? -- **Performant**: No jank, no slowdown -- **Appropriate**: Matches brand and context -- **Accessible**: Works with reduced motion, screen readers - -When the moments feel earned, hand off to `/impeccable polish` for the final pass. +When the personality feels earned, hand off to `/impeccable polish` for the final pass. diff --git a/.qoder/skills/impeccable/reference/document.md b/.qoder/skills/impeccable/reference/document.md index 0d38eb568..121a00ab1 100644 --- a/.qoder/skills/impeccable/reference/document.md +++ b/.qoder/skills/impeccable/reference/document.md @@ -1,6 +1,6 @@ Generate a `DESIGN.md` file at the project root that captures the current visual design system, so AI agents generating new screens stay on-brand. -DESIGN.md follows the [official DESIGN.md format spec](https://raw.githubusercontent.com/google-labs-code/design.md/main/docs/spec.md): YAML frontmatter carrying machine-readable design tokens, followed by a markdown body with exactly six sections in a fixed order. **Tokens are normative; prose provides context for how to apply them.** Sections may be omitted when not relevant, but **do not reorder them and do not rename them**. Section headers must match the spec character-for-character so the file stays parseable by other DESIGN.md-aware tools (Stitch itself, awesome-design-md, skill-rest, etc.). +DESIGN.md follows the [official DESIGN.md format spec](https://raw.githubusercontent.com/google-labs-code/design.md/main/docs/spec.md): optional YAML frontmatter carrying machine-readable design tokens, followed by up to eight markdown sections in a fixed order. **Tokens are normative; prose provides context for how to apply them.** Sections may be omitted when not relevant, but those present stay in the specified order. Use the canonical headings below so the file remains portable across DESIGN.md-aware tools. ## The frontmatter: token schema @@ -43,26 +43,28 @@ components: 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. +- **Colors accept any valid CSS color string.** Hex is the recommended default for portability, but preserve an incumbent `rgb()`, `hsl()`, `oklch()`, wide-gamut, or mixed-color value when it is the project's normative source. 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 (`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) +## The markdown body: eight sections (canonical order) 1. `## Overview` 2. `## Colors` 3. `## Typography` -4. `## Elevation` -5. `## Components` -6. `## Do's and Don'ts` +4. `## Layout` +5. `## Elevation & Depth` +6. `## Shapes` +7. `## Components` +8. `## Do's and Don'ts` -Optional evocative subtitles are allowed in the form `## 2. Colors: The [Name] Palette` (Stitch's own outputs do this), but the literal word in each header (Overview, Colors, Typography, Elevation, Components, Do's and Don'ts) must be present. Do NOT add extra top-level sections (Layout Principles, Responsive Behavior, Motion, Agent Prompt Guide). Fold that content into the six spec sections where it naturally belongs. +Omit irrelevant sections rather than filling them with invented rules. Put responsive layout in Layout, depth in Elevation & Depth, radius and form language in Shapes, and per-component behavior in Components. Unknown sections are preserved by the format, but new visual guidance should use the canonical structure whenever it fits. ## When to run -- 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. +- New-work found a coherent incumbent visual system but no `DESIGN.md`. +- The first implementation of a new world is complete and its provisional decisions need to be carbonized. - An existing `DESIGN.md` is stale (the design has drifted). - Before a large redesign, to capture the current state as a reference. @@ -71,9 +73,9 @@ 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 init, nothing built yet). Interview for five high-level answers, write a minimal DESIGN.md marked ``. Re-run in scan mode once there's code. +- **Seed mode**: the project is pre-implementation. Ensure PRODUCT.md exists, then reuse new-work's visual-world workshop and write its directional DESIGN.md 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. +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` requests new-work's world workshop, but it does not authorize replacing coherent code: when an incumbent system exists, offer scan mode or route an explicit identity-replacement request through new-work. ## Scan mode (approach C: auto-extract, then confirm descriptive language) @@ -97,7 +99,8 @@ Build a structured draft from the discovered tokens. For each token class: - **Typography**: Map observed sizes and weights to the Material hierarchy (display / headline / title / body / label). Note font-family stacks and the scale ratio. - **Elevation**: Catalogue the shadow vocabulary. If the project is flat and uses tonal layering instead, that's a valid answer; state it explicitly. - **Components**: For each common component (button, card, input, chip, list item, tooltip, nav), extract shape (radius), color assignment, hover/focus treatment, internal padding. -- **Spacing + layout**: Fold into Overview or relevant Components. The spec does NOT have a Layout section. +- **Layout + spacing**: Extract grid, container, breakpoint, rhythm, and density behavior into Layout. +- **Shapes**: Extract radius, corner, border, clipping, and recurring form behavior into Shapes. ### Step 2b: Stage the frontmatter @@ -112,19 +115,19 @@ Skip anything the project doesn't have. Empty scale keys or fabricated tokens po ### Step 3: Ask the user for qualitative language -The following require creative input that cannot be auto-extracted. Group them into one `AskUserQuestion` interaction: +The following require creative input that cannot be auto-extracted. Ask them in two structured rounds of no more than three questions each (or the harness's lower limit), waiting between rounds: - **Creative North Star**: a single named metaphor for the whole system ("The Editorial Sanctuary", "The Golden State Curator", "The Lab Notebook"). Offer 2-3 options that honor PRODUCT.md's brand personality. -- **Overview voice**: mood adjectives, aesthetic philosophy in 2-3 sentences, anti-references (what the system should not feel like). +- **Overview voice**: mood adjectives, aesthetic philosophy in 2-3 sentences, and any confirmed visual anti-reference. - **Color character** (for auto-extracted colors): descriptive names ("Deep Muted Teal-Navy", not "blue-800"). Suggest 2-3 options per key color based on hue/saturation. - **Elevation philosophy**: flat/layered/lifted. If shadows exist, is their role ambient or structural? - **Component philosophy**: the feel of buttons, cards, inputs in one phrase ("tactile and confident" vs. "refined and restrained"). -Quote a line from PRODUCT.md when possible so the user sees their own strategic language carry forward. +Carry a line from PRODUCT.md only when it is a durable brand commitment that actually constrains the visual system. Page strategy and surface concepts do not belong here. ### Step 4: Write DESIGN.md -The file opens with the YAML frontmatter staged in Step 2b (schema documented at the top of this reference), then the markdown body using the structure below. Headers must match character-for-character. Optional evocative subtitles (e.g. `## 2. Colors: The Coastal Palette`) are allowed. +The file opens with the YAML frontmatter staged in Step 2b (schema documented at the top of this reference), then the markdown body using the canonical structure below. ```markdown --- @@ -136,13 +139,13 @@ colors: # Design System: [Project Title] -## 1. Overview +## Overview **Creative North Star: "[Named metaphor in quotes]"** -[2-3 paragraph holistic description: personality, density, aesthetic philosophy. Start from the North Star and work outward. State what this system explicitly rejects (pulled from PRODUCT.md's anti-references). End with a short **Key Characteristics:** bullet list.] +[2-3 paragraph holistic description: personality, density, and aesthetic philosophy. Start from the North Star and work outward. State only confirmed visual rejections. End with a short **Key Characteristics:** bullet list.] -## 2. Colors +## Colors [Describe the palette character in one sentence.] @@ -162,7 +165,7 @@ colors: ### Named Rules (optional, powerful) **The [Rule Name] Rule.** [Short, forceful prohibition or doctrine, e.g. "The One Voice Rule. The primary accent is used on ≤10% of any given screen. Its rarity is the point."] -## 3. Typography +## Typography **Display Font:** [Family] (with [fallback]) **Body Font:** [Family] (with [fallback]) @@ -180,7 +183,11 @@ colors: ### Named Rules (optional) **The [Rule Name] Rule.** [Short doctrine about type use.] -## 4. Elevation +## Layout + +[Describe the grid or spatial model, container behavior, density, responsive changes, and the spacing rhythm. Include exact values only when observed.] + +## Elevation & Depth [One paragraph: does this system use shadows, tonal layering, or a hybrid? If "no shadows", say so explicitly and describe how depth is conveyed instead.] @@ -191,7 +198,11 @@ colors: ### Named Rules (optional) **The [Rule Name] Rule.** [e.g. "The Flat-By-Default Rule. Surfaces are flat at rest. Shadows appear only as a response to state (hover, elevation, focus)."] -## 5. Components +## Shapes + +[Describe the form language: corner/radius strategy, borders, clipping, and any recurring silhouette or geometry.] + +## Components For each component, lead with a short character line, then specify shape, color assignment, states, and any distinctive behavior. @@ -223,16 +234,16 @@ For each component, lead with a short character line, then specify shape, color ### [Signature Component] (optional; if the project has a distinctive custom component worth documenting) [Description.] -## 6. Do's and Don'ts +## Do's and Don'ts -Concrete, forceful guardrails. Lead each with "Do" or "Don't". Be specific: include exact colors, pixel values, and named anti-patterns the user mentioned in PRODUCT.md. **Every anti-reference in PRODUCT.md should show up here as a "Don't" with the same language**, so the visual spec carries the strategic line through. Quote PRODUCT.md directly where possible: if PRODUCT.md says *"avoid dark mode with purple gradients, neon accents, glassmorphism"*, the Don'ts here should repeat that by name. +Concrete visual guardrails grounded in the incumbent implementation or the user's chosen world. Lead each with "Do" or "Don't" and include exact values only when established. Do not turn a task-specific concept or surface strategy into a system-wide prohibition. ### Do: - **Do** [specific prescription with exact values / named rule]. - **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** [...] ``` @@ -307,7 +318,7 @@ The `html` and `css` fields must be **self-contained, drop-in snippets** that re Aim for a tight set of **5-10 components** that best represent the visual system: - **Canonical primitives (always include if the project has them):** button (each variant as a separate component entry), input/text field, navigation, chip/tag, card. -- **Signature components (include if distinctive):** hero CTA, featured card, filter pill, a distinctive table or list-row treatment, any custom pattern the user mentioned as important in PRODUCT.md. +- **Signature components (include if distinctive):** the recurring custom patterns that actually define the implemented system. - **Skip the rest.** Utility components, form building blocks, wrapper layouts: not worth documenting unless visually distinctive. If the project has **no component library yet** (bare landing page, new project), synthesize canonical primitives from the tokens using best-practice defaults consistent with the DESIGN.md's rules. Every `.impeccable/design.json` has *something* to render, even on day zero. @@ -338,64 +349,40 @@ Your own write is the freshest source; subsequent commands in this session don't ## Seed mode -For projects with no visual system to extract yet. Produces a minimal scaffold, not a full spec. +For projects with no visual system to extract yet. Produces a user-chosen visual-world scaffold, not a fabricated token spec. -### Step 1: Confirm seed mode +### Step 1: Route through new-work's workshop -Before interviewing: "There's no existing visual system to scan. I'll ask five quick questions to seed a starter DESIGN.md. You can re-run `/impeccable document` once there's code, to capture the real tokens and components. OK?" +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 the user prefers to skip, stop. No file. +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. -### Step 2: Five questions +If new-work already completed the workshop in this session, use its chosen direction directly. Do not ask again. -Group into one `AskUserQuestion` interaction. Options must be concrete. +### Step 2: Write seed DESIGN.md -1. **Color strategy.** Pick one: - - Restrained: tinted neutrals + one accent ≤10% - - Committed: one saturated color carries 30–60% of the surface - - Full palette: 3–4 named color roles, each deliberate - - Drenched: the surface IS the color - - Then: one hue family or anchor reference ("deep teal", "mustard", "Klim #ff4500 orange"). - -2. **Typography direction.** Pick one (specific fonts come later): - - Serif display + sans body - - Single sans (warm / technical / geometric / humanist; pick a feel) - - Display + mono - - Mono-forward - - Editorial script + sans - -3. **Motion energy.** Pick one: - - Restrained: state changes only - - Responsive: feedback + transitions, no choreography - - Choreographed: orchestrated entrances, scroll-driven sequences - -4. **Three named references.** Brands, products, printed objects. Not adjectives. - -5. **One anti-reference.** What it should NOT feel like. Also named. - -### Step 3: Write seed DESIGN.md - -Use the six-section spec from Scan mode. Populate what the interview answers; leave the rest as honest placeholders. The seed is a scaffold, not a fabricated spec. +Use the canonical section order from Scan mode. Populate the selected workshop direction and leave unresolved implementation facts as honest placeholders. The seed commits a world and its invariants; it does not pretend implementation tokens already exist. Lead the file with: ```markdown - + ``` Per-section guidance in seed mode: -- **Overview**: Creative North Star and philosophy phrased from the answers (color strategy + motion energy + references). Reference the user's anti-reference directly. -- **Colors**: Color strategy as a Named Rule (e.g. *"The Drenched Rule. The surface IS the color."*). Hue family or anchor reference. No hex values; mark as `[to be resolved during implementation]`. -- **Typography**: the direction the user picked (e.g. "Serif display + sans body"). No font names yet: `[font pairing to be chosen at implementation]`. -- **Elevation**: inferred from motion energy. Restrained/Responsive → flat by default; Choreographed → layered. One sentence. +- **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. +- **Elevation & Depth**: the selected material and depth behavior, stated as an invariant rather than inferred from a generic preset. +- **Shapes**: the selected form and corner language. - **Components**: omit entirely; no components exist yet. -- **Do's and Don'ts**: carry PRODUCT.md's anti-references directly plus the anti-reference named in Q5. +- **Do's and Don'ts**: record the durable guardrails confirmed during the world choice, not task-local refusals. 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 +### Step 3: 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." @@ -405,15 +392,15 @@ Your own write is the freshest source; no reload needed. ## Style guidelines - **Frontmatter first, prose second.** Tokens go in the YAML frontmatter; prose contextualizes them. Don't redefine a token value in two places; the frontmatter is normative. -- **Cite PRODUCT.md anti-references by name** in the Do's and Don'ts section. If PRODUCT.md lists "SaaS landing-page clichés" or "generic AI tool marketing" as anti-references, the DESIGN.md Don'ts should repeat those phrases verbatim so the visual spec enforces the strategic line. -- **Match the spec, don't invent new sections.** The six section names are fixed. If you have Layout/Motion/Responsive content to document, fold it into Overview (philosophy-level rules) or Components (per-component behavior). +- **Carry only durable product constraints.** A binding logo, identity asset, accessibility need, or brand commitment from PRODUCT.md may constrain DESIGN.md. Surface strategy stays in its surface brief. +- **Match the spec.** Use its eight canonical sections in order and omit any that are irrelevant. Put motion guidance with the world or component it affects rather than creating a token group the schema does not support. - **Descriptive > technical**: "Gently curved edges (8px radius)" > "rounded-lg". Include the technical value in parens, lead with the description. - **Functional > decorative**: for each token, explain WHERE and WHY it's used, not just WHAT it is. - **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 forceful**. The voice of a design director. "Prohibited", "forbidden", "never", "always", not "consider", "might", "prefer". Match PRODUCT.md's tone. -- **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. -- **Reference PRODUCT.md**. The anti-references section of PRODUCT.md should directly inform the Do's and Don'ts section here. Quote or paraphrase. +- **Be decisive where evidence is decisive.** Use hard language for actual invariants and softer language for provisional guidance. +- **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. ## Pitfalls @@ -423,7 +410,7 @@ Your own write is the freshest source; no reload needed. - Don't invent components that don't exist. If the project only has buttons and cards, only document those. - Don't overwrite an existing DESIGN.md without asking. - Don't duplicate content from PRODUCT.md. DESIGN.md is strictly visual. -- Don't add a "Layout Principles" or "Motion" or "Responsive Behavior" top-level section. The spec has six, not nine. Fold that content where it belongs. +- Don't replace canonical sections with near-synonyms. Put layout and responsive behavior in `Layout`; put motion with the affected world or component. - Don't rename sections even slightly. "Colors" not "Color Palette & Roles". "Typography" not "Typography Rules". Tooling parsing depends on exact headers. - Don't duplicate token values between frontmatter and prose. If a color is in `colors.primary` as hex, the prose can name it and describe its role but should not reassert a different hex. The frontmatter is normative. - Don't invent frontmatter token groups outside Stitch's schema (no `motion:`, `breakpoints:`, `shadows:` at the top level). Stitch's Zod schema only accepts `colors`, `typography`, `rounded`, `spacing`, `components`. Anything else belongs in the sidecar's `extensions`. diff --git a/.qoder/skills/impeccable/reference/hooks.md b/.qoder/skills/impeccable/reference/hooks.md index 580c6aa81..0218ece73 100644 --- a/.qoder/skills/impeccable/reference/hooks.md +++ b/.qoder/skills/impeccable/reference/hooks.md @@ -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. diff --git a/.qoder/skills/impeccable/reference/init.md b/.qoder/skills/impeccable/reference/init.md index 7a6c8ddfa..d3e3a1fbd 100644 --- a/.qoder/skills/impeccable/reference/init.md +++ b/.qoder/skills/impeccable/reference/init.md @@ -1,224 +1,114 @@ -# Init Flow +# Init flow -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 [DESIGN.md format spec](https://raw.githubusercontent.com/google-labs-code/design.md/main/docs/spec.md). Answers "how it looks". -- **`.impeccable/live/config.json`** (live mode): pre-configured so `/impeccable live` boots straight into variant mode with no first-time detour. - -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. +`init` captures durable product truth in PRODUCT.md. It does not invent a visual world and does not write DESIGN.md; [new-work.md](new-work.md) creates or expands one, and [document.md](document.md) records an incumbent one. Existing runnable web projects may also receive `.impeccable/live/config.json`. ## Step 1: Load current state -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). +Use the PRODUCT.md path resolved by context.mjs. Update it instead of creating a competing authority. In a child app inheriting root context, confirm shared versus app-specific scope before writing. -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. -- **PRODUCT.md exists, DESIGN.md missing**: skip to Step 5 and offer to run `/impeccable document` for DESIGN.md. -- **PRODUCT.md exists but has no `## Register` section (legacy)**: add it. Infer a hypothesis from the codebase (see Step 2), confirm with the user, write the field. -- **PRODUCT.md exists but has no `## Platform` section (legacy)**: add it the same way, but only when the project is native (`ios` / `android` / `adaptive`) or the user wants it explicit; a missing field already means `web`. -- **Both exist**: ask the user directly to clarify what you cannot infer. Ask which file to refresh. Skip the one the user doesn't want changed. -- **Just DESIGN.md exists (unusual)**: do Steps 2-4 to produce PRODUCT.md. +- **No PRODUCT.md:** explore, interview, and write it. +- **PRODUCT.md exists:** ask what product knowledge is stale or missing; do not reopen confirmed fields without a reason. +- **Legacy PRODUCT.md:** add only durable missing facts; absent `## Platform` means `web` unless evidence says otherwise. +- **Only DESIGN.md exists:** leave it untouched and create PRODUCT.md. +- **Redesign/rebrand request:** preserve confirmed product truth unless the user changes it. Visual replacement happens later in new-work, not here. -Never silently overwrite an existing file. Always confirm first. +Never silently overwrite an existing file or offer DESIGN.md during init. If another request invoked init, finish PRODUCT.md and resume it. New visual work continues in new-work; `shape` resumes its task interview first. -If init was invoked as a setup blocker by another command, such as `/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 project -## Step 2: Explore the codebase +Before asking, scan enough to avoid making the user repeat known facts: product docs and copy; package/config and app boundaries; features, workflows, routes, and roles; names, logos, legal/proof assets, and brand commitments; platform/accessibility signals; and the dev command/entry when live mode applies. -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: +Treat repository evidence as a hypothesis, not user approval. Note visual maturity without documenting, extending, or replacing the world. -- **README and docs**: Project purpose, target audience, any stated goals -- **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 -- **Any style guides or brand documentation** +Form a platform hypothesis: `web`, `ios`, `android`, or `adaptive` (one product that genuinely adapts its design language per OS). Mobile web remains `web`; a native wrapper around a website does not make its design language native. -Also form a **register hypothesis** from what you find: +## Step 3: Interview for product truth -- Brand signals: `/`, `/about`, `/pricing`, `/blog/*`, `/docs/*`, hero sections, big typography, scroll-driven sections, landing-page-shaped content. -- Product signals: `/app/*`, `/dashboard`, `/settings`, `/(auth)`, forms, data tables, side/top nav, app-shell components. +ask the user directly to clarify what you cannot infer. Ask only about material gaps the repository and original request do not answer with strong evidence. -Register is a hypothesis at this point, not a decision; Step 3 confirms it. +Use the structured question tool when available; otherwise ask and wait. Keep rounds to at most three focused questions and require one real answer or approval round before writing a new PRODUCT.md. Confirm inferences. -Also form a **platform hypothesis**: +Start with the unknowns that most change future product decisions: -- Native signals: React Native / Expo (`react-native`, `expo`), Flutter (`pubspec.yaml`, `flutter`), SwiftUI / UIKit (`.swift`, `.xcodeproj`, an `ios/` app target), Jetpack Compose / Android (`build.gradle`, an `android/` app module, `AndroidManifest.xml`). An `ios/` and/or `android/` directory that is a real app target, not just a Capacitor/Cordova wrapper around a website. -- Web signals (the default): a web framework (Vite, Next, Nuxt, SvelteKit, Astro), an HTML entry, a CSS/Tailwind setup, no native app target. +1. Who is the primary user, in what situation, and what job are they doing? +2. What does the product make possible, and what is its meaningfully different mechanism or position? +3. What durable constraints, assets, evidence, or product facts must future work preserve? -Values: `web` / `ios` / `android` / `adaptive` (one codebase, ships both, adapts per OS). Mobile web is still `web`. Like register, this is a hypothesis; Step 3 confirms it. +Confirm ambiguous platform separately. Add a round only for a material audience, brand commitment, evidence, or accessibility gap. Record undecided facts instead of inventing them. -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. +Do not ask for an aesthetic direction, emotional feel, visual references, colors, typography, or style during init. If the user volunteers a binding visual constraint, record it without expanding it. -## Step 3: Ask strategic questions (for PRODUCT.md) +### What belongs here -ask the user directly to clarify what you cannot infer. Ask about anything the codebase doesn't answer with strong, explicit evidence. +- users, jobs, workflows, purpose, success, positioning, and operating context; +- capabilities, constraints, terminology, evidence, platform, and accessibility; +- confirmed voice, assets, and brand commitments. -### Interview mode, not confirmation mode +### What does not belong here -If the repo is empty or the user's brief is sparse, run a short interview before proposing PRODUCT.md. Do **not** turn a one-sentence request into a complete inferred PRODUCT.md and ask for blanket confirmation. - -- Use the harness's structured question tool when one exists. Otherwise, ask directly in chat and stop: one question at a time, with lettered options where the crawl suggests likely answers, waiting for each answer before the next. -- Keep skill vocabulary (register, belief ladder, anti-references) out of question text; ask for the thing in words the user would use. For the brand register, ask like a magazine editor profiling the brand: curious and narrative, drawing out the story, the feel, and what a visitor should come to believe. -- Ask in focused rounds and wait for answers between them. Keep **one topic per question**; add rounds rather than fold several topics into one either-or choice. Options obey the same rule: an option answers only the question asked; never write a compound option that bundles a feeling with a business outcome or names an additional audience. -- Use inferred answers as hypotheses or options, not as finished facts. -- Complete at least one real user-answer round before drafting PRODUCT.md, unless every required answer is directly discoverable from repo docs. -- Round 1 should establish register, platform, users, purpose, positioning, and desired outcome. -- Round 2 should establish brand personality or references, anti-references, and accessibility needs, plus conversion & proof for Persuade surfaces. - -### Minimum viable interview - -Ask enough to complete PRODUCT.md. At minimum, cover register confirmation, **platform confirmation** (`web` / `ios` / `android` / `adaptive`), users, purpose, positioning, brand personality, anti-references, and accessibility needs (plus conversion & proof for Persuade surfaces) unless each answer is directly discoverable from repo context. Never let the template's default `web` stand unconfirmed for a native or cross-platform repo. After at least one interview round, you may propose inferred answers, but the user must confirm them before you write PRODUCT.md. Never synthesize PRODUCT.md from the original task prompt alone. - -### Register (ask first; it shapes everything below) - -Every design task serves one of four visitor modes (see SKILL.md's Registers): **Persuade** (marketing, landing, campaigns), **Experience** (portfolios, albums, bodies of work), **Operate** (app UI, admin, dashboards, tools), and **Read** (documentation, editorial, long-form). PRODUCT.md stores the two-value family that covers them: `brand` covers Persuade and Experience, `product` covers Operate and Read. The brief decides the exact mode per task; the stored register only sets the default family. - -If Step 2 produced a clear hypothesis, lead with it: *"From the codebase, this looks like a [brand / product] surface. Does that match your intent, or should we treat it differently?"* - -If the signal is genuinely split (e.g. a product with a big marketing landing), ask the user directly to clarify what you cannot infer. Ask which register describes the **primary** surface. The register can be overridden per task later, but PRODUCT.md carries one default. Settle the default before drafting any register-dependent questions; never batch Persuade-only questions (Conversion & proof) into the same round as the question that decides the register. - -### Platform (ask right after register) - -Every project targets **web** (includes responsive mobile web), **ios**, **android**, or **adaptive** (one codebase, ships both, adapts per OS: Flutter, React Native, KMP). Platform picks the native rulebook: HIG for `ios`, Material 3 for `android`, both for `adaptive`, none for `web`. - -If Step 2 produced a clear hypothesis, lead with it: *"From the codebase, this looks like a [web / ios / android / adaptive] project. Does that match?"* For cross-platform apps, decide by the **design language the app renders**, not the toolchain: one look on both platforms (Flutter's Material-everywhere default) takes that platform's value; genuine per-OS adaptation (Cupertino on iOS, Material on Android) is `adaptive`. When in doubt, `web`. - -A monorepo shipping both a website and a native app gets a PRODUCT.md per app, each with its own `## Platform`; the root PRODUCT.md carries the primary surface's platform. - -### Users & Purpose -- Who uses this? What's their context when using it? -- What job are they trying to get done? -- What is this for? A purpose stated in README or docs is a hypothesis, not strong evidence; confirm it, don't transcribe it. -- What does success look like? -- If more than one kind of user is plausible, confirm a primary and secondary audience; don't manufacture a split that isn't there. An audience implied by another answer (a success metric, a CTA) is still unconfirmed; ask before writing it as secondary. -- If the surface speaks to a different audience than the people who use the product, ask the user to name both. -- For brand: what emotions should the interface evoke? (confidence, delight, calm, urgency) Ask this standalone; don't fold emotions into the success question. -- For product: what workflow are they in? What's the primary task on any given screen? - -### Positioning -- In one line, what does this do that nothing else does? The single strategic claim every screen reinforces. - -### 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? - - 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? - -### Conversion & proof (Persuade surfaces only) - -Ask these only when the primary surface is a Persuade one (marketing, landing, campaigns). Experience and Read surfaces (portfolios, albums, long-form writing, documentation) get no CTA, belief-ladder, or proof questions even when the stored register is `brand`; skip this section and its PRODUCT.md counterpart. - -- What's the primary CTA? -- What's the secondary fallback, for visitors not ready for the primary? -- The one line a visitor should remember after 10 seconds. -- What must the visitor believe, in order, before taking the primary CTA? (The template's belief ladder.) -- What proof is on hand? Ask the user to hand over any testimonials, case studies, press, or client/partner logos they already have. If you can receive files directly, collect them; otherwise create `.impeccable/assets/proof/` and ask the user to add files there. Reference supplied files by path; record text proof inline. - -### Accessibility & Inclusion -- Specific accessibility requirements? (WCAG level, known user needs) -- Considerations for reduced motion, color blindness, or other accommodations? - -Skip questions where the answer is already clear. **Do NOT ask about colors, fonts, radii, or visual styling here.** Those belong in DESIGN.md, not PRODUCT.md. +- visual worlds, palettes, typography, components, or page concepts; +- visitor mode, narrative, CTA/proof sequence, or other surface strategy; +- invented testimonials, customers, benchmarks, pricing, licensing, or deployment claims; +- a requirement to decide every optional field. ## Step 4: Write PRODUCT.md -Write PRODUCT.md only after the user has confirmed the strategic answers from Step 3. If an inferred answer is uncertain or unconfirmed, ask before writing. Confirmed means what the user actually said yes to; do not pad a confirmed answer with extras they never picked (additional anti-references, audiences, roadmap claims, a WCAG level), whether drawn from the crawl, another answer, or your own option text. If an extra belongs in the doc, ask about it first. - -Synthesize into a strategic document: +Write only confirmed facts and explicitly marked open decisions. Omit irrelevant sections rather than filling them with generic prose. ```markdown # Product -## Register - -product - ## Platform web ## Users -[Who they are, their context, the job to be done. Primary audience; a secondary audience or a surface-vs-user split only when they apply.] +[Primary users, their situation, and job. Add other audiences only when confirmed.] ## Product Purpose -[What this product does, why it exists, what success looks like] +[What the product does, why it exists, and what success means.] ## Positioning -[The single strategic claim every screen reinforces. Not a visual rule, not an anti-reference.] +[The product mechanism or claim a neighboring product could not truthfully copy.] -## Conversion & proof -[Persuade surfaces only (marketing, landing, campaigns). Omit this section entirely, heading included, for the product register and for Experience or Read surfaces such as portfolios, albums, or long-form writing.] -- Primary and secondary CTA: [...] -- The line a visitor remembers after 10 seconds: [...] -- Belief ladder: [...] -- Proof on hand: [testimonials, case studies, press, or logos, referenced by path] +## Operating Context +[Workflows, environments, tools, documents, materials, and rituals that are factual parts of using or evaluating the product.] -## Brand Personality -[Voice, tone, 3-word personality, emotional goals] +## Capabilities and Constraints +[Confirmed functionality, technical constraints, terminology, and explicitly undecided product facts.] -## Anti-references -[What this should NOT look like. Specific bad-example sites or patterns to avoid.] +## Brand Commitments +[Existing name, voice, assets, personality, identity constraints, and references the user explicitly made binding. Omit when none exist.] -## Design Principles -[3-5 strategic principles derived from the conversation. Principles like "practice what you preach", "show, don't tell", "expert confidence". NOT visual rules like "use OKLCH" or "magenta accent".] +## Evidence on Hand +[Real content, data, demonstrations, testimonials, case studies, press, or assets, with paths where applicable. State absences that future work must not fabricate.] + +## Product Principles +[Three to five durable strategic principles derived from confirmed answers; no visual recipes.] ## Accessibility & Inclusion -[WCAG level, known user needs, considerations] +[Known user needs or required standard. Omit when no product-specific requirement was established.] ``` -Register is either `brand` or `product` as a bare value. No prose, no commentary. Platform is `web`, `ios`, `android`, or `adaptive`, also a bare value; omit the section only on legacy files you're leaving untouched, otherwise write `web` explicitly. +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. -Write fields as prose, and use bold sparingly: only where a word carries a decision, never as a label lead-in on every line. +### Completion gate -Write to `PROJECT_ROOT/PRODUCT.md`. If `.impeccable.md` existed, the loader already renamed it; merge into that content rather than starting from scratch. +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: Decide on DESIGN.md +## Step 5: Configure live mode when useful -Offer `/impeccable document` either way. Two paths: +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. -- **Code exists** (CSS tokens, components, a running site): "I can generate a DESIGN.md that captures your visual system (colors, typography, components) so variants stay on-brand. Want to do that now?" -- **Pre-implementation** (empty project): "I can seed a starter DESIGN.md from five quick questions about color strategy, type direction, motion energy, and references. You can re-run once there's code, to capture the real tokens. Want to do that now?" +## Step 6: Wrap up or resume -If the user agrees, delegate to `/impeccable document` (it auto-detects scan vs seed). Load its reference and follow that flow. +Summarize captured and deliberately undecided facts. Do not offer DESIGN.md merely because it is missing. -If the user prefers to skip, mention they can run `/impeccable document` any time later. +Recommend the next action from the actual project state: -## Step 6: Configure live mode (when code exists) +- Empty or early project: ask naturally for the surface to be built, or use `/impeccable shape ` when the user wants a confirmed brief without implementation. New-work will establish a visual world only when the requested work needs one. +- Existing coherent interface without DESIGN.md: `/impeccable document` if the user wants the incumbent system recorded independently of a new build. +- Existing surface needing work: name the most relevant scoped command. +- Web project ready for visual iteration: `/impeccable live` when configured. -**Skip this step when the platform is native** (`ios` / `android` / `adaptive`): live mode drives a browser overlay. A hybrid wrapper or Expo web target serving HTML doesn't change that. - -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 .qoder/skills/impeccable/scripts/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) and platform captured (web / ios / android / adaptive) -- 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 or live config is pending, one line on how to set it up later - -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 platform**, offer the 2-4 most relevant (not a menu dump), and give the exact command to type. Group by intent: - -- **Build something new**: `/impeccable craft ` (shape, then build end-to-end) or `/impeccable shape ` (plan first). Lead with this for empty or early-stage projects. -- **Improve what's there**: name the specific surface. `/impeccable critique ` for a scored UX review; `/impeccable audit ` for a11y / perf / responsive checks; `/impeccable polish ` 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** (web only): `/impeccable live` (configured in Step 6) to pick elements in the browser and generate variants in place. **Skip this group for native platforms.** - -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 the user directly to clarify what you cannot infer. Ask whether they'd like a brief summary of PRODUCT.md appended to AGENTS.md for easier agent reference. If yes, append a short **Design Context** pointer section there. +If init was invoked by another request, resume without rerunning context.mjs; new-work owns later visual decisions. diff --git a/.qoder/skills/impeccable/reference/ios.md b/.qoder/skills/impeccable/reference/ios.md index ef10ee0d2..ccef5d2c4 100644 --- a/.qoder/skills/impeccable/reference/ios.md +++ b/.qoder/skills/impeccable/reference/ios.md @@ -2,7 +2,7 @@ For native iOS / iPadOS apps: SwiftUI, UIKit, React Native, Expo, Flutter shipping to Apple hardware. -On native, register narrows. HIG conformance governs structure, navigation, and interaction whatever the register; brand expresses through the expressive layer the platform provides (tint, type, motion, content). Calm, Duolingo, and Spotify carry strong identity entirely inside HIG conventions. +On native, the visitor mode narrows what expression may override. HIG conformance governs structure, navigation, and interaction in every mode; brand expresses through the layer the platform leaves open (tint, type, motion, content). ## The iOS slop test diff --git a/.qoder/skills/impeccable/reference/layout.md b/.qoder/skills/impeccable/reference/layout.md index 758588b52..99ebb5372 100644 --- a/.qoder/skills/impeccable/reference/layout.md +++ b/.qoder/skills/impeccable/reference/layout.md @@ -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. --- -## Register +## 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, register, 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 .qoder/skills/impeccable/scripts/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 `/impeccable polish` for the final pass. +When the structure holds, hand off to `/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) * )`: 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. diff --git a/.qoder/skills/impeccable/reference/live.md b/.qoder/skills/impeccable/reference/live.md index 2b819b64c..8891f0f0a 100644 --- a/.qoder/skills/impeccable/reference/live.md +++ b/.qoder/skills/impeccable/reference/live.md @@ -33,7 +33,7 @@ Chat is overhead. No recap, no tutorial output, no pasting PRODUCT / DESIGN bodi node .qoder/skills/impeccable/scripts/live.mjs ``` -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. +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, DESIGN.md, and any surface brief already loaded by Setup in mind for variant generation: **DESIGN.md wins on visual decisions; PRODUCT.md wins on durable product and voice decisions; the surface brief wins on this surface's strategy.** 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 requires the user's explicit redesign/replacement intent. `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). @@ -187,7 +187,7 @@ All three carry `fallback: "agent-driven"`. Follow **Handle fallback** below. ### 3. Load the action's reference -If `event.action` is `impeccable` (the default freeform action), use SKILL.md's shared laws plus the loaded register reference (`brand.md` or `product.md`). Do not load a sub-command reference. **Freeform is not a pass to skip parameters:** you still follow the composition budget and the freeform bias in **§7 Parameters** below. Sub-command files list MUST-have signature knobs; freeform has no such file, so sizing knobs from surface weight and primary axes is entirely on you. +If `event.action` is `impeccable` (the default freeform action), use SKILL.md's shared laws and decide the visitor mode from the selected surface. Do not load a sub-command reference. **Freeform is not a pass to skip parameters:** you still follow the composition budget and the freeform bias in **§7 Parameters** below. Sub-command files list MUST-have signature knobs; freeform has no such file, so sizing knobs from surface weight and primary axes is entirely on you. Any other `event.action` (`bolder`, `quieter`, `distill`, `polish`, `typeset`, `colorize`, `layout`, `adapt`, `animate`, `delight`, `overdrive`): Read `reference/.md` before planning. Each sub-command encodes a specific discipline; skipping its reference produces generic output. Those files may require specific params; layer them on top of the §7 budget, not instead of it. @@ -216,7 +216,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. @@ -224,10 +224,7 @@ This sentence is the **identity lock**. Every variant must be readable as the sa **Default mode**: the existing identity is preserved. Variants vary expression axes within it. *This is the right mode for ~90% of live sessions.* The user picked an element on a real product they're shipping; they expect variants of *their* hero, not three different brands' heroes. -**Departure mode**: the existing identity is rejected. Variants propose alternatives consistent with PRODUCT.md voice. Trigger only when at least one is true: - -- PRODUCT.md anti-references explicitly call out the current surface ("the current `index.html` is itself an example"; "diffuse away from this"; "the page on screen is the failure"). Generic anti-references that describe what to avoid in general do **not** trigger departure mode; only ones that point at *this* surface specifically. -- The user's freeform prompt explicitly asks for departure ("rebuild this from scratch", "what if it weren't editorial at all", "show me something completely different"). +**Departure mode**: the existing identity is rejected. Variants propose alternatives consistent with durable product and brand truth. Trigger only when the user explicitly asks for departure in the current request or freeform prompt ("redesign this", "rebuild this from scratch", "what if it weren't editorial at all", "show me something completely different"). A stale page critique or an old task note is not replacement authorization. If you're unsure, you're in default mode. The cost of being wrong about default is "three on-brand variants with similar feel": recoverable, the user picks none. The cost of being wrong about departure is "three off-brand variants": unrecoverable, the user is annoyed. @@ -246,13 +243,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 the brand's stated voice and register in PRODUCT.md. Do NOT pick from a fixed catalog of lane categories. The right three directions for this brand are not the same as the right three for another brand, 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. @@ -262,7 +259,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. @@ -289,7 +286,7 @@ In **default mode**, the prompt narrows the axes you choose, not the identity. * In **departure mode**, the prompt narrows the lanes you draw from, not the families. *"Make it feel like a newspaper front page"* would itself be a departure-mode prompt; honor it but pick three meaningfully different newspaper-adjacent lanes (broadsheet vs. tabloid vs. trade journal), and run the family pass to confirm they don't collapse into one. -When the prompt and PRODUCT.md anti-references conflict (the prompt asks for X, the anti-references ban X), the anti-references win; they describe the brand's standing position, the prompt is one moment. +When the prompt conflicts with a confirmed binding brand commitment or DESIGN.md invariant, preserve the invariant unless the user explicitly revokes or replaces it. Task-local strategy from the matching surface brief may change when the user changes that surface's goal. ### 6. Write all variants in a single edit diff --git a/.qoder/skills/impeccable/reference/new-work.md b/.qoder/skills/impeccable/reference/new-work.md index a7e3b46f4..8ac2e8a6c 100644 --- a/.qoder/skills/impeccable/reference/new-work.md +++ b/.qoder/skills/impeccable/reference/new-work.md @@ -1,47 +1,178 @@ -# New identity work +# New visual work -Read this only when the project has no committed visual identity, or the user explicitly asked to replace the current one. A missing DESIGN.md is not enough: code, tokens, chosen type, components, and assets are incumbent design authority. If they exist, preserve and extend them unless the brief says to discard them. +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. -This playbook is task-scoped. PRODUCT.md owns durable product facts and DESIGN.md owns durable visual invariants; the direction for one page or feature belongs in the current task, not in either document. +## 1. Name the intent -## Choose the authority +- **Greenfield:** no coherent visual implementation. +- **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. -Classify the work before inventing anything: +A plain “redesign this page/site” authorizes replacement. “Redesign this within the current brand/system” means extension or refinement. Ask once only when the wording is genuinely ambiguous. -- **Extend**: an incumbent system exists and the task adds or improves a surface. Preserve its lineage, semantics, and affordances. -- **Surface redesign**: the current identity remains, but this surface may change its topology, hierarchy, and expression. -- **New identity**: nothing committed exists, or the user explicitly authorized a rebrand. +## 2. Resolve visual authority -When the evidence is mixed, preserve. Novelty is not permission to erase authored decisions. +Read DESIGN.md and representative code, tokens, components, and assets. Choose one path: -## Find the concept +### A. Explicit redesign -Name the subject, audience, page job, and visitor mode from SKILL.md. Then identify the product's unique mechanism: what it does, proves, or enables that a neighboring product cannot truthfully claim. +The old DESIGN.md and implementation are not authority. Keep only unrevoked product facts, content, function, native expectations, constraints, and brand commitments. Establish a replacement world. -Derive the concept from that mechanism through one primary transformation: information topology, evidence, temporal behavior, or spatial mapping. The transformation must change how the surface is organized or behaves, not merely how it is decorated. This produces stranger and more defensible work than imitating a culturally specific document or object. Borrowing a literal cultural form is allowed only when it is genuinely native to the product and audience, and never as costume. +### B. DESIGN.md covers this kind of surface -For **Operate** and **Read**, start with the task and information structure. Native controls remain native controls; expression comes from hierarchy, density, rhythm, state, and the system around them. For **Persuade** and **Experience** creating a new identity, explore more freely, but keep the product legible and the primary action obvious. +Use its invariants and normative tokens. Skip world-building and discover the surface. -Run `node .qoder/skills/impeccable/scripts/concept-seed.mjs` only for Persuade or Experience new-identity work when two or more directions remain equally credible. It is an entropy source, not an authority: never let the draw overrule the strongest fit. When color is genuinely unconstrained, `node .qoder/skills/impeccable/scripts/palette.mjs` may break a reflex palette; subject evidence and incumbent color still win. +### C. A coherent implementation exists but DESIGN.md does not -## State the direction +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. -Before code, write a task-scoped direction contract of at most 120 words in your reasoning: +### D. The brand exists, but a whole-surface family is unresolved -- the thesis this surface owns; -- the structural or behavioral transformation; -- what authority is preserved; -- the first viewport's composition and primary action; -- the signature element the surface will be remembered by. +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. -Do not embed this contract as a comment in the production artifact or turn it into a second design system. In an attended substantial build, confirm it once. In an unattended run, record it and continue. +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. -## Build once, commit fully +### E. No confirmed visual authority exists -Plan one compact token system and one layout source: the concept or the content's real structure. Build the strongest coherent direction once. Commitment means the transformation governs the whole surface; it does not mean every control must be rebuilt as a metaphor. +Establish a world. Scaffolds, framework defaults, and stray utilities are not identity. -Make the first viewport demonstrate the product's mechanism. Pace long surfaces through contrast in density and scale, and cut sections that only repeat claims. Briefs that depend on imagery ship real, verified imagery. Preserve semantic HTML, familiar interaction behavior, accessibility, and the project's technical conventions. +## 3. Discover the requested surface -## Finish proportionally +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. -Inspect desktop and mobile, write one honest critique against the brief and direction, patch material defects, then run the detector once. Repeat only when a real defect remains. A separate reviewer is optional when the harness already provides one and the extra cost is earned by the risk; it is not a default step and its presence is not evidence of quality. +Ask one attended round of at most three material questions without repeating durable facts. CTA hierarchy, proof sequence, content gaps, and interaction outcomes belong here, not PRODUCT.md. For a fully specified narrow request, state the interpretation and invite correction. + +When `shape` has already completed its discovery interview, reuse those confirmed answers and do not ask this round again. + +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. Each candidate joins a durable visual system (identity thesis, information/layout grammar, material and type behavior, color strategy, imagery, motion, reusable signature) to a concrete expression for this surface (topology, sequence, focal moment, and implementation consequence). Do not rank yet. +3. **Break the ranking rut once.** Run `node .qoder/skills/impeccable/scripts/concept-seed.mjs --scope direction`. Promote the assigned grounded pair and weigh its challengers only when they can become both a coherent system and a strong solution to this task. +4. **Test coupling and breadth.** Reject a pair if its surface could swap into another candidate unchanged, or if its world is merely the surface motif repeated. Test each survivor on the requested surface plus navigation, quiet and dense content, interaction/state, and one unlike future surface. Compare it with both the category habit and predictable contrarian response. +5. **Offer coupled choices.** Present two or three materially different pairs without recommendation cues. For each, show the world rules, this surface's 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. 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 .qoder/skills/impeccable/scripts/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 promoted candidate when a roll ran; otherwise use the strongest grounded structure. It must survive both tests. + +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). + +### Write or update DESIGN.md + +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: + +`` + +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 .qoder/skills/impeccable/scripts/surface-brief.mjs read ` + +Exit 0: preserve still-valid decisions and change only what the user changed. For redesign, retain valid product strategy, content, function, and open decisions; replace visual direction and contract. Exit 2: no brief. After the contract, write with `node .qoder/skills/impeccable/scripts/surface-brief.mjs write [related-target ...]`. + +The body is concise and contains: + +```markdown +# Surface brief: [name] + +## Scope +[Primary/related route or artifact, visitor mode, and what this surface owns.] + +## Product strategy +[Surface-specific audience and job, desired outcome, primary/secondary action or task, content and proof sequence, factual constraints, and explicitly open decisions.] + +## Selected direction +[Reference to the applicable DESIGN.md world or expression range, selected surface concept, focal moment, narrative/interaction sequence, and implementation consequence.] + +## Direction contract +[The six contract blocks below.] + +## Open decisions +[Only unresolved items that later work must not silently invent. Omit when empty.] +``` + +Commit `.impeccable/surfaces/.md` as stable later-work context. Exclude global truth, exact tokens, transient notes, and work logs. + +## 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. + +Before code, write a direction contract of at most 150 words in an opening HTML or framework comment. The first 200 characters must name `DIRECTION CONTRACT`. + +- `UNIQUE`: the task thesis tied to the product mechanism; +- `NOT-TEMPLATE`: the category-default arrangement this structure refuses; +- `OWN-WORLD`: the confirmed DESIGN.md invariants, tokens, and materials it uses; +- `STORY`: what the visitor understands, believes, and does; +- `FIRST VIEWPORT`: exact composition, hierarchy, action, and where the concept exceeds competent convention; +- `FORM`: chosen structure or behavior, signature, implementation consequence, and seed key. + +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. + +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. + +## 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. + +Build the strongest coherent direction once. Its grammar governs navigation, actions, controls, content, and transitions without disguising affordances. Give the focal form the scale that gives it force; do not trap it inside a standard hero panel. + +**Make the opening a thesis.** Demonstrate the mechanism immediately; leave an idea, interaction, or evidence, not merely mood. + +**Commit before correcting.** Land the hard move at full strength before refining it. In unattended work, safety is the known risk. + +**Commit at page scale.** Let color, material, image, or type own a region when the world calls for it. Scattered signature decoration is not commitment. + +**Pace the whole surface.** Vary density, scale, image, motion, and quiet inside one grammar. Cut repeated claims; prove the mechanism with real artifact, interaction, data, transformation, or content. + +**Author motion as material.** Motion expresses the world and task. Premium moments go beyond repeated transform/opacity through earned focus, depth, masks, continuity, light, or material change. Bound expensive effects, test in-browser, keep content visible by default, and design reduced motion. + +Preserve semantics, affordances, accessibility, performance, responsiveness, and project conventions. Operate/Read express through topology, hierarchy, density, rhythm, and state; Persuade/Experience may earn drama. + +## 8. Solidify the visual record + +After first implementation of a new/replacement world or approved expansion, refresh DESIGN.md from the build: + +- replace provisional direction with the exact type, color roles, tokens, spacing/radii, components, states, and motion that survived; +- add normative YAML tokens only for values the implementation actually uses; +- remove the directional-seed status once the record and implementation agree; +- preserve broader world invariants and expression ranges; +- do not promote the task's story, hero composition, or one-off motif into a global rule unless it is intentionally reusable. + +Ordinary extension does not rewrite DESIGN.md; only approved durable changes do. + +## 9. Finish like a 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. diff --git a/.qoder/skills/impeccable/reference/operate.md b/.qoder/skills/impeccable/reference/operate.md index 04ee9d7d2..f420cbaa2 100644 --- a/.qoder/skills/impeccable/reference/operate.md +++ b/.qoder/skills/impeccable/reference/operate.md @@ -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. - No orchestrated page-load sequences. Product loads into a task; users don't want to watch it load. -## Product bans (on top of the shared absolute bans) +## Product constraints - Decorative motion that doesn't convey state. - Inconsistent component vocabulary across screens. If the "save" button looks different in two places, one is wrong. @@ -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. diff --git a/.qoder/skills/impeccable/reference/polish.md b/.qoder/skills/impeccable/reference/polish.md index 08ad35e81..8a6594ef9 100644 --- a/.qoder/skills/impeccable/reference/polish.md +++ b/.qoder/skills/impeccable/reference/polish.md @@ -1,241 +1,98 @@ -> **Additional context needed**: quality bar (MVP vs flagship). +> **Additional context needed**: quality bar and shipping constraints. -Perform a meticulous final pass to catch all the small details that separate good work from great work. The difference between shipped and polished. +Polish is refinement, never concealed redesign. Preserve the incumbent visual world, content, behavior, and everything outside scope. If the concept itself is wrong, say so and recommend redesign or `bolder` instead of smuggling in a replacement. -Detector and automated QA output are defect evidence only. A clean script result is never proof that the design is strong; gather browser evidence and inspect the real interaction path. +A detector result is defect evidence, not proof of quality. Inspect the rendered experience and real interaction path. -## Design System Discovery +## 1. Establish the system -Aligning the feature to the design system is **not optional**. Polish without alignment is decoration on top of drift, and it makes the next person's job harder. Discovery comes before any other polish work. +Read DESIGN.md and representative tokens, shared components, patterns, and neighboring flows. If no formal system exists, use coherent project conventions. -1. **Find the design system**: Search for design system documentation, component libraries, style guides, or token definitions. Study the core patterns: design principles, target audience, color tokens, spacing scale, typography styles, component API, motion conventions. -2. **Note the conventions**: How are shared components imported? What spacing scale is used? Which colors come from tokens vs hard-coded values? What motion and interaction patterns are established? What flow shapes are used for comparable actions (modal vs full-page, inline vs route, save-on-blur vs explicit submit)? -3. **Identify drift, then name the root cause**: For every deviation, classify it as a **missing token** (the value should exist in the system but doesn't), a **one-off implementation** (a shared component already exists but wasn't used), or a **conceptual misalignment** (the feature's flow, IA, or hierarchy doesn't match neighboring features). The fix differs by category: patch the value, swap to the shared component, or rework the flow. Fixing the symptom without naming the cause is how drift compounds. +Classify each drift before fixing it: -If a design system exists, polish **must** align the feature with it. If none exists, polish against the conventions visible in the codebase. **If anything about the system is ambiguous, ask. Never guess at design system principles.** +- **missing token:** the system needs a reusable value; +- **one-off implementation:** an existing shared component or pattern should replace it; +- **conceptual mismatch:** the flow, information architecture, or hierarchy differs from comparable product areas; +- **local defect:** the implementation is simply incomplete or inconsistent. -## Pre-Polish Assessment +Fix the cause at the narrowest correct level. Ask when a binding system principle cannot be inferred. -Understand the current state and goals before touching anything: +## 2. Gather the evidence -1. **Review completeness**: - - Is it functionally complete? - - Are there known issues to preserve (mark with TODOs)? - - What's the quality bar? (MVP vs flagship feature?) - - When does it ship? (How much time for polish?) +Use the feature yourself at representative desktop and mobile sizes. Determine: -2. **Think experience-first**: Who actually uses this, and what's the best possible experience for them? Effective design beats decorative polish; a feature that looks beautiful but fights the user's flow is not polished. Walk the path from their perspective before opening DevTools. +- whether the path is functionally complete; +- the intended quality bar and time available; +- known constraints or deliberately unfinished work; +- the states, content lengths, roles, and input methods users will actually encounter. -3. **Identify polish areas**: - - Visual inconsistencies - - Spacing and alignment issues - - Interaction state gaps - - Copy inconsistencies - - Edge cases and error states - - Loading and transition smoothness - - Information architecture and flow drift (does this feature reveal complexity the way neighboring features do?) +If a prior critique exists, use it as one input: -4. **Pull in any prior critique** (optional signal): If `/impeccable critique` has been run on the same target, its priority issues are a useful prior for what to address first. Resolve the target to a file path or URL, then: - ```bash - slug=$(node .qoder/skills/impeccable/scripts/critique-storage.mjs slug "") - node .qoder/skills/impeccable/scripts/critique-storage.mjs latest "$slug" - ``` - Exit 0 with body = found; fold the P0/P1 items into your polish list and mention the snapshot path so the user sees what you read. Exit 2 = no snapshot, continue without it. The critique is one input among many. Do your own pass either way. +```bash +slug=$(node .qoder/skills/impeccable/scripts/critique-storage.mjs slug "") +node .qoder/skills/impeccable/scripts/critique-storage.mjs latest "$slug" +``` -5. **Triage cosmetic vs functional**: Classify each issue as **cosmetic** (looks off, doesn't impede the user) or **functional** (breaks, blocks, or confuses the experience). When polish time is tight, functional issues ship first; cosmetic ones can land in a follow-up. Quality should be consistent; never perfect one corner while leaving another rough. +Exit 0 returns the latest snapshot; incorporate relevant P0/P1 findings and name the snapshot read. Exit 2 means none exists. Perform an independent pass either way. -**CRITICAL**: Polish is the last step, not the first. Don't polish work that's not functionally complete. +## 3. Triage -## Polish Systematically +Separate functional defects from cosmetic ones and fix in this order: -Work through these dimensions methodically: +1. broken or blocked tasks, data loss, misleading state, and inaccessible paths; +2. missing loading, empty, error, success, disabled, and permission states; +3. flow, hierarchy, responsive, and design-system drift; +4. visual and motion inconsistencies; +5. code and asset cleanup. -### Visual Alignment & Spacing +Do not perfect one corner while leaving the rest below the same quality bar. -- **Pixel-perfect alignment**: Everything lines up to grid -- **Consistent spacing**: All gaps use spacing scale (no random 13px gaps) -- **Optical alignment**: Adjust for visual weight (icons may need offset for optical centering) -- **Responsive consistency**: Spacing and alignment work at all breakpoints -- **Grid adherence**: Elements snap to baseline grid +## 4. Polish the whole path -**Check**: -- Enable grid overlay and verify alignment -- Check spacing with browser inspector -- Test at multiple viewport sizes -- Look for elements that "feel" off +### Flow and hierarchy -### Information Architecture & Flow +- Match neighboring mental models, terminology, disclosure, routing, save behavior, and optimistic or pessimistic patterns. +- Make the primary task and current state obvious without flattening every element to equal weight. +- Ensure arrival, transition, empty, and recovery paths connect instead of behaving as isolated screens. -Visual polish on a misshapen flow is wasted work. Match the *shape* of the experience to the system, not just the surface. +### Layout and type -- **Progressive disclosure**: Match how much is revealed when, compared to neighboring features. A settings page exposing 40 fields when the rest of the app reveals 5 at a time is drift, even if every field is perfectly styled. -- **Established user flows**: Multi-step actions follow the same shape as comparable flows elsewhere: modal vs full-page, inline edit vs separate route, save-on-blur vs explicit submit, optimistic vs pessimistic updates. -- **Hierarchy & complexity**: The same conceptual weight gets the same visual weight throughout. Primary actions don't become tertiary in one corner of the product, and tertiary actions don't shout. -- **Empty, loading, and arrival transitions**: How content arrives, updates, and leaves matches how it does in adjacent features. -- **Naming and mental model**: The feature uses the same nouns and verbs as the rest of the system. A "Workspace" here shouldn't be a "Project" three screens away. +- Align to the project's grid and spacing scale; fix optical as well as mathematical alignment. +- Group related content tightly and separate distinct groups generously. +- Keep same-role typography consistent; test measure, wrapping, localization expansion, zoom, and font loading. +- Verify every supported viewport rather than correcting only the current screenshot. -### Typography Refinement +### Color, imagery, and icons -- **Hierarchy consistency**: Same elements use same sizes/weights throughout -- **Line length**: 45-75 characters for body text -- **Line height**: Appropriate for font size and context -- **Widows & orphans**: No single words on last line -- **Hyphenation**: Appropriate for language and column width -- **Kerning**: Adjust letter spacing where needed (especially headlines) -- **Font loading**: No FOUT/FOIT flashes +- Use semantic tokens and stable color meanings across themes. +- Verify text, control, and focus contrast in every state. +- Keep icon families, stroke/weight, sizing, and optical alignment coherent. +- Prevent image layout shift; use correct aspect ratios, responsive sources, and useful alt text. -### Color & Contrast +### Interaction and state -- **Contrast ratios**: All text meets WCAG standards -- **Consistent token usage**: No hard-coded colors, all use design tokens -- **Theme consistency**: Works in all theme variants -- **Color meaning**: Same colors mean same things throughout -- **Accessible focus**: Focus indicators visible with sufficient contrast -- **Gray on color**: Never put gray text on colored backgrounds; use a shade of that color or transparency +- Every control needs appropriate default, hover, focus, active, disabled, loading, error, and success behavior. +- Preserve visible keyboard focus, logical tab order, labels, and platform-appropriate touch targets. +- Keep motion coherent, interruptible, performant, and reduced-motion aware. Do not add animation merely to make polish visible. +- Validate long, missing, localized, offline, slow, and permission-limited content where the product can encounter it. -### Interaction States +### Content and code -Every interactive element needs all states: +- Keep terminology, capitalization, punctuation, and factual copy consistent. Ask before changing claims. +- Remove debug output, dead code, unused imports, obsolete styles, and polish-created duplication. +- Replace custom implementations with shared components where the system owns the pattern. +- Promote genuinely reusable values to tokens; do not create a system abstraction for one local exception. -- **Default**: Resting state -- **Hover**: Subtle feedback (color, scale, shadow) -- **Focus**: Keyboard focus indicator (never remove without replacement) -- **Active**: Click/tap feedback -- **Disabled**: Clearly non-interactive -- **Loading**: Async action feedback -- **Error**: Validation or error state -- **Success**: Successful completion +## 5. Verify and finish -**Missing states create confusion and broken experiences**. +Walk the complete path again with mouse, keyboard, and touch where applicable. Check: -### Micro-interactions & Transitions +- mobile, intermediate, and wide layouts; +- loading, empty, error, success, disabled, long-content, and missing-content states; +- zoom, contrast, focus, semantics, screen-reader names, and reduced motion; +- console errors, layout shift, interaction latency, image loading, and supported browsers; +- agreement with DESIGN.md, neighboring features, and the user's scope. -- **Smooth transitions**: All state changes animated appropriately (150-300ms) -- **Consistent easing**: Use ease-out-quart/quint/expo for natural deceleration. Never bounce or elastic; they feel dated. -- **No jank**: Smooth animations; use atmospheric blur/filter/mask/shadow effects when they add polish, but bound expensive paint areas and avoid casual layout-property animation -- **Appropriate motion**: Motion serves purpose, not decoration -- **Reduced motion**: Respects `prefers-reduced-motion` +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. -### Content & Copy - -- **Consistent terminology**: Same things called same names throughout -- **Consistent capitalization**: Title Case vs Sentence case applied consistently -- **Grammar & spelling**: No typos -- **Appropriate length**: Not too wordy, not too terse -- **Punctuation consistency**: Periods on sentences, not on labels (unless all labels have them) - -### Icons & Images - -- **Consistent style**: All icons from same family or matching style -- **Appropriate sizing**: Icons sized consistently for context -- **Proper alignment**: Icons align with adjacent text optically -- **Alt text**: All images have descriptive alt text -- **Loading states**: Images don't cause layout shift, proper aspect ratios -- **Retina support**: 2x assets for high-DPI screens - -### Forms & Inputs - -- **Label consistency**: All inputs properly labeled -- **Required indicators**: Clear and consistent -- **Error messages**: Helpful and consistent -- **Tab order**: Logical keyboard navigation -- **Auto-focus**: Appropriate (don't overuse) -- **Validation timing**: Consistent (on blur vs on submit) - -### Edge Cases & Error States - -- **Loading states**: All async actions have loading feedback -- **Empty states**: Helpful empty states, not just blank space -- **Error states**: Clear error messages with recovery paths -- **Success states**: Confirmation of successful actions -- **Long content**: Handles very long names, descriptions, etc. -- **No content**: Handles missing data gracefully -- **Offline**: Appropriate offline handling (if applicable) - -### Responsiveness - -- **All breakpoints**: Test mobile, tablet, desktop -- **Touch targets**: 44x44px minimum on touch devices -- **Readable text**: No text smaller than 14px on mobile -- **No horizontal scroll**: Content fits viewport -- **Appropriate reflow**: Content adapts logically - -### Performance - -- **Fast initial load**: Optimize critical path -- **No layout shift**: Elements don't jump after load (CLS) -- **Smooth interactions**: No lag or jank -- **Optimized images**: Appropriate formats and sizes -- **Lazy loading**: Off-screen content loads lazily - -### Code Quality - -- **Remove console logs**: No debug logging in production -- **Remove commented code**: Clean up dead code -- **Remove unused imports**: Clean up unused dependencies -- **Consistent naming**: Variables and functions follow conventions -- **Type safety**: No TypeScript `any` or ignored errors -- **Accessibility**: Proper ARIA labels and semantic HTML - -## Polish Checklist - -Go through systematically: - -- [ ] Aligned to the design system (drift named and resolved by root cause) -- [ ] Information architecture and flow shape match neighboring features -- [ ] Visual alignment perfect at all breakpoints -- [ ] Spacing uses design tokens consistently -- [ ] Typography hierarchy consistent -- [ ] All interactive states implemented -- [ ] All transitions smooth (60fps) -- [ ] Copy is consistent and polished -- [ ] Icons are consistent and properly sized -- [ ] All forms properly labeled and validated -- [ ] Error states are helpful -- [ ] Loading states are clear -- [ ] Empty states are welcoming -- [ ] Touch targets are 44x44px minimum -- [ ] Contrast ratios meet WCAG AA -- [ ] Keyboard navigation works -- [ ] Focus indicators visible -- [ ] No console errors or warnings -- [ ] No layout shift on load -- [ ] Works in all supported browsers -- [ ] Respects reduced motion preference -- [ ] Code is clean (no TODOs, console.logs, commented code) - -**IMPORTANT**: Polish is about details. Zoom in. Squint at it. Use it yourself. The little things add up. - -Sweat the details. Zoom in until the alignment is right and the spacing reads as deliberate. Then ship. - -**NEVER**: -- Polish before it's functionally complete -- Polish without aligning to the design system; that's decoration on drift -- Guess at design system principles instead of asking when something is ambiguous -- Spend hours on polish if it ships in 30 minutes (triage) -- Introduce bugs while polishing (test thoroughly) -- Ignore systematic issues (if spacing is off everywhere, fix the system, not just one screen) -- Perfect one thing while leaving others rough (consistent quality level) -- Create new one-off components when design system equivalents exist -- Hard-code values that should use design tokens -- Introduce new patterns or flows that diverge from established ones - -## Final Verification - -Before marking as done: - -- **Use it yourself**: Actually interact with the feature. -- **Test on real devices**: Not just browser DevTools. -- **Ask someone else to review**: Fresh eyes catch things. -- **Compare to design**: Match intended design. -- **Check all states**: Don't just test happy path. -- **Treat automation carefully**: Run detector or QA commands when they are available and relevant, fix their defects, but never cite a clean result as proof that the work is polished. - -## Clean Up - -After polishing, ensure code quality: - -- **Replace custom implementations**: If the design system provides a component you reimplemented, switch to the shared version. -- **Remove orphaned code**: Delete unused styles, components, or files made obsolete by polish. -- **Consolidate tokens**: If you introduced new values, check whether they should be tokens. -- **Verify DRYness**: Look for duplication introduced during polishing and consolidate. +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. diff --git a/.qoder/skills/impeccable/reference/quieter.md b/.qoder/skills/impeccable/reference/quieter.md index 55b00b6f3..bb163e40f 100644 --- a/.qoder/skills/impeccable/reference/quieter.md +++ b/.qoder/skills/impeccable/reference/quieter.md @@ -2,7 +2,7 @@ Quiet design is harder than bold design. Subtlety needs precision. Reduce visual --- -## Register +## Visitor mode Persuade + Experience: "quieter" means more restrained palette, more whitespace, more typographic air. Drama is reduced, not eliminated; the POV stays intact. diff --git a/.qoder/skills/impeccable/reference/routing.md b/.qoder/skills/impeccable/reference/routing.md index 571d2c3ba..4ac03b63a 100644 --- a/.qoder/skills/impeccable/reference/routing.md +++ b/.qoder/skills/impeccable/reference/routing.md @@ -11,7 +11,7 @@ Reason over the signals; there is no score to obey: - `critique.latest` with a low `score` or non-zero `p0` / `p1` → `polish` (it reads that snapshot as its backlog), or re-run `critique` if the snapshot looks stale. - `git.changedFiles` pointing at one surface → scope `audit` or `polish` to those files specifically, naming them. - `devServer.running` true → `live` is available for in-browser iteration; if false, don't lead with `live`. **`live` and the bundled `detect.mjs` are web-only.** If `setup.platform` is `ios`, `android`, or `adaptive`, don't lead with either; the browser overlay and the HTML rule engine don't apply to native app code. -- Otherwise group by intent exactly as init's "Recommend starting points" step does (build new / improve what's there / iterate visually), tailored to `setup.register`. +- Otherwise group by intent (build new / improve what's there / iterate visually), tailored to the current surface and `setup.platform`. **If `scan.targets` is non-empty and `setup.platform` is not `ios`/`android`/`adaptive`, run `node .qoder/skills/impeccable/scripts/detect.mjs --json ` once** (the bundled detector over local files: no network, no npx; it reads HTML/CSS, so skip it for native projects). `scan.via` tells you what they are: `git-changes` (the markup/style files in your dirty tree, the most relevant set), `source-dir` (e.g. `src`, `app`), `html`, or `root`. Fold the hits into your picks: many quality / contrast hits → `audit` or `polish`; a specific slop family → the matching command (gradient text or eyebrows → `quieter` / `typeset`, flat or gray palette → `colorize`, and so on). It's a real, current signal that beats guessing. If detect errors or the tree is large and slow, skip it and recommend the user run `audit` themselves; never block the suggestion on it. diff --git a/.qoder/skills/impeccable/reference/shape.md b/.qoder/skills/impeccable/reference/shape.md index 40804f6e4..acbb26e5b 100644 --- a/.qoder/skills/impeccable/reference/shape.md +++ b/.qoder/skills/impeccable/reference/shape.md @@ -1,167 +1,61 @@ -Shape the UX and UI for a feature before any code is written. This command produces a **design brief**: a structured artifact that guides implementation through discovery, not guesswork. +# Shape -**Scope**: Design planning only. This command does NOT write code. It produces the thinking that makes code good. +Discover what should be made and how it should work, then return a confirmed design brief without code. -**Output**: A design brief that can be handed off to /impeccable craft, or directly to /impeccable for freeform implementation. When visual direction probes are used, the images are supporting artifacts, not the primary output. +**Product gate:** when context reports that PRODUCT.md is missing, load and complete [init.md](init.md) before Phase 1. Do not substitute shape questions for the product interview. Once PRODUCT.md exists, return here; product context does not replace task discovery. -## Philosophy +## Phase 1: Discovery interview -Most AI-generated UIs fail not because of bad code, but because of skipped thinking. They jump to "here's a card grid" without asking "what is the user trying to accomplish?" This command inverts that: understand deeply first, so implementation is precise. +Do not write code or choose visual direction yet. -## Phase 1: Discovery Interview +### Cadence -**Do NOT write any code or make any design decisions during this phase.** Your only job is to understand the feature deeply enough to make excellent design decisions later. +- Use the structured question tool when available; otherwise ask and stop. +- Ask two or three related questions per round, then wait. One round is the default; add a second only when the answers expose a material gap. +- Do not dump a questionnaire, repeat settled facts, or turn obvious facts into menus. Assert the likely reading and invite correction. +- A sparse prompt requires at least one answer round. A precise prompt may need only a compact confirmation. -**Unattended runs:** when no user can respond (a one-shot task, an automated run, or an explicit instruction not to ask questions), answer the interview questions yourself from the brief, record the answers as the design brief, and continue without pausing at any confirmation gate in this file. The thinking is still required; only the waiting is waived. +### Round 1: purpose, people, and outcome -This is a required interaction, not optional guidance. Ask these questions in conversation, adapting based on answers. Don't dump them all at once; have a natural dialogue. ask the user directly to clarify what you cannot infer. +Choose the two or three questions that most change the result: -### Interview cadence +- What is this surface or feature for, and what problem must it solve? +- Who specifically reaches it, in what situation and state of mind? +- What is the primary thing they must understand or do? What would success look like? +- What is uniquely true here that a neighboring product or generic template could not claim? -Discovery includes at least one user-answer round unless PRODUCT.md, DESIGN.md, or an already-confirmed brief directly answers the needed inputs. With a sparse prompt, do **not** synthesize a complete brief for confirmation on the first response. +### Round 2: material, behavior, and boundaries -- Use the harness's structured question tool when one exists. Otherwise, ask directly in chat and stop. -- Ask **2-3 questions per round**, then wait for answers. -- Treat PRODUCT.md and DESIGN.md as anchors; they reduce repeated questions but do **not** replace shape for craft. Shape is task-specific. -- One round is the default. Add a second only if the first answers leave material gaps. Don't run a second round just to feel thorough. -- Round 1 should clarify purpose, audience/context, content/scope, and (for brand) visual direction. -- Round 2, when needed, fills in whatever's still genuinely missing. +Run only for material unresolved decisions: -**Assert-then-confirm, not menu-with-escape.** When PRODUCT.md and the user's prompt make one option obvious, name it and ask the user to confirm or override. Don't enumerate "Restrained / Committed / Or something else?" as a real choice; "This reads as Restrained, confirm?" beats a four-option menu when the answer is already clear. +- What real content, evidence, data, and assets must the experience carry? What are realistic minimum, typical, and maximum ranges? +- Which states and transitions matter: first-run, empty, loading, error, success, permissions, overflow, or expert use? +- What is the intended fidelity, breadth, and interactivity: exploration, production-ready screen, full flow, or broader surface? +- What must remain untouched? What would make the result feel wrong even if it looked polished? +- Which platform, framework, performance, accessibility, localization, or delivery constraints are binding? -### Purpose & Context -- What is this feature for? What problem does it solve? -- Who specifically will use it? (Not "users"; be specific: role, context, frequency) -- What does success look like? How will you know this feature is working? -- What's the user's state of mind when they reach this feature? (Rushed? Exploring? Anxious? Focused?) +Never ask for CSS values or canned aesthetic lanes. New-work owns visual-world and concept choices. -### Content & Data -- What content or data does this feature display or collect? -- What are the realistic ranges? (Minimum, typical, maximum, e.g., 0 items, 5 items, 500 items) -- What are the edge cases? (Empty state, error state, first-time use, power user) -- Is any content dynamic? What changes and how often? -- What visual assets are real content here? Note required images, product shots, illustrations, maps, textures, diagrams, generated objects, or existing project assets. +## Phase 2: Resolve the design direction -### Design Direction +For new surfaces, brand expansion, or replacement, follow [new-work.md](new-work.md) through visual authority, any world workshop, and concept choice. Reuse discovery, then return before its contract, persistence, or implementation. Inside an established world, use its concept process only when composition or interaction remains materially open. -Force a visual decision on three fronts. Skip anything PRODUCT.md or DESIGN.md already answers; ask only what's missing. +## Phase 3: Write the brief -- **Color strategy for this surface.** Pick one: Restrained / Committed / Full palette / Drenched. Can override the project default if the surface earns it (e.g. a drenched hero inside an otherwise Restrained product). -- **Theme via scene sentence.** Write one sentence of physical context for this surface: who uses it, where, under what ambient light, in what mood. The sentence forces dark vs light. If it doesn't, add detail until it does. -- **Two or three named anchor references.** Specific products, brands, objects. Not adjectives like "modern" or "clean." +Write the smallest useful brief: -### Scope +1. **Job and audience:** who arrives, their context, need, and visitor mode. +2. **Outcome and proof:** primary task/action, success, real evidence, and product-specific truth. +3. **Selected direction:** visual authority, structural/interaction thesis, sequence, focal moment, and implementation consequence. +4. **Scope and boundaries:** fidelity, breadth, interactivity, named target, what remains untouched, and explicit anti-goals. +5. **States and ranges:** realistic content/data ranges and material states. +6. **Interaction and layout:** hierarchy, topology, responsiveness, affordances, feedback, and transitions; intent, not CSS. +7. **Constraints and open decisions:** platform, delivery, accessibility, localization, reusable components, and choices a builder must not invent. -Always ask. Sketch quality and shipped quality are different outputs; don't guess between them. +Use three to five bullets when the task is settled; use the full structure only for ambiguous, multi-screen, or standalone planning. Do not restate the conversation. -- **Fidelity.** Sketch / mid-fi / high-fi / production-ready? -- **Breadth.** One screen / a flow / a whole surface? -- **Interactivity.** Static visual / interactive prototype / shipped-quality component? -- **Time intent.** Quick exploration, or polish until it ships? +## Confirm and stop -Scope answers are task-scoped. Don't write them to PRODUCT.md or DESIGN.md; carry them through the design brief only. +Present the brief for explicit confirmation or one correction round, then stop: shape never writes code or a direction contract. -### Constraints -- Are there technical constraints? (Framework, performance budget, browser support) -- Are there content constraints? (Localization, dynamic text length, user-generated content) -- Mobile/responsive requirements? -- Accessibility requirements beyond WCAG AA? - -### Anti-Goals -- What should this NOT be? What would be a wrong direction? -- What's the biggest risk of getting this wrong? - -## Phase 1.5: Visual Direction Probe (Capability-Gated) - -After the discovery interview, generate a small set of visual direction probes **before** writing the final brief when all of these are true: - -- The work is **net-new** or directionally ambiguous enough that visual exploration will clarify the brief. -- The requested fidelity is **mid-fi, high-fi, or production-ready**. Skip for sketch-only planning. -- The current harness gives you native image generation (Codex's `image_gen`, an equivalent MCP tool, or similar). Don't ask the user to install APIs or tooling. - -When those conditions are met, this step is mandatory. If image generation isn't natively available, do not ask the user to install APIs or tooling. State in one line that the image step is skipped because the harness lacks native image generation, then proceed. The one-line announcement is required, not optional; it forces a conscious decision instead of letting the step quietly evaporate. - -Use probes to explore visual lanes, not to replace the brief. - -Do not skip probes because the final UI will be semantic, editable, code-native, responsive, or accessible. Those are implementation requirements, not reasons to avoid visual exploration. - -### What to generate - -Generate **2 to 4** distinct direction probes based on the discovery answers, especially: - -- Color strategy -- Theme scene sentence -- Named anchor references -- Scope and fidelity - -The probes should differ in primary visual direction (hierarchy, topology, density, typographic voice, or color strategy), not just palette tweaks. - -### How to use the probes - -- Treat them as **direction tests**, not final designs. -- Use them to pressure-test whether the brief is pointing at the right lane. -- Ask the user which direction feels closest, what feels off, and what should carry forward. -- If the probes reveal a mismatch, revise the brief inputs before finalizing the brief. - -### Important limits - -- Do **not** skip discovery because image generation is available. -- Do **not** treat generated imagery as final UX specification, final copy, or final accessibility behavior. -- Do **not** use this step for minor refinements of existing work. It's for shaping a new surface or clarifying a big directional choice. - -If image generation isn't natively available, announce the skip in one line and proceed to the design brief. - -## Phase 2: Design Brief - -After the interview and any required probes, present a brief and **end your response**. The user must confirm before any implementation runs. Do not present a brief and then continue to code in the same response, even if the brief feels obvious to you. The user's confirmation is the gate. - -**Choose the brief shape based on how clear the answers are:** - -- **Compact form (3-5 bullets)** when discovery was crisp and the original prompt + PRODUCT.md already pinned scope, content, and direction. State what you're building, the visual lane, and end with one or two specific questions or a clear "confirm or override?" prompt. This is the default for typical craft requests with a clear prompt. -- **Full structured form (sections below)** when the task is genuinely ambiguous, multi-screen, or when the user asked for shape as a standalone step. Use this when the discipline of structure earns its weight. - -Don't pad a clear brief into a long one to look thorough. A 70-line brief restating answers the user just gave is noise, not rigor. Equally, don't skip the confirmation pause to look efficient: the pause is the point. - -Present the brief, then **stop and wait for explicit confirmation**. You are not the judge of whether the user already approved. Even when the brief feels obviously right, ask once and wait. The pause is what separates shape from premature implementation. - -### Brief Structure - -**1. Feature Summary** (2-3 sentences) -What this is, who it's for, what it needs to accomplish. - -**2. Primary User Action** -The single most important thing a user should do or understand here. - -**3. Design Direction** -Color strategy (Restrained / Committed / Full palette / Drenched) + the theme scene sentence + 2–3 named anchor references. Reference PRODUCT.md and DESIGN.md where they already answer, and note any per-surface overrides. - -If you ran the Visual Direction Probe step, name which probe direction won and what changed in the brief because of it. - -**4. Scope** -Fidelity, breadth, interactivity, and time intent from the Scope section of the interview. Task-scoped; these don't persist beyond the brief. - -**5. Layout Strategy** -High-level spatial approach: what gets emphasis, what's secondary, how information flows. Describe the visual hierarchy and rhythm, not specific CSS. - -**6. Key States** -List every state the feature needs: default, empty, loading, error, success, edge cases. For each, note what the user needs to see and feel. - -**7. Interaction Model** -How users interact with this feature. What happens on click, hover, scroll? What feedback do they get? What's the flow from entry to completion? - -**8. Content Requirements** -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., 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. - ---- - -ask the user directly to clarify what you cannot infer. Ask for explicit confirmation of the brief before finishing. - -If the user disagrees with any part, revisit the relevant discovery questions. A shape run is incomplete until the user confirms direction. - -Once confirmed, the brief is complete. The user can now hand it to /impeccable, or use it to guide any other implementation approach. (If the user wants the full discovery-then-build flow in one step, they should use /impeccable craft instead, which runs this command internally.) +When no human or structured answer mechanism exists, mark assumptions plainly, return the brief, and stop. diff --git a/.qoder/skills/impeccable/reference/typeset.md b/.qoder/skills/impeccable/reference/typeset.md index bfa4737a5..bd0492cb4 100644 --- a/.qoder/skills/impeccable/reference/typeset.md +++ b/.qoder/skills/impeccable/reference/typeset.md @@ -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. --- -## Register +## Visitor mode -New identity work: run the font selection procedure in [new-work.md](new-work.md). Fluid `clamp()` scale, ≥1.25 ratio between steps. +- **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, register, **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 .qoder/skills/impeccable/scripts/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 `/impeccable polish` for the final pass. +When the hierarchy holds, hand off to `/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) * )` 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 - -The tactical selection procedure and the reflex-reject list live in [new-work.md](new-work.md) (loaded for new identity work). 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 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. diff --git a/.qoder/skills/impeccable/scripts/command-metadata.json b/.qoder/skills/impeccable/scripts/command-metadata.json index 83796d54b..dad8ef2e0 100644 --- a/.qoder/skills/impeccable/scripts/command-metadata.json +++ b/.qoder/skills/impeccable/scripts/command-metadata.json @@ -1,6 +1,6 @@ { "craft": { - "description": "Full confirmed-brief-then-build flow. Runs multi-round shape discovery first, resolves visual probe and north-star mock gates when available, then builds and visually iterates. Use when building a new feature end-to-end.", + "description": "Deprecated compatibility alias for an ordinary Impeccable new-work request. It adds no behavior; natural build and redesign requests use the same flow.", "argumentHint": "[feature description]" }, "init": { diff --git a/.qoder/skills/impeccable/scripts/concept-seed.mjs b/.qoder/skills/impeccable/scripts/concept-seed.mjs index 1f141a80f..25f20d9ab 100644 --- a/.qoder/skills/impeccable/scripts/concept-seed.mjs +++ b/.qoder/skills/impeccable/scripts/concept-seed.mjs @@ -1,6 +1,7 @@ #!/usr/bin/env node /** - * Concept-seed picker: the dice half of the new-work concept procedure. + * External concept seed: the dice half of new-work's coupled-direction and + * established-world surface procedures. * * The model derives a grounded shortlist of candidate FORMS from the * audience's world and the subject's cultural home (see @@ -12,10 +13,9 @@ * * This script rolls them from outside, the same trick that made the * palette seed work: - * - BUILD INDEX (2-5): which entry of the model's own resonance-ordered - * shortlist to build. The dice never choose an ungrounded ingredient; - * they only refuse the argmax rut. (Index 1 is excluded: that's the - * concept every run would ship anyway.) + * - PROMOTED INDEX: which entry of the model's own resonance-ordered + * shortlist must be taken seriously beside its favorites. The dice never + * choose an ungrounded ingredient; they only refuse the argmax rut. * - CHALLENGERS (3): outside forms from concept-ingredients.json, weighed * against the derived candidates on exactly two axes — audience * identification and product clarity. They win only when they beat the @@ -23,8 +23,8 @@ * material and win over thin categories, which is the intended shape. * * Usage: - * node scripts/concept-seed.mjs # roll at random - * node scripts/concept-seed.mjs --from # deterministic (hash key) + * node scripts/concept-seed.mjs --scope direction + * node scripts/concept-seed.mjs --scope surface --from * * Env vars: * IMPECCABLE_CONCEPT_SEED — same as --from; for reproducible eval runs. @@ -40,6 +40,12 @@ const pool = JSON.parse(readFileSync(join(here, 'concept-ingredients.json'), 'ut const args = process.argv.slice(2); const fromIdx = args.indexOf('--from'); +const scopeIdx = args.indexOf('--scope'); +const scope = scopeIdx !== -1 ? args[scopeIdx + 1] : 'surface'; +if (scope !== 'surface' && scope !== 'direction') { + process.stderr.write('concept-seed: --scope must be direction or surface\n'); + process.exit(1); +} // When no key is supplied, generate one and print it: a user reporting a // bad outcome can hand us the key and we replay the exact roll. const key = fromIdx !== -1 @@ -47,7 +53,7 @@ const key = fromIdx !== -1 : (process.env.IMPECCABLE_CONCEPT_SEED || crypto.randomBytes(4).toString('hex')); function hashUnit(k, salt) { - const h = crypto.createHash('sha256').update(`${salt}:${k}`).digest(); + const h = crypto.createHash('sha256').update(`${scope}:${salt}:${k}`).digest(); return h.readUInt32BE(0) / 0xffffffff; } const unit = (salt) => hashUnit(key, salt); @@ -67,22 +73,48 @@ for (let i = 0; picks.length < 3 && i < 60; i++) { } } -process.stdout.write(`CONCEPT SEED (key: ${key}; rerun with --from ${key} to reproduce this roll) -BUILD INDEX: ${buildIndex} - After ordering your derived candidates by resonance, build the page whose - form comes from candidate number ${buildIndex}, exactly as if it had ranked - first: full commitment. Your top-ranked candidate is what every run in this - category would ship; the assignment exists to refuse that rut, not to - punish it. -CHALLENGERS (weigh against your derived candidates on the same two axes, -audience identification and product clarity; a challenger wins only when -it beats the grounded list on both): +const promotedInstruction = scope === 'direction' + ? `After ordering the grounded coupled directions by product fit, promote + candidate ${buildIndex} into the serious shortlist. Each candidate must join + a durable visual system to a concrete expression for the requested first + surface; select or revise that pair as one decision. It must survive the + current task plus navigation, quiet and dense content, interaction and state, + and a substantially different future surface.` + : `After ordering the task's grounded structural candidates by resonance, + promote candidate ${buildIndex} into the serious shortlist. In an attended + run, present it beside the strongest materially different candidates and + let the user select or revise the surface concept. In a truly unattended + run, use it when it survives audience identification and product clarity.`; + +const challengerInstruction = scope === 'direction' + ? `A challenger enters only when it can supply both reusable identity grammar + and a strong first-surface structure, not a one-page costume or a style pasted + onto an unrelated layout. Weigh audience identification, product clarity, + current-surface force, and cross-surface system breadth.` + : `A challenger wins only when it beats the grounded list on both audience + identification and product clarity. It may change task topology or + interaction, but never the committed visual identity.`; + +const authorityInstruction = scope === 'direction' + ? `PRODUCT.md and explicit incumbent brand commitments constrain every coupled +direction. The seed never chooses exact colors, fonts, tokens, or a user +preference, and it never permits the world and first surface to be selected +independently.` + : `PRODUCT.md and DESIGN.md constrain every surface candidate's identity +vocabulary; they do not cancel task-level composition. The seed never +authorizes a new palette, type system, material world, or unfamiliar control +behavior.`; + +process.stdout.write(`${scope.toUpperCase()} CONCEPT SEED (key: ${key}; rerun with --scope ${scope} --from ${key} to reproduce this roll) +PROMOTED INDEX: ${buildIndex} + ${promotedInstruction} + The promotion exists to refuse the model's ranking rut, not to outrank the + user or the brief. +CHALLENGERS: 1. ${picks[0]} 2. ${picks[1]} 3. ${picks[2]} -If a challenger wins, it replaces the assigned candidate. If the surface is -an existing world whose incumbent carries a deliberate, ownable idea, the -incumbent IS the chosen candidate: intensify its lineage and ignore the -roll entirely. The same override applies when the user, PRODUCT.md, or -DESIGN.md pins a direction: pinned direction beats the roll, always. +${challengerInstruction} +${authorityInstruction} +A user- or brief-pinned decision beats the roll, always. `); diff --git a/.qoder/skills/impeccable/scripts/context-signals.mjs b/.qoder/skills/impeccable/scripts/context-signals.mjs index 2fc27bea7..d296320d7 100644 --- a/.qoder/skills/impeccable/scripts/context-signals.mjs +++ b/.qoder/skills/impeccable/scripts/context-signals.mjs @@ -11,7 +11,7 @@ * output is always valid JSON. * * Signals: - * - setup: PRODUCT.md / DESIGN.md presence, register, whether code exists + * - setup: PRODUCT.md / DESIGN.md presence and whether code exists * - critique: the latest cached critique score (.impeccable/critique) * - git: branch + files changed vs the default branch (a scope hint) * - devServer: whether a local dev server answers on a common port (gates live) @@ -21,7 +21,7 @@ import net from 'node:net'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { execFileSync } from 'node:child_process'; -import { loadContext, extractRegister, extractPlatform } from './context.mjs'; +import { loadContext, extractPlatform } from './context.mjs'; import { getCritiqueDir } from './lib/impeccable-paths.mjs'; /** Is there code here at all, or just context files / an empty repo? */ @@ -196,7 +196,6 @@ export async function gatherSignals(cwd = process.cwd()) { hasDesign: ctx.hasDesign, designPath: ctx.designPath, hasCode: hasCode(cwd), - register: extractRegister(ctx.product), platform: extractPlatform(ctx.product), }, critique: { latest: latestCritique(cwd) }, diff --git a/.qoder/skills/impeccable/scripts/context.mjs b/.qoder/skills/impeccable/scripts/context.mjs index 2ef2d4ce8..080e7ee3a 100644 --- a/.qoder/skills/impeccable/scripts/context.mjs +++ b/.qoder/skills/impeccable/scripts/context.mjs @@ -1,8 +1,10 @@ /** - * Context loader: prints PRODUCT.md (and DESIGN.md if present) as one - * markdown block on stdout, or prints a `NO_PRODUCT_MD:` message when no + * Context loader: prints PRODUCT.md, DESIGN.md when present, the matching + * persisted surface brief when one can be resolved, and native-platform + * guidance selected from PRODUCT.md. It prints a + * `NO_PRODUCT_MD:` message when no * PRODUCT.md is found anywhere. The skill keys off that message to branch: - * from-scratch build commands (init / teach / craft / shape) and clear + * from-scratch build requests (plus init / teach / shape) and clear * build/shape intent divert into the init flow, while scoped commands proceed * using the existing code as context. * @@ -23,10 +25,13 @@ import os from 'node:os'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { parseTargetOptions } from './lib/target-args.mjs'; -import { IMPECCABLE_COMMAND } from './lib/provider.mjs'; +import { IMPECCABLE_COMMAND, IMPECCABLE_PROVIDER_ID } from './lib/provider.mjs'; +import { renderLlmOnlySlopReview } from './lib/slop-review.mjs'; +import { resolveSurfaceBrief } from './lib/surface-briefs.mjs'; const PRODUCT_NAMES = ['PRODUCT.md', 'Product.md', 'product.md']; const DESIGN_NAMES = ['DESIGN.md', 'Design.md', 'design.md']; +const SKILL_REFERENCE_DIR = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', 'reference'); const FALLBACK_DIRS = ['.agents/context', 'docs']; const MONOREPO_MARKER_FILES = ['pnpm-workspace.yaml', 'turbo.json', 'nx.json', 'lerna.json']; const MONOREPO_FALLBACK_PROJECT_DIRS = ['apps', 'packages']; @@ -41,6 +46,8 @@ const WORKSPACE_DISCOVERY_IGNORED_DIRS = new Set([ '.turbo', '.cache', 'coverage', + 'vendor', + 'vendors', ]); const VISUAL_SOURCE_DIRS = ['src', 'app', 'pages', 'components', 'site', 'public', 'styles']; const STYLE_EXTENSIONS = new Set(['.css', '.scss', '.sass', '.less', '.styl']); @@ -73,6 +80,12 @@ export function loadContext(cwd = process.cwd(), options = {}) { const designPath = resolved.designPath; const product = productPath ? safeRead(productPath) : null; const design = designPath ? safeRead(designPath) : null; + const platform = extractPlatform(product); + const surfaceResolution = resolveSurfaceBrief( + resolved.projectRoot, + hasTargetOption(options) ? options.targetPath : null, + ); + const surfaceBrief = surfaceResolution.brief; return { hasProduct: !!product, product, @@ -83,7 +96,18 @@ export function loadContext(cwd = process.cwd(), options = {}) { contextDir: resolved.contextDir, productContextDir: productPath ? path.dirname(productPath) : null, designContextDir: designPath ? path.dirname(designPath) : null, + hasSurfaceBrief: !!surfaceBrief, + surfaceBrief: surfaceBrief?.text ?? null, + surfaceBriefPath: surfaceBrief?.path ? path.relative(absCwd, surfaceBrief.path) : null, + surfaceBriefReason: surfaceResolution.reason, + surfaceBriefCandidates: surfaceResolution.candidates.map((brief) => ({ + slug: brief.slug, + path: path.relative(absCwd, brief.path), + primaryTarget: brief.primaryTarget, + relatedTargets: brief.relatedTargets, + })), hasVisualImplementation: hasVisualImplementation(resolved.projectRoot), + platform, projectRoot: resolved.projectRoot, repoRoot: resolved.repoRoot, isMonorepo: resolved.isMonorepo, @@ -696,6 +720,19 @@ function safeRead(p) { } } +function loadNativePlatformReferences(platform) { + const names = platform === 'adaptive' + ? ['ios', 'android'] + : platform === 'ios' || platform === 'android' + ? [platform] + : []; + return names.flatMap((name) => { + const filePath = path.join(SKILL_REFERENCE_DIR, `${name}.md`); + const content = safeRead(filePath); + return content ? [{ name, filePath, content }] : []; + }); +} + /** * Best-effort evidence that the project already has an incumbent visual * implementation. DESIGN.md is documentation, not the only source of design @@ -719,9 +756,11 @@ export function hasVisualImplementation(projectRoot) { let styledComponents = 0; const inspectFile = (filePath) => { - if (scannedFiles++ >= VISUAL_SCAN_FILE_LIMIT) return false; const ext = path.extname(filePath).toLowerCase(); if (!STYLE_EXTENSIONS.has(ext) && !UI_EXTENSIONS.has(ext)) return false; + const base = path.basename(filePath).toLowerCase(); + if (/\.min\.[a-z]+$/.test(base)) return false; + if (scannedFiles++ >= VISUAL_SCAN_FILE_LIMIT) return false; let body; try { body = fs.readFileSync(filePath, 'utf-8').slice(0, 64 * 1024); @@ -729,18 +768,28 @@ export function hasVisualImplementation(projectRoot) { return false; } - const base = path.basename(filePath).toLowerCase(); + const evidence = body + .replace(/\/\*[\s\S]*?\*\//g, '') + .replace(//g, '') + .replace(/^\s*\/\/.*$/gm, ''); if (STYLE_EXTENSIONS.has(ext)) { - const customProperties = body.match(/--[a-z0-9_-]+\s*:/gi)?.length ?? 0; - const visualDeclarations = body.match(/\b(?:color|background(?:-color)?|border(?:-color)?|font-family)\s*:/gi)?.length ?? 0; - if (/\b(?:tokens?|theme|design-system)\b/.test(base) && body.trim().length > 80) return true; + const customProperties = evidence.match(/--[a-z0-9_-]+\s*:/gi)?.length ?? 0; + const visualDeclarations = evidence.match(/\b(?:color|background(?:-color)?|border(?:-color)?|font-family)\s*:/gi)?.length ?? 0; + if (/\b(?:tokens?|theme|design-system)\b/.test(base) && evidence.trim().length > 80) return true; if (customProperties >= 3 || visualDeclarations >= 5) return true; } - if ((ext === '.html' || ext === '.htm') && body.length > 600 && /]+stylesheet/i.test(body)) { + if ((ext === '.html' || ext === '.htm') && evidence.length > 600 && /]+stylesheet/i.test(evidence)) { return true; } - if (!['.html', '.htm'].includes(ext) && body.length > 300 && /class(?:Name)?\s*=|style\s*=|styled\(|css`/i.test(body)) { + if (!['.html', '.htm'].includes(ext) && evidence.length > 300) { + const embeddedCustomProperties = evidence.match(/--[a-z0-9_-]+\s*:/gi)?.length ?? 0; + const embeddedVisualDeclarations = evidence.match(/\b(?:color|background(?:-color)?|border(?:-color)?|font-family)\s*:/gi)?.length ?? 0; + const classTokens = [...evidence.matchAll(/class(?:Name)?\s*=\s*["'`]([^"'`]+)["'`]/gi)] + .reduce((count, match) => count + match[1].trim().split(/\s+/).length, 0); + if ((embeddedCustomProperties >= 3 && embeddedVisualDeclarations >= 3) || embeddedVisualDeclarations >= 5 || classTokens >= 12) return true; + } + if (!['.html', '.htm'].includes(ext) && evidence.length > 300 && /class(?:Name)?\s*=|style\s*=|styled\(|css`/i.test(evidence)) { styledComponents += 1; if (styledComponents >= 3) return true; } @@ -781,9 +830,9 @@ function escapeRegExp(value) { /** * Read the first non-empty line under a bare `## ` section of - * PRODUCT.md (e.g. `## Register`, `## Platform`). Returns null when the + * PRODUCT.md (for example `## Platform`). Returns null when the * section is absent. The heading match is exact (`\s*$`) so near-miss - * headings like `## Register guidelines` don't shadow the real field. + * near-miss headings don't shadow the real field. */ export function extractSectionValue(product, heading) { if (!product) return null; @@ -802,16 +851,6 @@ export function extractSectionValue(product, heading) { return null; } -/** - * Pull the register (`brand` or `product`) out of PRODUCT.md by looking - * for a `## Register` section and reading the first non-empty line that - * follows it. Returns null when the file is legacy / register-less. - */ -export function extractRegister(product) { - const word = (extractSectionValue(product, 'Register') || '').toLowerCase(); - return word === 'brand' || word === 'product' ? word : null; -} - /** * Pull the platform (`web`, `ios`, `android`, or `adaptive`) out of PRODUCT.md * by looking for a `## Platform` section and reading the first non-empty line @@ -985,30 +1024,35 @@ async function cli() { const parts = ctx.hasVisualImplementation ? [ 'NO_PRODUCT_MD: This project has no PRODUCT.md yet, but it does have an incumbent visual implementation. ' + - 'For `init` or `teach`, load reference/init.md and create PRODUCT.md. For `craft`, `shape`, or other work ' + - 'against the existing product, read its CSS, tokens, components, and assets first and proceed without blocking; ' + - `offer \`${IMPECCABLE_COMMAND} init\` as a follow-up. Only treat the work as greenfield when the user explicitly ` + - 'asks to discard or replace the current identity.', - 'EXISTING_VISUAL_SYSTEM: Code and assets are the incumbent design authority. Missing DESIGN.md is a documentation ' + - 'gap, not permission to invent a replacement identity. Preserve and extend the implementation unless the user ' + - 'explicitly requests a rebrand, or inspection proves the detected files contain no coherent visual decisions.', + 'For `init`, `teach`, `shape`, or any request to create a new surface or replacement visual world, load reference/init.md and create PRODUCT.md with the user first. ' + + 'After init writes PRODUCT.md, reference/new-work.md preserves and documents the incumbent system for an ' + + 'extension or replaces it with the user for a redesign/rebrand. Other ' + + 'narrow refinement commands may read the CSS, tokens, components, and assets and proceed without blocking, then ' + + `offer \`${IMPECCABLE_COMMAND} init\` as a follow-up.`, + 'BUILD_INIT_REQUIRED: Before shape or any new-surface/redesign flow, init must capture PRODUCT.md with the human or structured ' + + 'simulated user. Init writes product truth only; reference/new-work.md owns every visual decision.', + 'SCOPED_EXISTING_ALLOWED: Narrow refinement commands may use the incumbent implementation as authority without ' + + 'blocking on context setup; they must preserve it and offer init afterward.', + 'EXISTING_VISUAL_SYSTEM: For refinement or extension, code and assets are incumbent design authority and missing ' + + 'DESIGN.md is a documentation gap. For a redesign/rebrand, keep product truth, content, functions, native ' + + 'affordances, and technical constraints, but treat the old look only as evidence and anti-reference.', ] : [ 'NO_PRODUCT_MD: This project has no PRODUCT.md yet. ' + - 'For `init`, `teach`, `craft`, `shape`, ' + + 'For `init`, `teach`, `shape`, ' + 'or wording that clearly maps to a from-scratch build/shape flow, load ' + - 'reference/init.md and write PRODUCT.md first, unless no user can ' + - 'respond (a one-shot or automated run, or the user said not to ask): ' + - 'then write a one-paragraph understanding of the product, audience, ' + - 'and the page\'s job from the brief, and continue. For any other ' + + 'reference/init.md, complete its human or structured simulated-user interview, and write PRODUCT.md before ' + + 'designing. If no answer mechanism truly exists, init may infer only from the explicit brief and must label its ' + + 'assumptions. It never writes DESIGN.md. For any other ' + '(scoped) command against existing code, proceed using the code as ' + `context and offer \`${IMPECCABLE_COMMAND} init\` as a suggestion (do not block).`, - 'NEW_WORK: No committed design context was found. If this task produces ' + - 'new design (a build from scratch, or a redesign that discards the ' + - 'current look), you MUST read reference/new-work.md before making any ' + - 'design decision. Scoped fixes to existing code do not need it.', + 'PRODUCT_INIT_REQUIRED: No product context or visual authority was found. New builds and redesigns ' + + 'must finish reference/init.md for PRODUCT.md, then reference/new-work.md establishes the world and surface. Scoped ' + + 'fixes to existing code do not need the new-surface flow.', ]; + appendSurfaceBriefContext(parts, ctx); parts.push(buildResolvedContextDirective(ctx, cliOptions, { targetExists })); + appendHookFallback(parts, ctx); if (shouldWarnMissingTarget(ctx, targetProvided, targetExists)) { parts.push(buildMissingTargetDirective()); } @@ -1020,37 +1064,29 @@ async function cli() { if (ctx.hasDesign) { parts.push(`# DESIGN.md\n\n${ctx.design.trim()}`); } + appendSurfaceBriefContext(parts, ctx); parts.push(buildResolvedContextDirective(ctx, cliOptions, { targetExists })); + appendHookFallback(parts, ctx); if (shouldWarnMissingTarget(ctx, targetProvided, targetExists)) { parts.push(buildMissingTargetDirective()); } - const register = extractRegister(ctx.product); - // The register field survives as a family hint (brand = Persuade/Experience, - // product = Operate/Read); SKILL.md's mode section carries the essentials - // inline, so no register-file read is mandated here. What IS mandated: - // the new-work playbook when no committed design system exists yet. if (!ctx.hasDesign) { parts.push(ctx.hasVisualImplementation - ? 'EXISTING_VISUAL_SYSTEM: PRODUCT.md exists and DESIGN.md is missing, but code contains incumbent visual decisions. ' + - 'Treat CSS, tokens, components, and assets as design authority. Do not route to new identity work merely because the ' + - 'document is absent; preserve and extend the implementation unless the user explicitly asks to discard it.' - : 'NEW_WORK: PRODUCT.md exists but no DESIGN.md or incumbent visual implementation was found. If this task produces ' + - 'new design or a redesign that discards the current look, you MUST read reference/new-work.md before making any ' + - 'design decision. Scoped fixes to existing code do not need it.'); + ? 'INCUMBENT_WORLD_UNDOCUMENTED: PRODUCT.md exists and DESIGN.md is missing, but code contains incumbent visual decisions. ' + + 'For shape or a new-surface/redesign request, load reference/new-work.md: an extension documents and preserves the code-defined world; ' + + 'a redesign replaces it with the user and uses the old look only as evidence and anti-reference. Narrow refinement ' + + 'commands may proceed using the implementation directly.' + : 'WORLD_DISCOVERY_REQUIRED: PRODUCT.md exists but no DESIGN.md or incumbent visual implementation was found. ' + + 'For a new build or redesign, load reference/new-work.md and establish the visual world with the human or structured ' + + 'simulated user before developing the task concept. Scoped fixes to existing code do not need this flow.'); } - if (register) { - parts.push(`REGISTER: \`${register}\` (family hint: brand = Persuade/Experience surfaces, product = Operate/Read). Derive the visitor's mode per SKILL.md.`); - } - const platform = extractPlatform(ctx.product); - const nativeRefs = - platform === 'adaptive' ? ['ios', 'android'] : platform === 'ios' || platform === 'android' ? [platform] : []; - if (nativeRefs.length) { - const refList = nativeRefs.map(p => `\`reference/${p}.md\``).join(' and '); - const label = platform === 'adaptive' ? '`adaptive` (both iOS and Android)' : `\`${platform}\``; + const platformReferences = loadNativePlatformReferences(ctx.platform); + for (const reference of platformReferences) { parts.push( - `NEXT STEP: This project targets ${label}. Also read ${refList} for native conventions, in addition to SKILL.md's mode guidance.`, + `# NATIVE PLATFORM REFERENCE: ${reference.name.toUpperCase()} (reference/${reference.name}.md)\n\n${reference.content.trim()}`, ); - } else if (!platform) { + } + if (!ctx.platform) { // A `## Platform` section that names something we don't recognize (a // toolchain like `flutter`, a typo) would otherwise silently fall back to // web — the wrong default exactly when the user tried to say "native". @@ -1078,6 +1114,72 @@ function pathExistsForTarget(cwd, targetPath) { return fs.existsSync(abs); } +const HOOK_MANIFESTS_BY_PROVIDER = Object.freeze({ + 'claude-code': ['.claude/settings.local.json', '.claude/settings.json'], + codex: ['.codex/hooks.json'], + agents: ['.codex/hooks.json'], + cursor: ['.cursor/hooks.json'], + github: ['.github/hooks/impeccable.json'], +}); + +function truthyEnv(value) { + return typeof value === 'string' && /^(1|true|yes|on)$/i.test(value.trim()); +} + +function valueHasHookMarker(value) { + if (typeof value === 'string') { + return value.includes('skills/impeccable/scripts/hook.mjs') + || value.includes('skills/impeccable/scripts/hook-before-edit.mjs'); + } + if (Array.isArray(value)) return value.some(valueHasHookMarker); + if (value && typeof value === 'object') return Object.values(value).some(valueHasHookMarker); + return false; +} + +function hookEnabledAt(root) { + if (truthyEnv(process.env.IMPECCABLE_HOOK_DISABLED)) return false; + let enabled = true; + for (const name of ['.impeccable/config.json', '.impeccable/config.local.json']) { + const raw = readJson(path.join(root, name)); + if (raw?.hook && Object.prototype.hasOwnProperty.call(raw.hook, 'enabled')) { + enabled = raw.hook.enabled !== false; + } + } + return enabled; +} + +const STOP_REVIEW_PROVIDERS = new Set(['claude-code', 'codex', 'agents']); + +function automaticHookMode(ctx) { + if (ctx.platform === 'ios' || ctx.platform === 'android' || ctx.platform === 'adaptive') { + return 'none'; + } + const activeRoot = path.resolve(ctx.projectRoot || process.cwd()); + if (!hookEnabledAt(activeRoot)) return 'none'; + const manifests = HOOK_MANIFESTS_BY_PROVIDER[IMPECCABLE_PROVIDER_ID] || []; + const roots = [...new Set([process.cwd(), ctx.projectRoot, ctx.repoRoot].filter(Boolean).map((root) => path.resolve(root)))]; + for (const root of roots) { + for (const rel of manifests) { + const raw = readJson(path.join(root, rel)); + if (raw?.hooks && valueHasHookMarker(raw.hooks)) { + return STOP_REVIEW_PROVIDERS.has(IMPECCABLE_PROVIDER_ID) ? 'stop' : 'per-edit'; + } + } + } + return 'none'; +} + +function appendHookFallback(parts, ctx) { + const hookMode = automaticHookMode(ctx); + if (hookMode === 'stop') return; + const native = ctx.platform === 'ios' || ctx.platform === 'android' || ctx.platform === 'adaptive'; + parts.push(renderLlmOnlySlopReview({ + automaticDetector: hookMode === 'per-edit', + manualDetector: hookMode === 'none' && !native, + scriptsPath: path.dirname(fileURLToPath(import.meta.url)), + })); +} + function buildResolvedContextDirective(ctx, options, { targetExists = null } = {}) { const targetPath = hasTargetOption(options) ? options.targetPath : null; return `RESOLVED_CONTEXT:\n${JSON.stringify({ @@ -1087,10 +1189,29 @@ function buildResolvedContextDirective(ctx, options, { targetExists = null } = { repoRoot: ctx.repoRoot, productPath: ctx.productPath, designPath: ctx.designPath, + surfaceBriefPath: ctx.surfaceBriefPath, + surfaceBriefReason: ctx.surfaceBriefReason, + surfaceBriefCandidates: ctx.surfaceBriefCandidates, hasVisualImplementation: ctx.hasVisualImplementation, + platform: ctx.platform, }, null, 2)}`; } +function appendSurfaceBriefContext(parts, ctx) { + if (ctx.hasSurfaceBrief && ctx.surfaceBrief) { + parts.push(`# SURFACE BRIEF (${ctx.surfaceBriefPath})\n\n${ctx.surfaceBrief.trim()}`); + return; + } + if (!ctx.surfaceBriefCandidates?.length) return; + const helper = path.join(path.dirname(fileURLToPath(import.meta.url)), 'surface-brief.mjs'); + parts.push( + 'SURFACE_CONTEXT_AVAILABLE: Persisted surface briefs exist, but none was selected unambiguously for this invocation. ' + + 'Resolve the requested surface to its concrete primary or related source path, then run ' + + `\`node ${helper} read \` once before changing that surface. Candidates:\n` + + JSON.stringify(ctx.surfaceBriefCandidates, null, 2), + ); +} + function shouldWarnMissingTarget(ctx, targetProvided, targetExists = null) { if (ctx.isMonorepo && targetProvided && targetExists === false) return true; return !!( diff --git a/.qoder/skills/impeccable/scripts/critique-storage.mjs b/.qoder/skills/impeccable/scripts/critique-storage.mjs index 6b4d225cf..38674285a 100644 --- a/.qoder/skills/impeccable/scripts/critique-storage.mjs +++ b/.qoder/skills/impeccable/scripts/critique-storage.mjs @@ -29,8 +29,9 @@ import fs from 'node:fs'; import path from 'node:path'; import { fileURLToPath, pathToFileURL } from 'node:url'; import { getCritiqueDir } from './lib/impeccable-paths.mjs'; +import { slugFromTarget } from './lib/target-slug.mjs'; -const SLUG_MAX = 50; +export { slugFromTarget } from './lib/target-slug.mjs'; /** * Mechanically derive a slug from a resolved target. Returns null if the @@ -40,46 +41,6 @@ const SLUG_MAX = 50; * concrete artifact before calling this — we never slug a natural-language * phrase. */ -export function slugFromTarget(resolved, { cwd = process.cwd() } = {}) { - if (!resolved || typeof resolved !== 'string') return null; - const trimmed = resolved.trim(); - if (!trimmed) return null; - - // URL - if (/^https?:\/\//i.test(trimmed)) { - let url; - try { url = new URL(trimmed); } catch { return null; } - const hostPath = `${url.hostname}${url.pathname}`; - return kebab(hostPath); - } - - // File path. Make it project-relative so two devs critiquing the same - // checkout get the same slug regardless of where their repo is cloned. - const abs = path.isAbsolute(trimmed) ? trimmed : path.resolve(cwd, trimmed); - let rel = path.relative(cwd, abs); - // If the target is outside cwd, fall back to the basename so we still - // produce a stable slug (vs the absolute path, which would include - // home dirs / usernames). - if (rel.startsWith('..') || path.isAbsolute(rel)) { - rel = path.basename(abs); - } - if (!rel || rel === '.' || rel === '') return null; - return kebab(rel); -} - -function kebab(s) { - const slug = s - .toLowerCase() - .replace(/[/\\.]+/g, '-') - .replace(/[^a-z0-9-]+/g, '-') - .replace(/-+/g, '-') - .replace(/^-|-$/g, ''); - if (!slug) return null; - // Cap from the tail — the tail (filename) is more identifying than the - // top-level directory. - return slug.length <= SLUG_MAX ? slug : slug.slice(slug.length - SLUG_MAX).replace(/^-/, ''); -} - /** * Filename-safe UTC ISO timestamp: hyphens for separators, trailing Z. * Plain colons aren't allowed on Windows filesystems. diff --git a/.qoder/skills/impeccable/scripts/detector/detect-antipatterns-browser.js b/.qoder/skills/impeccable/scripts/detector/detect-antipatterns-browser.js index 033573e36..76bf4ddeb 100644 --- a/.qoder/skills/impeccable/scripts/detector/detect-antipatterns-browser.js +++ b/.qoder/skills/impeccable/scripts/detector/detect-antipatterns-browser.js @@ -312,17 +312,6 @@ const ANTIPATTERNS = [ skillSection: 'Layout & Space', skillGuideline: 'numbered section markers', }, - { - id: 'numbered-section-markers', - category: 'slop', - scopes: ['layout'], - severity: 'advisory', - name: 'Numbered section markers (01 / 02 / 03)', - description: - 'Numbered display markers as section labels (01, 02, 03) are the AI editorial scaffold one tier deeper than tracked eyebrow chips. If you find yourself reaching for them, choose a different section cadence.', - skillSection: 'Layout & Space', - skillGuideline: 'numbered section markers', - }, { id: 'em-dash-overuse', category: 'slop', diff --git a/.qoder/skills/impeccable/scripts/detector/engines/regex/detect-text.mjs b/.qoder/skills/impeccable/scripts/detector/engines/regex/detect-text.mjs index 81c30ab99..f641d5917 100644 --- a/.qoder/skills/impeccable/scripts/detector/engines/regex/detect-text.mjs +++ b/.qoder/skills/impeccable/scripts/detector/engines/regex/detect-text.mjs @@ -280,22 +280,6 @@ const REGEX_ANALYZERS = [ if (count === 0) return []; return [finding('marketing-buzzword', filePath, `${count} buzzword phrase${count === 1 ? '' : 's'}: "${firstSample}"`)]; }, - // Numbered section markers (01 / 02 / 03 ...) - (content, filePath) => { - const text = stripHtmlToText(content); - const re = /\b(0[1-9]|1[0-2])\b/g; - const seen = new Set(); - let m; - while ((m = re.exec(text)) !== null) seen.add(m[1]); - if (seen.size < 3) return []; - const sorted = [...seen].sort(); - let sequential = 0; - for (let i = 1; i < sorted.length; i++) { - if (parseInt(sorted[i], 10) === parseInt(sorted[i - 1], 10) + 1) sequential++; - } - if (sequential < 2) return []; - return [finding('numbered-section-markers', filePath, `Sequence: ${sorted.slice(0, 6).join(', ')}`)]; - }, // Aphoristic cadence: manufactured-contrast + short-rebuttal (content, filePath) => { const text = stripHtmlToText(content); @@ -430,21 +414,20 @@ function runRegexMatchers(lines, filePath, lineOffset = 0, blockContext = null, } /** Page-level analyzers that scan rendered text content (em-dash use, - * buzzword phrases, numbered section markers, aphoristic cadence). + * buzzword phrases, aphoristic cadence). * These are detector-agnostic — they work on any HTML/text source * and don't need a parsed DOM. Exported so detectHtml can call them * for `.html` files (which otherwise skip the regex engine). */ const TEXT_CONTENT_ANALYZER_IDS = [ 'em-dash-overuse', 'marketing-buzzword', - 'numbered-section-markers', 'aphoristic-cadence', ]; function runTextContentAnalyzers(content, filePath, options = {}) { const profile = options?.profile; if (!shouldRunPageAnalyzers(content, filePath)) return []; - // The 4 text-content analyzers are at indices 3-6 in REGEX_ANALYZERS. + // The 3 text-content analyzers are at indices 3-5 in REGEX_ANALYZERS. const findings = []; for (let i = 0; i < TEXT_CONTENT_ANALYZER_IDS.length; i++) { const analyzer = REGEX_ANALYZERS[3 + i]; @@ -535,7 +518,6 @@ function detectText(content, filePath, options = {}) { 'monotonous-spacing', 'em-dash-overuse', 'marketing-buzzword', - 'numbered-section-markers', 'aphoristic-cadence', 'dark-glow', ]; diff --git a/.qoder/skills/impeccable/scripts/detector/registry/antipatterns.mjs b/.qoder/skills/impeccable/scripts/detector/registry/antipatterns.mjs index b85f7e62f..84c69b0b8 100644 --- a/.qoder/skills/impeccable/scripts/detector/registry/antipatterns.mjs +++ b/.qoder/skills/impeccable/scripts/detector/registry/antipatterns.mjs @@ -210,17 +210,6 @@ const ANTIPATTERNS = [ skillSection: 'Layout & Space', skillGuideline: 'numbered section markers', }, - { - id: 'numbered-section-markers', - category: 'slop', - scopes: ['layout'], - severity: 'advisory', - name: 'Numbered section markers (01 / 02 / 03)', - description: - 'Numbered display markers as section labels (01, 02, 03) are the AI editorial scaffold one tier deeper than tracked eyebrow chips. If you find yourself reaching for them, choose a different section cadence.', - skillSection: 'Layout & Space', - skillGuideline: 'numbered section markers', - }, { id: 'em-dash-overuse', category: 'slop', diff --git a/.qoder/skills/impeccable/scripts/detector/rules/checks.mjs b/.qoder/skills/impeccable/scripts/detector/rules/checks.mjs index 3fdd6b2dd..9bace11b3 100644 --- a/.qoder/skills/impeccable/scripts/detector/rules/checks.mjs +++ b/.qoder/skills/impeccable/scripts/detector/rules/checks.mjs @@ -352,27 +352,57 @@ function isAccentColor(cssColor) { return false; } +function resolveHeroHeadingSizePx(value) { + const input = String(value || '').trim().toLowerCase(); + if (!input) return 0; + + const simpleLengthPx = (token) => { + const match = /^(-?\d*\.?\d+)\s*(px|rem|em|%)?$/.exec(String(token || '').trim()); + if (!match) return null; + const amount = Number(match[1]); + if (!Number.isFinite(amount)) return null; + if (match[2] === 'rem' || match[2] === 'em') return amount * 16; + if (match[2] === '%') return amount * 0.16; + return amount; + }; + + const direct = simpleLengthPx(input); + if (direct !== null) return direct; + + // Static CSS engines cannot resolve viewport units, but clamp's min/max + // bounds still tell us whether the heading can ever reach hero scale. + const clamp = /^clamp\((.*)\)$/.exec(input); + if (clamp) { + const parts = clamp[1].split(','); + if (parts.length === 3) { + const bounds = [simpleLengthPx(parts[0]), simpleLengthPx(parts[2])] + .filter((candidate) => candidate !== null); + if (bounds.length > 0) return Math.max(...bounds); + } + } + + return 0; +} + // Sibling-relationship rule. Anchor on a hero-scale h1, look at the // previousElementSibling, and gate on EITHER the classic tracked- // uppercase eyebrow OR the modern accent-colored bold eyebrow. function checkHeroEyebrow(opts) { const { headingTag, headingText, headingFontSize, + headingInApplicationContext, siblingTag, siblingText, siblingTextTransform, siblingFontSize, siblingLetterSpacing, siblingFontWeight, siblingColor, siblingHasAccentDashPseudo, } = opts; if (headingTag !== 'h1') return []; - // We previously gated on headingFontSize >= 48 to anchor "hero scale". - // But modern hero h1s use clamp() / vw / var(--text-*), none of which - // jsdom can resolve — the computed value comes back as "2em" or - // "var(--text-9xl)" and parseFloat returns 2 or NaN. The gate fails - // on virtually every Tailwind v4 / framework build. The other gates - // (sibling text 2-60 chars, font-size ≤ 14px, accent-bold OR - // tracked-caps) are tight enough to avoid false positives on non- - // hero h1s — a tiny tan label directly above any h1 is the - // antipattern regardless of how big the h1 ends up. + // This is specifically a marketing-hero cliché, not a ban on compact + // context labels in product UI (for example, a station name inside a tab + // panel). Browser-computed sizes are reliable; the static adapter also + // resolves ordinary px/rem/em and clamp() bounds before reaching here. + if (headingInApplicationContext) return []; + if (!(headingFontSize >= 48)) return []; if (!siblingTag) return []; // An h2 above an h1 is a different anti-pattern (heading hierarchy / dual // headings) — never an eyebrow. @@ -1938,6 +1968,7 @@ function checkElementHeroEyebrowDOM(el) { headingTag: tag, headingText: el.textContent || '', headingFontSize: parseFloat(headStyle.fontSize) || 0, + headingInApplicationContext: !!el.closest('[role="tabpanel"], [role="dialog"], [role="application"], dialog'), siblingTag: sibling.tagName.toLowerCase(), siblingText: sibling.textContent || '', siblingTextTransform: sibStyle.textTransform || '', @@ -3367,7 +3398,8 @@ function checkElementHeroEyebrow(el, style, tag, window, customPropMap) { return checkHeroEyebrow({ headingTag: tag, headingText: el.textContent || '', - headingFontSize: parseFloat(headingFontSizeRaw) || 0, + headingFontSize: resolveHeroHeadingSizePx(headingFontSizeRaw), + headingInApplicationContext: !!el.closest?.('[role="tabpanel"], [role="dialog"], [role="application"], dialog'), siblingTag: sibling.tagName.toLowerCase(), siblingText: sibling.textContent || '', siblingTextTransform: sibStyle.textTransform || '', diff --git a/.qoder/skills/impeccable/scripts/hook-lib.mjs b/.qoder/skills/impeccable/scripts/hook-lib.mjs index e61ea692b..50aa2f8fb 100644 --- a/.qoder/skills/impeccable/scripts/hook-lib.mjs +++ b/.qoder/skills/impeccable/scripts/hook-lib.mjs @@ -46,6 +46,7 @@ import path from 'node:path'; import { pathToFileURL, fileURLToPath } from 'node:url'; import { extractPlatform, loadContext } from './context.mjs'; import { IMPECCABLE_COMMAND } from './lib/provider.mjs'; +import { renderStopSlopReview } from './lib/slop-review.mjs'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); @@ -1868,22 +1869,42 @@ export const CONTRACT_MAX_CHARS = 1800; // Cap contract sections per Stop emission so many touched artifacts cannot // stack an unbounded message. export const CONTRACT_AUDIT_MAX_FILES = 3; +export const CONTRACT_EXTS = new Set(['.html', '.htm', '.astro', '.svelte', '.vue', '.jsx', '.tsx']); +export const CONTRACT_REQUIRED_FIELDS = ['UNIQUE', 'NOT-TEMPLATE', 'OWN-WORLD', 'STORY', 'FIRST VIEWPORT', 'FORM']; /** - * Extract the artifact's own direction-contract comment: the first HTML - * comment in the head of the file, when its opening chars identify it as a - * contract/concept block. Returns the trimmed, length-capped body, or null - * when the file carries none (no comment, unclosed comment, marker missing, - * or the comment starts past the head window). + * Extract the artifact's own direction-contract comment from the head of an + * HTML or component file. Supports HTML-family comments and JSX block + * comments so the contract works in the Astro/Svelte/Vue/React scaffolds the + * skill actually builds. Returns the trimmed, length-capped body, or null + * when the file carries no valid contract block. */ export function extractDirectionContract(content) { if (typeof content !== 'string' || !content) return null; const head = content.slice(0, CONTRACT_HEAD_CHARS); - const m = //.exec(head); - if (!m) return null; - const body = m[1].trim(); - if (!body || !/contract|concept/i.test(body.slice(0, CONTRACT_MARKER_CHARS))) return null; - return body.slice(0, CONTRACT_MAX_CHARS); + const candidates = []; + for (const pattern of [//g, /\{\/\*([\s\S]*?)\*\/\}/g]) { + for (const match of head.matchAll(pattern)) { + const index = match.index ?? 0; + const linePrefix = head.slice(head.lastIndexOf('\n', index - 1) + 1, index).trim(); + if (linePrefix.startsWith('//')) continue; + candidates.push({ index, body: match[1].trim() }); + } + } + candidates.sort((a, b) => a.index - b.index); + for (const candidate of candidates) { + if (!candidate.body || !/direction\s+contract|concept\s+contract/i.test(candidate.body.slice(0, CONTRACT_MARKER_CHARS))) continue; + return candidate.body.slice(0, CONTRACT_MAX_CHARS); + } + return null; +} + +export function missingDirectionContractFields(contract) { + const body = typeof contract === 'string' ? contract : ''; + return CONTRACT_REQUIRED_FIELDS.filter((field) => { + const label = field.replace(/[.*+?^${}()|[\]\\]/g, '\\$&').replace(/\s+/g, '\\s+'); + return !new RegExp(`\\b${label}\\s*:`, 'i').test(body); + }); } /** @@ -1899,7 +1920,11 @@ export function renderContractAudit(entries, opts = {}) { const shown = entries.slice(0, CONTRACT_AUDIT_MAX_FILES); const blocks = shown.map(({ filePath, contract }) => { const display = relativize(filePath, cwd); - return `${display} opens with this direction contract, written when the direction was decided:\n\n${contract}`; + const missing = missingDirectionContractFields(contract); + const integrity = missing.length > 0 + ? `\n\nContract integrity defect: missing ${missing.join(', ')}. Repair the contract and the implementation together.` + : ''; + return `${display} opens with this direction contract, written when the direction was decided:\n\n${contract}${integrity}`; }); return [ `${ENVELOPE_PREFIX} Direction-contract audit. Before finishing, audit the rendered page against the contract it opens with, promise by promise.`, @@ -1984,9 +2009,22 @@ export async function runStopHook({ stdinJson, env = {}, cwd = process.cwd(), no return result({ skipped: 'native-platform', platform, durationMs: Date.now() - started }); } + const session = ensureSession(cache, sessionId); + const needsSlopReview = session.llmSlopReviewed !== true; + const det = detector || await loadDetector(); if (!det || typeof det.detectText !== 'function') { - return result({ skipped: 'detector-missing', durationMs: Date.now() - started }); + if (!needsSlopReview) return result({ skipped: 'detector-missing', durationMs: Date.now() - started }); + session.llmSlopReviewed = true; + session.updatedAt = Date.now(); + persistCache(projectCwd, cache); + const text = renderStopSlopReview(); + return { + exitCode: 0, + stdout: payload(text, 'Stop', harness), + emission: { kind: 'stop-llm-slop-review', llmSlopReview: true }, + audit: { ...audit, emitted: true, detectorMissing: true, llmSlopReview: true, chars: text.length, durationMs: Date.now() - started }, + }; } const scanOptions = designSystemOptions(config, det, projectCwd); @@ -2013,10 +2051,11 @@ export async function runStopHook({ stdinJson, env = {}, cwd = process.cwd(), no ? configuredExt.engine === 'html' : (ext === '.html' || ext === '.htm'); - // Direction-contract audit: HTML artifacts only, at most once per file + // Direction-contract audit: HTML and component artifacts, at most once per file // per session. The flag lives on the same session cache entry the // finding dedupe uses, so a second Stop fire stays quiet about it. - if (useHtmlEngine) { + const contractCapable = useHtmlEngine || CONTRACT_EXTS.has(ext); + if (contractCapable) { const fileEntry = ensureFile(cache, sessionId, filePath); if (!fileEntry.contractAudited) { const contract = extractDirectionContract(content); @@ -2046,10 +2085,15 @@ export async function runStopHook({ stdinJson, env = {}, cwd = process.cwd(), no } audit.scannedFiles = scanned; - if (freshGroups.length === 0 && contractEntries.length === 0) { + if (freshGroups.length === 0 && contractEntries.length === 0 && !needsSlopReview) { return result({ emitted: false, skipped: 'stop-clean', durationMs: Date.now() - started }); } + if (needsSlopReview) { + session.llmSlopReviewed = true; + session.updatedAt = Date.now(); + } + // Fresh findings and first-time contract audits earn the cache write; // both mark this batch as surfaced so the next Stop fire is silent // unless new issues appear. @@ -2064,6 +2108,9 @@ export async function runStopHook({ stdinJson, env = {}, cwd = process.cwd(), no if (contractEntries.length > 0) { parts.push(renderContractAudit(contractEntries, { cwd: projectCwd })); } + if (needsSlopReview) { + parts.push(renderStopSlopReview()); + } const text = appendDesignSystemNote(parts.join('\n\n'), scanOptions); return { exitCode: 0, @@ -2074,6 +2121,7 @@ export async function runStopHook({ stdinJson, env = {}, cwd = process.cwd(), no ...(contractEntries.length > 0 ? { contractFiles: contractEntries.map((entry) => entry.filePath) } : {}), + ...(needsSlopReview ? { llmSlopReview: true } : {}), }, audit: { ...audit, @@ -2081,6 +2129,7 @@ export async function runStopHook({ stdinJson, env = {}, cwd = process.cwd(), no freshFiles: freshGroups.length, freshFindings: freshGroups.reduce((sum, group) => sum + group.findings.length, 0), ...(contractEntries.length > 0 ? { contractAudits: contractEntries.length } : {}), + ...(needsSlopReview ? { llmSlopReview: true } : {}), chars: text.length, durationMs: Date.now() - started, }, diff --git a/.qoder/skills/impeccable/scripts/lib/provider.mjs b/.qoder/skills/impeccable/scripts/lib/provider.mjs index 7fa928a9c..8ea980b7d 100644 --- a/.qoder/skills/impeccable/scripts/lib/provider.mjs +++ b/.qoder/skills/impeccable/scripts/lib/provider.mjs @@ -1,4 +1,5 @@ // Source scripts default to slash commands. The provider build replaces only // this exact declaration, avoiding heuristic rewrites across executable code. export const IMPECCABLE_COMMAND_PREFIX = "/"; +export const IMPECCABLE_PROVIDER_ID = "qoder"; export const IMPECCABLE_COMMAND = `${IMPECCABLE_COMMAND_PREFIX}impeccable`; diff --git a/.qoder/skills/impeccable/scripts/lib/slop-review.mjs b/.qoder/skills/impeccable/scripts/lib/slop-review.mjs new file mode 100644 index 000000000..30f03e34d --- /dev/null +++ b/.qoder/skills/impeccable/scripts/lib/slop-review.mjs @@ -0,0 +1,30 @@ +export const LLM_ONLY_SLOP_ITEMS = Object.freeze([ + 'Monospace used merely to signal “technical” or “developer.”', + 'Light or dark mode chosen by category habit rather than the actual use scene.', + 'Everything wrapped in cards, or identical icon-heading-text cards repeated as the page structure.', + 'Hero-metric scaffolds: one oversized number, a small label, supporting stats, and an accent treatment.', + 'Decorative glassmorphism, meaningless sparklines, or generic rounded rectangles with drop shadows.', + 'A modal chosen by reflex when the task does not require interruption or protected focus.', +]); + +export function renderLlmOnlySlopReview({ manualDetector = false, automaticDetector = false, scriptsPath = null } = {}) { + const lines = [ + automaticDetector + ? 'AI_SLOP_REVIEW_REQUIRED: The automatic detector covers mechanical rules, but this harness has no reliable late review. Before finishing changed UI, inspect the authored result for detector-blind model reflexes:' + : 'AI_SLOP_REVIEW_REQUIRED: The automatic Impeccable design hook is not available for this session. Before finishing changed UI, inspect the authored result for detector-blind model reflexes:', + ...LLM_ONLY_SLOP_ITEMS.map((item) => `- ${item}`), + 'Fix reflexes, not intentional choices required by the brief or established visual authority.', + ]; + if (manualDetector && scriptsPath) { + lines.push(`Then run the mechanical detector once over the changed web UI: \`node ${scriptsPath}/detect.mjs --json \`. Do not run it earlier during concept selection.`); + } + return lines.join('\n'); +} + +export function renderStopSlopReview() { + return [ + '[impeccable@1] Detector-blind AI-slop review. The mechanical pass is complete; now inspect the rendered result for model reflexes it cannot reliably detect:', + ...LLM_ONLY_SLOP_ITEMS.map((item) => `- ${item}`), + 'Fix reflexes, not intentional choices required by the brief or established visual authority. Do not rerun the detector; this is the authored judgment layer.', + ].join('\n'); +} diff --git a/.qoder/skills/impeccable/scripts/lib/surface-briefs.mjs b/.qoder/skills/impeccable/scripts/lib/surface-briefs.mjs new file mode 100644 index 000000000..f83416f69 --- /dev/null +++ b/.qoder/skills/impeccable/scripts/lib/surface-briefs.mjs @@ -0,0 +1,151 @@ +import fs from 'node:fs'; +import path from 'node:path'; +import { slugFromTarget } from './target-slug.mjs'; + +export const SURFACE_BRIEF_VERSION = 1; + +export function getSurfaceBriefDir(projectRoot) { + return path.join(projectRoot, '.impeccable', 'surfaces'); +} + +export function normalizeSurfaceTarget(target, { projectRoot = process.cwd() } = {}) { + if (!target || typeof target !== 'string' || !target.trim()) return null; + const trimmed = target.trim(); + if (/^https?:\/\//i.test(trimmed)) { + try { + const url = new URL(trimmed); + url.hash = ''; + url.search = ''; + return url.toString().replace(/\/$/, '') || url.origin; + } catch { + return null; + } + } + if (/^route:/i.test(trimmed)) { + const route = trimmed.slice(trimmed.indexOf(':') + 1).trim(); + if (!route.startsWith('/') || route.includes('..')) return null; + const normalizedRoute = route.split(/[?#]/, 1)[0].replace(/\/{2,}/g, '/').replace(/\/$/, '') || '/'; + return `route:${normalizedRoute}`; + } + if (trimmed === '/') return 'route:/'; + if (trimmed.startsWith('/')) { + const absolute = path.resolve(trimmed); + const relativeToProject = path.relative(projectRoot, absolute); + const isProjectFile = relativeToProject && !relativeToProject.startsWith('..') && !path.isAbsolute(relativeToProject); + if (!isProjectFile && !fs.existsSync(absolute) && !trimmed.includes('..')) { + const normalizedRoute = trimmed.split(/[?#]/, 1)[0].replace(/\/{2,}/g, '/').replace(/\/$/, '') || '/'; + return `route:${normalizedRoute}`; + } + } + const abs = path.isAbsolute(trimmed) ? trimmed : path.resolve(projectRoot, trimmed); + const rel = path.relative(projectRoot, abs); + if (!rel || rel === '.' || rel.startsWith('..') || path.isAbsolute(rel)) return null; + return rel.split(path.sep).join('/'); +} + +export function surfaceBriefPathForTarget(target, { projectRoot = process.cwd() } = {}) { + const normalized = normalizeSurfaceTarget(target, { projectRoot }); + if (!normalized) return null; + const slugInput = normalized.startsWith('route:') ? `route${normalized.slice('route:'.length)}` : normalized; + const slug = slugFromTarget(slugInput, { cwd: projectRoot }); + return slug ? path.join(getSurfaceBriefDir(projectRoot), `${slug}.md`) : null; +} + +export function parseSurfaceBrief(text, filePath = null) { + const match = String(text || '').match(/^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/); + const meta = {}; + if (match) { + for (const line of match[1].split(/\r?\n/)) { + const colon = line.indexOf(':'); + if (colon < 0) continue; + const key = line.slice(0, colon).trim(); + const raw = line.slice(colon + 1).trim(); + if (!key) continue; + if (/^(?:\[|\{|\")/.test(raw) || /^(?:true|false|null|-?\d+(?:\.\d+)?)$/.test(raw)) { + try { meta[key] = JSON.parse(raw); continue; } catch { /* keep string */ } + } + meta[key] = raw.replace(/^['"]|['"]$/g, ''); + } + } + const primaryTarget = typeof meta.primary_target === 'string' ? meta.primary_target : null; + const relatedTargets = Array.isArray(meta.related_targets) + ? meta.related_targets.filter((value) => typeof value === 'string') + : []; + return { + path: filePath, + text: String(text || ''), + body: match ? String(text || '').slice(match[0].length).trim() : String(text || '').trim(), + meta, + slug: typeof meta.slug === 'string' ? meta.slug : filePath ? path.basename(filePath, '.md') : null, + primaryTarget, + relatedTargets, + targets: [primaryTarget, ...relatedTargets].filter(Boolean), + }; +} + +export function listSurfaceBriefs(projectRoot = process.cwd()) { + const dir = getSurfaceBriefDir(projectRoot); + let names; + try { + names = fs.readdirSync(dir).filter((name) => name.endsWith('.md')).sort(); + } catch { + return []; + } + return names.flatMap((name) => { + const filePath = path.join(dir, name); + try { + return [parseSurfaceBrief(fs.readFileSync(filePath, 'utf-8'), filePath)]; + } catch { + return []; + } + }); +} + +export function resolveSurfaceBrief(projectRoot = process.cwd(), target = null) { + const briefs = listSurfaceBriefs(projectRoot); + if (!target) { + return { + brief: briefs.length === 1 ? briefs[0] : null, + candidates: briefs, + reason: briefs.length === 1 ? 'only-brief' : briefs.length > 1 ? 'ambiguous' : 'none', + }; + } + + const normalized = normalizeSurfaceTarget(target, { projectRoot }); + if (!normalized) return { brief: null, candidates: briefs, reason: 'invalid-target' }; + const exactPath = surfaceBriefPathForTarget(normalized, { projectRoot }); + const exact = briefs.find((brief) => brief.path === exactPath && (!brief.targets.length || brief.targets.includes(normalized))); + if (exact) return { brief: exact, candidates: briefs, reason: 'slug' }; + const mapped = briefs.filter((brief) => brief.targets.includes(normalized)); + return { + brief: mapped.length === 1 ? mapped[0] : null, + candidates: mapped.length > 1 ? mapped : briefs, + reason: mapped.length === 1 ? 'mapping' : mapped.length > 1 ? 'ambiguous-target' : 'not-found', + }; +} + +export function writeSurfaceBrief({ + projectRoot = process.cwd(), + primaryTarget, + relatedTargets = [], + body, +}) { + const normalizedPrimary = normalizeSurfaceTarget(primaryTarget, { projectRoot }); + if (!normalizedPrimary) throw new Error('surface brief requires a concrete project-relative primary target or URL'); + const normalizedRelated = [...new Set(relatedTargets + .map((target) => normalizeSurfaceTarget(target, { projectRoot })) + .filter((target) => target && target !== normalizedPrimary))]; + const slug = slugFromTarget(normalizedPrimary, { cwd: projectRoot }); + const filePath = surfaceBriefPathForTarget(normalizedPrimary, { projectRoot }); + fs.mkdirSync(path.dirname(filePath), { recursive: true }); + const frontmatter = [ + '---', + `version: ${SURFACE_BRIEF_VERSION}`, + `slug: ${JSON.stringify(slug)}`, + `primary_target: ${JSON.stringify(normalizedPrimary)}`, + `related_targets: ${JSON.stringify(normalizedRelated)}`, + '---', + ].join('\n'); + fs.writeFileSync(filePath, `${frontmatter}\n\n${String(body || '').trim()}\n`, 'utf-8'); + return filePath; +} diff --git a/.qoder/skills/impeccable/scripts/lib/target-slug.mjs b/.qoder/skills/impeccable/scripts/lib/target-slug.mjs new file mode 100644 index 000000000..025915ad5 --- /dev/null +++ b/.qoder/skills/impeccable/scripts/lib/target-slug.mjs @@ -0,0 +1,33 @@ +import path from 'node:path'; + +const SLUG_MAX = 50; + +/** Derive one clone-stable slug from a concrete file path or URL. */ +export function slugFromTarget(resolved, { cwd = process.cwd() } = {}) { + if (!resolved || typeof resolved !== 'string') return null; + const trimmed = resolved.trim(); + if (!trimmed) return null; + + if (/^https?:\/\//i.test(trimmed)) { + let url; + try { url = new URL(trimmed); } catch { return null; } + return kebab(`${url.hostname}${url.pathname}`); + } + + const abs = path.isAbsolute(trimmed) ? trimmed : path.resolve(cwd, trimmed); + let rel = path.relative(cwd, abs); + if (rel.startsWith('..') || path.isAbsolute(rel)) rel = path.basename(abs); + if (!rel || rel === '.') return null; + return kebab(rel); +} + +export function kebab(value) { + const slug = String(value || '') + .toLowerCase() + .replace(/[/\\.]+/g, '-') + .replace(/[^a-z0-9-]+/g, '-') + .replace(/-+/g, '-') + .replace(/^-|-$/g, ''); + if (!slug) return null; + return slug.length <= SLUG_MAX ? slug : slug.slice(slug.length - SLUG_MAX).replace(/^-/, ''); +} diff --git a/.qoder/skills/impeccable/scripts/live-browser.js b/.qoder/skills/impeccable/scripts/live-browser.js index 616f1290a..47bbfefbf 100644 --- a/.qoder/skills/impeccable/scripts/live-browser.js +++ b/.qoder/skills/impeccable/scripts/live-browser.js @@ -2592,7 +2592,7 @@ minWidth: '16px', height: '16px', padding: '0 4px', borderRadius: '999px', background: tuneOpen ? C.brand : BP.hairline, - color: tuneOpen ? 'oklch(98% 0 0)' : 'inherit', + color: tuneOpen ? C.ink : 'inherit', fontFamily: MONO, fontSize: '9.5px', fontWeight: '600', lineHeight: '1', boxSizing: 'border-box', @@ -3131,7 +3131,7 @@ position: 'absolute', top: '2px', left: initial ? '18px' : '2px', width: '16px', height: '16px', borderRadius: '50%', - background: 'oklch(98% 0 0)', + background: C.ink, transition: 'left 0.18s ' + EASE, boxShadow: '0 1px 2px oklch(0% 0 0 / 0.2)', }); @@ -3165,7 +3165,7 @@ const b = el('button', { padding: '5px 4px', border: 'none', borderRadius: '3px', background: active ? C.brand : 'transparent', - color: active ? 'oklch(98% 0 0)' : P.text, + color: active ? C.ink : P.text, fontFamily: FONT, fontSize: '10.5px', fontWeight: '500', cursor: 'pointer', whiteSpace: 'nowrap', transition: 'background 0.1s ease, color 0.1s ease', @@ -3178,7 +3178,7 @@ segBtns.forEach(({ btn, val }) => { const on = val === o.value; btn.style.background = on ? C.brand : 'transparent'; - btn.style.color = on ? 'oklch(98% 0 0)' : P.text; + btn.style.color = on ? C.ink : P.text; }); applyParamValue(variantEl, p, o.value); queueCheckpoint('param_changed'); diff --git a/.qoder/skills/impeccable/scripts/surface-brief.mjs b/.qoder/skills/impeccable/scripts/surface-brief.mjs new file mode 100644 index 000000000..723f7c1b4 --- /dev/null +++ b/.qoder/skills/impeccable/scripts/surface-brief.mjs @@ -0,0 +1,74 @@ +#!/usr/bin/env node +import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath, pathToFileURL } from 'node:url'; +import { resolveProjectRoot } from './context.mjs'; +import { + listSurfaceBriefs, + resolveSurfaceBrief, + surfaceBriefPathForTarget, + writeSurfaceBrief, +} from './lib/surface-briefs.mjs'; + +function summary(brief, projectRoot) { + return { + slug: brief.slug, + path: path.relative(projectRoot, brief.path).split(path.sep).join('/'), + primaryTarget: brief.primaryTarget, + relatedTargets: brief.relatedTargets, + }; +} + +function main(argv) { + const [command, target, bodyFile, ...relatedTargets] = argv; + const projectRoot = resolveProjectRoot(process.cwd(), target ? { targetPath: target } : {}); + if (command === 'path') { + const filePath = surfaceBriefPathForTarget(target, { projectRoot }); + if (!filePath) throw new Error('surface brief path requires a concrete target'); + process.stdout.write(`${path.relative(process.cwd(), filePath) || filePath}\n`); + return; + } + if (command === 'list') { + process.stdout.write(`${JSON.stringify(listSurfaceBriefs(projectRoot).map((brief) => summary(brief, projectRoot)), null, 2)}\n`); + return; + } + if (command === 'read') { + const result = resolveSurfaceBrief(projectRoot, target || null); + if (result.brief) { + process.stdout.write(result.brief.text); + return; + } + if (result.candidates.length) process.stderr.write(`${JSON.stringify(result.candidates.map((brief) => summary(brief, projectRoot)), null, 2)}\n`); + process.exit(2); + } + if (command === 'write') { + if (!target || !bodyFile) throw new Error('usage: surface-brief.mjs write '); + const filePath = writeSurfaceBrief({ + projectRoot, + primaryTarget: target, + relatedTargets, + body: fs.readFileSync(bodyFile, 'utf-8'), + }); + process.stdout.write(`${path.relative(process.cwd(), filePath) || filePath}\n`); + return; + } + throw new Error('usage: surface-brief.mjs [target] [body-file] [related-target ...]'); +} + +function isMainModule() { + if (!process.argv[1]) return false; + try { + return fs.realpathSync(fileURLToPath(import.meta.url)) === fs.realpathSync(process.argv[1]); + } catch { + return import.meta.url === pathToFileURL(process.argv[1]).href; + } +} + +if (isMainModule()) { + try { + main(process.argv.slice(2)); + } catch (error) { + process.stderr.write(`${error?.message || error}\n`); + process.exit(1); + } +} diff --git a/.rovodev/skills/impeccable/SKILL.md b/.rovodev/skills/impeccable/SKILL.md index 27af02961..fa200a58f 100644 --- a/.rovodev/skills/impeccable/SKILL.md +++ b/.rovodev/skills/impeccable/SKILL.md @@ -1,7 +1,7 @@ --- name: impeccable description: Use when the user wants to design, redesign, shape, critique, audit, polish, clarify, distill, harden, optimize, adapt, animate, colorize, extract, or otherwise improve a frontend interface. Covers websites, landing pages, dashboards, product UI, app shells, components, forms, settings, onboarding, and empty states. Handles UX review, visual hierarchy, information architecture, cognitive load, accessibility, performance, responsive behavior, theming, anti-patterns, typography, fonts, spacing, layout, alignment, color, motion, micro-interactions, UX copy, error states, edge cases, i18n, and reusable design systems or tokens. Also use for bland designs that need to become bolder or more delightful, loud designs that should become quieter, live browser iteration on UI elements, or ambitious visual effects that should feel technically extraordinary. Not for backend-only or non-UI tasks. -version: 4.0.0-alpha.9 +version: 4.0.0-alpha.10 user-invocable: true argument-hint: "[craft|shape · audit|critique · animate|bolder|colorize|delight|layout|overdrive|quieter|typeset · adapt|clarify|distill · harden|onboard|optimize|polish · init|document|extract|live] [target]" license: Apache 2.0 @@ -10,56 +10,55 @@ allowed-tools: - Bash(node .rovodev/skills/impeccable/scripts/*) --- -Designs and iterates production-grade frontend interfaces. Real working code, committed design choices, exceptional craft. +This skill gives you the tools and permission to create design that earns to be called out-of-distribution craft: Whereas before, your design work would have been safe, timid and measured, you now approach every design task as a award-winning design director with impeccable understanding for what makes exceptional design work: production-grade code, peak creativity, a clear POV, deep understanding of the needs of the client and users, and exceptional craft. -Approach every design task as the design lead at a small studio known for giving every client a visual identity that could not be mistaken for anyone else's. The client has already rejected work that felt templated; they are paying for a point of view. Rovo Dev is capable of extraordinary work. Don't hold back. +Core principles: +- Go all out. No hedging, no shortcuts. The deliverable must be complete (except assets the user must provide). +- Dream big and bold. Distinct, beautiful, outstanding and highly inspiring work. +- Iterate with tools available to you (e.g. visual understanding, browser screenshots) until you think this meets the bar. ## Setup -1. Run `node .rovodev/skills/impeccable/scripts/context.mjs` once per session (if the runtime shows this skill's loaded base directory, run `node /scripts/context.mjs`; keep cwd at the user's project). It prints the project's context and its directives; follow what it prints. Once its output is in the conversation, never rerun it on a later turn. -2. `craft` and `shape` are build-path exceptions: the new-work gate below owns their flow, and on unattended runs its checkpoints resolve without pausing. For any other invoked sub-command (`audit`, `polish`, `live`, ...), immediately read **`reference/.md`** after `context.mjs` (the `.native` variant from the Commands table when the platform is `ios`/`android`/`adaptive`) and follow it. This read is a hard gate: do not inspect the target, run command-specific scripts, or edit files until the reference is loaded. -3. Read at least one project file (CSS / tokens / theme / a representative component) to learn what world you're in. If PRODUCT.md's `## Platform` is `ios` or `android`, also read `reference/.md` (`adaptive` reads both). +1. Run `node .rovodev/skills/impeccable/scripts/context.mjs` once per session (if the runtime shows this skill's loaded base directory, run `node /scripts/context.mjs`; keep cwd at the user's project). Pass a named source file or route as `--target `. It loads PRODUCT.md, DESIGN.md, the matching surface brief, and native-platform guidance when applicable; follow its directives and do not rerun it. +2. Before acting, load the one playbook that owns the request: the Commands table's reference for an explicit or clearly implied sub-command, or [reference/new-work.md](reference/new-work.md) for a new surface or replacement visual world. Then inspect the target and at least one representative source of incumbent visual truth (tokens, theme, CSS, component, or asset) before editing. ## How to design -**The brief wins.** Where the brief pins down a direction (a named aesthetic, an era, a place, a material, a specific font or palette), follow it exactly, including when it asks for a look this skill warns is saturated. Redirecting a pinned direction toward your own taste is a failure, not a save. - -**Existing worlds are sacred.** Most of impeccable's work happens inside a site or app that already exists. When the surface has a committed design system (real tokens, deliberately chosen faces, a palette the brand owns), work inside that world: extend it, sharpen it, leave it unmistakably the same brand, and never degrade a working page's performance. Inventing parallel colors, fonts, or styles on an existing surface is a defect, not creativity. A scoped refinement keeps the named scope's content and media footprint unless the brief explicitly changes them; build the added emphasis by recomposing what is already there through hierarchy, proportion, rhythm, and the system's own motifs. - -**New identity work reads the playbook first.** When nothing committed exists (greenfield, or a codebase with no real tokens or chosen faces), or the user explicitly asks for a redesign that discards the current look, you MUST read [reference/new-work.md](reference/new-work.md) before making any design decision. Missing DESIGN.md alone does not make work greenfield: code, tokens, chosen type, components, and assets are incumbent design authority. Preserve and extend them unless the user asked to replace them. Producing a genuinely new identity without the playbook yields the generic default this skill exists to prevent. Scoped fixes inside an existing world don't need it; the craft floor below governs them. `context.mjs` prints the appropriate authority directive. +- **The brief wins.** Honor pinned aesthetics, eras, materials, fonts, and palettes even when they conflict with a saturated-pattern warning. Redirecting a clear brief toward your taste is failure. +- **Refinement preserves; redesign replaces.** Refinement keeps the incumbent identity, behavior, copy, and everything outside scope. Ask before replacing factual copy or adding claims. Redesign keeps product truth, content, function, native affordances, and constraints, but treats the old look as evidence and anti-reference; choose a replacement world in new-work and replace DESIGN.md. Never split the difference into polish on the discarded look. +- **Visual authority is evidence, not a filename.** Missing DESIGN.md alone does not make a project greenfield; new-work decides whether to preserve, expand, or replace the incumbent world. ## Modes -Name the visitor's mode before designing; the page's grammar follows from it, and most ruined pages are one mode wearing another mode's grammar. **The mode belongs to the surface, not the subject**: a landing page for a dense technical tool is still Persuade, with Persuade's full permission to be striking; a docs page for a fashion house is still Read. Deciding a page can be plain because its subject is workmanlike is the same category error in reverse. The brief and the surface decide the mode; PRODUCT.md's `register` field survives only as a family hint (`brand` covers Persuade and Experience, `product` covers Operate and Read). Depth beyond the paragraphs below: [reference/new-work.md](reference/new-work.md) when inventing identity, [reference/operate.md](reference/operate.md) for substantial Operate and Read work. +Choose the mode from the requested surface, not the product, and persist it only in that surface brief. A tool's landing page is still Persuade; a fashion house's documentation is still Read. See [new-work.md](reference/new-work.md) for new surfaces and [operate.md](reference/operate.md) for deeper Operate/Read guidance. -**Persuade** (the surface exists to win someone over; design IS the product). The deliverable is an impression that stops the scroll, earns the click, converts. Spans every genre; don't collapse them into one look. On new surfaces, briefs that imply imagery must ship real, verified imagery; a colored rectangle where a photo belongs reads as incomplete. New Persuade surfaces take their typeface procedure and reject list from [reference/new-work.md](reference/new-work.md). - -**Operate** (the surface is a tool someone works in; design SERVES the task). A person getting something done: scanability and consistency outrank expressiveness. These surfaces earn trust by feeling native to their platform: system font stacks and workhorse UI faces are legitimate and often correct here (the Persuade reject list does not apply). The brand lives in the details: focus states, empty states, microcopy, one owned accent. The usage scene is part of the spec: an interface read outdoors, in motion, or at a glance must survive its real ambient light, and the theme follows the scene, not the category's habit. - -**Read** (the surface exists to be understood; long-form, reference, guidance). The deliverable is comprehension, and comprehension is earned twice: a structure the reader can hold in their head with nothing standing between them and the answer, and a reading experience good enough to stay in, through typographic quality and whatever visual or interactive support genuinely helps the reader follow. The brand lives in type, spacing, and small accents. - -**Experience** (the surface presents a body of work; the page IS the work). The artifact leads, the interface recedes, and the visitor meets the work itself in the first viewport at every screen size. Boldness here means trusting the work. +- **Persuade:** win someone over; design is the product. Earn attention and action. Ship real imagery when the brief needs it; follow the committed world, not category habit. +- **Operate:** help someone do work. Scanability, consistency, native expectations, and the real usage scene outrank expression. Brand lives in precise details. +- **Read:** make something understood. Structure for comprehension, then make the reading experience worth staying in. +- **Experience:** present a body of work. Let the artifact lead from the first viewport; the interface recedes. ## Craft floor -Build to this floor without announcing it. The design detector (the project hook, `node .rovodev/skills/impeccable/scripts/detect.mjs --json `, or `audit`) verifies most of it mechanically; resolve every finding before finalizing. Fix real defects, but use context judgment rather than distorting intentional design to appease a false positive. Classify any intentional exception explicitly and use the hook system's narrowest appropriate waiver when it must persist. +Build to this floor without announcing it. -- Contrast: body text ≥4.5:1 against its background (placeholders too); large text ≥3:1. Gray text on a colored background looks washed out: use a darker shade of the background's own hue, or a transparency of the text color. -- Shadows describe real light: an offset and a soft blur. A zero-offset colored halo is decoration announcing itself. -- Spacing has rhythm: generous separations, tight groupings; cramped padding reads as broken; the space above a heading exceeds the space below it. Verify computed spacing, not intended spacing. -- Type: body line length 65-75ch; display clamp() max ≤6rem; letter-spacing ≥-0.04em; `text-wrap: balance` on headings; modular scale ≥1.25 between steps; light-on-dark adds 0.05-0.1 line-height. Pair faces on a contrast axis, never two similar-but-not-identical ones; one family with committed weight contrast beats a timid pair. Test headings at every breakpoint; overflow means reduce the clamp or rewrite the copy. -- Motion is part of the build: one orchestrated moment beats scattered effects; ease-out exponential curves; reveals enhance an already-visible default (content gated on a class-triggered transition ships blank in hidden tabs and headless renderers). Responsive down to mobile and visible keyboard focus are part of the floor. -- Ship real content (no placeholders, dead links, or fake controls) and cover the interaction states people will actually hit (hover, focus, disabled, loading, error, empty). -- Copy is design material: name things the way the page's own people speak, make every control say what it does, and make every error say what happened and what to do next. -- Before finishing, re-read the brief: every requirement it names must exist on the page, findable in seconds. A beautiful page missing an asked-for feature is unfinished. +- **Contrast:** body and placeholder text ≥4.5:1; large text ≥3:1. On colored surfaces, tint secondary text from that hue or the foreground instead of using gray. +- **Depth:** shadows describe light with offset and soft blur; zero-offset colored halos are decoration. +- **Spacing:** tight groups, generous separation, no cramped containers; space above a heading exceeds space below. Verify computed values. +- **Type:** body measure 65–75ch; display max 6rem and tracking floor -0.04em; balance headings; use clear scale/weight contrast; test overflow at every breakpoint. +- **Motion:** author one coherent moment instead of scattered effects. Use exponential ease-out and an already-visible default. Premium motion is not transform/opacity alone: it may add focus, depth, masks, light, or material change through blur/filter, backdrop-filter, clip-path/masks, or shadow when smooth. Always provide reduced motion. +- **Shipping:** real content, working controls, responsive composition, keyboard focus, and the states users hit: hover, disabled, loading, error, and empty. +- **Copy:** use the product's language; controls name their action, errors name the problem and recovery. +- **Coverage:** every brief requirement must exist and be findable within seconds. + +Before finishing changed UI, follow the quality guidance supplied by `context.mjs` and hooks. Context requests a manual scan only when no automatic detector is active; never add a second detector pass. ## Commands | Command | Category | Description | Reference | |---|---|---|---| -| `craft [feature]` | Build | The standard build flow with attended checkpoints | [reference/new-work.md](reference/new-work.md) | +| `craft [feature]` | Build | Deprecated alias for an ordinary new-work request | [reference/craft.md](reference/craft.md) | | `shape [feature]` | Build | Plan UX/UI before writing code | [reference/shape.md](reference/shape.md) | -| `init` | Build | Set up project context: PRODUCT.md, DESIGN.md, live config, next steps | [reference/init.md](reference/init.md) | +| `init` | Build | Capture durable product context in PRODUCT.md | [reference/init.md](reference/init.md) | | `document` | Build | Generate DESIGN.md from existing project code | [reference/document.md](reference/document.md) | | `extract [target]` | Build | Pull reusable tokens and components into design system | [reference/extract.md](reference/extract.md) | | `critique [target]` | Evaluate | UX design review with heuristic scoring | [reference/critique.md](reference/critique.md) | @@ -81,7 +80,14 @@ Build to this floor without announcing it. The design detector (the project hook | `optimize [target]` | Fix | Diagnose and fix UI performance | [reference/optimize.md](reference/optimize.md) | | `live` | Iterate | Visual variant mode: pick elements in the browser, generate alternatives | [reference/live.md](reference/live.md) | -Routing: **no argument** → read [reference/routing.md](reference/routing.md) and present the context-aware menu (never auto-run a command). **First word matches a command** (or `pin` / `unpin` / `hooks`) → load its reference (native variant on native platforms) and follow it; everything after the command name is the target. **Intent clearly maps to one command** ("fix the spacing" → `layout`, "rewrite this error" → `clarify`) → same; if two fit, ask once. **Otherwise** → general design invocation: apply Setup and this file's guidance; builds flow through the new-work gate above, whose playbook carries the direction checkpoint and the finishing pass. `teach` routes to `init`, and `craft` routes to the standard build flow with attended checkpoints. If setup diverted into `init` for a build request, finish init, refresh context, then resume. +Routing: + +- **No argument:** read [routing.md](reference/routing.md) and present its context-aware menu; never auto-run a command. +- **Explicit or clearly implied command:** load its reference (native variant on native platforms) and follow it. Ask once if two commands fit. +- **Otherwise:** treat the request as general design work. Missing PRODUCT.md routes through init; new surfaces and replacement worlds use new-work. +- `teach` aliases `init`. `craft` is a deprecated alias for ordinary new-work and adds nothing. `shape` owns task discovery, then enters new-work only for visual-world and surface-concept decisions. + +After init writes PRODUCT.md, resume without rerunning `context.mjs`. **Pin / Unpin:** `node .rovodev/skills/impeccable/scripts/pin.mjs ` creates or removes a standalone `/` shortcut. Report the script's result concisely; relay stderr verbatim on error. diff --git a/.rovodev/skills/impeccable/reference/android.md b/.rovodev/skills/impeccable/reference/android.md index 3575c31fb..6337b9018 100644 --- a/.rovodev/skills/impeccable/reference/android.md +++ b/.rovodev/skills/impeccable/reference/android.md @@ -2,7 +2,7 @@ For native Android apps: Jetpack Compose, Android Views, React Native, Expo, Flutter shipping to Android hardware. -On native, register narrows. Material Design 3 governs structure, navigation, and interaction whatever the register; brand expresses through Material's theming (color roles, type scale, shape, motion). A Material-everywhere cross-platform app that also ships to iPhone still owes iOS its OS guarantees on that hardware: safe-area insets, Reduce Motion, edge-swipe back. +On native, the visitor mode narrows what expression may override. Material Design 3 governs structure, navigation, and interaction in every mode; brand expresses through Material's theming (color roles, type scale, shape, motion). A Material-everywhere cross-platform app that also ships to iPhone still owes iOS its OS guarantees on that hardware: safe-area insets, Reduce Motion, edge-swipe back. ## The Android slop test diff --git a/.rovodev/skills/impeccable/reference/animate.md b/.rovodev/skills/impeccable/reference/animate.md index 1a1098a5b..d51bb6d63 100644 --- a/.rovodev/skills/impeccable/reference/animate.md +++ b/.rovodev/skills/impeccable/reference/animate.md @@ -1,203 +1,88 @@ > **Additional context needed**: performance constraints. -Add motion that conveys state, gives feedback, and clarifies hierarchy. Cut motion that exists only for decoration. Animation fatigue is a real cost; spend the budget on the moments that need it. +Use motion to explain state, relationship, and hierarchy, or to create one authored moment the surface has earned. Decoration without purpose is animation debt. --- -## Register +## Visitor mode -Persuade + Experience: 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. +- **Persuade + Experience:** motion may carry the voice. Prefer one rehearsed focal sequence to repeated section reveals. +- **Operate + Read:** motion serves feedback, state, and continuity. Keep routine transitions fast and do not make users wait through page-load choreography. +- **Native (`ios` / `android` / `adaptive`):** follow the Motion section of [ios.md](ios.md) or [android.md](android.md), including the platform's Reduce Motion behavior. Do not apply the web tooling below. -Operate + Read: 150–250 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. +## Find the job -Native (`ios` / `android` / `adaptive`): implementation follows the Motion section of [ios.md](ios.md) / [android.md](android.md) (read it first if Setup hasn't already): system transitions and OS Reduce Motion, never the web tooling below. +Inspect the existing motion language, interaction states, target devices, and performance budget. Find only the places where motion would: ---- +- acknowledge an action; +- make a state change or spatial relationship legible; +- preserve continuity through navigation or layout change; +- direct attention at a meaningful moment; +- embody the selected visual world. -## Assess Animation Opportunities +Ask only when a material constraint cannot be inferred. Do not animate a static area merely because it exists. -Analyze where motion would improve the experience: +## Set the motion thesis -1. **Identify static areas**: - - **Missing feedback**: Actions without visual acknowledgment (button clicks, form submission, etc.) - - **Jarring transitions**: Instant state changes that feel abrupt (show/hide, page loads, route changes) - - **Unclear relationships**: Spatial or hierarchical relationships that aren't obvious - - **Lack of delight**: Functional but joyless interactions - - **Missed guidance**: Opportunities to direct attention or explain behavior +Write a short plan before implementation: -2. **Understand the context**: - - What's the personality? (Playful vs serious, energetic vs calm) - - What's the performance budget? (Mobile-first? Complex page?) - - Who's the audience? (Motion-sensitive users? Power users who want speed?) - - What matters most? (One hero animation vs many micro-interactions?) +- **Focal moment:** the one sequence or interaction that deserves authorship, if any. +- **Continuity:** the state, layout, or navigation changes that need explanation. +- **Feedback:** the controls and outcomes that need acknowledgment. +- **Budget:** which effects may be expensive, how often they run, and the reduced-motion equivalent. -If any of these are unclear from the codebase, ask the user directly to clarify what you cannot infer. +The focal moment must come from this product and surface concept. A generic fade-and-rise, hover lift, parallax layer, or scroll reveal is not a thesis. -**CRITICAL**: Respect `prefers-reduced-motion`. Always provide non-animated alternatives for users who need them. +## Choose material by meaning -## Plan Animation Strategy +Transform and opacity are reliable foundations, not the entire palette. Choose properties for what the transition communicates: -Create a purposeful animation plan: +- **Continuity and relationship:** shared-element motion, FLIP-style transforms, view transitions, or deliberate spatial movement. +- **Focus and depth:** bounded blur, filter, backdrop, light, or shadow changes. +- **Reveal and composition:** masks, clip paths, cropping, or controlled occlusion. +- **Material and energy:** color, gradient position, texture, distortion, or shader effects when the world and runtime support them. +- **State and feedback:** the smallest change that makes cause and result unmistakable. -- **Feedback layer**: Which interactions need acknowledgment? -- **Transition layer**: Which state changes need smoothing? -- **Entrance moment**: The ONE entrance worth rehearsing, where the mode invites it. Not every surface wants one. -- **Delight layer**: Where can we surprise and delight? +Do not stack techniques for spectacle. One strong material idea, carried through the focal sequence and quiet supporting states, is usually enough. -**IMPORTANT**: One well-orchestrated experience beats scattered animations everywhere. Focus on high-impact moments. +Sibling stagger is appropriate when a list appears as a list. Cap the total delay, and never reinterpret every scrolled section as a staggered list. -## Implement Animations +## Timing and easing -Add motion systematically across these categories: +Timing should express distance and consequence: -### Micro-interactions -- **Button feedback**: - - Hover: Subtle scale (1.02-1.05), color shift, shadow increase - - Click: Quick scale down then up (0.95 → 1), ripple effect - - Loading: Spinner or pulse state -- **Form interactions**: - - Input focus: Border color transition, slight scale or glow - - Validation: Shake on error, check mark on success, smooth color transitions -- **Toggle switches**: Smooth slide + color transition (200-300ms) -- **Checkboxes/radio**: Check mark animation, ripple effect -- **Like/favorite**: Scale + rotation, particle effects, color transition +| Duration | Typical use | +|---|---| +| 100–150 ms | immediate feedback | +| 150–300 ms | routine state change | +| 300–500 ms | layout, overlay, or view transition | +| 500–800 ms | a deliberately authored focal entrance | -### State Transitions -- **Show/hide**: Fade + slide (not instant), appropriate timing (200-300ms) -- **Expand/collapse**: Height transition with overflow handling, icon rotation -- **Loading states**: Skeleton screen fades, spinner animations, progress bars -- **Success/error**: Color transitions, icon animations, gentle scale pulse -- **Enable/disable**: Opacity transitions, cursor changes +Exit faster than entrance. Use natural deceleration such as `cubic-bezier(0.16, 1, 0.3, 1)` for confident arrivals; do not use bounce or elastic curves by reflex. Long feedback feels like latency. -### Entrance Animations -- **The one entrance moment** (where the mode invites it): a committed entrance for primary content (scale, parallax, or a creative effect) -- **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. +## Implement to the runtime - 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. +- Use CSS transitions and keyframes for declarative state and bounded sequences. +- Use Web Animations API or the project's existing motion library for interruption, sequencing, and dynamic values. +- Use View Transitions or shared-element techniques when continuity across states is the point. +- Use scroll-driven motion only when the scroll relationship itself carries meaning, with a robust fallback. +- Do not add a dependency for an effect the existing stack can express cleanly. -### Navigation & Flow -- **Page transitions**: Crossfade between routes, shared element transitions -- **Tab switching**: Slide indicator, content fade/slide -- **Carousel/slider**: Smooth transforms, snap points, momentum -- **Scroll effects**: Parallax layers, sticky headers with state changes, scroll progress indicators +Keep content visible in the default state so failed scripts do not hide the page. Avoid casually animating layout-driving properties such as `width`, `height`, `top`, `left`, and margins; use FLIP, transforms, or grid techniques when appropriate. Bound blur, filter, shadow, canvas, and shader work to isolated regions. Apply `will-change` only during known animation. Measure on target viewports and devices rather than assuming transform means fast. -### Feedback & Guidance -- **Hover hints**: Tooltip fade-ins, cursor changes, element highlights -- **Drag & drop**: Lift effect (shadow + scale), drop zone highlights, smooth repositioning -- **Copy/paste**: Brief highlight flash on paste, "copied" confirmation -- **Focus flow**: Highlight path through form or workflow +## Accessibility and control -### Delight Moments -- **Empty states**: Subtle floating animations on illustrations -- **Completed actions**: Confetti, check mark flourish, success celebrations -- **Easter eggs**: Hidden interactions for discovery -- **Contextual animation**: Weather effects, time-of-day themes, seasonal touches +Honor `prefers-reduced-motion` with an intentional alternative. Preserve state change and hierarchy while removing travel, parallax, flashing, or prolonged sequences. Do not replace every animation globally with `0.01ms` if that destroys useful feedback. Motion must never block focus, interaction, reading, or task completion. -## Technical Implementation +Respect autoplay and sound preferences. Any nonessential loop must stop when offscreen or hidden. -Use appropriate techniques for each animation: +## Verify -### Timing & Easing +- The focal motion is specific to the selected world and surface. +- Every supporting animation explains feedback, state, or relationship. +- Interruption and repeated use behave correctly. +- Desktop, mobile, keyboard, and reduced-motion paths remain usable. +- Expensive effects stay smooth on the target device. +- Removing an animation would lose meaning or authored character, not merely decoration. -**Duration: the 100/300/500 rule.** Timing matters more than easing for "feels right": - -| Duration | Use Case | Examples | -|----------|----------|----------| -| **100–150ms** | Instant feedback | Button press, toggle, color change | -| **200–300ms** | State changes | Menu open, tooltip, hover state | -| **300–500ms** | Layout changes | Accordion, modal, drawer | -| **500–800ms** | Entrance animations | Page load, hero reveal | - -**Easing curves (use these, not CSS defaults):** -```css -/* Recommended: natural deceleration */ ---ease-out-quart: cubic-bezier(0.25, 1, 0.5, 1); /* Smooth */ ---ease-out-quint: cubic-bezier(0.22, 1, 0.36, 1); /* Slightly snappier */ ---ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1); /* Confident, decisive */ - -/* AVOID: feel dated and tacky */ -/* bounce: cubic-bezier(0.34, 1.56, 0.64, 1); */ -/* elastic: cubic-bezier(0.68, -0.6, 0.32, 1.6); */ -``` - -**Exit animations are faster than entrances.** Use ~75% of enter duration. - -### CSS Animations -```css -/* Prefer for simple, declarative animations */ -- transitions for state changes -- @keyframes for complex sequences -- transform and opacity for reliable movement -- blur, filters, masks, clip paths, shadows, and color shifts for premium atmospheric effects when verified smooth -``` - -### JavaScript Animation -```javascript -/* Use for complex, interactive animations */ -- Web Animations API for programmatic control -- Framer Motion for React -- 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 -- **Layout safety**: Avoid casual animation of layout-driving properties (`width`, `height`, `top`, `left`, margins) -- **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) { - * { - animation-duration: 0.01ms !important; - animation-iteration-count: 1 !important; - transition-duration: 0.01ms !important; - } -} -``` - -**NEVER**: -- Use bounce or elastic easing curves; they feel dated and draw attention to the animation itself -- Animate layout properties casually (`width`, `height`, `top`, `left`, margins) when transform, FLIP, or grid-based techniques would work -- Use durations over 500ms for feedback (it feels laggy) -- Animate without purpose (every animation needs a reason) -- Ignore `prefers-reduced-motion` (this is an accessibility violation) -- Animate everything (animation fatigue makes interfaces feel exhausting) -- Block interaction during animations unless intentional - -## Verify Quality - -Test animations thoroughly: - -- **Smooth at 60fps**: No jank on target devices -- **Feels natural**: Easing curves feel organic, not robotic -- **Appropriate timing**: Not too fast (jarring) or too slow (laggy) -- **Reduced motion works**: Animations disabled or simplified appropriately -- **Doesn't block**: Users can interact during/after animations -- **Adds value**: Makes interface clearer or more delightful - -When the motion clarifies state instead of decorating it, hand off to `/impeccable polish` for the final pass. +When motion earns its place, hand off to `/impeccable polish` for the final pass. diff --git a/.rovodev/skills/impeccable/reference/audit.md b/.rovodev/skills/impeccable/reference/audit.md index 72815af69..70d80771a 100644 --- a/.rovodev/skills/impeccable/reference/audit.md +++ b/.rovodev/skills/impeccable/reference/audit.md @@ -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 - diff --git a/.rovodev/skills/impeccable/reference/audit.native.md b/.rovodev/skills/impeccable/reference/audit.native.md index 737d50e4f..0126fa157 100644 --- a/.rovodev/skills/impeccable/reference/audit.native.md +++ b/.rovodev/skills/impeccable/reference/audit.native.md @@ -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) diff --git a/.rovodev/skills/impeccable/reference/bolder.md b/.rovodev/skills/impeccable/reference/bolder.md index 6b44f0e38..78f5e4811 100644 --- a/.rovodev/skills/impeccable/reference/bolder.md +++ b/.rovodev/skills/impeccable/reference/bolder.md @@ -8,12 +8,12 @@ ## Why it reads flat -A section usually reads flat for reasons its neighbors have already solved. Look at what the rest of the page does that this section does not: the display type at full strength, the structural devices that carry meaning, the signature motif, the density and pacing, the specificity of the copy. A flat section is typically one that quietly opts out of the system's own strongest moves. The most reliable bolder pass brings the target up to the expressive level its neighbors already reach, in the system's own vocabulary rather than a new one. +A section usually reads flat for reasons its neighbors have already solved. Look at what the rest of the page does that this section does not: the display type at full strength, the structural devices that carry meaning, the signature motif, the density and pacing. A flat section is typically one that quietly opts out of the system's own strongest moves. The most reliable bolder pass brings the target up to the expressive level its neighbors already reach, in the system's own vocabulary rather than a new one. ## The amplification - **Amplify what the system already owns.** Reuse its motif and its type scale at full strength, turned up for this section rather than invented for it. The bolder version should look more like the same brand, not less. -- **Let the content carry the weight.** Flat copy makes a flat design, and no amount of size fixes a generic claim. Make every word specific enough to earn its place, and let the section's real evidence do the work that a decorative addition would otherwise fake. +- **Keep content true.** Existing claims are part of the scope: preserve them unless the user supplies replacements. If real evidence is essential to the direction but absent, ask for it. - **Commit, then clarify.** Half-measures read as noise. Make the one decisive move completely, then quiet everything around it so the move is legible. If every element got louder, the section got flatter. - **Give it its own rhythm.** The target should read as a peak in the scroll, a shift in density or pace from what surrounds it, not simply more of the same. diff --git a/.rovodev/skills/impeccable/reference/clarify.md b/.rovodev/skills/impeccable/reference/clarify.md index 00ee978c3..3047a9d82 100644 --- a/.rovodev/skills/impeccable/reference/clarify.md +++ b/.rovodev/skills/impeccable/reference/clarify.md @@ -1,288 +1,94 @@ -> **Additional context needed**: audience technical level and users' mental state in context. +> **Additional context needed**: audience knowledge and emotional state. -Find the unclear, confusing, or poorly written interface text and rewrite it. Vague copy creates support tickets and abandonment; specific copy gets users through the task. +Rewrite unclear interface text so users understand what happened, what matters, and what to do next. Preserve factual meaning, product terminology, and brand voice. +## Audit the language ---- +Read the entire interaction path, not isolated strings. Identify: -## Assess Current Copy +- ambiguous nouns, verbs, and actions; +- internal jargon or assumed knowledge; +- vague labels, outcomes, and system states; +- missing consequences, recovery, or timing; +- inconsistent terminology and capitalization; +- redundant headings, intros, helper text, and confirmations; +- text that breaks at realistic widths or in translation; +- tone that ignores stress, risk, success, or urgency. -Identify what makes the text unclear or ineffective: +Infer audience and task from product context and surrounding UI. Ask before changing factual claims, legal meaning, or a term that may be domain-specific. -1. **Find clarity problems**: - - **Jargon**: Technical terms users won't understand - - **Ambiguity**: Multiple interpretations possible - - **Passive voice**: "Your file has been uploaded" vs "We uploaded your file" - - **Length**: Too wordy or too terse - - **Assumptions**: Assuming user knowledge they don't have - - **Missing context**: Users don't know what to do or why - - **Tone mismatch**: Too formal, too casual, or inappropriate for situation +## Set the message hierarchy -2. **Understand the context**: - - Who's the audience? (Technical? General? First-time users?) - - What's the user's mental state? (Stressed during error? Confident during success?) - - What's the action? (What do we want users to do?) - - What's the constraint? (Character limits? Space limitations?) +For each state, decide: -**CRITICAL**: Clear copy helps users succeed. Unclear copy creates frustration, errors, and support tickets. +1. the one fact the user needs now; +2. the action available next; +3. supporting context that changes the decision; +4. the appropriate tone for this moment. -## Plan Copy Improvements +Say each idea once. If the heading already explains the state, the introduction should add new information or disappear. -Create a strategy for clearer communication: +## Rewrite by function -- **Primary message**: What's the ONE thing users need to know? -- **Action needed**: What should users do next (if anything)? -- **Tone**: How should this feel? (Helpful? Apologetic? Encouraging?) -- **Constraints**: Length limits, brand voice, localization considerations +### Actions and navigation -**IMPORTANT**: Good UX writing is invisible. Users should understand immediately without noticing the words. +Use a specific verb and object when the outcome is not already obvious. Labels should describe what will happen, not the gesture used to trigger it. Keep the same noun and verb for the same concept throughout the product. -## Improve Copy Systematically +For destructive actions, name the object and consequence. Prefer undo over confirmation when recovery is safe. When confirmation is necessary, name the action on both the message and button instead of using `Yes`, `No`, `OK`, or `Submit`. -Refine text across these common areas: +### Forms -### Error Messages -**Bad**: "Error 403: Forbidden" -**Good**: "You don't have permission to view this page. Contact your admin for access." +Use persistent labels; placeholders are examples, not labels. Put format and eligibility requirements before submission. Explain why information is requested only when it is not obvious. Required and optional treatment should be consistent. -**Bad**: "Invalid input" -**Good**: "Email addresses need an @ symbol. Try: name@example.com" +Validation says what needs attention and how to correct it without blaming the user. Keep related instructions near the field and announce errors accessibly. -**Principles**: -- Explain what went wrong in plain language -- Suggest how to fix it -- Don't blame the user -- Include examples when helpful -- Link to help/support if applicable - -### Form Labels & Instructions -**Bad**: "DOB (MM/DD/YYYY)" -**Good**: "Date of birth" (with placeholder showing format) - -**Bad**: "Enter value here" -**Good**: "Your email address" or "Company name" - -**Principles**: -- Use clear, specific labels (not generic placeholders) -- Show format expectations with examples -- Explain why you're asking (when not obvious) -- Put instructions before the field, not after -- Keep required field indicators clear - -### Button & CTA Text -**Bad**: "Click here" | "Submit" | "OK" -**Good**: "Create account" | "Save changes" | "Got it, thanks" +### Errors and permissions -**Principles**: -- Describe the action specifically -- Use active voice (verb + noun) -- Match user's mental model -- Be specific ("Save" is better than "OK") +An actionable error answers: -### Help Text & Tooltips -**Bad**: "This is the username field" -**Good**: "Choose a username. You can change this later in Settings." +1. what failed; +2. why, when known and useful; +3. how to recover or what alternative remains. -**Principles**: -- Add value (don't just repeat the label) -- Answer the implicit question ("What is this?" or "Why do you need this?") -- Keep it brief but complete -- Link to detailed docs if needed +Do not expose internal codes as the primary message. Do not promise a cause or resolution the system cannot know. Treat privacy, payment, deletion, access loss, and blocked work seriously; warmth is welcome, jokes are not. -### Empty States -**Bad**: "No items" -**Good**: "No projects yet. Create your first project to get started." +### Loading, empty, and success states -**Principles**: -- Explain why it's empty (if not obvious) -- Show next action clearly -- Make it welcoming, not dead-end +Loading text names the real operation and sets an honest expectation when the wait is meaningful. Show determinate progress when available; never invent progress. -### Success Messages -**Bad**: "Success" -**Good**: "Settings saved! Your changes will take effect immediately." +An empty state distinguishes first use, no results, filters, permissions, and failure. Explain the state and provide the next useful action. -**Principles**: -- Confirm what happened -- Explain what happens next (if relevant) -- Be brief but complete -- Match the user's emotional moment (celebrate big wins) - -### Loading States -**Bad**: "Loading..." (for 30+ seconds) -**Good**: "Analyzing your data... this usually takes 30-60 seconds" - -**Principles**: -- Set expectations (how long?) -- Explain what's happening (when it's not obvious) -- Show progress when possible -- Offer escape hatch if appropriate ("Cancel") +Success confirms the completed outcome and mentions the next consequence only when it changes what the user should do. Routine success should be brief. -### Confirmation Dialogs -**Bad**: "Are you sure?" -**Good**: "Delete 'Project Alpha'? This can't be undone." +### Help and instructional text -**Principles**: -- State the specific action -- Explain consequences (especially for destructive actions) -- Use clear button labels ("Delete project" not "Yes") -- Don't overuse confirmations (only for risky actions) +Helper text answers an implicit question instead of restating the control. Use progressive disclosure for uncommon detail. Link text must make sense out of context; icon-only controls need accessible names. -### Navigation & Wayfinding -**Bad**: Generic labels like "Items" | "Things" | "Stuff" -**Good**: Specific labels like "Your projects" | "Team members" | "Settings" +## Voice, accessibility, and localization -**Principles**: -- Be specific and descriptive -- Use language users understand (not internal jargon) -- Make hierarchy clear -- Consider information scent (breadcrumbs, current location) +Voice stays consistent; tone adapts to the moment. Use plain language without flattening terminology the audience genuinely knows. -## Apply Clarity Principles +- Write complete translatable messages rather than concatenated fragments. +- Keep variables and numbers structured so translators can reorder them. +- Allow expansion instead of abbreviating prematurely. +- Make alt text convey the image's information; use empty alt for decoration. +- Keep screen-reader names aligned with visible labels and outcomes. +- Do not rely on punctuation, color, or iconography to carry the message alone. -Every piece of copy should follow these rules: +Maintain a short terminology glossary when inconsistency spans the product. Do not vary words for literary effect in an interface. -1. **Be specific**: "Enter email" not "Enter value" -2. **Be concise**: Cut unnecessary words (but don't sacrifice clarity) -3. **Be active**: "Save changes" not "Changes will be saved" -4. **Be human**: "Oops, something went wrong" not "System error encountered" -5. **Tell users what to do**, not just what happened -6. **Be consistent**: Use same terms throughout (don't vary for variety) +## Verify -**NEVER**: -- Use jargon without explanation -- Blame users ("You made an error" → "This field is required") -- Be vague ("Something went wrong" without explanation) -- Use passive voice unnecessarily -- Write overly long explanations (be concise) -- Use humor for errors (be empathetic instead) -- Assume technical knowledge -- Vary terminology (pick one term and stick with it) -- Repeat information (headers restating intros, redundant explanations) -- Use placeholders as the only labels (they disappear when users type) +Read the flow in context and test: -## Verify Improvements +- comprehension without hidden product knowledge; +- actionability at errors, empty states, and decision points; +- factual accuracy and consistent terminology; +- scanability at target widths and 200% zoom; +- long names, localization expansion, pluralization, and dynamic values; +- accessible names and announced state changes; +- tone appropriate to consequence and emotional context. -Test that copy improvements work: +The final copy is as short as it can be without removing meaning or recovery. -- **Comprehension**: Can users understand without context? -- **Actionability**: Do users know what to do next? -- **Brevity**: Is it as short as possible while remaining clear? -- **Consistency**: Does it match terminology elsewhere? -- **Tone**: Is it appropriate for the situation? - -When the copy reads cleanly, hand off to `/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. +When the language reads cleanly, hand off to `/impeccable polish` for the final pass. diff --git a/.rovodev/skills/impeccable/reference/codex.md b/.rovodev/skills/impeccable/reference/codex.md index 18a924a91..6c550f1dc 100644 --- a/.rovodev/skills/impeccable/reference/codex.md +++ b/.rovodev/skills/impeccable/reference/codex.md @@ -1,105 +1,38 @@ -# Codex: Visual Direction & Asset Production +# Codex: Surface Probes & Asset Production -This file is loaded by `/impeccable craft` when the harness has native image generation (currently Codex via `image_gen`). Other harnesses skip it. It covers the two craft steps that depend on real image generation: landing the visual direction, and producing the raster assets the implementation will compose. +Load this from [new-work.md](new-work.md) only when the harness has native image generation and a substantial, high-fidelity surface would benefit from seeing the shortlisted concept before code. PRODUCT.md and DESIGN.md are preconditions. New-work has already resolved the visual world; this file must not reopen it. -Read this *before* generating any images. The order matters, and the per-step user pauses are what keep generated imagery from drifting away from the brief. +The purpose of a probe is to test composition, narrative, hierarchy, density, focal moment, signature use, and image requirements. It is not a second identity workshop. Keep DESIGN.md's palette, typography direction, material language, component character, imagery stance, and motion grammar fixed. -### Four stop points before code +## Generate the smallest useful probe set -Steps A through D each end with the user. Do not advance past any of them on your own read of the situation. +Generate one to three high-fidelity north-star comps using the native image-generation capability. Base them on the real content and the surface concepts already developed with the user. -1. **STOP after Step A questions.** Wait for answers. -2. **STOP after Step B palette generation.** Wait for "confirm palette." -3. **STOP after Step C mocks.** Wait for direction approval or delegation. -4. **Only after Step D approves a direction** do you return to the build (new-work.md's plan, then code). +- When the user shortlisted multiple concepts, show one clear expression of each. +- When one concept is already selected, vary only the structural uncertainty that the image can resolve: topology, sequence, density, hierarchy, focal composition, or interaction framing. +- Show enough beyond the opening moment to prove the concept can govern the whole requested surface. +- Do not generate a palette artifact, ask new atmosphere questions, introduce a different type voice, or invent a new motif. If the committed world cannot support the concept, return to the concept shortlist rather than changing the world. -Prior shape approval does **not** satisfy any of these. Shape's "confirm or override" advances you into Step A; it is not a substitute for it. +Treat each comp as a direction test, not a screenshot specification. Core UI text, responsive behavior, accessibility, semantics, and interaction states remain implementation responsibilities. -## Step A: Explore Directions with the User +## One approval point -Before generating anything, run a brief direction conversation grounded in the shape brief. +Show the probes together and ask what should carry forward, what feels false to the world, and whether the selected surface concept should be approved, combined, revised, or rejected. Then stop and wait. A structured simulated user counts as attended and receives the same question. -**Step A is required even when shape just produced a confirmed brief.** The shape questions and Step A questions cover different ground: shape pins purpose, content, scope; Step A pins palette, atmosphere, and named visual references for the comps you're about to generate. The only time you can skip Step A is when the user has already answered these exact palette/atmosphere/reference questions in the same session. +Do not begin code until the user approves a direction or explicitly delegates the choice. If they delegate, choose using the task brief, PRODUCT.md, and DESIGN.md, and state the evidence. Approval refines the task concept; it does not modify DESIGN.md. -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: +After approval, summarize the composition and the parts of the comp that must not be literalized. Return to new-work.md and write the direction contract from the approved surface concept before code. -- "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.)" +## Inventory implementation fidelity -**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. +Before building, inventory the approved comp's major visible ingredients and choose an implementation medium for each: semantic HTML/CSS/SVG, existing project asset, generated raster, sourced raster, icon library, canvas/WebGL, or accepted omission. -## Step B: Generate the Brand Palette First +Pay special attention to the dominant composition, signature use, image-native content, second-fold system, and any interaction the still image only implies. If the concept depends on a photograph, architectural scene, product object, portrait, or other raster-native material, do not silently replace it with generic CSS scenery. -Generate **one** palette artifact before any mocks. This is a small, focused image: typography pairing on the chosen background, primary + accent color swatches, one signature ornament or motif. Single image, single pass. +Treat the comp as a north star, not something to trace. Do not rasterize core UI text or controls. Do not substitute a different visual driver after approval without asking. -Why palette first: mocks generated against a vague color sense produce noise that drowns out the structural decisions. A confirmed palette is the first concrete contract for everything downstream. +## Produce only the assets the build needs -Show the palette to the user. Ask one question: "This is the palette I'm locking in for the mocks. Confirm, or call out what to shift?" +When clean raster ingredients are required and a scoped subagent is available and authorized, use `impeccable_asset_producer`. Give it the approved comp, output paths, required dimensions and formats, transparency needs, crop notes, and what must remain semantic code. Otherwise produce the minimum required assets with the native image-generation capability in the current thread. -**STOP and wait for confirmation.** Do not generate mocks against an unconfirmed palette. "Probably good enough" is the wrong call here; the palette is the contract for everything downstream. - -## Step C: Generate 1-3 Visual Mocks Against the Palette - -Once the palette is confirmed, generate **1 to 3** high-fidelity north-star comps. Each mock must use the confirmed palette and typography. Mocks differ in *structural* direction (hierarchy, topology, density, composition), not in color or motif. - -- Brand work: push visual identity, composition, mood, and signature motifs. -- Product work: push hierarchy, topology, density, tone, grounded in realistic product structure. -- Landing pages and long-form brand surfaces: show enough of the second fold to establish the system beyond the hero. - -Use the `image_gen` tool directly (or via the imagegen skill when available). Don't ask the user to install anything. - -## Step D: Approval Loop - -Show the comps. Ask what carries forward. Iterate until **one direction is approved** or the user explicitly delegates. - -**STOP and wait for the approval or the delegation.** Do not begin Step E or return to the build until a single direction is named. If the user delegates, pick the strongest direction and explain it from the brief, not personal taste. - -Before moving to assets, summarize what to carry into code and what *not* to literalize from the mock. This is the handoff between visual exploration and semantic implementation. - -## Step E: Mock Fidelity Inventory - -Inventory the approved mock's major visible ingredients. For each, decide implementation: semantic HTML/CSS/SVG, generated raster, sourced raster, icon library, canvas/WebGL, or accepted omission. - -Common ingredients to inventory: - -- Hero silhouette and dominant composition -- Signature motifs (planets, devices, portraits, charts, route lines, insets, badges, etc.) -- Nav and primary-action treatment (when the surface has one) -- Section sequence, especially the second fold -- Image-native content the concept depends on -- Typography, density, color/material treatment, motion cues - -Treat the mock as a north star, not a screenshot to trace. Don't rasterize core UI text. But if the live result lacks the mock's major ingredients, the implementation is wrong. - -If a photographic, architectural, product, or place-led mock becomes generic CSS scenery, decorative diagrams, bullets, or copy, stop and fix it. That's a broken implementation, not a harmless interpretation. - -Don't substitute a different hero composition or visual driver post-approval without user sign-off. - -## Step F: Asset Slicing via the Asset Producer - -Raster ingredients identified in Step E need clean production assets. Use the bundled `impeccable_asset_producer` subagent rather than producing inline. - -Spawn it as a scoped subagent. If you do not have explicit permission to use agents, stop and ask: - -```text -Asset production will work better as a scoped subagent job. Should I spawn the Impeccable asset producer subagent for this step? -``` - -Pass to the agent: - -- Approved mock path or screenshot reference -- Crop paths or a contact sheet with crop ids -- Output directory -- Required dimensions, format, transparency needs -- Avoid list -- Notes on what should remain semantic HTML/CSS/SVG instead of raster - -Attach image generation capability to the spawned agent when the harness supports it. Do **not** load image-generation reference material into the parent thread. - -Inline asset production is allowed only if the user declines subagents, the harness cannot spawn the authorized agent, or the user explicitly asks for single-thread mode. - -Prefer HTML/CSS/SVG/canvas when they can credibly reproduce an ingredient; reach for real, generated, or stock imagery when the mock or subject matter calls for actual visual content. - -## After This File - -Once Steps A through F are complete, return to the build: implement per new-work.md and SKILL.md's craft floor. The implementation builds against the confirmed palette, approved mock, and the assets the producer wrote. +Return to [new-work.md](new-work.md) for the direction contract, implementation, and finishing pass. diff --git a/.rovodev/skills/impeccable/reference/colorize.md b/.rovodev/skills/impeccable/reference/colorize.md index abee1acf8..dc45f88c5 100644 --- a/.rovodev/skills/impeccable/reference/colorize.md +++ b/.rovodev/skills/impeccable/reference/colorize.md @@ -1,257 +1,86 @@ > **Additional context needed**: existing brand colors. -Replace timid grayscale or single-accent designs with a strategic palette: pick a color strategy, choose a hue family that fits the brand, then apply color with intent. More color ≠ better. Strategic color beats rainbow vomit. +Introduce color as hierarchy, meaning, and atmosphere. Preserve confirmed brand and semantic conventions; do not replace a visual world under the guise of colorizing it. --- -## Register +## Visitor mode -Persuade + Experience: palette IS voice. Pick a color strategy first per SKILL.md (Restrained / Committed / Full palette / Drenched) and follow its dosage. Committed, Full palette, and Drenched deliberately exceed the ≤10% rule; that rule is Restrained only. Unexpected combinations are allowed; a dominant color can own the page when the chosen strategy calls for it. +- **Persuade + Experience:** color may carry the voice and own large regions when the selected world calls for it. +- **Operate + Read:** color primarily encodes action, selection, status, wayfinding, and reading hierarchy. Rarity gives an accent force. -Operate + Read: semantic-first and almost always Restrained. Accent color is reserved for primary action, current selection, and state indicators. Not decoration. Every color has a consistent meaning across every screen. +## Audit before choosing ---- +Read DESIGN.md, tokens, assets, current themes, and representative states. Identify: -## Assess Color Opportunity +- which colors are confirmed brand commitments; +- current surface, text, action, and semantic roles; +- places where grayscale obscures hierarchy or state; +- contrast failures and color-only communication; +- light/dark or data-visualization requirements; +- whether the task asks for more color or a new identity. -Analyze the current state and identify opportunities: +If a new identity is required, use [new-work.md](new-work.md). Ask only when a binding brand decision cannot be inferred. -1. **Understand current state**: - - **Color absence**: Pure grayscale? Limited neutrals? One timid accent? - - **Missed opportunities**: Where could color add meaning, hierarchy, or delight? - - **Context**: What's appropriate for this domain and audience? - - **Brand**: Are there existing brand colors we should use? +## Choose a strategy -2. **Identify where color adds value**: - - **Semantic meaning**: Success (green), error (red), warning (yellow/orange), info (blue) - - **Hierarchy**: Drawing attention to important elements - - **Categorization**: Different sections, types, or states - - **Emotional tone**: Warmth, energy, trust, creativity - - **Wayfinding**: Helping users navigate and understand structure - - **Delight**: Moments of visual interest and personality +Name the intended emotional temperature, dominant relationship, contrast range, and color dosage before editing. The strategy may be restrained or immersive; it must follow the brief and selected world rather than a fixed percentage rule. -If any of these are unclear from the codebase, ask the user directly to clarify what you cannot infer. +Build roles, not a bag of swatches: -**CRITICAL**: More color ≠ better. Strategic color beats rainbow vomit every time. Every color should have a purpose. +- canvas and elevated surfaces; +- primary and secondary text; +- action, focus, and selection; +- borders and separators; +- success, warning, error, and information; +- data categories or scales when needed. -## Plan Color Strategy +Use the project's existing color space. For a new web palette, prefer OKLCH because lightness and chroma can be adjusted predictably. Choose hue from product meaning and visual direction, never from a default category association. -Create a purposeful color introduction plan: +## Apply at system scale -- **Color palette**: What colors match the brand/context? (Choose 2-4 colors max beyond neutrals) -- **Dominant color**: Which color owns 60% of colored elements? -- **Accent colors**: Which colors provide contrast and highlights? (30% and 10%) -- **Application strategy**: Where does each color appear and why? +- Let the strongest color own a deliberate region or role instead of scattering tiny accents. +- Keep the primary action easy to find; do not spend its color on decoration. +- Tint neutrals only when the brand hue genuinely creates cohesion. Neutral gray is valid when it serves the world. +- On colored surfaces, derive secondary text from the foreground or surface hue rather than using washed-out generic gray. +- Keep semantic meanings consistent, but respect platform and domain conventions instead of assuming fixed hues. +- For data, use distinct lightness, chroma, shape, label, or pattern so color is not the only code. +- 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. -**IMPORTANT**: Color should enhance hierarchy and meaning, not create chaos. Less is more when it matters more. +Decoration without a relationship to hierarchy, state, content, or the visual world is not a color strategy. -## Introduce Color Strategically +## Contrast and perception -Add color systematically across these dimensions: +Verify computed foreground/background pairs: -### Semantic Color -- **State indicators**: - - Success: Green tones (emerald, forest, mint) - - Error: Red/pink tones (rose, crimson, coral) - - Warning: Orange/amber tones - - Info: Blue tones (sky, ocean, indigo) - - Neutral: Gray/slate for inactive states +| Content | WCAG AA minimum | +|---|---| +| body text | 4.5:1 | +| large text | 3:1 | +| controls, icons, focus indicators | 3:1 | -- **Status badges**: Colored backgrounds or borders for states (active, pending, completed, etc.) -- **Progress indicators**: Colored bars, rings, or charts showing completion or health +Do not rely on eyesight alone. Check interactive states, overlays, text on images, disabled content, and both themes. Simulate common vision deficiencies. Information conveyed by color also needs text, shape, iconography, or position. -### Accent Color Application -- **Primary actions**: Color the most important buttons/CTAs -- **Links**: Add color to clickable text (maintain accessibility) -- **Icons**: Colorize key icons for recognition and personality -- **Headers/titles**: Add color to section headers or key labels -- **Hover states**: Introduce color on interaction +When deriving OKLCH ramps, vary lightness and reduce chroma near white and black. Do not keep high chroma at extreme lightness merely to make the math uniform. Prefer explicit colors over chains of translucent overlays when alpha would make contrast context-dependent. -### Background & Surfaces -- **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 toward the brand, not "for warmth" by reflex +## Verify -**Use OKLCH for color**: It's perceptually uniform, meaning equal steps in lightness *look* equal. Great for generating harmonious scales. - -### Data Visualization -- **Charts & graphs**: Use color to encode categories or values -- **Heatmaps**: Color intensity shows density or importance -- **Comparison**: Color coding for different datasets or timeframes - -### Borders & Accents -- **Hairline borders**: 1px colored borders on full perimeter (not side-stripes; see the absolute ban on `border-left/right > 1px`) -- **Underlines**: Color underlines for emphasis or active states -- **Dividers**: Subtle colored dividers instead of gray lines -- **Focus rings**: Colored focus indicators matching brand -- **Surface tints**: A 4-8% background wash of the accent color instead of a stripe - -**NEVER**: `border-left` or `border-right` greater than 1px as a colored accent stripe. This is one of the three absolute bans in the parent skill. If you want to mark a card as "active" or "warning", use a full hairline border, a background tint, a leading glyph, or a numbered prefix. Not a side stripe. - -### Typography Color -- **Colored headings**: Use brand colors for section headings (maintain contrast) -- **Highlight text**: Color for emphasis or categories -- **Labels & tags**: Small colored labels for metadata or categories - -### Decorative Elements -- **Illustrations**: Add colored illustrations or icons -- **Shapes**: Geometric shapes in brand colors as background elements -- **Gradients**: Colorful gradient overlays or mesh backgrounds -- **Blobs/organic shapes**: Soft colored shapes for visual interest - -## Balance & Refinement - -Ensure color addition improves rather than overwhelms: - -### Maintain Hierarchy -- **Dominant color** (60%): Primary brand color or most used accent -- **Secondary color** (30%): Supporting color for variety -- **Accent color** (10%): High contrast for key moments -- **Neutrals** (remaining): Gray/black/white for structure - -### Accessibility -- **Contrast ratios**: Ensure WCAG compliance (4.5:1 for text, 3:1 for UI components) -- **Don't rely on color alone**: Use icons, labels, or patterns alongside color -- **Test for color blindness**: Verify red/green combinations work for all users - -### Cohesion -- **Consistent palette**: Use colors from defined palette, not arbitrary choices -- **Systematic application**: Same color meanings throughout (green always = success) -- **Temperature consistency**: Warm palette stays warm, cool stays cool - -**NEVER**: -- 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 -- Violate WCAG contrast requirements -- Use color as the only indicator (accessibility issue) -- Make everything colorful (defeats the purpose) -- Default to purple-blue gradients (AI slop aesthetic) - -## Verify Color Addition - -Test that colorization improves the experience: - -- **Better hierarchy**: Does color guide attention appropriately? -- **Clearer meaning**: Does color help users understand states/categories? -- **More engaging**: Does the interface feel warmer and more inviting? -- **Still accessible**: Do all color combinations meet WCAG standards? -- **Not overwhelming**: Is color balanced and purposeful? +- Every color has a stable role or a world-specific atmospheric purpose. +- Attention lands on the intended action, content, or state. +- The palette works across quiet, dense, interactive, error, and empty states. +- Light and dark themes are each composed, not mechanically inverted. +- Contrast and non-color cues pass in all relevant states. +- The result is recognizably this product, not a generic “colorful” treatment. When the palette earns its place, hand off to `/impeccable polish` for the final pass. ## Live-mode signature params -When invoked from live mode, each variant MUST declare a `color-amount` param so the user can dial between a restrained accent and a drenched surface without regeneration. Author the variant's CSS against `var(--p-color-amount, 0.5)`, typically as the alpha multiplier on backgrounds, or as a scaling factor on the chroma axis in an OKLCH expression. 0 = neutral/monochrome, 1 = full saturation / dominant coverage. +When invoked from live mode, every variant declares a `color-amount` parameter. Author CSS against `var(--p-color-amount, 0.5)` so the user can move from neutral to the variant's full color strategy without regeneration. ```json {"id":"color-amount","kind":"range","min":0,"max":1,"step":0.05,"default":0.5,"label":"Color amount"} ``` -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). +Add at most two variant-specific parameters, such as palette, temperature, or tint behavior. Follow [live.md](live.md)'s parameter contract. diff --git a/.rovodev/skills/impeccable/reference/craft.md b/.rovodev/skills/impeccable/reference/craft.md index cc7f98d37..dbbc9402c 100644 --- a/.rovodev/skills/impeccable/reference/craft.md +++ b/.rovodev/skills/impeccable/reference/craft.md @@ -1,3 +1,5 @@ # Craft (deprecated alias) -`craft` is a deprecated alias for the standard build flow. Builds route through SKILL.md's new-work gate; [new-work.md](new-work.md) carries the direction checkpoint ("Decide, then build"), the identity playbook, and the finishing pass. Invoking `craft` explicitly forces the attended checkpoints: pause at the stated direction for confirmation, and follow [codex.md](codex.md)'s mock flow when the harness generates images. Nothing else differs from a plain build request. +`craft` is a deprecated alias for an ordinary request to make new visual work. It adds no setup, interview, checkpoint, tool, or quality behavior. Apply SKILL.md's normal routing: create missing PRODUCT.md through [init.md](init.md), then follow [new-work.md](new-work.md) for visual authority, world and surface decisions, implementation, and finish. + +Do not tell users they need to invoke `craft`. Natural requests such as “build this feature,” “make a landing page,” or “redesign this screen” use the same flow. diff --git a/.rovodev/skills/impeccable/reference/critique.md b/.rovodev/skills/impeccable/reference/critique.md index 986656a29..d5a228ddb 100644 --- a/.rovodev/skills/impeccable/reference/critique.md +++ b/.rovodev/skills/impeccable/reference/critique.md @@ -43,13 +43,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 @@ -115,11 +115,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. @@ -176,7 +176,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. 2. **Pass the structured metadata** through `IMPECCABLE_CRITIQUE_META` (JSON), then run the write command: ```bash diff --git a/.rovodev/skills/impeccable/reference/delight.md b/.rovodev/skills/impeccable/reference/delight.md index 596cff09e..40aaddc34 100644 --- a/.rovodev/skills/impeccable/reference/delight.md +++ b/.rovodev/skills/impeccable/reference/delight.md @@ -1,302 +1,70 @@ -> **Additional context needed**: what's appropriate for the domain (playful vs professional vs quirky vs elegant). +> **Additional context needed**: the brand's emotional range. -Find the moments where personality and unexpected polish would turn a functional interface into one users remember and tell other people about. Add only where the moment earns it; delight everywhere reads as noise. +Make the experience memorable at moments that earn it. Delight is not a layer of generic whimsy; it is product character revealed through a useful interaction, a humane response, or an unexpectedly considered detail. --- -## Register +## Visitor mode -Persuade + Experience: delight can be distributed across copy voice, section transitions, discovery rewards, seasonal touches, personality across the whole surface. +- **Persuade + Experience:** personality may run through voice, composition, motion, and discovery, provided the artifact remains the focus. +- **Operate + Read:** concentrate delight at meaningful moments such as first use, completion, recovery, or mastery. Reliability carries everything else. -Operate + Read: delight at specific moments, not pages. Completion, first-time actions, error recovery, milestone crossings. Reliability and consistency carry the rest of the experience; delight pushed everywhere reads as noise. +## Find the opportunity ---- +Inspect the target, DESIGN.md, product voice, repeated-use frequency, and emotional context. Look for: -## Assess Delight Opportunities +- effort worth acknowledging; +- waiting that can become informative; +- an empty or first-use state that can orient; +- an error or recovery moment that needs empathy; +- an interaction whose physical or verbal response could express the brand; +- a useful capability people might enjoy discovering. -Identify where delight would enhance (not distract from) the experience: +Do not manufacture a celebration for an ordinary click. Ask only when the brand's emotional range or the stakes cannot be inferred. -1. **Find natural delight moments**: - - **Success states**: Completed actions (save, send, publish) - - **Empty states**: First-time experiences, onboarding - - **Loading states**: Waiting periods that could be entertaining - - **Achievements**: Milestones, streaks, completions - - **Interactions**: Hover states, clicks, drags - - **Errors**: Softening frustrating moments - - **Easter eggs**: Hidden discoveries for curious users +## Define one delight thesis -2. **Understand the context**: - - What's the brand personality? (Playful? Professional? Quirky? Elegant?) - - Who's the audience? (Tech-savvy? Creative? Corporate?) - - What's the emotional context? (Accomplishment? Exploration? Frustration?) - - What's appropriate? (Banking app ≠ gaming app) +State in one sentence what the user should feel and why that feeling belongs to this product. Then choose the smallest system that can deliver it: -3. **Define delight strategy**: - - **Subtle sophistication**: Refined micro-interactions (luxury brands) - - **Playful personality**: Whimsical illustrations and copy (consumer apps) - - **Helpful surprises**: Anticipating needs before users ask (productivity tools) - - **Sensory richness**: Satisfying sounds, smooth animations (creative tools) +- a distinctive response to a meaningful action; +- product-specific language that clarifies while carrying voice; +- an interaction or transition with a recognizable material behavior; +- an illustration, sound, haptic, or environmental detail grounded in the product world; +- a discovery reward that reveals real utility. -If any of these are unclear from the codebase, ask the user directly to clarify what you cannot infer. +Derive the treatment from product mechanism and visual world, not a stock catalog. -**CRITICAL**: Delight should enhance usability, never obscure it. If users notice the delight more than accomplishing their goal, you've gone too far. +## Build for the emotional moment -## Delight Principles +- **Success:** match the response to the effort and consequence. Major milestones can expand; routine saves should simply feel certain. +- **Waiting:** show truthful progress, useful context, or product-specific activity. Never fake work or delay completion to stage a flourish. +- **Empty and first use:** make the next action clear before adding personality. +- **Error and recovery:** lead with the problem and recovery. Warmth may reduce stress; jokes must not trivialize loss, money, privacy, or blocked work. +- **Repeated interaction:** keep the response satisfying after the hundredth use. Variation is useful only when it remains coherent and predictable enough to trust. +- **Discovery:** reward curiosity without hiding required functionality. -Follow these guidelines: +Copy must use the product's language. Generic whimsy is worse than neutral clarity. -### Delight Amplifies, Never Blocks -- Delight moments should be quick (< 1 second) -- Never delay core functionality for delight -- Make delight skippable or subtle -- Respect user's time and task focus +## Protect the experience -### Surprise and Discovery -- Hide delightful details for users to discover -- Reward exploration and curiosity -- Don't announce every delight moment -- Let users share discoveries with others +Delight must not: -### Appropriate to Context -- Match delight to emotional moment (celebrate success, empathize with errors) -- Respect the user's state (don't be playful during critical errors) -- Match brand personality and audience expectations -- Cultural sensitivity (what's delightful varies by culture) +- delay, block, or obscure the primary task; +- override platform conventions or accessibility; +- add unrequested factual claims; +- play sound without consent or ignore mute settings; +- become mandatory, unskippable, or exhausting on repeat; +- add a dependency or asset cost disproportionate to the moment. -### Compound Over Time -- Delight should remain fresh with repeated use -- Vary responses (not same animation every time) -- Reveal deeper layers with continued use -- Build anticipation through patterns +For authored motion, load [animate.md](animate.md). Respect reduced motion, screen readers, keyboard use, touch, localization, and cultural context. Nonessential loops stop when hidden. Make celebration intensity proportional to frequency and consequence. -## Delight Techniques +## Verify -Add personality and joy through these methods: +- The moment is specific enough that a neighboring product could not use it unchanged. +- It improves comprehension, confidence, motivation, or emotional recovery. +- The interface remains fast and obvious without the flourish. +- Repetition does not turn charm into friction. +- Reduced-motion, muted, keyboard, touch, and localized paths work. +- The result feels like the selected world, not a generic “delight” treatment. -### Micro-interactions & Animation - -**Button delight**: -```css -/* Satisfying button press */ -.button { - transition: transform 0.1s, box-shadow 0.1s; -} -.button:active { - transform: translateY(2px); - box-shadow: 0 2px 4px rgba(0,0,0,0.2); -} - -/* Ripple effect on click */ -/* Smooth lift on hover */ -.button:hover { - transform: translateY(-2px); - transition: transform 0.2s cubic-bezier(0.25, 1, 0.5, 1); /* ease-out-quart */ -} -``` - -**Loading delight**: -- Playful loading animations (not just spinners) -- Personality in loading messages (write product-specific ones, not generic AI filler) -- Progress indication with encouraging messages -- Skeleton screens with subtle animations - -**Success animations**: -- Checkmark draw animation -- Confetti burst for major achievements -- Gentle scale + fade for confirmation -- Satisfying sound effects (subtle) - -**Hover surprises**: -- Icons that animate on hover -- Color shifts or glow effects -- Tooltip reveals with personality -- Cursor changes (custom cursors for branded experiences) - -### Personality in Copy - -**Playful error messages**: -``` -"Error 404" -"This page is playing hide and seek. (And winning)" - -"Connection failed" -"Looks like the internet took a coffee break. Want to retry?" -``` - -**Encouraging empty states**: -``` -"No projects" -"Your canvas awaits. Create something amazing." - -"No saved articles" -"Nothing on the nightstand yet. Save a story for later." -``` - -**Playful labels & tooltips**: -``` -"Delete" -"Send to void" (for playful brand) - -"Help" -"Rescue me" (tooltip) -``` - -**IMPORTANT**: Match copy personality to brand. Banks shouldn't be wacky, but they can be warm. - -### Illustrations & Visual Personality - -**Custom illustrations**: -- Empty state illustrations (not stock icons) -- Error state illustrations (friendly monsters, quirky characters) -- Loading state illustrations (animated characters) -- Success state illustrations (celebrations) - -**Icon personality**: -- Custom icon set matching brand personality -- Animated icons (subtle motion on hover/click) -- Illustrative icons (more detailed than generic) -- Consistent style across all icons - -**Background effects**: -- Subtle particle effects -- Gradient mesh backgrounds -- Geometric patterns -- Parallax depth -- Time-of-day themes (morning vs night) - -### Satisfying Interactions - -**Drag and drop delight**: -- Lift effect on drag (shadow, scale) -- Snap animation when dropped -- Satisfying placement sound -- Undo toast ("Dropped in wrong place? [Undo]") - -**Toggle switches**: -- Smooth slide with spring physics -- Color transition -- Haptic feedback on mobile -- Optional sound effect - -**Progress & achievements** (Operate surfaces with recurring tasks only; gamification on editorial, portfolio, or one-visit surfaces reads as noise): -- Streak counters with celebratory milestones -- Progress bars that "celebrate" at 100% -- Badge unlocks with animation -- Playful stats ("You're on fire! 5 days in a row") - -**Form interactions**: -- Input fields that animate on focus -- Checkboxes with a satisfying scale pulse when checked -- Success state that celebrates valid input -- Auto-grow textareas - -### Sound Design - -**Subtle audio cues** (when appropriate): -- Notification sounds (distinctive but not annoying) -- Success sounds (satisfying "ding") -- Error sounds (empathetic, not harsh) -- Typing sounds for chat/messaging -- Ambient background audio (very subtle) - -**IMPORTANT**: -- Respect system sound settings -- Provide mute option -- Keep volumes quiet (subtle cues, not alarms) -- Don't play on every interaction (sound fatigue is real) - -### Easter Eggs & Hidden Delights - -**Discovery rewards**: -- Konami code unlocks special theme -- Hidden keyboard shortcuts (Cmd+K for special features) -- Hover reveals on logos or illustrations -- Alt text jokes on images (for screen reader users too!) -- A view-source or console colophon (a note on the typefaces, a thank-you, a hiring line) - -**Seasonal touches**: -- Holiday themes (subtle, tasteful) -- Seasonal color shifts -- Weather-based variations -- Time-based changes (dark at night, light during day) - -**Contextual personality**: -- Different messages based on time of day -- Responses to specific user actions -- Randomized variations (not same every time) -- Progressive reveals with continued use - -### Loading & Waiting States - -**Make waiting engaging**: -- Interesting loading messages that rotate -- Progress bars with personality -- Mini-games during long loads -- Fun facts or tips while waiting -- Countdown with encouraging messages - -``` -Loading messages: write ones specific to your product, not generic AI filler: -- "Syncing with your team's changes..." -- "Fetching this week's issue..." -- "Developing your photos..." -- "Checking tomorrow's tide tables..." -``` - -**WARNING**: Avoid cliched loading messages like "Herding pixels", "Teaching robots to dance", "Consulting the magic 8-ball", "Counting backwards from infinity". These are AI-slop copy, instantly recognizable as machine-generated. Write messages that are specific to what your product actually does. - -### Celebration Moments - -**Success celebrations**: -- Confetti for major milestones -- Animated checkmarks for completions -- Progress bar celebrations at 100% -- "Achievement unlocked" style notifications -- Personalized messages ("You published your 10th article!") - -**Milestone recognition** (same scoping as Progress & achievements: Operate surfaces with recurring tasks): -- First-time actions get special treatment -- Streak tracking and celebration -- Progress toward goals -- Anniversary celebrations - -## Implementation Patterns - -**Animation libraries**: -- Framer Motion (React) -- GSAP (universal) -- Lottie (After Effects animations) -- Canvas confetti (party effects) - -**Sound libraries**: -- Howler.js (audio management) -- Use-sound (React hook) - -**Physics libraries**: -- React Spring (spring physics) -- Popmotion (animation primitives) - -**IMPORTANT**: File size matters. Compress images, optimize animations, lazy load delight features. - -**NEVER**: -- Delay core functionality for delight -- Force users through delightful moments (make skippable) -- Use delight to hide poor UX -- Overdo it (less is more) -- Ignore accessibility (animate responsibly, provide alternatives) -- Make every interaction delightful (special moments should be special) -- Sacrifice performance for delight -- Be inappropriate for context (read the room) - -## Verify Delight Quality - -Test that delight actually delights: - -- **User reactions**: Do users smile? Share screenshots? -- **Doesn't annoy**: Still pleasant after 100th time? -- **Doesn't block**: Can users opt out or skip? -- **Performant**: No jank, no slowdown -- **Appropriate**: Matches brand and context -- **Accessible**: Works with reduced motion, screen readers - -When the moments feel earned, hand off to `/impeccable polish` for the final pass. +When the personality feels earned, hand off to `/impeccable polish` for the final pass. diff --git a/.rovodev/skills/impeccable/reference/document.md b/.rovodev/skills/impeccable/reference/document.md index 0d38eb568..121a00ab1 100644 --- a/.rovodev/skills/impeccable/reference/document.md +++ b/.rovodev/skills/impeccable/reference/document.md @@ -1,6 +1,6 @@ Generate a `DESIGN.md` file at the project root that captures the current visual design system, so AI agents generating new screens stay on-brand. -DESIGN.md follows the [official DESIGN.md format spec](https://raw.githubusercontent.com/google-labs-code/design.md/main/docs/spec.md): YAML frontmatter carrying machine-readable design tokens, followed by a markdown body with exactly six sections in a fixed order. **Tokens are normative; prose provides context for how to apply them.** Sections may be omitted when not relevant, but **do not reorder them and do not rename them**. Section headers must match the spec character-for-character so the file stays parseable by other DESIGN.md-aware tools (Stitch itself, awesome-design-md, skill-rest, etc.). +DESIGN.md follows the [official DESIGN.md format spec](https://raw.githubusercontent.com/google-labs-code/design.md/main/docs/spec.md): optional YAML frontmatter carrying machine-readable design tokens, followed by up to eight markdown sections in a fixed order. **Tokens are normative; prose provides context for how to apply them.** Sections may be omitted when not relevant, but those present stay in the specified order. Use the canonical headings below so the file remains portable across DESIGN.md-aware tools. ## The frontmatter: token schema @@ -43,26 +43,28 @@ components: 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. +- **Colors accept any valid CSS color string.** Hex is the recommended default for portability, but preserve an incumbent `rgb()`, `hsl()`, `oklch()`, wide-gamut, or mixed-color value when it is the project's normative source. 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 (`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) +## The markdown body: eight sections (canonical order) 1. `## Overview` 2. `## Colors` 3. `## Typography` -4. `## Elevation` -5. `## Components` -6. `## Do's and Don'ts` +4. `## Layout` +5. `## Elevation & Depth` +6. `## Shapes` +7. `## Components` +8. `## Do's and Don'ts` -Optional evocative subtitles are allowed in the form `## 2. Colors: The [Name] Palette` (Stitch's own outputs do this), but the literal word in each header (Overview, Colors, Typography, Elevation, Components, Do's and Don'ts) must be present. Do NOT add extra top-level sections (Layout Principles, Responsive Behavior, Motion, Agent Prompt Guide). Fold that content into the six spec sections where it naturally belongs. +Omit irrelevant sections rather than filling them with invented rules. Put responsive layout in Layout, depth in Elevation & Depth, radius and form language in Shapes, and per-component behavior in Components. Unknown sections are preserved by the format, but new visual guidance should use the canonical structure whenever it fits. ## When to run -- 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. +- New-work found a coherent incumbent visual system but no `DESIGN.md`. +- The first implementation of a new world is complete and its provisional decisions need to be carbonized. - An existing `DESIGN.md` is stale (the design has drifted). - Before a large redesign, to capture the current state as a reference. @@ -71,9 +73,9 @@ 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 init, nothing built yet). Interview for five high-level answers, write a minimal DESIGN.md marked ``. Re-run in scan mode once there's code. +- **Seed mode**: the project is pre-implementation. Ensure PRODUCT.md exists, then reuse new-work's visual-world workshop and write its directional DESIGN.md 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. +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` requests new-work's world workshop, but it does not authorize replacing coherent code: when an incumbent system exists, offer scan mode or route an explicit identity-replacement request through new-work. ## Scan mode (approach C: auto-extract, then confirm descriptive language) @@ -97,7 +99,8 @@ Build a structured draft from the discovered tokens. For each token class: - **Typography**: Map observed sizes and weights to the Material hierarchy (display / headline / title / body / label). Note font-family stacks and the scale ratio. - **Elevation**: Catalogue the shadow vocabulary. If the project is flat and uses tonal layering instead, that's a valid answer; state it explicitly. - **Components**: For each common component (button, card, input, chip, list item, tooltip, nav), extract shape (radius), color assignment, hover/focus treatment, internal padding. -- **Spacing + layout**: Fold into Overview or relevant Components. The spec does NOT have a Layout section. +- **Layout + spacing**: Extract grid, container, breakpoint, rhythm, and density behavior into Layout. +- **Shapes**: Extract radius, corner, border, clipping, and recurring form behavior into Shapes. ### Step 2b: Stage the frontmatter @@ -112,19 +115,19 @@ Skip anything the project doesn't have. Empty scale keys or fabricated tokens po ### Step 3: Ask the user for qualitative language -The following require creative input that cannot be auto-extracted. Group them into one `AskUserQuestion` interaction: +The following require creative input that cannot be auto-extracted. Ask them in two structured rounds of no more than three questions each (or the harness's lower limit), waiting between rounds: - **Creative North Star**: a single named metaphor for the whole system ("The Editorial Sanctuary", "The Golden State Curator", "The Lab Notebook"). Offer 2-3 options that honor PRODUCT.md's brand personality. -- **Overview voice**: mood adjectives, aesthetic philosophy in 2-3 sentences, anti-references (what the system should not feel like). +- **Overview voice**: mood adjectives, aesthetic philosophy in 2-3 sentences, and any confirmed visual anti-reference. - **Color character** (for auto-extracted colors): descriptive names ("Deep Muted Teal-Navy", not "blue-800"). Suggest 2-3 options per key color based on hue/saturation. - **Elevation philosophy**: flat/layered/lifted. If shadows exist, is their role ambient or structural? - **Component philosophy**: the feel of buttons, cards, inputs in one phrase ("tactile and confident" vs. "refined and restrained"). -Quote a line from PRODUCT.md when possible so the user sees their own strategic language carry forward. +Carry a line from PRODUCT.md only when it is a durable brand commitment that actually constrains the visual system. Page strategy and surface concepts do not belong here. ### Step 4: Write DESIGN.md -The file opens with the YAML frontmatter staged in Step 2b (schema documented at the top of this reference), then the markdown body using the structure below. Headers must match character-for-character. Optional evocative subtitles (e.g. `## 2. Colors: The Coastal Palette`) are allowed. +The file opens with the YAML frontmatter staged in Step 2b (schema documented at the top of this reference), then the markdown body using the canonical structure below. ```markdown --- @@ -136,13 +139,13 @@ colors: # Design System: [Project Title] -## 1. Overview +## Overview **Creative North Star: "[Named metaphor in quotes]"** -[2-3 paragraph holistic description: personality, density, aesthetic philosophy. Start from the North Star and work outward. State what this system explicitly rejects (pulled from PRODUCT.md's anti-references). End with a short **Key Characteristics:** bullet list.] +[2-3 paragraph holistic description: personality, density, and aesthetic philosophy. Start from the North Star and work outward. State only confirmed visual rejections. End with a short **Key Characteristics:** bullet list.] -## 2. Colors +## Colors [Describe the palette character in one sentence.] @@ -162,7 +165,7 @@ colors: ### Named Rules (optional, powerful) **The [Rule Name] Rule.** [Short, forceful prohibition or doctrine, e.g. "The One Voice Rule. The primary accent is used on ≤10% of any given screen. Its rarity is the point."] -## 3. Typography +## Typography **Display Font:** [Family] (with [fallback]) **Body Font:** [Family] (with [fallback]) @@ -180,7 +183,11 @@ colors: ### Named Rules (optional) **The [Rule Name] Rule.** [Short doctrine about type use.] -## 4. Elevation +## Layout + +[Describe the grid or spatial model, container behavior, density, responsive changes, and the spacing rhythm. Include exact values only when observed.] + +## Elevation & Depth [One paragraph: does this system use shadows, tonal layering, or a hybrid? If "no shadows", say so explicitly and describe how depth is conveyed instead.] @@ -191,7 +198,11 @@ colors: ### Named Rules (optional) **The [Rule Name] Rule.** [e.g. "The Flat-By-Default Rule. Surfaces are flat at rest. Shadows appear only as a response to state (hover, elevation, focus)."] -## 5. Components +## Shapes + +[Describe the form language: corner/radius strategy, borders, clipping, and any recurring silhouette or geometry.] + +## Components For each component, lead with a short character line, then specify shape, color assignment, states, and any distinctive behavior. @@ -223,16 +234,16 @@ For each component, lead with a short character line, then specify shape, color ### [Signature Component] (optional; if the project has a distinctive custom component worth documenting) [Description.] -## 6. Do's and Don'ts +## Do's and Don'ts -Concrete, forceful guardrails. Lead each with "Do" or "Don't". Be specific: include exact colors, pixel values, and named anti-patterns the user mentioned in PRODUCT.md. **Every anti-reference in PRODUCT.md should show up here as a "Don't" with the same language**, so the visual spec carries the strategic line through. Quote PRODUCT.md directly where possible: if PRODUCT.md says *"avoid dark mode with purple gradients, neon accents, glassmorphism"*, the Don'ts here should repeat that by name. +Concrete visual guardrails grounded in the incumbent implementation or the user's chosen world. Lead each with "Do" or "Don't" and include exact values only when established. Do not turn a task-specific concept or surface strategy into a system-wide prohibition. ### Do: - **Do** [specific prescription with exact values / named rule]. - **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** [...] ``` @@ -307,7 +318,7 @@ The `html` and `css` fields must be **self-contained, drop-in snippets** that re Aim for a tight set of **5-10 components** that best represent the visual system: - **Canonical primitives (always include if the project has them):** button (each variant as a separate component entry), input/text field, navigation, chip/tag, card. -- **Signature components (include if distinctive):** hero CTA, featured card, filter pill, a distinctive table or list-row treatment, any custom pattern the user mentioned as important in PRODUCT.md. +- **Signature components (include if distinctive):** the recurring custom patterns that actually define the implemented system. - **Skip the rest.** Utility components, form building blocks, wrapper layouts: not worth documenting unless visually distinctive. If the project has **no component library yet** (bare landing page, new project), synthesize canonical primitives from the tokens using best-practice defaults consistent with the DESIGN.md's rules. Every `.impeccable/design.json` has *something* to render, even on day zero. @@ -338,64 +349,40 @@ Your own write is the freshest source; subsequent commands in this session don't ## Seed mode -For projects with no visual system to extract yet. Produces a minimal scaffold, not a full spec. +For projects with no visual system to extract yet. Produces a user-chosen visual-world scaffold, not a fabricated token spec. -### Step 1: Confirm seed mode +### Step 1: Route through new-work's workshop -Before interviewing: "There's no existing visual system to scan. I'll ask five quick questions to seed a starter DESIGN.md. You can re-run `/impeccable document` once there's code, to capture the real tokens and components. OK?" +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 the user prefers to skip, stop. No file. +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. -### Step 2: Five questions +If new-work already completed the workshop in this session, use its chosen direction directly. Do not ask again. -Group into one `AskUserQuestion` interaction. Options must be concrete. +### Step 2: Write seed DESIGN.md -1. **Color strategy.** Pick one: - - Restrained: tinted neutrals + one accent ≤10% - - Committed: one saturated color carries 30–60% of the surface - - Full palette: 3–4 named color roles, each deliberate - - Drenched: the surface IS the color - - Then: one hue family or anchor reference ("deep teal", "mustard", "Klim #ff4500 orange"). - -2. **Typography direction.** Pick one (specific fonts come later): - - Serif display + sans body - - Single sans (warm / technical / geometric / humanist; pick a feel) - - Display + mono - - Mono-forward - - Editorial script + sans - -3. **Motion energy.** Pick one: - - Restrained: state changes only - - Responsive: feedback + transitions, no choreography - - Choreographed: orchestrated entrances, scroll-driven sequences - -4. **Three named references.** Brands, products, printed objects. Not adjectives. - -5. **One anti-reference.** What it should NOT feel like. Also named. - -### Step 3: Write seed DESIGN.md - -Use the six-section spec from Scan mode. Populate what the interview answers; leave the rest as honest placeholders. The seed is a scaffold, not a fabricated spec. +Use the canonical section order from Scan mode. Populate the selected workshop direction and leave unresolved implementation facts as honest placeholders. The seed commits a world and its invariants; it does not pretend implementation tokens already exist. Lead the file with: ```markdown - + ``` Per-section guidance in seed mode: -- **Overview**: Creative North Star and philosophy phrased from the answers (color strategy + motion energy + references). Reference the user's anti-reference directly. -- **Colors**: Color strategy as a Named Rule (e.g. *"The Drenched Rule. The surface IS the color."*). Hue family or anchor reference. No hex values; mark as `[to be resolved during implementation]`. -- **Typography**: the direction the user picked (e.g. "Serif display + sans body"). No font names yet: `[font pairing to be chosen at implementation]`. -- **Elevation**: inferred from motion energy. Restrained/Responsive → flat by default; Choreographed → layered. One sentence. +- **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. +- **Elevation & Depth**: the selected material and depth behavior, stated as an invariant rather than inferred from a generic preset. +- **Shapes**: the selected form and corner language. - **Components**: omit entirely; no components exist yet. -- **Do's and Don'ts**: carry PRODUCT.md's anti-references directly plus the anti-reference named in Q5. +- **Do's and Don'ts**: record the durable guardrails confirmed during the world choice, not task-local refusals. 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 +### Step 3: 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." @@ -405,15 +392,15 @@ Your own write is the freshest source; no reload needed. ## Style guidelines - **Frontmatter first, prose second.** Tokens go in the YAML frontmatter; prose contextualizes them. Don't redefine a token value in two places; the frontmatter is normative. -- **Cite PRODUCT.md anti-references by name** in the Do's and Don'ts section. If PRODUCT.md lists "SaaS landing-page clichés" or "generic AI tool marketing" as anti-references, the DESIGN.md Don'ts should repeat those phrases verbatim so the visual spec enforces the strategic line. -- **Match the spec, don't invent new sections.** The six section names are fixed. If you have Layout/Motion/Responsive content to document, fold it into Overview (philosophy-level rules) or Components (per-component behavior). +- **Carry only durable product constraints.** A binding logo, identity asset, accessibility need, or brand commitment from PRODUCT.md may constrain DESIGN.md. Surface strategy stays in its surface brief. +- **Match the spec.** Use its eight canonical sections in order and omit any that are irrelevant. Put motion guidance with the world or component it affects rather than creating a token group the schema does not support. - **Descriptive > technical**: "Gently curved edges (8px radius)" > "rounded-lg". Include the technical value in parens, lead with the description. - **Functional > decorative**: for each token, explain WHERE and WHY it's used, not just WHAT it is. - **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 forceful**. The voice of a design director. "Prohibited", "forbidden", "never", "always", not "consider", "might", "prefer". Match PRODUCT.md's tone. -- **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. -- **Reference PRODUCT.md**. The anti-references section of PRODUCT.md should directly inform the Do's and Don'ts section here. Quote or paraphrase. +- **Be decisive where evidence is decisive.** Use hard language for actual invariants and softer language for provisional guidance. +- **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. ## Pitfalls @@ -423,7 +410,7 @@ Your own write is the freshest source; no reload needed. - Don't invent components that don't exist. If the project only has buttons and cards, only document those. - Don't overwrite an existing DESIGN.md without asking. - Don't duplicate content from PRODUCT.md. DESIGN.md is strictly visual. -- Don't add a "Layout Principles" or "Motion" or "Responsive Behavior" top-level section. The spec has six, not nine. Fold that content where it belongs. +- Don't replace canonical sections with near-synonyms. Put layout and responsive behavior in `Layout`; put motion with the affected world or component. - Don't rename sections even slightly. "Colors" not "Color Palette & Roles". "Typography" not "Typography Rules". Tooling parsing depends on exact headers. - Don't duplicate token values between frontmatter and prose. If a color is in `colors.primary` as hex, the prose can name it and describe its role but should not reassert a different hex. The frontmatter is normative. - Don't invent frontmatter token groups outside Stitch's schema (no `motion:`, `breakpoints:`, `shadows:` at the top level). Stitch's Zod schema only accepts `colors`, `typography`, `rounded`, `spacing`, `components`. Anything else belongs in the sidecar's `extensions`. diff --git a/.rovodev/skills/impeccable/reference/hooks.md b/.rovodev/skills/impeccable/reference/hooks.md index 23dcdf84f..0e2c7c798 100644 --- a/.rovodev/skills/impeccable/reference/hooks.md +++ b/.rovodev/skills/impeccable/reference/hooks.md @@ -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. diff --git a/.rovodev/skills/impeccable/reference/init.md b/.rovodev/skills/impeccable/reference/init.md index 38dc35965..d3e3a1fbd 100644 --- a/.rovodev/skills/impeccable/reference/init.md +++ b/.rovodev/skills/impeccable/reference/init.md @@ -1,224 +1,114 @@ -# Init Flow +# Init flow -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 [DESIGN.md format spec](https://raw.githubusercontent.com/google-labs-code/design.md/main/docs/spec.md). Answers "how it looks". -- **`.impeccable/live/config.json`** (live mode): pre-configured so `/impeccable live` boots straight into variant mode with no first-time detour. - -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. +`init` captures durable product truth in PRODUCT.md. It does not invent a visual world and does not write DESIGN.md; [new-work.md](new-work.md) creates or expands one, and [document.md](document.md) records an incumbent one. Existing runnable web projects may also receive `.impeccable/live/config.json`. ## Step 1: Load current state -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). +Use the PRODUCT.md path resolved by context.mjs. Update it instead of creating a competing authority. In a child app inheriting root context, confirm shared versus app-specific scope before writing. -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. -- **PRODUCT.md exists, DESIGN.md missing**: skip to Step 5 and offer to run `/impeccable document` for DESIGN.md. -- **PRODUCT.md exists but has no `## Register` section (legacy)**: add it. Infer a hypothesis from the codebase (see Step 2), confirm with the user, write the field. -- **PRODUCT.md exists but has no `## Platform` section (legacy)**: add it the same way, but only when the project is native (`ios` / `android` / `adaptive`) or the user wants it explicit; a missing field already means `web`. -- **Both exist**: ask the user directly to clarify what you cannot infer. Ask which file to refresh. Skip the one the user doesn't want changed. -- **Just DESIGN.md exists (unusual)**: do Steps 2-4 to produce PRODUCT.md. +- **No PRODUCT.md:** explore, interview, and write it. +- **PRODUCT.md exists:** ask what product knowledge is stale or missing; do not reopen confirmed fields without a reason. +- **Legacy PRODUCT.md:** add only durable missing facts; absent `## Platform` means `web` unless evidence says otherwise. +- **Only DESIGN.md exists:** leave it untouched and create PRODUCT.md. +- **Redesign/rebrand request:** preserve confirmed product truth unless the user changes it. Visual replacement happens later in new-work, not here. -Never silently overwrite an existing file. Always confirm first. +Never silently overwrite an existing file or offer DESIGN.md during init. If another request invoked init, finish PRODUCT.md and resume it. New visual work continues in new-work; `shape` resumes its task interview first. -If init was invoked as a setup blocker by another command, such as `/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 project -## Step 2: Explore the codebase +Before asking, scan enough to avoid making the user repeat known facts: product docs and copy; package/config and app boundaries; features, workflows, routes, and roles; names, logos, legal/proof assets, and brand commitments; platform/accessibility signals; and the dev command/entry when live mode applies. -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: +Treat repository evidence as a hypothesis, not user approval. Note visual maturity without documenting, extending, or replacing the world. -- **README and docs**: Project purpose, target audience, any stated goals -- **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 -- **Any style guides or brand documentation** +Form a platform hypothesis: `web`, `ios`, `android`, or `adaptive` (one product that genuinely adapts its design language per OS). Mobile web remains `web`; a native wrapper around a website does not make its design language native. -Also form a **register hypothesis** from what you find: +## Step 3: Interview for product truth -- Brand signals: `/`, `/about`, `/pricing`, `/blog/*`, `/docs/*`, hero sections, big typography, scroll-driven sections, landing-page-shaped content. -- Product signals: `/app/*`, `/dashboard`, `/settings`, `/(auth)`, forms, data tables, side/top nav, app-shell components. +ask the user directly to clarify what you cannot infer. Ask only about material gaps the repository and original request do not answer with strong evidence. -Register is a hypothesis at this point, not a decision; Step 3 confirms it. +Use the structured question tool when available; otherwise ask and wait. Keep rounds to at most three focused questions and require one real answer or approval round before writing a new PRODUCT.md. Confirm inferences. -Also form a **platform hypothesis**: +Start with the unknowns that most change future product decisions: -- Native signals: React Native / Expo (`react-native`, `expo`), Flutter (`pubspec.yaml`, `flutter`), SwiftUI / UIKit (`.swift`, `.xcodeproj`, an `ios/` app target), Jetpack Compose / Android (`build.gradle`, an `android/` app module, `AndroidManifest.xml`). An `ios/` and/or `android/` directory that is a real app target, not just a Capacitor/Cordova wrapper around a website. -- Web signals (the default): a web framework (Vite, Next, Nuxt, SvelteKit, Astro), an HTML entry, a CSS/Tailwind setup, no native app target. +1. Who is the primary user, in what situation, and what job are they doing? +2. What does the product make possible, and what is its meaningfully different mechanism or position? +3. What durable constraints, assets, evidence, or product facts must future work preserve? -Values: `web` / `ios` / `android` / `adaptive` (one codebase, ships both, adapts per OS). Mobile web is still `web`. Like register, this is a hypothesis; Step 3 confirms it. +Confirm ambiguous platform separately. Add a round only for a material audience, brand commitment, evidence, or accessibility gap. Record undecided facts instead of inventing them. -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. +Do not ask for an aesthetic direction, emotional feel, visual references, colors, typography, or style during init. If the user volunteers a binding visual constraint, record it without expanding it. -## Step 3: Ask strategic questions (for PRODUCT.md) +### What belongs here -ask the user directly to clarify what you cannot infer. Ask about anything the codebase doesn't answer with strong, explicit evidence. +- users, jobs, workflows, purpose, success, positioning, and operating context; +- capabilities, constraints, terminology, evidence, platform, and accessibility; +- confirmed voice, assets, and brand commitments. -### Interview mode, not confirmation mode +### What does not belong here -If the repo is empty or the user's brief is sparse, run a short interview before proposing PRODUCT.md. Do **not** turn a one-sentence request into a complete inferred PRODUCT.md and ask for blanket confirmation. - -- Use the harness's structured question tool when one exists. Otherwise, ask directly in chat and stop: one question at a time, with lettered options where the crawl suggests likely answers, waiting for each answer before the next. -- Keep skill vocabulary (register, belief ladder, anti-references) out of question text; ask for the thing in words the user would use. For the brand register, ask like a magazine editor profiling the brand: curious and narrative, drawing out the story, the feel, and what a visitor should come to believe. -- Ask in focused rounds and wait for answers between them. Keep **one topic per question**; add rounds rather than fold several topics into one either-or choice. Options obey the same rule: an option answers only the question asked; never write a compound option that bundles a feeling with a business outcome or names an additional audience. -- Use inferred answers as hypotheses or options, not as finished facts. -- Complete at least one real user-answer round before drafting PRODUCT.md, unless every required answer is directly discoverable from repo docs. -- Round 1 should establish register, platform, users, purpose, positioning, and desired outcome. -- Round 2 should establish brand personality or references, anti-references, and accessibility needs, plus conversion & proof for Persuade surfaces. - -### Minimum viable interview - -Ask enough to complete PRODUCT.md. At minimum, cover register confirmation, **platform confirmation** (`web` / `ios` / `android` / `adaptive`), users, purpose, positioning, brand personality, anti-references, and accessibility needs (plus conversion & proof for Persuade surfaces) unless each answer is directly discoverable from repo context. Never let the template's default `web` stand unconfirmed for a native or cross-platform repo. After at least one interview round, you may propose inferred answers, but the user must confirm them before you write PRODUCT.md. Never synthesize PRODUCT.md from the original task prompt alone. - -### Register (ask first; it shapes everything below) - -Every design task serves one of four visitor modes (see SKILL.md's Registers): **Persuade** (marketing, landing, campaigns), **Experience** (portfolios, albums, bodies of work), **Operate** (app UI, admin, dashboards, tools), and **Read** (documentation, editorial, long-form). PRODUCT.md stores the two-value family that covers them: `brand` covers Persuade and Experience, `product` covers Operate and Read. The brief decides the exact mode per task; the stored register only sets the default family. - -If Step 2 produced a clear hypothesis, lead with it: *"From the codebase, this looks like a [brand / product] surface. Does that match your intent, or should we treat it differently?"* - -If the signal is genuinely split (e.g. a product with a big marketing landing), ask the user directly to clarify what you cannot infer. Ask which register describes the **primary** surface. The register can be overridden per task later, but PRODUCT.md carries one default. Settle the default before drafting any register-dependent questions; never batch Persuade-only questions (Conversion & proof) into the same round as the question that decides the register. - -### Platform (ask right after register) - -Every project targets **web** (includes responsive mobile web), **ios**, **android**, or **adaptive** (one codebase, ships both, adapts per OS: Flutter, React Native, KMP). Platform picks the native rulebook: HIG for `ios`, Material 3 for `android`, both for `adaptive`, none for `web`. - -If Step 2 produced a clear hypothesis, lead with it: *"From the codebase, this looks like a [web / ios / android / adaptive] project. Does that match?"* For cross-platform apps, decide by the **design language the app renders**, not the toolchain: one look on both platforms (Flutter's Material-everywhere default) takes that platform's value; genuine per-OS adaptation (Cupertino on iOS, Material on Android) is `adaptive`. When in doubt, `web`. - -A monorepo shipping both a website and a native app gets a PRODUCT.md per app, each with its own `## Platform`; the root PRODUCT.md carries the primary surface's platform. - -### Users & Purpose -- Who uses this? What's their context when using it? -- What job are they trying to get done? -- What is this for? A purpose stated in README or docs is a hypothesis, not strong evidence; confirm it, don't transcribe it. -- What does success look like? -- If more than one kind of user is plausible, confirm a primary and secondary audience; don't manufacture a split that isn't there. An audience implied by another answer (a success metric, a CTA) is still unconfirmed; ask before writing it as secondary. -- If the surface speaks to a different audience than the people who use the product, ask the user to name both. -- For brand: what emotions should the interface evoke? (confidence, delight, calm, urgency) Ask this standalone; don't fold emotions into the success question. -- For product: what workflow are they in? What's the primary task on any given screen? - -### Positioning -- In one line, what does this do that nothing else does? The single strategic claim every screen reinforces. - -### 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? - - 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? - -### Conversion & proof (Persuade surfaces only) - -Ask these only when the primary surface is a Persuade one (marketing, landing, campaigns). Experience and Read surfaces (portfolios, albums, long-form writing, documentation) get no CTA, belief-ladder, or proof questions even when the stored register is `brand`; skip this section and its PRODUCT.md counterpart. - -- What's the primary CTA? -- What's the secondary fallback, for visitors not ready for the primary? -- The one line a visitor should remember after 10 seconds. -- What must the visitor believe, in order, before taking the primary CTA? (The template's belief ladder.) -- What proof is on hand? Ask the user to hand over any testimonials, case studies, press, or client/partner logos they already have. If you can receive files directly, collect them; otherwise create `.impeccable/assets/proof/` and ask the user to add files there. Reference supplied files by path; record text proof inline. - -### Accessibility & Inclusion -- Specific accessibility requirements? (WCAG level, known user needs) -- Considerations for reduced motion, color blindness, or other accommodations? - -Skip questions where the answer is already clear. **Do NOT ask about colors, fonts, radii, or visual styling here.** Those belong in DESIGN.md, not PRODUCT.md. +- visual worlds, palettes, typography, components, or page concepts; +- visitor mode, narrative, CTA/proof sequence, or other surface strategy; +- invented testimonials, customers, benchmarks, pricing, licensing, or deployment claims; +- a requirement to decide every optional field. ## Step 4: Write PRODUCT.md -Write PRODUCT.md only after the user has confirmed the strategic answers from Step 3. If an inferred answer is uncertain or unconfirmed, ask before writing. Confirmed means what the user actually said yes to; do not pad a confirmed answer with extras they never picked (additional anti-references, audiences, roadmap claims, a WCAG level), whether drawn from the crawl, another answer, or your own option text. If an extra belongs in the doc, ask about it first. - -Synthesize into a strategic document: +Write only confirmed facts and explicitly marked open decisions. Omit irrelevant sections rather than filling them with generic prose. ```markdown # Product -## Register - -product - ## Platform web ## Users -[Who they are, their context, the job to be done. Primary audience; a secondary audience or a surface-vs-user split only when they apply.] +[Primary users, their situation, and job. Add other audiences only when confirmed.] ## Product Purpose -[What this product does, why it exists, what success looks like] +[What the product does, why it exists, and what success means.] ## Positioning -[The single strategic claim every screen reinforces. Not a visual rule, not an anti-reference.] +[The product mechanism or claim a neighboring product could not truthfully copy.] -## Conversion & proof -[Persuade surfaces only (marketing, landing, campaigns). Omit this section entirely, heading included, for the product register and for Experience or Read surfaces such as portfolios, albums, or long-form writing.] -- Primary and secondary CTA: [...] -- The line a visitor remembers after 10 seconds: [...] -- Belief ladder: [...] -- Proof on hand: [testimonials, case studies, press, or logos, referenced by path] +## Operating Context +[Workflows, environments, tools, documents, materials, and rituals that are factual parts of using or evaluating the product.] -## Brand Personality -[Voice, tone, 3-word personality, emotional goals] +## Capabilities and Constraints +[Confirmed functionality, technical constraints, terminology, and explicitly undecided product facts.] -## Anti-references -[What this should NOT look like. Specific bad-example sites or patterns to avoid.] +## Brand Commitments +[Existing name, voice, assets, personality, identity constraints, and references the user explicitly made binding. Omit when none exist.] -## Design Principles -[3-5 strategic principles derived from the conversation. Principles like "practice what you preach", "show, don't tell", "expert confidence". NOT visual rules like "use OKLCH" or "magenta accent".] +## Evidence on Hand +[Real content, data, demonstrations, testimonials, case studies, press, or assets, with paths where applicable. State absences that future work must not fabricate.] + +## Product Principles +[Three to five durable strategic principles derived from confirmed answers; no visual recipes.] ## Accessibility & Inclusion -[WCAG level, known user needs, considerations] +[Known user needs or required standard. Omit when no product-specific requirement was established.] ``` -Register is either `brand` or `product` as a bare value. No prose, no commentary. Platform is `web`, `ios`, `android`, or `adaptive`, also a bare value; omit the section only on legacy files you're leaving untouched, otherwise write `web` explicitly. +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. -Write fields as prose, and use bold sparingly: only where a word carries a decision, never as a label lead-in on every line. +### Completion gate -Write to `PROJECT_ROOT/PRODUCT.md`. If `.impeccable.md` existed, the loader already renamed it; merge into that content rather than starting from scratch. +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: Decide on DESIGN.md +## Step 5: Configure live mode when useful -Offer `/impeccable document` either way. Two paths: +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. -- **Code exists** (CSS tokens, components, a running site): "I can generate a DESIGN.md that captures your visual system (colors, typography, components) so variants stay on-brand. Want to do that now?" -- **Pre-implementation** (empty project): "I can seed a starter DESIGN.md from five quick questions about color strategy, type direction, motion energy, and references. You can re-run once there's code, to capture the real tokens. Want to do that now?" +## Step 6: Wrap up or resume -If the user agrees, delegate to `/impeccable document` (it auto-detects scan vs seed). Load its reference and follow that flow. +Summarize captured and deliberately undecided facts. Do not offer DESIGN.md merely because it is missing. -If the user prefers to skip, mention they can run `/impeccable document` any time later. +Recommend the next action from the actual project state: -## Step 6: Configure live mode (when code exists) +- Empty or early project: ask naturally for the surface to be built, or use `/impeccable shape ` when the user wants a confirmed brief without implementation. New-work will establish a visual world only when the requested work needs one. +- Existing coherent interface without DESIGN.md: `/impeccable document` if the user wants the incumbent system recorded independently of a new build. +- Existing surface needing work: name the most relevant scoped command. +- Web project ready for visual iteration: `/impeccable live` when configured. -**Skip this step when the platform is native** (`ios` / `android` / `adaptive`): live mode drives a browser overlay. A hybrid wrapper or Expo web target serving HTML doesn't change that. - -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 .rovodev/skills/impeccable/scripts/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) and platform captured (web / ios / android / adaptive) -- 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 or live config is pending, one line on how to set it up later - -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 platform**, offer the 2-4 most relevant (not a menu dump), and give the exact command to type. Group by intent: - -- **Build something new**: `/impeccable craft ` (shape, then build end-to-end) or `/impeccable shape ` (plan first). Lead with this for empty or early-stage projects. -- **Improve what's there**: name the specific surface. `/impeccable critique ` for a scored UX review; `/impeccable audit ` for a11y / perf / responsive checks; `/impeccable polish ` 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** (web only): `/impeccable live` (configured in Step 6) to pick elements in the browser and generate variants in place. **Skip this group for native platforms.** - -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 the user directly to clarify what you cannot infer. Ask whether they'd like a brief summary of PRODUCT.md appended to AGENTS.md for easier agent reference. If yes, append a short **Design Context** pointer section there. +If init was invoked by another request, resume without rerunning context.mjs; new-work owns later visual decisions. diff --git a/.rovodev/skills/impeccable/reference/ios.md b/.rovodev/skills/impeccable/reference/ios.md index ef10ee0d2..ccef5d2c4 100644 --- a/.rovodev/skills/impeccable/reference/ios.md +++ b/.rovodev/skills/impeccable/reference/ios.md @@ -2,7 +2,7 @@ For native iOS / iPadOS apps: SwiftUI, UIKit, React Native, Expo, Flutter shipping to Apple hardware. -On native, register narrows. HIG conformance governs structure, navigation, and interaction whatever the register; brand expresses through the expressive layer the platform provides (tint, type, motion, content). Calm, Duolingo, and Spotify carry strong identity entirely inside HIG conventions. +On native, the visitor mode narrows what expression may override. HIG conformance governs structure, navigation, and interaction in every mode; brand expresses through the layer the platform leaves open (tint, type, motion, content). ## The iOS slop test diff --git a/.rovodev/skills/impeccable/reference/layout.md b/.rovodev/skills/impeccable/reference/layout.md index e9e37d3b5..3c0fdf3b4 100644 --- a/.rovodev/skills/impeccable/reference/layout.md +++ b/.rovodev/skills/impeccable/reference/layout.md @@ -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. --- -## Register +## 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, register, 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 .rovodev/skills/impeccable/scripts/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 `/impeccable polish` for the final pass. +When the structure holds, hand off to `/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) * )`: 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. diff --git a/.rovodev/skills/impeccable/reference/live.md b/.rovodev/skills/impeccable/reference/live.md index d3d4f5caa..30848e8b3 100644 --- a/.rovodev/skills/impeccable/reference/live.md +++ b/.rovodev/skills/impeccable/reference/live.md @@ -33,7 +33,7 @@ Chat is overhead. No recap, no tutorial output, no pasting PRODUCT / DESIGN bodi node .rovodev/skills/impeccable/scripts/live.mjs ``` -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. +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, DESIGN.md, and any surface brief already loaded by Setup in mind for variant generation: **DESIGN.md wins on visual decisions; PRODUCT.md wins on durable product and voice decisions; the surface brief wins on this surface's strategy.** 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 requires the user's explicit redesign/replacement intent. `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). @@ -187,7 +187,7 @@ All three carry `fallback: "agent-driven"`. Follow **Handle fallback** below. ### 3. Load the action's reference -If `event.action` is `impeccable` (the default freeform action), use SKILL.md's shared laws plus the loaded register reference (`brand.md` or `product.md`). Do not load a sub-command reference. **Freeform is not a pass to skip parameters:** you still follow the composition budget and the freeform bias in **§7 Parameters** below. Sub-command files list MUST-have signature knobs; freeform has no such file, so sizing knobs from surface weight and primary axes is entirely on you. +If `event.action` is `impeccable` (the default freeform action), use SKILL.md's shared laws and decide the visitor mode from the selected surface. Do not load a sub-command reference. **Freeform is not a pass to skip parameters:** you still follow the composition budget and the freeform bias in **§7 Parameters** below. Sub-command files list MUST-have signature knobs; freeform has no such file, so sizing knobs from surface weight and primary axes is entirely on you. Any other `event.action` (`bolder`, `quieter`, `distill`, `polish`, `typeset`, `colorize`, `layout`, `adapt`, `animate`, `delight`, `overdrive`): Read `reference/.md` before planning. Each sub-command encodes a specific discipline; skipping its reference produces generic output. Those files may require specific params; layer them on top of the §7 budget, not instead of it. @@ -216,7 +216,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. @@ -224,10 +224,7 @@ This sentence is the **identity lock**. Every variant must be readable as the sa **Default mode**: the existing identity is preserved. Variants vary expression axes within it. *This is the right mode for ~90% of live sessions.* The user picked an element on a real product they're shipping; they expect variants of *their* hero, not three different brands' heroes. -**Departure mode**: the existing identity is rejected. Variants propose alternatives consistent with PRODUCT.md voice. Trigger only when at least one is true: - -- PRODUCT.md anti-references explicitly call out the current surface ("the current `index.html` is itself an example"; "diffuse away from this"; "the page on screen is the failure"). Generic anti-references that describe what to avoid in general do **not** trigger departure mode; only ones that point at *this* surface specifically. -- The user's freeform prompt explicitly asks for departure ("rebuild this from scratch", "what if it weren't editorial at all", "show me something completely different"). +**Departure mode**: the existing identity is rejected. Variants propose alternatives consistent with durable product and brand truth. Trigger only when the user explicitly asks for departure in the current request or freeform prompt ("redesign this", "rebuild this from scratch", "what if it weren't editorial at all", "show me something completely different"). A stale page critique or an old task note is not replacement authorization. If you're unsure, you're in default mode. The cost of being wrong about default is "three on-brand variants with similar feel": recoverable, the user picks none. The cost of being wrong about departure is "three off-brand variants": unrecoverable, the user is annoyed. @@ -246,13 +243,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 the brand's stated voice and register in PRODUCT.md. Do NOT pick from a fixed catalog of lane categories. The right three directions for this brand are not the same as the right three for another brand, 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. @@ -262,7 +259,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. @@ -289,7 +286,7 @@ In **default mode**, the prompt narrows the axes you choose, not the identity. * In **departure mode**, the prompt narrows the lanes you draw from, not the families. *"Make it feel like a newspaper front page"* would itself be a departure-mode prompt; honor it but pick three meaningfully different newspaper-adjacent lanes (broadsheet vs. tabloid vs. trade journal), and run the family pass to confirm they don't collapse into one. -When the prompt and PRODUCT.md anti-references conflict (the prompt asks for X, the anti-references ban X), the anti-references win; they describe the brand's standing position, the prompt is one moment. +When the prompt conflicts with a confirmed binding brand commitment or DESIGN.md invariant, preserve the invariant unless the user explicitly revokes or replaces it. Task-local strategy from the matching surface brief may change when the user changes that surface's goal. ### 6. Write all variants in a single edit diff --git a/.rovodev/skills/impeccable/reference/new-work.md b/.rovodev/skills/impeccable/reference/new-work.md index 33c3f8e6f..5fd14d969 100644 --- a/.rovodev/skills/impeccable/reference/new-work.md +++ b/.rovodev/skills/impeccable/reference/new-work.md @@ -1,47 +1,178 @@ -# New identity work +# New visual work -Read this only when the project has no committed visual identity, or the user explicitly asked to replace the current one. A missing DESIGN.md is not enough: code, tokens, chosen type, components, and assets are incumbent design authority. If they exist, preserve and extend them unless the brief says to discard them. +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. -This playbook is task-scoped. PRODUCT.md owns durable product facts and DESIGN.md owns durable visual invariants; the direction for one page or feature belongs in the current task, not in either document. +## 1. Name the intent -## Choose the authority +- **Greenfield:** no coherent visual implementation. +- **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. -Classify the work before inventing anything: +A plain “redesign this page/site” authorizes replacement. “Redesign this within the current brand/system” means extension or refinement. Ask once only when the wording is genuinely ambiguous. -- **Extend**: an incumbent system exists and the task adds or improves a surface. Preserve its lineage, semantics, and affordances. -- **Surface redesign**: the current identity remains, but this surface may change its topology, hierarchy, and expression. -- **New identity**: nothing committed exists, or the user explicitly authorized a rebrand. +## 2. Resolve visual authority -When the evidence is mixed, preserve. Novelty is not permission to erase authored decisions. +Read DESIGN.md and representative code, tokens, components, and assets. Choose one path: -## Find the concept +### A. Explicit redesign -Name the subject, audience, page job, and visitor mode from SKILL.md. Then identify the product's unique mechanism: what it does, proves, or enables that a neighboring product cannot truthfully claim. +The old DESIGN.md and implementation are not authority. Keep only unrevoked product facts, content, function, native expectations, constraints, and brand commitments. Establish a replacement world. -Derive the concept from that mechanism through one primary transformation: information topology, evidence, temporal behavior, or spatial mapping. The transformation must change how the surface is organized or behaves, not merely how it is decorated. This produces stranger and more defensible work than imitating a culturally specific document or object. Borrowing a literal cultural form is allowed only when it is genuinely native to the product and audience, and never as costume. +### B. DESIGN.md covers this kind of surface -For **Operate** and **Read**, start with the task and information structure. Native controls remain native controls; expression comes from hierarchy, density, rhythm, state, and the system around them. For **Persuade** and **Experience** creating a new identity, explore more freely, but keep the product legible and the primary action obvious. +Use its invariants and normative tokens. Skip world-building and discover the surface. -Run `node .rovodev/skills/impeccable/scripts/concept-seed.mjs` only for Persuade or Experience new-identity work when two or more directions remain equally credible. It is an entropy source, not an authority: never let the draw overrule the strongest fit. When color is genuinely unconstrained, `node .rovodev/skills/impeccable/scripts/palette.mjs` may break a reflex palette; subject evidence and incumbent color still win. +### C. A coherent implementation exists but DESIGN.md does not -## State the direction +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. -Before code, write a task-scoped direction contract of at most 120 words in your reasoning: +### D. The brand exists, but a whole-surface family is unresolved -- the thesis this surface owns; -- the structural or behavioral transformation; -- what authority is preserved; -- the first viewport's composition and primary action; -- the signature element the surface will be remembered by. +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. -Do not embed this contract as a comment in the production artifact or turn it into a second design system. In an attended substantial build, confirm it once. In an unattended run, record it and continue. +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. -## Build once, commit fully +### E. No confirmed visual authority exists -Plan one compact token system and one layout source: the concept or the content's real structure. Build the strongest coherent direction once. Commitment means the transformation governs the whole surface; it does not mean every control must be rebuilt as a metaphor. +Establish a world. Scaffolds, framework defaults, and stray utilities are not identity. -Make the first viewport demonstrate the product's mechanism. Pace long surfaces through contrast in density and scale, and cut sections that only repeat claims. Briefs that depend on imagery ship real, verified imagery. Preserve semantic HTML, familiar interaction behavior, accessibility, and the project's technical conventions. +## 3. Discover the requested surface -## Finish proportionally +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. -Inspect desktop and mobile, write one honest critique against the brief and direction, patch material defects, then run the detector once. Repeat only when a real defect remains. A separate reviewer is optional when the harness already provides one and the extra cost is earned by the risk; it is not a default step and its presence is not evidence of quality. +Ask one attended round of at most three material questions without repeating durable facts. CTA hierarchy, proof sequence, content gaps, and interaction outcomes belong here, not PRODUCT.md. For a fully specified narrow request, state the interpretation and invite correction. + +When `shape` has already completed its discovery interview, reuse those confirmed answers and do not ask this round again. + +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. Each candidate joins a durable visual system (identity thesis, information/layout grammar, material and type behavior, color strategy, imagery, motion, reusable signature) to a concrete expression for this surface (topology, sequence, focal moment, and implementation consequence). Do not rank yet. +3. **Break the ranking rut once.** Run `node .rovodev/skills/impeccable/scripts/concept-seed.mjs --scope direction`. Promote the assigned grounded pair and weigh its challengers only when they can become both a coherent system and a strong solution to this task. +4. **Test coupling and breadth.** Reject a pair if its surface could swap into another candidate unchanged, or if its world is merely the surface motif repeated. Test each survivor on the requested surface plus navigation, quiet and dense content, interaction/state, and one unlike future surface. Compare it with both the category habit and predictable contrarian response. +5. **Offer coupled choices.** Present two or three materially different pairs without recommendation cues. For each, show the world rules, this surface's 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. 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 .rovodev/skills/impeccable/scripts/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 promoted candidate when a roll ran; otherwise use the strongest grounded structure. It must survive both tests. + +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). + +### Write or update DESIGN.md + +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: + +`` + +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 .rovodev/skills/impeccable/scripts/surface-brief.mjs read ` + +Exit 0: preserve still-valid decisions and change only what the user changed. For redesign, retain valid product strategy, content, function, and open decisions; replace visual direction and contract. Exit 2: no brief. After the contract, write with `node .rovodev/skills/impeccable/scripts/surface-brief.mjs write [related-target ...]`. + +The body is concise and contains: + +```markdown +# Surface brief: [name] + +## Scope +[Primary/related route or artifact, visitor mode, and what this surface owns.] + +## Product strategy +[Surface-specific audience and job, desired outcome, primary/secondary action or task, content and proof sequence, factual constraints, and explicitly open decisions.] + +## Selected direction +[Reference to the applicable DESIGN.md world or expression range, selected surface concept, focal moment, narrative/interaction sequence, and implementation consequence.] + +## Direction contract +[The six contract blocks below.] + +## Open decisions +[Only unresolved items that later work must not silently invent. Omit when empty.] +``` + +Commit `.impeccable/surfaces/.md` as stable later-work context. Exclude global truth, exact tokens, transient notes, and work logs. + +## 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. + +Before code, write a direction contract of at most 150 words in an opening HTML or framework comment. The first 200 characters must name `DIRECTION CONTRACT`. + +- `UNIQUE`: the task thesis tied to the product mechanism; +- `NOT-TEMPLATE`: the category-default arrangement this structure refuses; +- `OWN-WORLD`: the confirmed DESIGN.md invariants, tokens, and materials it uses; +- `STORY`: what the visitor understands, believes, and does; +- `FIRST VIEWPORT`: exact composition, hierarchy, action, and where the concept exceeds competent convention; +- `FORM`: chosen structure or behavior, signature, implementation consequence, and seed key. + +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. + +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. + +## 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. + +Build the strongest coherent direction once. Its grammar governs navigation, actions, controls, content, and transitions without disguising affordances. Give the focal form the scale that gives it force; do not trap it inside a standard hero panel. + +**Make the opening a thesis.** Demonstrate the mechanism immediately; leave an idea, interaction, or evidence, not merely mood. + +**Commit before correcting.** Land the hard move at full strength before refining it. In unattended work, safety is the known risk. + +**Commit at page scale.** Let color, material, image, or type own a region when the world calls for it. Scattered signature decoration is not commitment. + +**Pace the whole surface.** Vary density, scale, image, motion, and quiet inside one grammar. Cut repeated claims; prove the mechanism with real artifact, interaction, data, transformation, or content. + +**Author motion as material.** Motion expresses the world and task. Premium moments go beyond repeated transform/opacity through earned focus, depth, masks, continuity, light, or material change. Bound expensive effects, test in-browser, keep content visible by default, and design reduced motion. + +Preserve semantics, affordances, accessibility, performance, responsiveness, and project conventions. Operate/Read express through topology, hierarchy, density, rhythm, and state; Persuade/Experience may earn drama. + +## 8. Solidify the visual record + +After first implementation of a new/replacement world or approved expansion, refresh DESIGN.md from the build: + +- replace provisional direction with the exact type, color roles, tokens, spacing/radii, components, states, and motion that survived; +- add normative YAML tokens only for values the implementation actually uses; +- remove the directional-seed status once the record and implementation agree; +- preserve broader world invariants and expression ranges; +- do not promote the task's story, hero composition, or one-off motif into a global rule unless it is intentionally reusable. + +Ordinary extension does not rewrite DESIGN.md; only approved durable changes do. + +## 9. Finish like a 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. diff --git a/.rovodev/skills/impeccable/reference/operate.md b/.rovodev/skills/impeccable/reference/operate.md index 04ee9d7d2..f420cbaa2 100644 --- a/.rovodev/skills/impeccable/reference/operate.md +++ b/.rovodev/skills/impeccable/reference/operate.md @@ -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. - No orchestrated page-load sequences. Product loads into a task; users don't want to watch it load. -## Product bans (on top of the shared absolute bans) +## Product constraints - Decorative motion that doesn't convey state. - Inconsistent component vocabulary across screens. If the "save" button looks different in two places, one is wrong. @@ -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. diff --git a/.rovodev/skills/impeccable/reference/polish.md b/.rovodev/skills/impeccable/reference/polish.md index 9b7f6af2c..6e3000283 100644 --- a/.rovodev/skills/impeccable/reference/polish.md +++ b/.rovodev/skills/impeccable/reference/polish.md @@ -1,241 +1,98 @@ -> **Additional context needed**: quality bar (MVP vs flagship). +> **Additional context needed**: quality bar and shipping constraints. -Perform a meticulous final pass to catch all the small details that separate good work from great work. The difference between shipped and polished. +Polish is refinement, never concealed redesign. Preserve the incumbent visual world, content, behavior, and everything outside scope. If the concept itself is wrong, say so and recommend redesign or `bolder` instead of smuggling in a replacement. -Detector and automated QA output are defect evidence only. A clean script result is never proof that the design is strong; gather browser evidence and inspect the real interaction path. +A detector result is defect evidence, not proof of quality. Inspect the rendered experience and real interaction path. -## Design System Discovery +## 1. Establish the system -Aligning the feature to the design system is **not optional**. Polish without alignment is decoration on top of drift, and it makes the next person's job harder. Discovery comes before any other polish work. +Read DESIGN.md and representative tokens, shared components, patterns, and neighboring flows. If no formal system exists, use coherent project conventions. -1. **Find the design system**: Search for design system documentation, component libraries, style guides, or token definitions. Study the core patterns: design principles, target audience, color tokens, spacing scale, typography styles, component API, motion conventions. -2. **Note the conventions**: How are shared components imported? What spacing scale is used? Which colors come from tokens vs hard-coded values? What motion and interaction patterns are established? What flow shapes are used for comparable actions (modal vs full-page, inline vs route, save-on-blur vs explicit submit)? -3. **Identify drift, then name the root cause**: For every deviation, classify it as a **missing token** (the value should exist in the system but doesn't), a **one-off implementation** (a shared component already exists but wasn't used), or a **conceptual misalignment** (the feature's flow, IA, or hierarchy doesn't match neighboring features). The fix differs by category: patch the value, swap to the shared component, or rework the flow. Fixing the symptom without naming the cause is how drift compounds. +Classify each drift before fixing it: -If a design system exists, polish **must** align the feature with it. If none exists, polish against the conventions visible in the codebase. **If anything about the system is ambiguous, ask. Never guess at design system principles.** +- **missing token:** the system needs a reusable value; +- **one-off implementation:** an existing shared component or pattern should replace it; +- **conceptual mismatch:** the flow, information architecture, or hierarchy differs from comparable product areas; +- **local defect:** the implementation is simply incomplete or inconsistent. -## Pre-Polish Assessment +Fix the cause at the narrowest correct level. Ask when a binding system principle cannot be inferred. -Understand the current state and goals before touching anything: +## 2. Gather the evidence -1. **Review completeness**: - - Is it functionally complete? - - Are there known issues to preserve (mark with TODOs)? - - What's the quality bar? (MVP vs flagship feature?) - - When does it ship? (How much time for polish?) +Use the feature yourself at representative desktop and mobile sizes. Determine: -2. **Think experience-first**: Who actually uses this, and what's the best possible experience for them? Effective design beats decorative polish; a feature that looks beautiful but fights the user's flow is not polished. Walk the path from their perspective before opening DevTools. +- whether the path is functionally complete; +- the intended quality bar and time available; +- known constraints or deliberately unfinished work; +- the states, content lengths, roles, and input methods users will actually encounter. -3. **Identify polish areas**: - - Visual inconsistencies - - Spacing and alignment issues - - Interaction state gaps - - Copy inconsistencies - - Edge cases and error states - - Loading and transition smoothness - - Information architecture and flow drift (does this feature reveal complexity the way neighboring features do?) +If a prior critique exists, use it as one input: -4. **Pull in any prior critique** (optional signal): If `/impeccable critique` has been run on the same target, its priority issues are a useful prior for what to address first. Resolve the target to a file path or URL, then: - ```bash - slug=$(node .rovodev/skills/impeccable/scripts/critique-storage.mjs slug "") - node .rovodev/skills/impeccable/scripts/critique-storage.mjs latest "$slug" - ``` - Exit 0 with body = found; fold the P0/P1 items into your polish list and mention the snapshot path so the user sees what you read. Exit 2 = no snapshot, continue without it. The critique is one input among many. Do your own pass either way. +```bash +slug=$(node .rovodev/skills/impeccable/scripts/critique-storage.mjs slug "") +node .rovodev/skills/impeccable/scripts/critique-storage.mjs latest "$slug" +``` -5. **Triage cosmetic vs functional**: Classify each issue as **cosmetic** (looks off, doesn't impede the user) or **functional** (breaks, blocks, or confuses the experience). When polish time is tight, functional issues ship first; cosmetic ones can land in a follow-up. Quality should be consistent; never perfect one corner while leaving another rough. +Exit 0 returns the latest snapshot; incorporate relevant P0/P1 findings and name the snapshot read. Exit 2 means none exists. Perform an independent pass either way. -**CRITICAL**: Polish is the last step, not the first. Don't polish work that's not functionally complete. +## 3. Triage -## Polish Systematically +Separate functional defects from cosmetic ones and fix in this order: -Work through these dimensions methodically: +1. broken or blocked tasks, data loss, misleading state, and inaccessible paths; +2. missing loading, empty, error, success, disabled, and permission states; +3. flow, hierarchy, responsive, and design-system drift; +4. visual and motion inconsistencies; +5. code and asset cleanup. -### Visual Alignment & Spacing +Do not perfect one corner while leaving the rest below the same quality bar. -- **Pixel-perfect alignment**: Everything lines up to grid -- **Consistent spacing**: All gaps use spacing scale (no random 13px gaps) -- **Optical alignment**: Adjust for visual weight (icons may need offset for optical centering) -- **Responsive consistency**: Spacing and alignment work at all breakpoints -- **Grid adherence**: Elements snap to baseline grid +## 4. Polish the whole path -**Check**: -- Enable grid overlay and verify alignment -- Check spacing with browser inspector -- Test at multiple viewport sizes -- Look for elements that "feel" off +### Flow and hierarchy -### Information Architecture & Flow +- Match neighboring mental models, terminology, disclosure, routing, save behavior, and optimistic or pessimistic patterns. +- Make the primary task and current state obvious without flattening every element to equal weight. +- Ensure arrival, transition, empty, and recovery paths connect instead of behaving as isolated screens. -Visual polish on a misshapen flow is wasted work. Match the *shape* of the experience to the system, not just the surface. +### Layout and type -- **Progressive disclosure**: Match how much is revealed when, compared to neighboring features. A settings page exposing 40 fields when the rest of the app reveals 5 at a time is drift, even if every field is perfectly styled. -- **Established user flows**: Multi-step actions follow the same shape as comparable flows elsewhere: modal vs full-page, inline edit vs separate route, save-on-blur vs explicit submit, optimistic vs pessimistic updates. -- **Hierarchy & complexity**: The same conceptual weight gets the same visual weight throughout. Primary actions don't become tertiary in one corner of the product, and tertiary actions don't shout. -- **Empty, loading, and arrival transitions**: How content arrives, updates, and leaves matches how it does in adjacent features. -- **Naming and mental model**: The feature uses the same nouns and verbs as the rest of the system. A "Workspace" here shouldn't be a "Project" three screens away. +- Align to the project's grid and spacing scale; fix optical as well as mathematical alignment. +- Group related content tightly and separate distinct groups generously. +- Keep same-role typography consistent; test measure, wrapping, localization expansion, zoom, and font loading. +- Verify every supported viewport rather than correcting only the current screenshot. -### Typography Refinement +### Color, imagery, and icons -- **Hierarchy consistency**: Same elements use same sizes/weights throughout -- **Line length**: 45-75 characters for body text -- **Line height**: Appropriate for font size and context -- **Widows & orphans**: No single words on last line -- **Hyphenation**: Appropriate for language and column width -- **Kerning**: Adjust letter spacing where needed (especially headlines) -- **Font loading**: No FOUT/FOIT flashes +- Use semantic tokens and stable color meanings across themes. +- Verify text, control, and focus contrast in every state. +- Keep icon families, stroke/weight, sizing, and optical alignment coherent. +- Prevent image layout shift; use correct aspect ratios, responsive sources, and useful alt text. -### Color & Contrast +### Interaction and state -- **Contrast ratios**: All text meets WCAG standards -- **Consistent token usage**: No hard-coded colors, all use design tokens -- **Theme consistency**: Works in all theme variants -- **Color meaning**: Same colors mean same things throughout -- **Accessible focus**: Focus indicators visible with sufficient contrast -- **Gray on color**: Never put gray text on colored backgrounds; use a shade of that color or transparency +- Every control needs appropriate default, hover, focus, active, disabled, loading, error, and success behavior. +- Preserve visible keyboard focus, logical tab order, labels, and platform-appropriate touch targets. +- Keep motion coherent, interruptible, performant, and reduced-motion aware. Do not add animation merely to make polish visible. +- Validate long, missing, localized, offline, slow, and permission-limited content where the product can encounter it. -### Interaction States +### Content and code -Every interactive element needs all states: +- Keep terminology, capitalization, punctuation, and factual copy consistent. Ask before changing claims. +- Remove debug output, dead code, unused imports, obsolete styles, and polish-created duplication. +- Replace custom implementations with shared components where the system owns the pattern. +- Promote genuinely reusable values to tokens; do not create a system abstraction for one local exception. -- **Default**: Resting state -- **Hover**: Subtle feedback (color, scale, shadow) -- **Focus**: Keyboard focus indicator (never remove without replacement) -- **Active**: Click/tap feedback -- **Disabled**: Clearly non-interactive -- **Loading**: Async action feedback -- **Error**: Validation or error state -- **Success**: Successful completion +## 5. Verify and finish -**Missing states create confusion and broken experiences**. +Walk the complete path again with mouse, keyboard, and touch where applicable. Check: -### Micro-interactions & Transitions +- mobile, intermediate, and wide layouts; +- loading, empty, error, success, disabled, long-content, and missing-content states; +- zoom, contrast, focus, semantics, screen-reader names, and reduced motion; +- console errors, layout shift, interaction latency, image loading, and supported browsers; +- agreement with DESIGN.md, neighboring features, and the user's scope. -- **Smooth transitions**: All state changes animated appropriately (150-300ms) -- **Consistent easing**: Use ease-out-quart/quint/expo for natural deceleration. Never bounce or elastic; they feel dated. -- **No jank**: Smooth animations; use atmospheric blur/filter/mask/shadow effects when they add polish, but bound expensive paint areas and avoid casual layout-property animation -- **Appropriate motion**: Motion serves purpose, not decoration -- **Reduced motion**: Respects `prefers-reduced-motion` +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. -### Content & Copy - -- **Consistent terminology**: Same things called same names throughout -- **Consistent capitalization**: Title Case vs Sentence case applied consistently -- **Grammar & spelling**: No typos -- **Appropriate length**: Not too wordy, not too terse -- **Punctuation consistency**: Periods on sentences, not on labels (unless all labels have them) - -### Icons & Images - -- **Consistent style**: All icons from same family or matching style -- **Appropriate sizing**: Icons sized consistently for context -- **Proper alignment**: Icons align with adjacent text optically -- **Alt text**: All images have descriptive alt text -- **Loading states**: Images don't cause layout shift, proper aspect ratios -- **Retina support**: 2x assets for high-DPI screens - -### Forms & Inputs - -- **Label consistency**: All inputs properly labeled -- **Required indicators**: Clear and consistent -- **Error messages**: Helpful and consistent -- **Tab order**: Logical keyboard navigation -- **Auto-focus**: Appropriate (don't overuse) -- **Validation timing**: Consistent (on blur vs on submit) - -### Edge Cases & Error States - -- **Loading states**: All async actions have loading feedback -- **Empty states**: Helpful empty states, not just blank space -- **Error states**: Clear error messages with recovery paths -- **Success states**: Confirmation of successful actions -- **Long content**: Handles very long names, descriptions, etc. -- **No content**: Handles missing data gracefully -- **Offline**: Appropriate offline handling (if applicable) - -### Responsiveness - -- **All breakpoints**: Test mobile, tablet, desktop -- **Touch targets**: 44x44px minimum on touch devices -- **Readable text**: No text smaller than 14px on mobile -- **No horizontal scroll**: Content fits viewport -- **Appropriate reflow**: Content adapts logically - -### Performance - -- **Fast initial load**: Optimize critical path -- **No layout shift**: Elements don't jump after load (CLS) -- **Smooth interactions**: No lag or jank -- **Optimized images**: Appropriate formats and sizes -- **Lazy loading**: Off-screen content loads lazily - -### Code Quality - -- **Remove console logs**: No debug logging in production -- **Remove commented code**: Clean up dead code -- **Remove unused imports**: Clean up unused dependencies -- **Consistent naming**: Variables and functions follow conventions -- **Type safety**: No TypeScript `any` or ignored errors -- **Accessibility**: Proper ARIA labels and semantic HTML - -## Polish Checklist - -Go through systematically: - -- [ ] Aligned to the design system (drift named and resolved by root cause) -- [ ] Information architecture and flow shape match neighboring features -- [ ] Visual alignment perfect at all breakpoints -- [ ] Spacing uses design tokens consistently -- [ ] Typography hierarchy consistent -- [ ] All interactive states implemented -- [ ] All transitions smooth (60fps) -- [ ] Copy is consistent and polished -- [ ] Icons are consistent and properly sized -- [ ] All forms properly labeled and validated -- [ ] Error states are helpful -- [ ] Loading states are clear -- [ ] Empty states are welcoming -- [ ] Touch targets are 44x44px minimum -- [ ] Contrast ratios meet WCAG AA -- [ ] Keyboard navigation works -- [ ] Focus indicators visible -- [ ] No console errors or warnings -- [ ] No layout shift on load -- [ ] Works in all supported browsers -- [ ] Respects reduced motion preference -- [ ] Code is clean (no TODOs, console.logs, commented code) - -**IMPORTANT**: Polish is about details. Zoom in. Squint at it. Use it yourself. The little things add up. - -Sweat the details. Zoom in until the alignment is right and the spacing reads as deliberate. Then ship. - -**NEVER**: -- Polish before it's functionally complete -- Polish without aligning to the design system; that's decoration on drift -- Guess at design system principles instead of asking when something is ambiguous -- Spend hours on polish if it ships in 30 minutes (triage) -- Introduce bugs while polishing (test thoroughly) -- Ignore systematic issues (if spacing is off everywhere, fix the system, not just one screen) -- Perfect one thing while leaving others rough (consistent quality level) -- Create new one-off components when design system equivalents exist -- Hard-code values that should use design tokens -- Introduce new patterns or flows that diverge from established ones - -## Final Verification - -Before marking as done: - -- **Use it yourself**: Actually interact with the feature. -- **Test on real devices**: Not just browser DevTools. -- **Ask someone else to review**: Fresh eyes catch things. -- **Compare to design**: Match intended design. -- **Check all states**: Don't just test happy path. -- **Treat automation carefully**: Run detector or QA commands when they are available and relevant, fix their defects, but never cite a clean result as proof that the work is polished. - -## Clean Up - -After polishing, ensure code quality: - -- **Replace custom implementations**: If the design system provides a component you reimplemented, switch to the shared version. -- **Remove orphaned code**: Delete unused styles, components, or files made obsolete by polish. -- **Consolidate tokens**: If you introduced new values, check whether they should be tokens. -- **Verify DRYness**: Look for duplication introduced during polishing and consolidate. +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. diff --git a/.rovodev/skills/impeccable/reference/quieter.md b/.rovodev/skills/impeccable/reference/quieter.md index 55b00b6f3..bb163e40f 100644 --- a/.rovodev/skills/impeccable/reference/quieter.md +++ b/.rovodev/skills/impeccable/reference/quieter.md @@ -2,7 +2,7 @@ Quiet design is harder than bold design. Subtlety needs precision. Reduce visual --- -## Register +## Visitor mode Persuade + Experience: "quieter" means more restrained palette, more whitespace, more typographic air. Drama is reduced, not eliminated; the POV stays intact. diff --git a/.rovodev/skills/impeccable/reference/routing.md b/.rovodev/skills/impeccable/reference/routing.md index b18eee90e..86debb2c7 100644 --- a/.rovodev/skills/impeccable/reference/routing.md +++ b/.rovodev/skills/impeccable/reference/routing.md @@ -11,7 +11,7 @@ Reason over the signals; there is no score to obey: - `critique.latest` with a low `score` or non-zero `p0` / `p1` → `polish` (it reads that snapshot as its backlog), or re-run `critique` if the snapshot looks stale. - `git.changedFiles` pointing at one surface → scope `audit` or `polish` to those files specifically, naming them. - `devServer.running` true → `live` is available for in-browser iteration; if false, don't lead with `live`. **`live` and the bundled `detect.mjs` are web-only.** If `setup.platform` is `ios`, `android`, or `adaptive`, don't lead with either; the browser overlay and the HTML rule engine don't apply to native app code. -- Otherwise group by intent exactly as init's "Recommend starting points" step does (build new / improve what's there / iterate visually), tailored to `setup.register`. +- Otherwise group by intent (build new / improve what's there / iterate visually), tailored to the current surface and `setup.platform`. **If `scan.targets` is non-empty and `setup.platform` is not `ios`/`android`/`adaptive`, run `node .rovodev/skills/impeccable/scripts/detect.mjs --json ` once** (the bundled detector over local files: no network, no npx; it reads HTML/CSS, so skip it for native projects). `scan.via` tells you what they are: `git-changes` (the markup/style files in your dirty tree, the most relevant set), `source-dir` (e.g. `src`, `app`), `html`, or `root`. Fold the hits into your picks: many quality / contrast hits → `audit` or `polish`; a specific slop family → the matching command (gradient text or eyebrows → `quieter` / `typeset`, flat or gray palette → `colorize`, and so on). It's a real, current signal that beats guessing. If detect errors or the tree is large and slow, skip it and recommend the user run `audit` themselves; never block the suggestion on it. diff --git a/.rovodev/skills/impeccable/reference/shape.md b/.rovodev/skills/impeccable/reference/shape.md index 40804f6e4..acbb26e5b 100644 --- a/.rovodev/skills/impeccable/reference/shape.md +++ b/.rovodev/skills/impeccable/reference/shape.md @@ -1,167 +1,61 @@ -Shape the UX and UI for a feature before any code is written. This command produces a **design brief**: a structured artifact that guides implementation through discovery, not guesswork. +# Shape -**Scope**: Design planning only. This command does NOT write code. It produces the thinking that makes code good. +Discover what should be made and how it should work, then return a confirmed design brief without code. -**Output**: A design brief that can be handed off to /impeccable craft, or directly to /impeccable for freeform implementation. When visual direction probes are used, the images are supporting artifacts, not the primary output. +**Product gate:** when context reports that PRODUCT.md is missing, load and complete [init.md](init.md) before Phase 1. Do not substitute shape questions for the product interview. Once PRODUCT.md exists, return here; product context does not replace task discovery. -## Philosophy +## Phase 1: Discovery interview -Most AI-generated UIs fail not because of bad code, but because of skipped thinking. They jump to "here's a card grid" without asking "what is the user trying to accomplish?" This command inverts that: understand deeply first, so implementation is precise. +Do not write code or choose visual direction yet. -## Phase 1: Discovery Interview +### Cadence -**Do NOT write any code or make any design decisions during this phase.** Your only job is to understand the feature deeply enough to make excellent design decisions later. +- Use the structured question tool when available; otherwise ask and stop. +- Ask two or three related questions per round, then wait. One round is the default; add a second only when the answers expose a material gap. +- Do not dump a questionnaire, repeat settled facts, or turn obvious facts into menus. Assert the likely reading and invite correction. +- A sparse prompt requires at least one answer round. A precise prompt may need only a compact confirmation. -**Unattended runs:** when no user can respond (a one-shot task, an automated run, or an explicit instruction not to ask questions), answer the interview questions yourself from the brief, record the answers as the design brief, and continue without pausing at any confirmation gate in this file. The thinking is still required; only the waiting is waived. +### Round 1: purpose, people, and outcome -This is a required interaction, not optional guidance. Ask these questions in conversation, adapting based on answers. Don't dump them all at once; have a natural dialogue. ask the user directly to clarify what you cannot infer. +Choose the two or three questions that most change the result: -### Interview cadence +- What is this surface or feature for, and what problem must it solve? +- Who specifically reaches it, in what situation and state of mind? +- What is the primary thing they must understand or do? What would success look like? +- What is uniquely true here that a neighboring product or generic template could not claim? -Discovery includes at least one user-answer round unless PRODUCT.md, DESIGN.md, or an already-confirmed brief directly answers the needed inputs. With a sparse prompt, do **not** synthesize a complete brief for confirmation on the first response. +### Round 2: material, behavior, and boundaries -- Use the harness's structured question tool when one exists. Otherwise, ask directly in chat and stop. -- Ask **2-3 questions per round**, then wait for answers. -- Treat PRODUCT.md and DESIGN.md as anchors; they reduce repeated questions but do **not** replace shape for craft. Shape is task-specific. -- One round is the default. Add a second only if the first answers leave material gaps. Don't run a second round just to feel thorough. -- Round 1 should clarify purpose, audience/context, content/scope, and (for brand) visual direction. -- Round 2, when needed, fills in whatever's still genuinely missing. +Run only for material unresolved decisions: -**Assert-then-confirm, not menu-with-escape.** When PRODUCT.md and the user's prompt make one option obvious, name it and ask the user to confirm or override. Don't enumerate "Restrained / Committed / Or something else?" as a real choice; "This reads as Restrained, confirm?" beats a four-option menu when the answer is already clear. +- What real content, evidence, data, and assets must the experience carry? What are realistic minimum, typical, and maximum ranges? +- Which states and transitions matter: first-run, empty, loading, error, success, permissions, overflow, or expert use? +- What is the intended fidelity, breadth, and interactivity: exploration, production-ready screen, full flow, or broader surface? +- What must remain untouched? What would make the result feel wrong even if it looked polished? +- Which platform, framework, performance, accessibility, localization, or delivery constraints are binding? -### Purpose & Context -- What is this feature for? What problem does it solve? -- Who specifically will use it? (Not "users"; be specific: role, context, frequency) -- What does success look like? How will you know this feature is working? -- What's the user's state of mind when they reach this feature? (Rushed? Exploring? Anxious? Focused?) +Never ask for CSS values or canned aesthetic lanes. New-work owns visual-world and concept choices. -### Content & Data -- What content or data does this feature display or collect? -- What are the realistic ranges? (Minimum, typical, maximum, e.g., 0 items, 5 items, 500 items) -- What are the edge cases? (Empty state, error state, first-time use, power user) -- Is any content dynamic? What changes and how often? -- What visual assets are real content here? Note required images, product shots, illustrations, maps, textures, diagrams, generated objects, or existing project assets. +## Phase 2: Resolve the design direction -### Design Direction +For new surfaces, brand expansion, or replacement, follow [new-work.md](new-work.md) through visual authority, any world workshop, and concept choice. Reuse discovery, then return before its contract, persistence, or implementation. Inside an established world, use its concept process only when composition or interaction remains materially open. -Force a visual decision on three fronts. Skip anything PRODUCT.md or DESIGN.md already answers; ask only what's missing. +## Phase 3: Write the brief -- **Color strategy for this surface.** Pick one: Restrained / Committed / Full palette / Drenched. Can override the project default if the surface earns it (e.g. a drenched hero inside an otherwise Restrained product). -- **Theme via scene sentence.** Write one sentence of physical context for this surface: who uses it, where, under what ambient light, in what mood. The sentence forces dark vs light. If it doesn't, add detail until it does. -- **Two or three named anchor references.** Specific products, brands, objects. Not adjectives like "modern" or "clean." +Write the smallest useful brief: -### Scope +1. **Job and audience:** who arrives, their context, need, and visitor mode. +2. **Outcome and proof:** primary task/action, success, real evidence, and product-specific truth. +3. **Selected direction:** visual authority, structural/interaction thesis, sequence, focal moment, and implementation consequence. +4. **Scope and boundaries:** fidelity, breadth, interactivity, named target, what remains untouched, and explicit anti-goals. +5. **States and ranges:** realistic content/data ranges and material states. +6. **Interaction and layout:** hierarchy, topology, responsiveness, affordances, feedback, and transitions; intent, not CSS. +7. **Constraints and open decisions:** platform, delivery, accessibility, localization, reusable components, and choices a builder must not invent. -Always ask. Sketch quality and shipped quality are different outputs; don't guess between them. +Use three to five bullets when the task is settled; use the full structure only for ambiguous, multi-screen, or standalone planning. Do not restate the conversation. -- **Fidelity.** Sketch / mid-fi / high-fi / production-ready? -- **Breadth.** One screen / a flow / a whole surface? -- **Interactivity.** Static visual / interactive prototype / shipped-quality component? -- **Time intent.** Quick exploration, or polish until it ships? +## Confirm and stop -Scope answers are task-scoped. Don't write them to PRODUCT.md or DESIGN.md; carry them through the design brief only. +Present the brief for explicit confirmation or one correction round, then stop: shape never writes code or a direction contract. -### Constraints -- Are there technical constraints? (Framework, performance budget, browser support) -- Are there content constraints? (Localization, dynamic text length, user-generated content) -- Mobile/responsive requirements? -- Accessibility requirements beyond WCAG AA? - -### Anti-Goals -- What should this NOT be? What would be a wrong direction? -- What's the biggest risk of getting this wrong? - -## Phase 1.5: Visual Direction Probe (Capability-Gated) - -After the discovery interview, generate a small set of visual direction probes **before** writing the final brief when all of these are true: - -- The work is **net-new** or directionally ambiguous enough that visual exploration will clarify the brief. -- The requested fidelity is **mid-fi, high-fi, or production-ready**. Skip for sketch-only planning. -- The current harness gives you native image generation (Codex's `image_gen`, an equivalent MCP tool, or similar). Don't ask the user to install APIs or tooling. - -When those conditions are met, this step is mandatory. If image generation isn't natively available, do not ask the user to install APIs or tooling. State in one line that the image step is skipped because the harness lacks native image generation, then proceed. The one-line announcement is required, not optional; it forces a conscious decision instead of letting the step quietly evaporate. - -Use probes to explore visual lanes, not to replace the brief. - -Do not skip probes because the final UI will be semantic, editable, code-native, responsive, or accessible. Those are implementation requirements, not reasons to avoid visual exploration. - -### What to generate - -Generate **2 to 4** distinct direction probes based on the discovery answers, especially: - -- Color strategy -- Theme scene sentence -- Named anchor references -- Scope and fidelity - -The probes should differ in primary visual direction (hierarchy, topology, density, typographic voice, or color strategy), not just palette tweaks. - -### How to use the probes - -- Treat them as **direction tests**, not final designs. -- Use them to pressure-test whether the brief is pointing at the right lane. -- Ask the user which direction feels closest, what feels off, and what should carry forward. -- If the probes reveal a mismatch, revise the brief inputs before finalizing the brief. - -### Important limits - -- Do **not** skip discovery because image generation is available. -- Do **not** treat generated imagery as final UX specification, final copy, or final accessibility behavior. -- Do **not** use this step for minor refinements of existing work. It's for shaping a new surface or clarifying a big directional choice. - -If image generation isn't natively available, announce the skip in one line and proceed to the design brief. - -## Phase 2: Design Brief - -After the interview and any required probes, present a brief and **end your response**. The user must confirm before any implementation runs. Do not present a brief and then continue to code in the same response, even if the brief feels obvious to you. The user's confirmation is the gate. - -**Choose the brief shape based on how clear the answers are:** - -- **Compact form (3-5 bullets)** when discovery was crisp and the original prompt + PRODUCT.md already pinned scope, content, and direction. State what you're building, the visual lane, and end with one or two specific questions or a clear "confirm or override?" prompt. This is the default for typical craft requests with a clear prompt. -- **Full structured form (sections below)** when the task is genuinely ambiguous, multi-screen, or when the user asked for shape as a standalone step. Use this when the discipline of structure earns its weight. - -Don't pad a clear brief into a long one to look thorough. A 70-line brief restating answers the user just gave is noise, not rigor. Equally, don't skip the confirmation pause to look efficient: the pause is the point. - -Present the brief, then **stop and wait for explicit confirmation**. You are not the judge of whether the user already approved. Even when the brief feels obviously right, ask once and wait. The pause is what separates shape from premature implementation. - -### Brief Structure - -**1. Feature Summary** (2-3 sentences) -What this is, who it's for, what it needs to accomplish. - -**2. Primary User Action** -The single most important thing a user should do or understand here. - -**3. Design Direction** -Color strategy (Restrained / Committed / Full palette / Drenched) + the theme scene sentence + 2–3 named anchor references. Reference PRODUCT.md and DESIGN.md where they already answer, and note any per-surface overrides. - -If you ran the Visual Direction Probe step, name which probe direction won and what changed in the brief because of it. - -**4. Scope** -Fidelity, breadth, interactivity, and time intent from the Scope section of the interview. Task-scoped; these don't persist beyond the brief. - -**5. Layout Strategy** -High-level spatial approach: what gets emphasis, what's secondary, how information flows. Describe the visual hierarchy and rhythm, not specific CSS. - -**6. Key States** -List every state the feature needs: default, empty, loading, error, success, edge cases. For each, note what the user needs to see and feel. - -**7. Interaction Model** -How users interact with this feature. What happens on click, hover, scroll? What feedback do they get? What's the flow from entry to completion? - -**8. Content Requirements** -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., 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. - ---- - -ask the user directly to clarify what you cannot infer. Ask for explicit confirmation of the brief before finishing. - -If the user disagrees with any part, revisit the relevant discovery questions. A shape run is incomplete until the user confirms direction. - -Once confirmed, the brief is complete. The user can now hand it to /impeccable, or use it to guide any other implementation approach. (If the user wants the full discovery-then-build flow in one step, they should use /impeccable craft instead, which runs this command internally.) +When no human or structured answer mechanism exists, mark assumptions plainly, return the brief, and stop. diff --git a/.rovodev/skills/impeccable/reference/typeset.md b/.rovodev/skills/impeccable/reference/typeset.md index e94632159..fe71b4696 100644 --- a/.rovodev/skills/impeccable/reference/typeset.md +++ b/.rovodev/skills/impeccable/reference/typeset.md @@ -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. --- -## Register +## Visitor mode -New identity work: run the font selection procedure in [new-work.md](new-work.md). Fluid `clamp()` scale, ≥1.25 ratio between steps. +- **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, register, **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 .rovodev/skills/impeccable/scripts/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 `/impeccable polish` for the final pass. +When the hierarchy holds, hand off to `/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) * )` 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 - -The tactical selection procedure and the reflex-reject list live in [new-work.md](new-work.md) (loaded for new identity work). 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 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. diff --git a/.rovodev/skills/impeccable/scripts/command-metadata.json b/.rovodev/skills/impeccable/scripts/command-metadata.json index 83796d54b..dad8ef2e0 100644 --- a/.rovodev/skills/impeccable/scripts/command-metadata.json +++ b/.rovodev/skills/impeccable/scripts/command-metadata.json @@ -1,6 +1,6 @@ { "craft": { - "description": "Full confirmed-brief-then-build flow. Runs multi-round shape discovery first, resolves visual probe and north-star mock gates when available, then builds and visually iterates. Use when building a new feature end-to-end.", + "description": "Deprecated compatibility alias for an ordinary Impeccable new-work request. It adds no behavior; natural build and redesign requests use the same flow.", "argumentHint": "[feature description]" }, "init": { diff --git a/.rovodev/skills/impeccable/scripts/concept-seed.mjs b/.rovodev/skills/impeccable/scripts/concept-seed.mjs index 1f141a80f..25f20d9ab 100644 --- a/.rovodev/skills/impeccable/scripts/concept-seed.mjs +++ b/.rovodev/skills/impeccable/scripts/concept-seed.mjs @@ -1,6 +1,7 @@ #!/usr/bin/env node /** - * Concept-seed picker: the dice half of the new-work concept procedure. + * External concept seed: the dice half of new-work's coupled-direction and + * established-world surface procedures. * * The model derives a grounded shortlist of candidate FORMS from the * audience's world and the subject's cultural home (see @@ -12,10 +13,9 @@ * * This script rolls them from outside, the same trick that made the * palette seed work: - * - BUILD INDEX (2-5): which entry of the model's own resonance-ordered - * shortlist to build. The dice never choose an ungrounded ingredient; - * they only refuse the argmax rut. (Index 1 is excluded: that's the - * concept every run would ship anyway.) + * - PROMOTED INDEX: which entry of the model's own resonance-ordered + * shortlist must be taken seriously beside its favorites. The dice never + * choose an ungrounded ingredient; they only refuse the argmax rut. * - CHALLENGERS (3): outside forms from concept-ingredients.json, weighed * against the derived candidates on exactly two axes — audience * identification and product clarity. They win only when they beat the @@ -23,8 +23,8 @@ * material and win over thin categories, which is the intended shape. * * Usage: - * node scripts/concept-seed.mjs # roll at random - * node scripts/concept-seed.mjs --from # deterministic (hash key) + * node scripts/concept-seed.mjs --scope direction + * node scripts/concept-seed.mjs --scope surface --from * * Env vars: * IMPECCABLE_CONCEPT_SEED — same as --from; for reproducible eval runs. @@ -40,6 +40,12 @@ const pool = JSON.parse(readFileSync(join(here, 'concept-ingredients.json'), 'ut const args = process.argv.slice(2); const fromIdx = args.indexOf('--from'); +const scopeIdx = args.indexOf('--scope'); +const scope = scopeIdx !== -1 ? args[scopeIdx + 1] : 'surface'; +if (scope !== 'surface' && scope !== 'direction') { + process.stderr.write('concept-seed: --scope must be direction or surface\n'); + process.exit(1); +} // When no key is supplied, generate one and print it: a user reporting a // bad outcome can hand us the key and we replay the exact roll. const key = fromIdx !== -1 @@ -47,7 +53,7 @@ const key = fromIdx !== -1 : (process.env.IMPECCABLE_CONCEPT_SEED || crypto.randomBytes(4).toString('hex')); function hashUnit(k, salt) { - const h = crypto.createHash('sha256').update(`${salt}:${k}`).digest(); + const h = crypto.createHash('sha256').update(`${scope}:${salt}:${k}`).digest(); return h.readUInt32BE(0) / 0xffffffff; } const unit = (salt) => hashUnit(key, salt); @@ -67,22 +73,48 @@ for (let i = 0; picks.length < 3 && i < 60; i++) { } } -process.stdout.write(`CONCEPT SEED (key: ${key}; rerun with --from ${key} to reproduce this roll) -BUILD INDEX: ${buildIndex} - After ordering your derived candidates by resonance, build the page whose - form comes from candidate number ${buildIndex}, exactly as if it had ranked - first: full commitment. Your top-ranked candidate is what every run in this - category would ship; the assignment exists to refuse that rut, not to - punish it. -CHALLENGERS (weigh against your derived candidates on the same two axes, -audience identification and product clarity; a challenger wins only when -it beats the grounded list on both): +const promotedInstruction = scope === 'direction' + ? `After ordering the grounded coupled directions by product fit, promote + candidate ${buildIndex} into the serious shortlist. Each candidate must join + a durable visual system to a concrete expression for the requested first + surface; select or revise that pair as one decision. It must survive the + current task plus navigation, quiet and dense content, interaction and state, + and a substantially different future surface.` + : `After ordering the task's grounded structural candidates by resonance, + promote candidate ${buildIndex} into the serious shortlist. In an attended + run, present it beside the strongest materially different candidates and + let the user select or revise the surface concept. In a truly unattended + run, use it when it survives audience identification and product clarity.`; + +const challengerInstruction = scope === 'direction' + ? `A challenger enters only when it can supply both reusable identity grammar + and a strong first-surface structure, not a one-page costume or a style pasted + onto an unrelated layout. Weigh audience identification, product clarity, + current-surface force, and cross-surface system breadth.` + : `A challenger wins only when it beats the grounded list on both audience + identification and product clarity. It may change task topology or + interaction, but never the committed visual identity.`; + +const authorityInstruction = scope === 'direction' + ? `PRODUCT.md and explicit incumbent brand commitments constrain every coupled +direction. The seed never chooses exact colors, fonts, tokens, or a user +preference, and it never permits the world and first surface to be selected +independently.` + : `PRODUCT.md and DESIGN.md constrain every surface candidate's identity +vocabulary; they do not cancel task-level composition. The seed never +authorizes a new palette, type system, material world, or unfamiliar control +behavior.`; + +process.stdout.write(`${scope.toUpperCase()} CONCEPT SEED (key: ${key}; rerun with --scope ${scope} --from ${key} to reproduce this roll) +PROMOTED INDEX: ${buildIndex} + ${promotedInstruction} + The promotion exists to refuse the model's ranking rut, not to outrank the + user or the brief. +CHALLENGERS: 1. ${picks[0]} 2. ${picks[1]} 3. ${picks[2]} -If a challenger wins, it replaces the assigned candidate. If the surface is -an existing world whose incumbent carries a deliberate, ownable idea, the -incumbent IS the chosen candidate: intensify its lineage and ignore the -roll entirely. The same override applies when the user, PRODUCT.md, or -DESIGN.md pins a direction: pinned direction beats the roll, always. +${challengerInstruction} +${authorityInstruction} +A user- or brief-pinned decision beats the roll, always. `); diff --git a/.rovodev/skills/impeccable/scripts/context-signals.mjs b/.rovodev/skills/impeccable/scripts/context-signals.mjs index 2fc27bea7..d296320d7 100644 --- a/.rovodev/skills/impeccable/scripts/context-signals.mjs +++ b/.rovodev/skills/impeccable/scripts/context-signals.mjs @@ -11,7 +11,7 @@ * output is always valid JSON. * * Signals: - * - setup: PRODUCT.md / DESIGN.md presence, register, whether code exists + * - setup: PRODUCT.md / DESIGN.md presence and whether code exists * - critique: the latest cached critique score (.impeccable/critique) * - git: branch + files changed vs the default branch (a scope hint) * - devServer: whether a local dev server answers on a common port (gates live) @@ -21,7 +21,7 @@ import net from 'node:net'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { execFileSync } from 'node:child_process'; -import { loadContext, extractRegister, extractPlatform } from './context.mjs'; +import { loadContext, extractPlatform } from './context.mjs'; import { getCritiqueDir } from './lib/impeccable-paths.mjs'; /** Is there code here at all, or just context files / an empty repo? */ @@ -196,7 +196,6 @@ export async function gatherSignals(cwd = process.cwd()) { hasDesign: ctx.hasDesign, designPath: ctx.designPath, hasCode: hasCode(cwd), - register: extractRegister(ctx.product), platform: extractPlatform(ctx.product), }, critique: { latest: latestCritique(cwd) }, diff --git a/.rovodev/skills/impeccable/scripts/context.mjs b/.rovodev/skills/impeccable/scripts/context.mjs index 2ef2d4ce8..080e7ee3a 100644 --- a/.rovodev/skills/impeccable/scripts/context.mjs +++ b/.rovodev/skills/impeccable/scripts/context.mjs @@ -1,8 +1,10 @@ /** - * Context loader: prints PRODUCT.md (and DESIGN.md if present) as one - * markdown block on stdout, or prints a `NO_PRODUCT_MD:` message when no + * Context loader: prints PRODUCT.md, DESIGN.md when present, the matching + * persisted surface brief when one can be resolved, and native-platform + * guidance selected from PRODUCT.md. It prints a + * `NO_PRODUCT_MD:` message when no * PRODUCT.md is found anywhere. The skill keys off that message to branch: - * from-scratch build commands (init / teach / craft / shape) and clear + * from-scratch build requests (plus init / teach / shape) and clear * build/shape intent divert into the init flow, while scoped commands proceed * using the existing code as context. * @@ -23,10 +25,13 @@ import os from 'node:os'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { parseTargetOptions } from './lib/target-args.mjs'; -import { IMPECCABLE_COMMAND } from './lib/provider.mjs'; +import { IMPECCABLE_COMMAND, IMPECCABLE_PROVIDER_ID } from './lib/provider.mjs'; +import { renderLlmOnlySlopReview } from './lib/slop-review.mjs'; +import { resolveSurfaceBrief } from './lib/surface-briefs.mjs'; const PRODUCT_NAMES = ['PRODUCT.md', 'Product.md', 'product.md']; const DESIGN_NAMES = ['DESIGN.md', 'Design.md', 'design.md']; +const SKILL_REFERENCE_DIR = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', 'reference'); const FALLBACK_DIRS = ['.agents/context', 'docs']; const MONOREPO_MARKER_FILES = ['pnpm-workspace.yaml', 'turbo.json', 'nx.json', 'lerna.json']; const MONOREPO_FALLBACK_PROJECT_DIRS = ['apps', 'packages']; @@ -41,6 +46,8 @@ const WORKSPACE_DISCOVERY_IGNORED_DIRS = new Set([ '.turbo', '.cache', 'coverage', + 'vendor', + 'vendors', ]); const VISUAL_SOURCE_DIRS = ['src', 'app', 'pages', 'components', 'site', 'public', 'styles']; const STYLE_EXTENSIONS = new Set(['.css', '.scss', '.sass', '.less', '.styl']); @@ -73,6 +80,12 @@ export function loadContext(cwd = process.cwd(), options = {}) { const designPath = resolved.designPath; const product = productPath ? safeRead(productPath) : null; const design = designPath ? safeRead(designPath) : null; + const platform = extractPlatform(product); + const surfaceResolution = resolveSurfaceBrief( + resolved.projectRoot, + hasTargetOption(options) ? options.targetPath : null, + ); + const surfaceBrief = surfaceResolution.brief; return { hasProduct: !!product, product, @@ -83,7 +96,18 @@ export function loadContext(cwd = process.cwd(), options = {}) { contextDir: resolved.contextDir, productContextDir: productPath ? path.dirname(productPath) : null, designContextDir: designPath ? path.dirname(designPath) : null, + hasSurfaceBrief: !!surfaceBrief, + surfaceBrief: surfaceBrief?.text ?? null, + surfaceBriefPath: surfaceBrief?.path ? path.relative(absCwd, surfaceBrief.path) : null, + surfaceBriefReason: surfaceResolution.reason, + surfaceBriefCandidates: surfaceResolution.candidates.map((brief) => ({ + slug: brief.slug, + path: path.relative(absCwd, brief.path), + primaryTarget: brief.primaryTarget, + relatedTargets: brief.relatedTargets, + })), hasVisualImplementation: hasVisualImplementation(resolved.projectRoot), + platform, projectRoot: resolved.projectRoot, repoRoot: resolved.repoRoot, isMonorepo: resolved.isMonorepo, @@ -696,6 +720,19 @@ function safeRead(p) { } } +function loadNativePlatformReferences(platform) { + const names = platform === 'adaptive' + ? ['ios', 'android'] + : platform === 'ios' || platform === 'android' + ? [platform] + : []; + return names.flatMap((name) => { + const filePath = path.join(SKILL_REFERENCE_DIR, `${name}.md`); + const content = safeRead(filePath); + return content ? [{ name, filePath, content }] : []; + }); +} + /** * Best-effort evidence that the project already has an incumbent visual * implementation. DESIGN.md is documentation, not the only source of design @@ -719,9 +756,11 @@ export function hasVisualImplementation(projectRoot) { let styledComponents = 0; const inspectFile = (filePath) => { - if (scannedFiles++ >= VISUAL_SCAN_FILE_LIMIT) return false; const ext = path.extname(filePath).toLowerCase(); if (!STYLE_EXTENSIONS.has(ext) && !UI_EXTENSIONS.has(ext)) return false; + const base = path.basename(filePath).toLowerCase(); + if (/\.min\.[a-z]+$/.test(base)) return false; + if (scannedFiles++ >= VISUAL_SCAN_FILE_LIMIT) return false; let body; try { body = fs.readFileSync(filePath, 'utf-8').slice(0, 64 * 1024); @@ -729,18 +768,28 @@ export function hasVisualImplementation(projectRoot) { return false; } - const base = path.basename(filePath).toLowerCase(); + const evidence = body + .replace(/\/\*[\s\S]*?\*\//g, '') + .replace(//g, '') + .replace(/^\s*\/\/.*$/gm, ''); if (STYLE_EXTENSIONS.has(ext)) { - const customProperties = body.match(/--[a-z0-9_-]+\s*:/gi)?.length ?? 0; - const visualDeclarations = body.match(/\b(?:color|background(?:-color)?|border(?:-color)?|font-family)\s*:/gi)?.length ?? 0; - if (/\b(?:tokens?|theme|design-system)\b/.test(base) && body.trim().length > 80) return true; + const customProperties = evidence.match(/--[a-z0-9_-]+\s*:/gi)?.length ?? 0; + const visualDeclarations = evidence.match(/\b(?:color|background(?:-color)?|border(?:-color)?|font-family)\s*:/gi)?.length ?? 0; + if (/\b(?:tokens?|theme|design-system)\b/.test(base) && evidence.trim().length > 80) return true; if (customProperties >= 3 || visualDeclarations >= 5) return true; } - if ((ext === '.html' || ext === '.htm') && body.length > 600 && /]+stylesheet/i.test(body)) { + if ((ext === '.html' || ext === '.htm') && evidence.length > 600 && /]+stylesheet/i.test(evidence)) { return true; } - if (!['.html', '.htm'].includes(ext) && body.length > 300 && /class(?:Name)?\s*=|style\s*=|styled\(|css`/i.test(body)) { + if (!['.html', '.htm'].includes(ext) && evidence.length > 300) { + const embeddedCustomProperties = evidence.match(/--[a-z0-9_-]+\s*:/gi)?.length ?? 0; + const embeddedVisualDeclarations = evidence.match(/\b(?:color|background(?:-color)?|border(?:-color)?|font-family)\s*:/gi)?.length ?? 0; + const classTokens = [...evidence.matchAll(/class(?:Name)?\s*=\s*["'`]([^"'`]+)["'`]/gi)] + .reduce((count, match) => count + match[1].trim().split(/\s+/).length, 0); + if ((embeddedCustomProperties >= 3 && embeddedVisualDeclarations >= 3) || embeddedVisualDeclarations >= 5 || classTokens >= 12) return true; + } + if (!['.html', '.htm'].includes(ext) && evidence.length > 300 && /class(?:Name)?\s*=|style\s*=|styled\(|css`/i.test(evidence)) { styledComponents += 1; if (styledComponents >= 3) return true; } @@ -781,9 +830,9 @@ function escapeRegExp(value) { /** * Read the first non-empty line under a bare `## ` section of - * PRODUCT.md (e.g. `## Register`, `## Platform`). Returns null when the + * PRODUCT.md (for example `## Platform`). Returns null when the * section is absent. The heading match is exact (`\s*$`) so near-miss - * headings like `## Register guidelines` don't shadow the real field. + * near-miss headings don't shadow the real field. */ export function extractSectionValue(product, heading) { if (!product) return null; @@ -802,16 +851,6 @@ export function extractSectionValue(product, heading) { return null; } -/** - * Pull the register (`brand` or `product`) out of PRODUCT.md by looking - * for a `## Register` section and reading the first non-empty line that - * follows it. Returns null when the file is legacy / register-less. - */ -export function extractRegister(product) { - const word = (extractSectionValue(product, 'Register') || '').toLowerCase(); - return word === 'brand' || word === 'product' ? word : null; -} - /** * Pull the platform (`web`, `ios`, `android`, or `adaptive`) out of PRODUCT.md * by looking for a `## Platform` section and reading the first non-empty line @@ -985,30 +1024,35 @@ async function cli() { const parts = ctx.hasVisualImplementation ? [ 'NO_PRODUCT_MD: This project has no PRODUCT.md yet, but it does have an incumbent visual implementation. ' + - 'For `init` or `teach`, load reference/init.md and create PRODUCT.md. For `craft`, `shape`, or other work ' + - 'against the existing product, read its CSS, tokens, components, and assets first and proceed without blocking; ' + - `offer \`${IMPECCABLE_COMMAND} init\` as a follow-up. Only treat the work as greenfield when the user explicitly ` + - 'asks to discard or replace the current identity.', - 'EXISTING_VISUAL_SYSTEM: Code and assets are the incumbent design authority. Missing DESIGN.md is a documentation ' + - 'gap, not permission to invent a replacement identity. Preserve and extend the implementation unless the user ' + - 'explicitly requests a rebrand, or inspection proves the detected files contain no coherent visual decisions.', + 'For `init`, `teach`, `shape`, or any request to create a new surface or replacement visual world, load reference/init.md and create PRODUCT.md with the user first. ' + + 'After init writes PRODUCT.md, reference/new-work.md preserves and documents the incumbent system for an ' + + 'extension or replaces it with the user for a redesign/rebrand. Other ' + + 'narrow refinement commands may read the CSS, tokens, components, and assets and proceed without blocking, then ' + + `offer \`${IMPECCABLE_COMMAND} init\` as a follow-up.`, + 'BUILD_INIT_REQUIRED: Before shape or any new-surface/redesign flow, init must capture PRODUCT.md with the human or structured ' + + 'simulated user. Init writes product truth only; reference/new-work.md owns every visual decision.', + 'SCOPED_EXISTING_ALLOWED: Narrow refinement commands may use the incumbent implementation as authority without ' + + 'blocking on context setup; they must preserve it and offer init afterward.', + 'EXISTING_VISUAL_SYSTEM: For refinement or extension, code and assets are incumbent design authority and missing ' + + 'DESIGN.md is a documentation gap. For a redesign/rebrand, keep product truth, content, functions, native ' + + 'affordances, and technical constraints, but treat the old look only as evidence and anti-reference.', ] : [ 'NO_PRODUCT_MD: This project has no PRODUCT.md yet. ' + - 'For `init`, `teach`, `craft`, `shape`, ' + + 'For `init`, `teach`, `shape`, ' + 'or wording that clearly maps to a from-scratch build/shape flow, load ' + - 'reference/init.md and write PRODUCT.md first, unless no user can ' + - 'respond (a one-shot or automated run, or the user said not to ask): ' + - 'then write a one-paragraph understanding of the product, audience, ' + - 'and the page\'s job from the brief, and continue. For any other ' + + 'reference/init.md, complete its human or structured simulated-user interview, and write PRODUCT.md before ' + + 'designing. If no answer mechanism truly exists, init may infer only from the explicit brief and must label its ' + + 'assumptions. It never writes DESIGN.md. For any other ' + '(scoped) command against existing code, proceed using the code as ' + `context and offer \`${IMPECCABLE_COMMAND} init\` as a suggestion (do not block).`, - 'NEW_WORK: No committed design context was found. If this task produces ' + - 'new design (a build from scratch, or a redesign that discards the ' + - 'current look), you MUST read reference/new-work.md before making any ' + - 'design decision. Scoped fixes to existing code do not need it.', + 'PRODUCT_INIT_REQUIRED: No product context or visual authority was found. New builds and redesigns ' + + 'must finish reference/init.md for PRODUCT.md, then reference/new-work.md establishes the world and surface. Scoped ' + + 'fixes to existing code do not need the new-surface flow.', ]; + appendSurfaceBriefContext(parts, ctx); parts.push(buildResolvedContextDirective(ctx, cliOptions, { targetExists })); + appendHookFallback(parts, ctx); if (shouldWarnMissingTarget(ctx, targetProvided, targetExists)) { parts.push(buildMissingTargetDirective()); } @@ -1020,37 +1064,29 @@ async function cli() { if (ctx.hasDesign) { parts.push(`# DESIGN.md\n\n${ctx.design.trim()}`); } + appendSurfaceBriefContext(parts, ctx); parts.push(buildResolvedContextDirective(ctx, cliOptions, { targetExists })); + appendHookFallback(parts, ctx); if (shouldWarnMissingTarget(ctx, targetProvided, targetExists)) { parts.push(buildMissingTargetDirective()); } - const register = extractRegister(ctx.product); - // The register field survives as a family hint (brand = Persuade/Experience, - // product = Operate/Read); SKILL.md's mode section carries the essentials - // inline, so no register-file read is mandated here. What IS mandated: - // the new-work playbook when no committed design system exists yet. if (!ctx.hasDesign) { parts.push(ctx.hasVisualImplementation - ? 'EXISTING_VISUAL_SYSTEM: PRODUCT.md exists and DESIGN.md is missing, but code contains incumbent visual decisions. ' + - 'Treat CSS, tokens, components, and assets as design authority. Do not route to new identity work merely because the ' + - 'document is absent; preserve and extend the implementation unless the user explicitly asks to discard it.' - : 'NEW_WORK: PRODUCT.md exists but no DESIGN.md or incumbent visual implementation was found. If this task produces ' + - 'new design or a redesign that discards the current look, you MUST read reference/new-work.md before making any ' + - 'design decision. Scoped fixes to existing code do not need it.'); + ? 'INCUMBENT_WORLD_UNDOCUMENTED: PRODUCT.md exists and DESIGN.md is missing, but code contains incumbent visual decisions. ' + + 'For shape or a new-surface/redesign request, load reference/new-work.md: an extension documents and preserves the code-defined world; ' + + 'a redesign replaces it with the user and uses the old look only as evidence and anti-reference. Narrow refinement ' + + 'commands may proceed using the implementation directly.' + : 'WORLD_DISCOVERY_REQUIRED: PRODUCT.md exists but no DESIGN.md or incumbent visual implementation was found. ' + + 'For a new build or redesign, load reference/new-work.md and establish the visual world with the human or structured ' + + 'simulated user before developing the task concept. Scoped fixes to existing code do not need this flow.'); } - if (register) { - parts.push(`REGISTER: \`${register}\` (family hint: brand = Persuade/Experience surfaces, product = Operate/Read). Derive the visitor's mode per SKILL.md.`); - } - const platform = extractPlatform(ctx.product); - const nativeRefs = - platform === 'adaptive' ? ['ios', 'android'] : platform === 'ios' || platform === 'android' ? [platform] : []; - if (nativeRefs.length) { - const refList = nativeRefs.map(p => `\`reference/${p}.md\``).join(' and '); - const label = platform === 'adaptive' ? '`adaptive` (both iOS and Android)' : `\`${platform}\``; + const platformReferences = loadNativePlatformReferences(ctx.platform); + for (const reference of platformReferences) { parts.push( - `NEXT STEP: This project targets ${label}. Also read ${refList} for native conventions, in addition to SKILL.md's mode guidance.`, + `# NATIVE PLATFORM REFERENCE: ${reference.name.toUpperCase()} (reference/${reference.name}.md)\n\n${reference.content.trim()}`, ); - } else if (!platform) { + } + if (!ctx.platform) { // A `## Platform` section that names something we don't recognize (a // toolchain like `flutter`, a typo) would otherwise silently fall back to // web — the wrong default exactly when the user tried to say "native". @@ -1078,6 +1114,72 @@ function pathExistsForTarget(cwd, targetPath) { return fs.existsSync(abs); } +const HOOK_MANIFESTS_BY_PROVIDER = Object.freeze({ + 'claude-code': ['.claude/settings.local.json', '.claude/settings.json'], + codex: ['.codex/hooks.json'], + agents: ['.codex/hooks.json'], + cursor: ['.cursor/hooks.json'], + github: ['.github/hooks/impeccable.json'], +}); + +function truthyEnv(value) { + return typeof value === 'string' && /^(1|true|yes|on)$/i.test(value.trim()); +} + +function valueHasHookMarker(value) { + if (typeof value === 'string') { + return value.includes('skills/impeccable/scripts/hook.mjs') + || value.includes('skills/impeccable/scripts/hook-before-edit.mjs'); + } + if (Array.isArray(value)) return value.some(valueHasHookMarker); + if (value && typeof value === 'object') return Object.values(value).some(valueHasHookMarker); + return false; +} + +function hookEnabledAt(root) { + if (truthyEnv(process.env.IMPECCABLE_HOOK_DISABLED)) return false; + let enabled = true; + for (const name of ['.impeccable/config.json', '.impeccable/config.local.json']) { + const raw = readJson(path.join(root, name)); + if (raw?.hook && Object.prototype.hasOwnProperty.call(raw.hook, 'enabled')) { + enabled = raw.hook.enabled !== false; + } + } + return enabled; +} + +const STOP_REVIEW_PROVIDERS = new Set(['claude-code', 'codex', 'agents']); + +function automaticHookMode(ctx) { + if (ctx.platform === 'ios' || ctx.platform === 'android' || ctx.platform === 'adaptive') { + return 'none'; + } + const activeRoot = path.resolve(ctx.projectRoot || process.cwd()); + if (!hookEnabledAt(activeRoot)) return 'none'; + const manifests = HOOK_MANIFESTS_BY_PROVIDER[IMPECCABLE_PROVIDER_ID] || []; + const roots = [...new Set([process.cwd(), ctx.projectRoot, ctx.repoRoot].filter(Boolean).map((root) => path.resolve(root)))]; + for (const root of roots) { + for (const rel of manifests) { + const raw = readJson(path.join(root, rel)); + if (raw?.hooks && valueHasHookMarker(raw.hooks)) { + return STOP_REVIEW_PROVIDERS.has(IMPECCABLE_PROVIDER_ID) ? 'stop' : 'per-edit'; + } + } + } + return 'none'; +} + +function appendHookFallback(parts, ctx) { + const hookMode = automaticHookMode(ctx); + if (hookMode === 'stop') return; + const native = ctx.platform === 'ios' || ctx.platform === 'android' || ctx.platform === 'adaptive'; + parts.push(renderLlmOnlySlopReview({ + automaticDetector: hookMode === 'per-edit', + manualDetector: hookMode === 'none' && !native, + scriptsPath: path.dirname(fileURLToPath(import.meta.url)), + })); +} + function buildResolvedContextDirective(ctx, options, { targetExists = null } = {}) { const targetPath = hasTargetOption(options) ? options.targetPath : null; return `RESOLVED_CONTEXT:\n${JSON.stringify({ @@ -1087,10 +1189,29 @@ function buildResolvedContextDirective(ctx, options, { targetExists = null } = { repoRoot: ctx.repoRoot, productPath: ctx.productPath, designPath: ctx.designPath, + surfaceBriefPath: ctx.surfaceBriefPath, + surfaceBriefReason: ctx.surfaceBriefReason, + surfaceBriefCandidates: ctx.surfaceBriefCandidates, hasVisualImplementation: ctx.hasVisualImplementation, + platform: ctx.platform, }, null, 2)}`; } +function appendSurfaceBriefContext(parts, ctx) { + if (ctx.hasSurfaceBrief && ctx.surfaceBrief) { + parts.push(`# SURFACE BRIEF (${ctx.surfaceBriefPath})\n\n${ctx.surfaceBrief.trim()}`); + return; + } + if (!ctx.surfaceBriefCandidates?.length) return; + const helper = path.join(path.dirname(fileURLToPath(import.meta.url)), 'surface-brief.mjs'); + parts.push( + 'SURFACE_CONTEXT_AVAILABLE: Persisted surface briefs exist, but none was selected unambiguously for this invocation. ' + + 'Resolve the requested surface to its concrete primary or related source path, then run ' + + `\`node ${helper} read \` once before changing that surface. Candidates:\n` + + JSON.stringify(ctx.surfaceBriefCandidates, null, 2), + ); +} + function shouldWarnMissingTarget(ctx, targetProvided, targetExists = null) { if (ctx.isMonorepo && targetProvided && targetExists === false) return true; return !!( diff --git a/.rovodev/skills/impeccable/scripts/critique-storage.mjs b/.rovodev/skills/impeccable/scripts/critique-storage.mjs index 6b4d225cf..38674285a 100644 --- a/.rovodev/skills/impeccable/scripts/critique-storage.mjs +++ b/.rovodev/skills/impeccable/scripts/critique-storage.mjs @@ -29,8 +29,9 @@ import fs from 'node:fs'; import path from 'node:path'; import { fileURLToPath, pathToFileURL } from 'node:url'; import { getCritiqueDir } from './lib/impeccable-paths.mjs'; +import { slugFromTarget } from './lib/target-slug.mjs'; -const SLUG_MAX = 50; +export { slugFromTarget } from './lib/target-slug.mjs'; /** * Mechanically derive a slug from a resolved target. Returns null if the @@ -40,46 +41,6 @@ const SLUG_MAX = 50; * concrete artifact before calling this — we never slug a natural-language * phrase. */ -export function slugFromTarget(resolved, { cwd = process.cwd() } = {}) { - if (!resolved || typeof resolved !== 'string') return null; - const trimmed = resolved.trim(); - if (!trimmed) return null; - - // URL - if (/^https?:\/\//i.test(trimmed)) { - let url; - try { url = new URL(trimmed); } catch { return null; } - const hostPath = `${url.hostname}${url.pathname}`; - return kebab(hostPath); - } - - // File path. Make it project-relative so two devs critiquing the same - // checkout get the same slug regardless of where their repo is cloned. - const abs = path.isAbsolute(trimmed) ? trimmed : path.resolve(cwd, trimmed); - let rel = path.relative(cwd, abs); - // If the target is outside cwd, fall back to the basename so we still - // produce a stable slug (vs the absolute path, which would include - // home dirs / usernames). - if (rel.startsWith('..') || path.isAbsolute(rel)) { - rel = path.basename(abs); - } - if (!rel || rel === '.' || rel === '') return null; - return kebab(rel); -} - -function kebab(s) { - const slug = s - .toLowerCase() - .replace(/[/\\.]+/g, '-') - .replace(/[^a-z0-9-]+/g, '-') - .replace(/-+/g, '-') - .replace(/^-|-$/g, ''); - if (!slug) return null; - // Cap from the tail — the tail (filename) is more identifying than the - // top-level directory. - return slug.length <= SLUG_MAX ? slug : slug.slice(slug.length - SLUG_MAX).replace(/^-/, ''); -} - /** * Filename-safe UTC ISO timestamp: hyphens for separators, trailing Z. * Plain colons aren't allowed on Windows filesystems. diff --git a/.rovodev/skills/impeccable/scripts/detector/detect-antipatterns-browser.js b/.rovodev/skills/impeccable/scripts/detector/detect-antipatterns-browser.js index 033573e36..76bf4ddeb 100644 --- a/.rovodev/skills/impeccable/scripts/detector/detect-antipatterns-browser.js +++ b/.rovodev/skills/impeccable/scripts/detector/detect-antipatterns-browser.js @@ -312,17 +312,6 @@ const ANTIPATTERNS = [ skillSection: 'Layout & Space', skillGuideline: 'numbered section markers', }, - { - id: 'numbered-section-markers', - category: 'slop', - scopes: ['layout'], - severity: 'advisory', - name: 'Numbered section markers (01 / 02 / 03)', - description: - 'Numbered display markers as section labels (01, 02, 03) are the AI editorial scaffold one tier deeper than tracked eyebrow chips. If you find yourself reaching for them, choose a different section cadence.', - skillSection: 'Layout & Space', - skillGuideline: 'numbered section markers', - }, { id: 'em-dash-overuse', category: 'slop', diff --git a/.rovodev/skills/impeccable/scripts/detector/engines/regex/detect-text.mjs b/.rovodev/skills/impeccable/scripts/detector/engines/regex/detect-text.mjs index 81c30ab99..f641d5917 100644 --- a/.rovodev/skills/impeccable/scripts/detector/engines/regex/detect-text.mjs +++ b/.rovodev/skills/impeccable/scripts/detector/engines/regex/detect-text.mjs @@ -280,22 +280,6 @@ const REGEX_ANALYZERS = [ if (count === 0) return []; return [finding('marketing-buzzword', filePath, `${count} buzzword phrase${count === 1 ? '' : 's'}: "${firstSample}"`)]; }, - // Numbered section markers (01 / 02 / 03 ...) - (content, filePath) => { - const text = stripHtmlToText(content); - const re = /\b(0[1-9]|1[0-2])\b/g; - const seen = new Set(); - let m; - while ((m = re.exec(text)) !== null) seen.add(m[1]); - if (seen.size < 3) return []; - const sorted = [...seen].sort(); - let sequential = 0; - for (let i = 1; i < sorted.length; i++) { - if (parseInt(sorted[i], 10) === parseInt(sorted[i - 1], 10) + 1) sequential++; - } - if (sequential < 2) return []; - return [finding('numbered-section-markers', filePath, `Sequence: ${sorted.slice(0, 6).join(', ')}`)]; - }, // Aphoristic cadence: manufactured-contrast + short-rebuttal (content, filePath) => { const text = stripHtmlToText(content); @@ -430,21 +414,20 @@ function runRegexMatchers(lines, filePath, lineOffset = 0, blockContext = null, } /** Page-level analyzers that scan rendered text content (em-dash use, - * buzzword phrases, numbered section markers, aphoristic cadence). + * buzzword phrases, aphoristic cadence). * These are detector-agnostic — they work on any HTML/text source * and don't need a parsed DOM. Exported so detectHtml can call them * for `.html` files (which otherwise skip the regex engine). */ const TEXT_CONTENT_ANALYZER_IDS = [ 'em-dash-overuse', 'marketing-buzzword', - 'numbered-section-markers', 'aphoristic-cadence', ]; function runTextContentAnalyzers(content, filePath, options = {}) { const profile = options?.profile; if (!shouldRunPageAnalyzers(content, filePath)) return []; - // The 4 text-content analyzers are at indices 3-6 in REGEX_ANALYZERS. + // The 3 text-content analyzers are at indices 3-5 in REGEX_ANALYZERS. const findings = []; for (let i = 0; i < TEXT_CONTENT_ANALYZER_IDS.length; i++) { const analyzer = REGEX_ANALYZERS[3 + i]; @@ -535,7 +518,6 @@ function detectText(content, filePath, options = {}) { 'monotonous-spacing', 'em-dash-overuse', 'marketing-buzzword', - 'numbered-section-markers', 'aphoristic-cadence', 'dark-glow', ]; diff --git a/.rovodev/skills/impeccable/scripts/detector/registry/antipatterns.mjs b/.rovodev/skills/impeccable/scripts/detector/registry/antipatterns.mjs index b85f7e62f..84c69b0b8 100644 --- a/.rovodev/skills/impeccable/scripts/detector/registry/antipatterns.mjs +++ b/.rovodev/skills/impeccable/scripts/detector/registry/antipatterns.mjs @@ -210,17 +210,6 @@ const ANTIPATTERNS = [ skillSection: 'Layout & Space', skillGuideline: 'numbered section markers', }, - { - id: 'numbered-section-markers', - category: 'slop', - scopes: ['layout'], - severity: 'advisory', - name: 'Numbered section markers (01 / 02 / 03)', - description: - 'Numbered display markers as section labels (01, 02, 03) are the AI editorial scaffold one tier deeper than tracked eyebrow chips. If you find yourself reaching for them, choose a different section cadence.', - skillSection: 'Layout & Space', - skillGuideline: 'numbered section markers', - }, { id: 'em-dash-overuse', category: 'slop', diff --git a/.rovodev/skills/impeccable/scripts/detector/rules/checks.mjs b/.rovodev/skills/impeccable/scripts/detector/rules/checks.mjs index 3fdd6b2dd..9bace11b3 100644 --- a/.rovodev/skills/impeccable/scripts/detector/rules/checks.mjs +++ b/.rovodev/skills/impeccable/scripts/detector/rules/checks.mjs @@ -352,27 +352,57 @@ function isAccentColor(cssColor) { return false; } +function resolveHeroHeadingSizePx(value) { + const input = String(value || '').trim().toLowerCase(); + if (!input) return 0; + + const simpleLengthPx = (token) => { + const match = /^(-?\d*\.?\d+)\s*(px|rem|em|%)?$/.exec(String(token || '').trim()); + if (!match) return null; + const amount = Number(match[1]); + if (!Number.isFinite(amount)) return null; + if (match[2] === 'rem' || match[2] === 'em') return amount * 16; + if (match[2] === '%') return amount * 0.16; + return amount; + }; + + const direct = simpleLengthPx(input); + if (direct !== null) return direct; + + // Static CSS engines cannot resolve viewport units, but clamp's min/max + // bounds still tell us whether the heading can ever reach hero scale. + const clamp = /^clamp\((.*)\)$/.exec(input); + if (clamp) { + const parts = clamp[1].split(','); + if (parts.length === 3) { + const bounds = [simpleLengthPx(parts[0]), simpleLengthPx(parts[2])] + .filter((candidate) => candidate !== null); + if (bounds.length > 0) return Math.max(...bounds); + } + } + + return 0; +} + // Sibling-relationship rule. Anchor on a hero-scale h1, look at the // previousElementSibling, and gate on EITHER the classic tracked- // uppercase eyebrow OR the modern accent-colored bold eyebrow. function checkHeroEyebrow(opts) { const { headingTag, headingText, headingFontSize, + headingInApplicationContext, siblingTag, siblingText, siblingTextTransform, siblingFontSize, siblingLetterSpacing, siblingFontWeight, siblingColor, siblingHasAccentDashPseudo, } = opts; if (headingTag !== 'h1') return []; - // We previously gated on headingFontSize >= 48 to anchor "hero scale". - // But modern hero h1s use clamp() / vw / var(--text-*), none of which - // jsdom can resolve — the computed value comes back as "2em" or - // "var(--text-9xl)" and parseFloat returns 2 or NaN. The gate fails - // on virtually every Tailwind v4 / framework build. The other gates - // (sibling text 2-60 chars, font-size ≤ 14px, accent-bold OR - // tracked-caps) are tight enough to avoid false positives on non- - // hero h1s — a tiny tan label directly above any h1 is the - // antipattern regardless of how big the h1 ends up. + // This is specifically a marketing-hero cliché, not a ban on compact + // context labels in product UI (for example, a station name inside a tab + // panel). Browser-computed sizes are reliable; the static adapter also + // resolves ordinary px/rem/em and clamp() bounds before reaching here. + if (headingInApplicationContext) return []; + if (!(headingFontSize >= 48)) return []; if (!siblingTag) return []; // An h2 above an h1 is a different anti-pattern (heading hierarchy / dual // headings) — never an eyebrow. @@ -1938,6 +1968,7 @@ function checkElementHeroEyebrowDOM(el) { headingTag: tag, headingText: el.textContent || '', headingFontSize: parseFloat(headStyle.fontSize) || 0, + headingInApplicationContext: !!el.closest('[role="tabpanel"], [role="dialog"], [role="application"], dialog'), siblingTag: sibling.tagName.toLowerCase(), siblingText: sibling.textContent || '', siblingTextTransform: sibStyle.textTransform || '', @@ -3367,7 +3398,8 @@ function checkElementHeroEyebrow(el, style, tag, window, customPropMap) { return checkHeroEyebrow({ headingTag: tag, headingText: el.textContent || '', - headingFontSize: parseFloat(headingFontSizeRaw) || 0, + headingFontSize: resolveHeroHeadingSizePx(headingFontSizeRaw), + headingInApplicationContext: !!el.closest?.('[role="tabpanel"], [role="dialog"], [role="application"], dialog'), siblingTag: sibling.tagName.toLowerCase(), siblingText: sibling.textContent || '', siblingTextTransform: sibStyle.textTransform || '', diff --git a/.rovodev/skills/impeccable/scripts/hook-lib.mjs b/.rovodev/skills/impeccable/scripts/hook-lib.mjs index e61ea692b..50aa2f8fb 100644 --- a/.rovodev/skills/impeccable/scripts/hook-lib.mjs +++ b/.rovodev/skills/impeccable/scripts/hook-lib.mjs @@ -46,6 +46,7 @@ import path from 'node:path'; import { pathToFileURL, fileURLToPath } from 'node:url'; import { extractPlatform, loadContext } from './context.mjs'; import { IMPECCABLE_COMMAND } from './lib/provider.mjs'; +import { renderStopSlopReview } from './lib/slop-review.mjs'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); @@ -1868,22 +1869,42 @@ export const CONTRACT_MAX_CHARS = 1800; // Cap contract sections per Stop emission so many touched artifacts cannot // stack an unbounded message. export const CONTRACT_AUDIT_MAX_FILES = 3; +export const CONTRACT_EXTS = new Set(['.html', '.htm', '.astro', '.svelte', '.vue', '.jsx', '.tsx']); +export const CONTRACT_REQUIRED_FIELDS = ['UNIQUE', 'NOT-TEMPLATE', 'OWN-WORLD', 'STORY', 'FIRST VIEWPORT', 'FORM']; /** - * Extract the artifact's own direction-contract comment: the first HTML - * comment in the head of the file, when its opening chars identify it as a - * contract/concept block. Returns the trimmed, length-capped body, or null - * when the file carries none (no comment, unclosed comment, marker missing, - * or the comment starts past the head window). + * Extract the artifact's own direction-contract comment from the head of an + * HTML or component file. Supports HTML-family comments and JSX block + * comments so the contract works in the Astro/Svelte/Vue/React scaffolds the + * skill actually builds. Returns the trimmed, length-capped body, or null + * when the file carries no valid contract block. */ export function extractDirectionContract(content) { if (typeof content !== 'string' || !content) return null; const head = content.slice(0, CONTRACT_HEAD_CHARS); - const m = //.exec(head); - if (!m) return null; - const body = m[1].trim(); - if (!body || !/contract|concept/i.test(body.slice(0, CONTRACT_MARKER_CHARS))) return null; - return body.slice(0, CONTRACT_MAX_CHARS); + const candidates = []; + for (const pattern of [//g, /\{\/\*([\s\S]*?)\*\/\}/g]) { + for (const match of head.matchAll(pattern)) { + const index = match.index ?? 0; + const linePrefix = head.slice(head.lastIndexOf('\n', index - 1) + 1, index).trim(); + if (linePrefix.startsWith('//')) continue; + candidates.push({ index, body: match[1].trim() }); + } + } + candidates.sort((a, b) => a.index - b.index); + for (const candidate of candidates) { + if (!candidate.body || !/direction\s+contract|concept\s+contract/i.test(candidate.body.slice(0, CONTRACT_MARKER_CHARS))) continue; + return candidate.body.slice(0, CONTRACT_MAX_CHARS); + } + return null; +} + +export function missingDirectionContractFields(contract) { + const body = typeof contract === 'string' ? contract : ''; + return CONTRACT_REQUIRED_FIELDS.filter((field) => { + const label = field.replace(/[.*+?^${}()|[\]\\]/g, '\\$&').replace(/\s+/g, '\\s+'); + return !new RegExp(`\\b${label}\\s*:`, 'i').test(body); + }); } /** @@ -1899,7 +1920,11 @@ export function renderContractAudit(entries, opts = {}) { const shown = entries.slice(0, CONTRACT_AUDIT_MAX_FILES); const blocks = shown.map(({ filePath, contract }) => { const display = relativize(filePath, cwd); - return `${display} opens with this direction contract, written when the direction was decided:\n\n${contract}`; + const missing = missingDirectionContractFields(contract); + const integrity = missing.length > 0 + ? `\n\nContract integrity defect: missing ${missing.join(', ')}. Repair the contract and the implementation together.` + : ''; + return `${display} opens with this direction contract, written when the direction was decided:\n\n${contract}${integrity}`; }); return [ `${ENVELOPE_PREFIX} Direction-contract audit. Before finishing, audit the rendered page against the contract it opens with, promise by promise.`, @@ -1984,9 +2009,22 @@ export async function runStopHook({ stdinJson, env = {}, cwd = process.cwd(), no return result({ skipped: 'native-platform', platform, durationMs: Date.now() - started }); } + const session = ensureSession(cache, sessionId); + const needsSlopReview = session.llmSlopReviewed !== true; + const det = detector || await loadDetector(); if (!det || typeof det.detectText !== 'function') { - return result({ skipped: 'detector-missing', durationMs: Date.now() - started }); + if (!needsSlopReview) return result({ skipped: 'detector-missing', durationMs: Date.now() - started }); + session.llmSlopReviewed = true; + session.updatedAt = Date.now(); + persistCache(projectCwd, cache); + const text = renderStopSlopReview(); + return { + exitCode: 0, + stdout: payload(text, 'Stop', harness), + emission: { kind: 'stop-llm-slop-review', llmSlopReview: true }, + audit: { ...audit, emitted: true, detectorMissing: true, llmSlopReview: true, chars: text.length, durationMs: Date.now() - started }, + }; } const scanOptions = designSystemOptions(config, det, projectCwd); @@ -2013,10 +2051,11 @@ export async function runStopHook({ stdinJson, env = {}, cwd = process.cwd(), no ? configuredExt.engine === 'html' : (ext === '.html' || ext === '.htm'); - // Direction-contract audit: HTML artifacts only, at most once per file + // Direction-contract audit: HTML and component artifacts, at most once per file // per session. The flag lives on the same session cache entry the // finding dedupe uses, so a second Stop fire stays quiet about it. - if (useHtmlEngine) { + const contractCapable = useHtmlEngine || CONTRACT_EXTS.has(ext); + if (contractCapable) { const fileEntry = ensureFile(cache, sessionId, filePath); if (!fileEntry.contractAudited) { const contract = extractDirectionContract(content); @@ -2046,10 +2085,15 @@ export async function runStopHook({ stdinJson, env = {}, cwd = process.cwd(), no } audit.scannedFiles = scanned; - if (freshGroups.length === 0 && contractEntries.length === 0) { + if (freshGroups.length === 0 && contractEntries.length === 0 && !needsSlopReview) { return result({ emitted: false, skipped: 'stop-clean', durationMs: Date.now() - started }); } + if (needsSlopReview) { + session.llmSlopReviewed = true; + session.updatedAt = Date.now(); + } + // Fresh findings and first-time contract audits earn the cache write; // both mark this batch as surfaced so the next Stop fire is silent // unless new issues appear. @@ -2064,6 +2108,9 @@ export async function runStopHook({ stdinJson, env = {}, cwd = process.cwd(), no if (contractEntries.length > 0) { parts.push(renderContractAudit(contractEntries, { cwd: projectCwd })); } + if (needsSlopReview) { + parts.push(renderStopSlopReview()); + } const text = appendDesignSystemNote(parts.join('\n\n'), scanOptions); return { exitCode: 0, @@ -2074,6 +2121,7 @@ export async function runStopHook({ stdinJson, env = {}, cwd = process.cwd(), no ...(contractEntries.length > 0 ? { contractFiles: contractEntries.map((entry) => entry.filePath) } : {}), + ...(needsSlopReview ? { llmSlopReview: true } : {}), }, audit: { ...audit, @@ -2081,6 +2129,7 @@ export async function runStopHook({ stdinJson, env = {}, cwd = process.cwd(), no freshFiles: freshGroups.length, freshFindings: freshGroups.reduce((sum, group) => sum + group.findings.length, 0), ...(contractEntries.length > 0 ? { contractAudits: contractEntries.length } : {}), + ...(needsSlopReview ? { llmSlopReview: true } : {}), chars: text.length, durationMs: Date.now() - started, }, diff --git a/.rovodev/skills/impeccable/scripts/lib/provider.mjs b/.rovodev/skills/impeccable/scripts/lib/provider.mjs index 7fa928a9c..dbcbab28d 100644 --- a/.rovodev/skills/impeccable/scripts/lib/provider.mjs +++ b/.rovodev/skills/impeccable/scripts/lib/provider.mjs @@ -1,4 +1,5 @@ // Source scripts default to slash commands. The provider build replaces only // this exact declaration, avoiding heuristic rewrites across executable code. export const IMPECCABLE_COMMAND_PREFIX = "/"; +export const IMPECCABLE_PROVIDER_ID = "rovo-dev"; export const IMPECCABLE_COMMAND = `${IMPECCABLE_COMMAND_PREFIX}impeccable`; diff --git a/.rovodev/skills/impeccable/scripts/lib/slop-review.mjs b/.rovodev/skills/impeccable/scripts/lib/slop-review.mjs new file mode 100644 index 000000000..30f03e34d --- /dev/null +++ b/.rovodev/skills/impeccable/scripts/lib/slop-review.mjs @@ -0,0 +1,30 @@ +export const LLM_ONLY_SLOP_ITEMS = Object.freeze([ + 'Monospace used merely to signal “technical” or “developer.”', + 'Light or dark mode chosen by category habit rather than the actual use scene.', + 'Everything wrapped in cards, or identical icon-heading-text cards repeated as the page structure.', + 'Hero-metric scaffolds: one oversized number, a small label, supporting stats, and an accent treatment.', + 'Decorative glassmorphism, meaningless sparklines, or generic rounded rectangles with drop shadows.', + 'A modal chosen by reflex when the task does not require interruption or protected focus.', +]); + +export function renderLlmOnlySlopReview({ manualDetector = false, automaticDetector = false, scriptsPath = null } = {}) { + const lines = [ + automaticDetector + ? 'AI_SLOP_REVIEW_REQUIRED: The automatic detector covers mechanical rules, but this harness has no reliable late review. Before finishing changed UI, inspect the authored result for detector-blind model reflexes:' + : 'AI_SLOP_REVIEW_REQUIRED: The automatic Impeccable design hook is not available for this session. Before finishing changed UI, inspect the authored result for detector-blind model reflexes:', + ...LLM_ONLY_SLOP_ITEMS.map((item) => `- ${item}`), + 'Fix reflexes, not intentional choices required by the brief or established visual authority.', + ]; + if (manualDetector && scriptsPath) { + lines.push(`Then run the mechanical detector once over the changed web UI: \`node ${scriptsPath}/detect.mjs --json \`. Do not run it earlier during concept selection.`); + } + return lines.join('\n'); +} + +export function renderStopSlopReview() { + return [ + '[impeccable@1] Detector-blind AI-slop review. The mechanical pass is complete; now inspect the rendered result for model reflexes it cannot reliably detect:', + ...LLM_ONLY_SLOP_ITEMS.map((item) => `- ${item}`), + 'Fix reflexes, not intentional choices required by the brief or established visual authority. Do not rerun the detector; this is the authored judgment layer.', + ].join('\n'); +} diff --git a/.rovodev/skills/impeccable/scripts/lib/surface-briefs.mjs b/.rovodev/skills/impeccable/scripts/lib/surface-briefs.mjs new file mode 100644 index 000000000..f83416f69 --- /dev/null +++ b/.rovodev/skills/impeccable/scripts/lib/surface-briefs.mjs @@ -0,0 +1,151 @@ +import fs from 'node:fs'; +import path from 'node:path'; +import { slugFromTarget } from './target-slug.mjs'; + +export const SURFACE_BRIEF_VERSION = 1; + +export function getSurfaceBriefDir(projectRoot) { + return path.join(projectRoot, '.impeccable', 'surfaces'); +} + +export function normalizeSurfaceTarget(target, { projectRoot = process.cwd() } = {}) { + if (!target || typeof target !== 'string' || !target.trim()) return null; + const trimmed = target.trim(); + if (/^https?:\/\//i.test(trimmed)) { + try { + const url = new URL(trimmed); + url.hash = ''; + url.search = ''; + return url.toString().replace(/\/$/, '') || url.origin; + } catch { + return null; + } + } + if (/^route:/i.test(trimmed)) { + const route = trimmed.slice(trimmed.indexOf(':') + 1).trim(); + if (!route.startsWith('/') || route.includes('..')) return null; + const normalizedRoute = route.split(/[?#]/, 1)[0].replace(/\/{2,}/g, '/').replace(/\/$/, '') || '/'; + return `route:${normalizedRoute}`; + } + if (trimmed === '/') return 'route:/'; + if (trimmed.startsWith('/')) { + const absolute = path.resolve(trimmed); + const relativeToProject = path.relative(projectRoot, absolute); + const isProjectFile = relativeToProject && !relativeToProject.startsWith('..') && !path.isAbsolute(relativeToProject); + if (!isProjectFile && !fs.existsSync(absolute) && !trimmed.includes('..')) { + const normalizedRoute = trimmed.split(/[?#]/, 1)[0].replace(/\/{2,}/g, '/').replace(/\/$/, '') || '/'; + return `route:${normalizedRoute}`; + } + } + const abs = path.isAbsolute(trimmed) ? trimmed : path.resolve(projectRoot, trimmed); + const rel = path.relative(projectRoot, abs); + if (!rel || rel === '.' || rel.startsWith('..') || path.isAbsolute(rel)) return null; + return rel.split(path.sep).join('/'); +} + +export function surfaceBriefPathForTarget(target, { projectRoot = process.cwd() } = {}) { + const normalized = normalizeSurfaceTarget(target, { projectRoot }); + if (!normalized) return null; + const slugInput = normalized.startsWith('route:') ? `route${normalized.slice('route:'.length)}` : normalized; + const slug = slugFromTarget(slugInput, { cwd: projectRoot }); + return slug ? path.join(getSurfaceBriefDir(projectRoot), `${slug}.md`) : null; +} + +export function parseSurfaceBrief(text, filePath = null) { + const match = String(text || '').match(/^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/); + const meta = {}; + if (match) { + for (const line of match[1].split(/\r?\n/)) { + const colon = line.indexOf(':'); + if (colon < 0) continue; + const key = line.slice(0, colon).trim(); + const raw = line.slice(colon + 1).trim(); + if (!key) continue; + if (/^(?:\[|\{|\")/.test(raw) || /^(?:true|false|null|-?\d+(?:\.\d+)?)$/.test(raw)) { + try { meta[key] = JSON.parse(raw); continue; } catch { /* keep string */ } + } + meta[key] = raw.replace(/^['"]|['"]$/g, ''); + } + } + const primaryTarget = typeof meta.primary_target === 'string' ? meta.primary_target : null; + const relatedTargets = Array.isArray(meta.related_targets) + ? meta.related_targets.filter((value) => typeof value === 'string') + : []; + return { + path: filePath, + text: String(text || ''), + body: match ? String(text || '').slice(match[0].length).trim() : String(text || '').trim(), + meta, + slug: typeof meta.slug === 'string' ? meta.slug : filePath ? path.basename(filePath, '.md') : null, + primaryTarget, + relatedTargets, + targets: [primaryTarget, ...relatedTargets].filter(Boolean), + }; +} + +export function listSurfaceBriefs(projectRoot = process.cwd()) { + const dir = getSurfaceBriefDir(projectRoot); + let names; + try { + names = fs.readdirSync(dir).filter((name) => name.endsWith('.md')).sort(); + } catch { + return []; + } + return names.flatMap((name) => { + const filePath = path.join(dir, name); + try { + return [parseSurfaceBrief(fs.readFileSync(filePath, 'utf-8'), filePath)]; + } catch { + return []; + } + }); +} + +export function resolveSurfaceBrief(projectRoot = process.cwd(), target = null) { + const briefs = listSurfaceBriefs(projectRoot); + if (!target) { + return { + brief: briefs.length === 1 ? briefs[0] : null, + candidates: briefs, + reason: briefs.length === 1 ? 'only-brief' : briefs.length > 1 ? 'ambiguous' : 'none', + }; + } + + const normalized = normalizeSurfaceTarget(target, { projectRoot }); + if (!normalized) return { brief: null, candidates: briefs, reason: 'invalid-target' }; + const exactPath = surfaceBriefPathForTarget(normalized, { projectRoot }); + const exact = briefs.find((brief) => brief.path === exactPath && (!brief.targets.length || brief.targets.includes(normalized))); + if (exact) return { brief: exact, candidates: briefs, reason: 'slug' }; + const mapped = briefs.filter((brief) => brief.targets.includes(normalized)); + return { + brief: mapped.length === 1 ? mapped[0] : null, + candidates: mapped.length > 1 ? mapped : briefs, + reason: mapped.length === 1 ? 'mapping' : mapped.length > 1 ? 'ambiguous-target' : 'not-found', + }; +} + +export function writeSurfaceBrief({ + projectRoot = process.cwd(), + primaryTarget, + relatedTargets = [], + body, +}) { + const normalizedPrimary = normalizeSurfaceTarget(primaryTarget, { projectRoot }); + if (!normalizedPrimary) throw new Error('surface brief requires a concrete project-relative primary target or URL'); + const normalizedRelated = [...new Set(relatedTargets + .map((target) => normalizeSurfaceTarget(target, { projectRoot })) + .filter((target) => target && target !== normalizedPrimary))]; + const slug = slugFromTarget(normalizedPrimary, { cwd: projectRoot }); + const filePath = surfaceBriefPathForTarget(normalizedPrimary, { projectRoot }); + fs.mkdirSync(path.dirname(filePath), { recursive: true }); + const frontmatter = [ + '---', + `version: ${SURFACE_BRIEF_VERSION}`, + `slug: ${JSON.stringify(slug)}`, + `primary_target: ${JSON.stringify(normalizedPrimary)}`, + `related_targets: ${JSON.stringify(normalizedRelated)}`, + '---', + ].join('\n'); + fs.writeFileSync(filePath, `${frontmatter}\n\n${String(body || '').trim()}\n`, 'utf-8'); + return filePath; +} diff --git a/.rovodev/skills/impeccable/scripts/lib/target-slug.mjs b/.rovodev/skills/impeccable/scripts/lib/target-slug.mjs new file mode 100644 index 000000000..025915ad5 --- /dev/null +++ b/.rovodev/skills/impeccable/scripts/lib/target-slug.mjs @@ -0,0 +1,33 @@ +import path from 'node:path'; + +const SLUG_MAX = 50; + +/** Derive one clone-stable slug from a concrete file path or URL. */ +export function slugFromTarget(resolved, { cwd = process.cwd() } = {}) { + if (!resolved || typeof resolved !== 'string') return null; + const trimmed = resolved.trim(); + if (!trimmed) return null; + + if (/^https?:\/\//i.test(trimmed)) { + let url; + try { url = new URL(trimmed); } catch { return null; } + return kebab(`${url.hostname}${url.pathname}`); + } + + const abs = path.isAbsolute(trimmed) ? trimmed : path.resolve(cwd, trimmed); + let rel = path.relative(cwd, abs); + if (rel.startsWith('..') || path.isAbsolute(rel)) rel = path.basename(abs); + if (!rel || rel === '.') return null; + return kebab(rel); +} + +export function kebab(value) { + const slug = String(value || '') + .toLowerCase() + .replace(/[/\\.]+/g, '-') + .replace(/[^a-z0-9-]+/g, '-') + .replace(/-+/g, '-') + .replace(/^-|-$/g, ''); + if (!slug) return null; + return slug.length <= SLUG_MAX ? slug : slug.slice(slug.length - SLUG_MAX).replace(/^-/, ''); +} diff --git a/.rovodev/skills/impeccable/scripts/live-browser.js b/.rovodev/skills/impeccable/scripts/live-browser.js index 616f1290a..47bbfefbf 100644 --- a/.rovodev/skills/impeccable/scripts/live-browser.js +++ b/.rovodev/skills/impeccable/scripts/live-browser.js @@ -2592,7 +2592,7 @@ minWidth: '16px', height: '16px', padding: '0 4px', borderRadius: '999px', background: tuneOpen ? C.brand : BP.hairline, - color: tuneOpen ? 'oklch(98% 0 0)' : 'inherit', + color: tuneOpen ? C.ink : 'inherit', fontFamily: MONO, fontSize: '9.5px', fontWeight: '600', lineHeight: '1', boxSizing: 'border-box', @@ -3131,7 +3131,7 @@ position: 'absolute', top: '2px', left: initial ? '18px' : '2px', width: '16px', height: '16px', borderRadius: '50%', - background: 'oklch(98% 0 0)', + background: C.ink, transition: 'left 0.18s ' + EASE, boxShadow: '0 1px 2px oklch(0% 0 0 / 0.2)', }); @@ -3165,7 +3165,7 @@ const b = el('button', { padding: '5px 4px', border: 'none', borderRadius: '3px', background: active ? C.brand : 'transparent', - color: active ? 'oklch(98% 0 0)' : P.text, + color: active ? C.ink : P.text, fontFamily: FONT, fontSize: '10.5px', fontWeight: '500', cursor: 'pointer', whiteSpace: 'nowrap', transition: 'background 0.1s ease, color 0.1s ease', @@ -3178,7 +3178,7 @@ segBtns.forEach(({ btn, val }) => { const on = val === o.value; btn.style.background = on ? C.brand : 'transparent'; - btn.style.color = on ? 'oklch(98% 0 0)' : P.text; + btn.style.color = on ? C.ink : P.text; }); applyParamValue(variantEl, p, o.value); queueCheckpoint('param_changed'); diff --git a/.rovodev/skills/impeccable/scripts/surface-brief.mjs b/.rovodev/skills/impeccable/scripts/surface-brief.mjs new file mode 100644 index 000000000..723f7c1b4 --- /dev/null +++ b/.rovodev/skills/impeccable/scripts/surface-brief.mjs @@ -0,0 +1,74 @@ +#!/usr/bin/env node +import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath, pathToFileURL } from 'node:url'; +import { resolveProjectRoot } from './context.mjs'; +import { + listSurfaceBriefs, + resolveSurfaceBrief, + surfaceBriefPathForTarget, + writeSurfaceBrief, +} from './lib/surface-briefs.mjs'; + +function summary(brief, projectRoot) { + return { + slug: brief.slug, + path: path.relative(projectRoot, brief.path).split(path.sep).join('/'), + primaryTarget: brief.primaryTarget, + relatedTargets: brief.relatedTargets, + }; +} + +function main(argv) { + const [command, target, bodyFile, ...relatedTargets] = argv; + const projectRoot = resolveProjectRoot(process.cwd(), target ? { targetPath: target } : {}); + if (command === 'path') { + const filePath = surfaceBriefPathForTarget(target, { projectRoot }); + if (!filePath) throw new Error('surface brief path requires a concrete target'); + process.stdout.write(`${path.relative(process.cwd(), filePath) || filePath}\n`); + return; + } + if (command === 'list') { + process.stdout.write(`${JSON.stringify(listSurfaceBriefs(projectRoot).map((brief) => summary(brief, projectRoot)), null, 2)}\n`); + return; + } + if (command === 'read') { + const result = resolveSurfaceBrief(projectRoot, target || null); + if (result.brief) { + process.stdout.write(result.brief.text); + return; + } + if (result.candidates.length) process.stderr.write(`${JSON.stringify(result.candidates.map((brief) => summary(brief, projectRoot)), null, 2)}\n`); + process.exit(2); + } + if (command === 'write') { + if (!target || !bodyFile) throw new Error('usage: surface-brief.mjs write '); + const filePath = writeSurfaceBrief({ + projectRoot, + primaryTarget: target, + relatedTargets, + body: fs.readFileSync(bodyFile, 'utf-8'), + }); + process.stdout.write(`${path.relative(process.cwd(), filePath) || filePath}\n`); + return; + } + throw new Error('usage: surface-brief.mjs [target] [body-file] [related-target ...]'); +} + +function isMainModule() { + if (!process.argv[1]) return false; + try { + return fs.realpathSync(fileURLToPath(import.meta.url)) === fs.realpathSync(process.argv[1]); + } catch { + return import.meta.url === pathToFileURL(process.argv[1]).href; + } +} + +if (isMainModule()) { + try { + main(process.argv.slice(2)); + } catch (error) { + process.stderr.write(`${error?.message || error}\n`); + process.exit(1); + } +} diff --git a/.trae-cn/skills/impeccable/SKILL.md b/.trae-cn/skills/impeccable/SKILL.md index 9294ae5c7..95e1bda48 100644 --- a/.trae-cn/skills/impeccable/SKILL.md +++ b/.trae-cn/skills/impeccable/SKILL.md @@ -1,62 +1,61 @@ --- name: impeccable description: Use when the user wants to design, redesign, shape, critique, audit, polish, clarify, distill, harden, optimize, adapt, animate, colorize, extract, or otherwise improve a frontend interface. Covers websites, landing pages, dashboards, product UI, app shells, components, forms, settings, onboarding, and empty states. Handles UX review, visual hierarchy, information architecture, cognitive load, accessibility, performance, responsive behavior, theming, anti-patterns, typography, fonts, spacing, layout, alignment, color, motion, micro-interactions, UX copy, error states, edge cases, i18n, and reusable design systems or tokens. Also use for bland designs that need to become bolder or more delightful, loud designs that should become quieter, live browser iteration on UI elements, or ambitious visual effects that should feel technically extraordinary. Not for backend-only or non-UI tasks. -version: 4.0.0-alpha.9 +version: 4.0.0-alpha.10 user-invocable: true argument-hint: "[craft|shape · audit|critique · animate|bolder|colorize|delight|layout|overdrive|quieter|typeset · adapt|clarify|distill · harden|onboard|optimize|polish · init|document|extract|live] [target]" license: Apache 2.0 --- -Designs and iterates production-grade frontend interfaces. Real working code, committed design choices, exceptional craft. +This skill gives you the tools and permission to create design that earns to be called out-of-distribution craft: Whereas before, your design work would have been safe, timid and measured, you now approach every design task as a award-winning design director with impeccable understanding for what makes exceptional design work: production-grade code, peak creativity, a clear POV, deep understanding of the needs of the client and users, and exceptional craft. -Approach every design task as the design lead at a small studio known for giving every client a visual identity that could not be mistaken for anyone else's. The client has already rejected work that felt templated; they are paying for a point of view. the model is capable of extraordinary work. Don't hold back. +Core principles: +- Go all out. No hedging, no shortcuts. The deliverable must be complete (except assets the user must provide). +- Dream big and bold. Distinct, beautiful, outstanding and highly inspiring work. +- Iterate with tools available to you (e.g. visual understanding, browser screenshots) until you think this meets the bar. ## Setup -1. Run `node .trae-cn/skills/impeccable/scripts/context.mjs` once per session (if the runtime shows this skill's loaded base directory, run `node /scripts/context.mjs`; keep cwd at the user's project). It prints the project's context and its directives; follow what it prints. Once its output is in the conversation, never rerun it on a later turn. -2. `craft` and `shape` are build-path exceptions: the new-work gate below owns their flow, and on unattended runs its checkpoints resolve without pausing. For any other invoked sub-command (`audit`, `polish`, `live`, ...), immediately read **`reference/.md`** after `context.mjs` (the `.native` variant from the Commands table when the platform is `ios`/`android`/`adaptive`) and follow it. This read is a hard gate: do not inspect the target, run command-specific scripts, or edit files until the reference is loaded. -3. Read at least one project file (CSS / tokens / theme / a representative component) to learn what world you're in. If PRODUCT.md's `## Platform` is `ios` or `android`, also read `reference/.md` (`adaptive` reads both). +1. Run `node .trae-cn/skills/impeccable/scripts/context.mjs` once per session (if the runtime shows this skill's loaded base directory, run `node /scripts/context.mjs`; keep cwd at the user's project). Pass a named source file or route as `--target `. It loads PRODUCT.md, DESIGN.md, the matching surface brief, and native-platform guidance when applicable; follow its directives and do not rerun it. +2. Before acting, load the one playbook that owns the request: the Commands table's reference for an explicit or clearly implied sub-command, or [reference/new-work.md](reference/new-work.md) for a new surface or replacement visual world. Then inspect the target and at least one representative source of incumbent visual truth (tokens, theme, CSS, component, or asset) before editing. ## How to design -**The brief wins.** Where the brief pins down a direction (a named aesthetic, an era, a place, a material, a specific font or palette), follow it exactly, including when it asks for a look this skill warns is saturated. Redirecting a pinned direction toward your own taste is a failure, not a save. - -**Existing worlds are sacred.** Most of impeccable's work happens inside a site or app that already exists. When the surface has a committed design system (real tokens, deliberately chosen faces, a palette the brand owns), work inside that world: extend it, sharpen it, leave it unmistakably the same brand, and never degrade a working page's performance. Inventing parallel colors, fonts, or styles on an existing surface is a defect, not creativity. A scoped refinement keeps the named scope's content and media footprint unless the brief explicitly changes them; build the added emphasis by recomposing what is already there through hierarchy, proportion, rhythm, and the system's own motifs. - -**New identity work reads the playbook first.** When nothing committed exists (greenfield, or a codebase with no real tokens or chosen faces), or the user explicitly asks for a redesign that discards the current look, you MUST read [reference/new-work.md](reference/new-work.md) before making any design decision. Missing DESIGN.md alone does not make work greenfield: code, tokens, chosen type, components, and assets are incumbent design authority. Preserve and extend them unless the user asked to replace them. Producing a genuinely new identity without the playbook yields the generic default this skill exists to prevent. Scoped fixes inside an existing world don't need it; the craft floor below governs them. `context.mjs` prints the appropriate authority directive. +- **The brief wins.** Honor pinned aesthetics, eras, materials, fonts, and palettes even when they conflict with a saturated-pattern warning. Redirecting a clear brief toward your taste is failure. +- **Refinement preserves; redesign replaces.** Refinement keeps the incumbent identity, behavior, copy, and everything outside scope. Ask before replacing factual copy or adding claims. Redesign keeps product truth, content, function, native affordances, and constraints, but treats the old look as evidence and anti-reference; choose a replacement world in new-work and replace DESIGN.md. Never split the difference into polish on the discarded look. +- **Visual authority is evidence, not a filename.** Missing DESIGN.md alone does not make a project greenfield; new-work decides whether to preserve, expand, or replace the incumbent world. ## Modes -Name the visitor's mode before designing; the page's grammar follows from it, and most ruined pages are one mode wearing another mode's grammar. **The mode belongs to the surface, not the subject**: a landing page for a dense technical tool is still Persuade, with Persuade's full permission to be striking; a docs page for a fashion house is still Read. Deciding a page can be plain because its subject is workmanlike is the same category error in reverse. The brief and the surface decide the mode; PRODUCT.md's `register` field survives only as a family hint (`brand` covers Persuade and Experience, `product` covers Operate and Read). Depth beyond the paragraphs below: [reference/new-work.md](reference/new-work.md) when inventing identity, [reference/operate.md](reference/operate.md) for substantial Operate and Read work. +Choose the mode from the requested surface, not the product, and persist it only in that surface brief. A tool's landing page is still Persuade; a fashion house's documentation is still Read. See [new-work.md](reference/new-work.md) for new surfaces and [operate.md](reference/operate.md) for deeper Operate/Read guidance. -**Persuade** (the surface exists to win someone over; design IS the product). The deliverable is an impression that stops the scroll, earns the click, converts. Spans every genre; don't collapse them into one look. On new surfaces, briefs that imply imagery must ship real, verified imagery; a colored rectangle where a photo belongs reads as incomplete. New Persuade surfaces take their typeface procedure and reject list from [reference/new-work.md](reference/new-work.md). - -**Operate** (the surface is a tool someone works in; design SERVES the task). A person getting something done: scanability and consistency outrank expressiveness. These surfaces earn trust by feeling native to their platform: system font stacks and workhorse UI faces are legitimate and often correct here (the Persuade reject list does not apply). The brand lives in the details: focus states, empty states, microcopy, one owned accent. The usage scene is part of the spec: an interface read outdoors, in motion, or at a glance must survive its real ambient light, and the theme follows the scene, not the category's habit. - -**Read** (the surface exists to be understood; long-form, reference, guidance). The deliverable is comprehension, and comprehension is earned twice: a structure the reader can hold in their head with nothing standing between them and the answer, and a reading experience good enough to stay in, through typographic quality and whatever visual or interactive support genuinely helps the reader follow. The brand lives in type, spacing, and small accents. - -**Experience** (the surface presents a body of work; the page IS the work). The artifact leads, the interface recedes, and the visitor meets the work itself in the first viewport at every screen size. Boldness here means trusting the work. +- **Persuade:** win someone over; design is the product. Earn attention and action. Ship real imagery when the brief needs it; follow the committed world, not category habit. +- **Operate:** help someone do work. Scanability, consistency, native expectations, and the real usage scene outrank expression. Brand lives in precise details. +- **Read:** make something understood. Structure for comprehension, then make the reading experience worth staying in. +- **Experience:** present a body of work. Let the artifact lead from the first viewport; the interface recedes. ## Craft floor -Build to this floor without announcing it. The design detector (the project hook, `node .trae-cn/skills/impeccable/scripts/detect.mjs --json `, or `audit`) verifies most of it mechanically; resolve every finding before finalizing. Fix real defects, but use context judgment rather than distorting intentional design to appease a false positive. Classify any intentional exception explicitly and use the hook system's narrowest appropriate waiver when it must persist. +Build to this floor without announcing it. -- Contrast: body text ≥4.5:1 against its background (placeholders too); large text ≥3:1. Gray text on a colored background looks washed out: use a darker shade of the background's own hue, or a transparency of the text color. -- Shadows describe real light: an offset and a soft blur. A zero-offset colored halo is decoration announcing itself. -- Spacing has rhythm: generous separations, tight groupings; cramped padding reads as broken; the space above a heading exceeds the space below it. Verify computed spacing, not intended spacing. -- Type: body line length 65-75ch; display clamp() max ≤6rem; letter-spacing ≥-0.04em; `text-wrap: balance` on headings; modular scale ≥1.25 between steps; light-on-dark adds 0.05-0.1 line-height. Pair faces on a contrast axis, never two similar-but-not-identical ones; one family with committed weight contrast beats a timid pair. Test headings at every breakpoint; overflow means reduce the clamp or rewrite the copy. -- Motion is part of the build: one orchestrated moment beats scattered effects; ease-out exponential curves; reveals enhance an already-visible default (content gated on a class-triggered transition ships blank in hidden tabs and headless renderers). Responsive down to mobile and visible keyboard focus are part of the floor. -- Ship real content (no placeholders, dead links, or fake controls) and cover the interaction states people will actually hit (hover, focus, disabled, loading, error, empty). -- Copy is design material: name things the way the page's own people speak, make every control say what it does, and make every error say what happened and what to do next. -- Before finishing, re-read the brief: every requirement it names must exist on the page, findable in seconds. A beautiful page missing an asked-for feature is unfinished. +- **Contrast:** body and placeholder text ≥4.5:1; large text ≥3:1. On colored surfaces, tint secondary text from that hue or the foreground instead of using gray. +- **Depth:** shadows describe light with offset and soft blur; zero-offset colored halos are decoration. +- **Spacing:** tight groups, generous separation, no cramped containers; space above a heading exceeds space below. Verify computed values. +- **Type:** body measure 65–75ch; display max 6rem and tracking floor -0.04em; balance headings; use clear scale/weight contrast; test overflow at every breakpoint. +- **Motion:** author one coherent moment instead of scattered effects. Use exponential ease-out and an already-visible default. Premium motion is not transform/opacity alone: it may add focus, depth, masks, light, or material change through blur/filter, backdrop-filter, clip-path/masks, or shadow when smooth. Always provide reduced motion. +- **Shipping:** real content, working controls, responsive composition, keyboard focus, and the states users hit: hover, disabled, loading, error, and empty. +- **Copy:** use the product's language; controls name their action, errors name the problem and recovery. +- **Coverage:** every brief requirement must exist and be findable within seconds. + +Before finishing changed UI, follow the quality guidance supplied by `context.mjs` and hooks. Context requests a manual scan only when no automatic detector is active; never add a second detector pass. ## Commands | Command | Category | Description | Reference | |---|---|---|---| -| `craft [feature]` | Build | The standard build flow with attended checkpoints | [reference/new-work.md](reference/new-work.md) | +| `craft [feature]` | Build | Deprecated alias for an ordinary new-work request | [reference/craft.md](reference/craft.md) | | `shape [feature]` | Build | Plan UX/UI before writing code | [reference/shape.md](reference/shape.md) | -| `init` | Build | Set up project context: PRODUCT.md, DESIGN.md, live config, next steps | [reference/init.md](reference/init.md) | +| `init` | Build | Capture durable product context in PRODUCT.md | [reference/init.md](reference/init.md) | | `document` | Build | Generate DESIGN.md from existing project code | [reference/document.md](reference/document.md) | | `extract [target]` | Build | Pull reusable tokens and components into design system | [reference/extract.md](reference/extract.md) | | `critique [target]` | Evaluate | UX design review with heuristic scoring | [reference/critique.md](reference/critique.md) | @@ -78,7 +77,14 @@ Build to this floor without announcing it. The design detector (the project hook | `optimize [target]` | Fix | Diagnose and fix UI performance | [reference/optimize.md](reference/optimize.md) | | `live` | Iterate | Visual variant mode: pick elements in the browser, generate alternatives | [reference/live.md](reference/live.md) | -Routing: **no argument** → read [reference/routing.md](reference/routing.md) and present the context-aware menu (never auto-run a command). **First word matches a command** (or `pin` / `unpin` / `hooks`) → load its reference (native variant on native platforms) and follow it; everything after the command name is the target. **Intent clearly maps to one command** ("fix the spacing" → `layout`, "rewrite this error" → `clarify`) → same; if two fit, ask once. **Otherwise** → general design invocation: apply Setup and this file's guidance; builds flow through the new-work gate above, whose playbook carries the direction checkpoint and the finishing pass. `teach` routes to `init`, and `craft` routes to the standard build flow with attended checkpoints. If setup diverted into `init` for a build request, finish init, refresh context, then resume. +Routing: + +- **No argument:** read [routing.md](reference/routing.md) and present its context-aware menu; never auto-run a command. +- **Explicit or clearly implied command:** load its reference (native variant on native platforms) and follow it. Ask once if two commands fit. +- **Otherwise:** treat the request as general design work. Missing PRODUCT.md routes through init; new surfaces and replacement worlds use new-work. +- `teach` aliases `init`. `craft` is a deprecated alias for ordinary new-work and adds nothing. `shape` owns task discovery, then enters new-work only for visual-world and surface-concept decisions. + +After init writes PRODUCT.md, resume without rerunning `context.mjs`. **Pin / Unpin:** `node .trae-cn/skills/impeccable/scripts/pin.mjs ` creates or removes a standalone `/` shortcut. Report the script's result concisely; relay stderr verbatim on error. diff --git a/.trae-cn/skills/impeccable/reference/android.md b/.trae-cn/skills/impeccable/reference/android.md index 3575c31fb..6337b9018 100644 --- a/.trae-cn/skills/impeccable/reference/android.md +++ b/.trae-cn/skills/impeccable/reference/android.md @@ -2,7 +2,7 @@ For native Android apps: Jetpack Compose, Android Views, React Native, Expo, Flutter shipping to Android hardware. -On native, register narrows. Material Design 3 governs structure, navigation, and interaction whatever the register; brand expresses through Material's theming (color roles, type scale, shape, motion). A Material-everywhere cross-platform app that also ships to iPhone still owes iOS its OS guarantees on that hardware: safe-area insets, Reduce Motion, edge-swipe back. +On native, the visitor mode narrows what expression may override. Material Design 3 governs structure, navigation, and interaction in every mode; brand expresses through Material's theming (color roles, type scale, shape, motion). A Material-everywhere cross-platform app that also ships to iPhone still owes iOS its OS guarantees on that hardware: safe-area insets, Reduce Motion, edge-swipe back. ## The Android slop test diff --git a/.trae-cn/skills/impeccable/reference/animate.md b/.trae-cn/skills/impeccable/reference/animate.md index 1a1098a5b..d51bb6d63 100644 --- a/.trae-cn/skills/impeccable/reference/animate.md +++ b/.trae-cn/skills/impeccable/reference/animate.md @@ -1,203 +1,88 @@ > **Additional context needed**: performance constraints. -Add motion that conveys state, gives feedback, and clarifies hierarchy. Cut motion that exists only for decoration. Animation fatigue is a real cost; spend the budget on the moments that need it. +Use motion to explain state, relationship, and hierarchy, or to create one authored moment the surface has earned. Decoration without purpose is animation debt. --- -## Register +## Visitor mode -Persuade + Experience: 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. +- **Persuade + Experience:** motion may carry the voice. Prefer one rehearsed focal sequence to repeated section reveals. +- **Operate + Read:** motion serves feedback, state, and continuity. Keep routine transitions fast and do not make users wait through page-load choreography. +- **Native (`ios` / `android` / `adaptive`):** follow the Motion section of [ios.md](ios.md) or [android.md](android.md), including the platform's Reduce Motion behavior. Do not apply the web tooling below. -Operate + Read: 150–250 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. +## Find the job -Native (`ios` / `android` / `adaptive`): implementation follows the Motion section of [ios.md](ios.md) / [android.md](android.md) (read it first if Setup hasn't already): system transitions and OS Reduce Motion, never the web tooling below. +Inspect the existing motion language, interaction states, target devices, and performance budget. Find only the places where motion would: ---- +- acknowledge an action; +- make a state change or spatial relationship legible; +- preserve continuity through navigation or layout change; +- direct attention at a meaningful moment; +- embody the selected visual world. -## Assess Animation Opportunities +Ask only when a material constraint cannot be inferred. Do not animate a static area merely because it exists. -Analyze where motion would improve the experience: +## Set the motion thesis -1. **Identify static areas**: - - **Missing feedback**: Actions without visual acknowledgment (button clicks, form submission, etc.) - - **Jarring transitions**: Instant state changes that feel abrupt (show/hide, page loads, route changes) - - **Unclear relationships**: Spatial or hierarchical relationships that aren't obvious - - **Lack of delight**: Functional but joyless interactions - - **Missed guidance**: Opportunities to direct attention or explain behavior +Write a short plan before implementation: -2. **Understand the context**: - - What's the personality? (Playful vs serious, energetic vs calm) - - What's the performance budget? (Mobile-first? Complex page?) - - Who's the audience? (Motion-sensitive users? Power users who want speed?) - - What matters most? (One hero animation vs many micro-interactions?) +- **Focal moment:** the one sequence or interaction that deserves authorship, if any. +- **Continuity:** the state, layout, or navigation changes that need explanation. +- **Feedback:** the controls and outcomes that need acknowledgment. +- **Budget:** which effects may be expensive, how often they run, and the reduced-motion equivalent. -If any of these are unclear from the codebase, ask the user directly to clarify what you cannot infer. +The focal moment must come from this product and surface concept. A generic fade-and-rise, hover lift, parallax layer, or scroll reveal is not a thesis. -**CRITICAL**: Respect `prefers-reduced-motion`. Always provide non-animated alternatives for users who need them. +## Choose material by meaning -## Plan Animation Strategy +Transform and opacity are reliable foundations, not the entire palette. Choose properties for what the transition communicates: -Create a purposeful animation plan: +- **Continuity and relationship:** shared-element motion, FLIP-style transforms, view transitions, or deliberate spatial movement. +- **Focus and depth:** bounded blur, filter, backdrop, light, or shadow changes. +- **Reveal and composition:** masks, clip paths, cropping, or controlled occlusion. +- **Material and energy:** color, gradient position, texture, distortion, or shader effects when the world and runtime support them. +- **State and feedback:** the smallest change that makes cause and result unmistakable. -- **Feedback layer**: Which interactions need acknowledgment? -- **Transition layer**: Which state changes need smoothing? -- **Entrance moment**: The ONE entrance worth rehearsing, where the mode invites it. Not every surface wants one. -- **Delight layer**: Where can we surprise and delight? +Do not stack techniques for spectacle. One strong material idea, carried through the focal sequence and quiet supporting states, is usually enough. -**IMPORTANT**: One well-orchestrated experience beats scattered animations everywhere. Focus on high-impact moments. +Sibling stagger is appropriate when a list appears as a list. Cap the total delay, and never reinterpret every scrolled section as a staggered list. -## Implement Animations +## Timing and easing -Add motion systematically across these categories: +Timing should express distance and consequence: -### Micro-interactions -- **Button feedback**: - - Hover: Subtle scale (1.02-1.05), color shift, shadow increase - - Click: Quick scale down then up (0.95 → 1), ripple effect - - Loading: Spinner or pulse state -- **Form interactions**: - - Input focus: Border color transition, slight scale or glow - - Validation: Shake on error, check mark on success, smooth color transitions -- **Toggle switches**: Smooth slide + color transition (200-300ms) -- **Checkboxes/radio**: Check mark animation, ripple effect -- **Like/favorite**: Scale + rotation, particle effects, color transition +| Duration | Typical use | +|---|---| +| 100–150 ms | immediate feedback | +| 150–300 ms | routine state change | +| 300–500 ms | layout, overlay, or view transition | +| 500–800 ms | a deliberately authored focal entrance | -### State Transitions -- **Show/hide**: Fade + slide (not instant), appropriate timing (200-300ms) -- **Expand/collapse**: Height transition with overflow handling, icon rotation -- **Loading states**: Skeleton screen fades, spinner animations, progress bars -- **Success/error**: Color transitions, icon animations, gentle scale pulse -- **Enable/disable**: Opacity transitions, cursor changes +Exit faster than entrance. Use natural deceleration such as `cubic-bezier(0.16, 1, 0.3, 1)` for confident arrivals; do not use bounce or elastic curves by reflex. Long feedback feels like latency. -### Entrance Animations -- **The one entrance moment** (where the mode invites it): a committed entrance for primary content (scale, parallax, or a creative effect) -- **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. +## Implement to the runtime - 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. +- Use CSS transitions and keyframes for declarative state and bounded sequences. +- Use Web Animations API or the project's existing motion library for interruption, sequencing, and dynamic values. +- Use View Transitions or shared-element techniques when continuity across states is the point. +- Use scroll-driven motion only when the scroll relationship itself carries meaning, with a robust fallback. +- Do not add a dependency for an effect the existing stack can express cleanly. -### Navigation & Flow -- **Page transitions**: Crossfade between routes, shared element transitions -- **Tab switching**: Slide indicator, content fade/slide -- **Carousel/slider**: Smooth transforms, snap points, momentum -- **Scroll effects**: Parallax layers, sticky headers with state changes, scroll progress indicators +Keep content visible in the default state so failed scripts do not hide the page. Avoid casually animating layout-driving properties such as `width`, `height`, `top`, `left`, and margins; use FLIP, transforms, or grid techniques when appropriate. Bound blur, filter, shadow, canvas, and shader work to isolated regions. Apply `will-change` only during known animation. Measure on target viewports and devices rather than assuming transform means fast. -### Feedback & Guidance -- **Hover hints**: Tooltip fade-ins, cursor changes, element highlights -- **Drag & drop**: Lift effect (shadow + scale), drop zone highlights, smooth repositioning -- **Copy/paste**: Brief highlight flash on paste, "copied" confirmation -- **Focus flow**: Highlight path through form or workflow +## Accessibility and control -### Delight Moments -- **Empty states**: Subtle floating animations on illustrations -- **Completed actions**: Confetti, check mark flourish, success celebrations -- **Easter eggs**: Hidden interactions for discovery -- **Contextual animation**: Weather effects, time-of-day themes, seasonal touches +Honor `prefers-reduced-motion` with an intentional alternative. Preserve state change and hierarchy while removing travel, parallax, flashing, or prolonged sequences. Do not replace every animation globally with `0.01ms` if that destroys useful feedback. Motion must never block focus, interaction, reading, or task completion. -## Technical Implementation +Respect autoplay and sound preferences. Any nonessential loop must stop when offscreen or hidden. -Use appropriate techniques for each animation: +## Verify -### Timing & Easing +- The focal motion is specific to the selected world and surface. +- Every supporting animation explains feedback, state, or relationship. +- Interruption and repeated use behave correctly. +- Desktop, mobile, keyboard, and reduced-motion paths remain usable. +- Expensive effects stay smooth on the target device. +- Removing an animation would lose meaning or authored character, not merely decoration. -**Duration: the 100/300/500 rule.** Timing matters more than easing for "feels right": - -| Duration | Use Case | Examples | -|----------|----------|----------| -| **100–150ms** | Instant feedback | Button press, toggle, color change | -| **200–300ms** | State changes | Menu open, tooltip, hover state | -| **300–500ms** | Layout changes | Accordion, modal, drawer | -| **500–800ms** | Entrance animations | Page load, hero reveal | - -**Easing curves (use these, not CSS defaults):** -```css -/* Recommended: natural deceleration */ ---ease-out-quart: cubic-bezier(0.25, 1, 0.5, 1); /* Smooth */ ---ease-out-quint: cubic-bezier(0.22, 1, 0.36, 1); /* Slightly snappier */ ---ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1); /* Confident, decisive */ - -/* AVOID: feel dated and tacky */ -/* bounce: cubic-bezier(0.34, 1.56, 0.64, 1); */ -/* elastic: cubic-bezier(0.68, -0.6, 0.32, 1.6); */ -``` - -**Exit animations are faster than entrances.** Use ~75% of enter duration. - -### CSS Animations -```css -/* Prefer for simple, declarative animations */ -- transitions for state changes -- @keyframes for complex sequences -- transform and opacity for reliable movement -- blur, filters, masks, clip paths, shadows, and color shifts for premium atmospheric effects when verified smooth -``` - -### JavaScript Animation -```javascript -/* Use for complex, interactive animations */ -- Web Animations API for programmatic control -- Framer Motion for React -- 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 -- **Layout safety**: Avoid casual animation of layout-driving properties (`width`, `height`, `top`, `left`, margins) -- **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) { - * { - animation-duration: 0.01ms !important; - animation-iteration-count: 1 !important; - transition-duration: 0.01ms !important; - } -} -``` - -**NEVER**: -- Use bounce or elastic easing curves; they feel dated and draw attention to the animation itself -- Animate layout properties casually (`width`, `height`, `top`, `left`, margins) when transform, FLIP, or grid-based techniques would work -- Use durations over 500ms for feedback (it feels laggy) -- Animate without purpose (every animation needs a reason) -- Ignore `prefers-reduced-motion` (this is an accessibility violation) -- Animate everything (animation fatigue makes interfaces feel exhausting) -- Block interaction during animations unless intentional - -## Verify Quality - -Test animations thoroughly: - -- **Smooth at 60fps**: No jank on target devices -- **Feels natural**: Easing curves feel organic, not robotic -- **Appropriate timing**: Not too fast (jarring) or too slow (laggy) -- **Reduced motion works**: Animations disabled or simplified appropriately -- **Doesn't block**: Users can interact during/after animations -- **Adds value**: Makes interface clearer or more delightful - -When the motion clarifies state instead of decorating it, hand off to `/impeccable polish` for the final pass. +When motion earns its place, hand off to `/impeccable polish` for the final pass. diff --git a/.trae-cn/skills/impeccable/reference/audit.md b/.trae-cn/skills/impeccable/reference/audit.md index 72815af69..70d80771a 100644 --- a/.trae-cn/skills/impeccable/reference/audit.md +++ b/.trae-cn/skills/impeccable/reference/audit.md @@ -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 - diff --git a/.trae-cn/skills/impeccable/reference/audit.native.md b/.trae-cn/skills/impeccable/reference/audit.native.md index 737d50e4f..0126fa157 100644 --- a/.trae-cn/skills/impeccable/reference/audit.native.md +++ b/.trae-cn/skills/impeccable/reference/audit.native.md @@ -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) diff --git a/.trae-cn/skills/impeccable/reference/bolder.md b/.trae-cn/skills/impeccable/reference/bolder.md index 6b44f0e38..78f5e4811 100644 --- a/.trae-cn/skills/impeccable/reference/bolder.md +++ b/.trae-cn/skills/impeccable/reference/bolder.md @@ -8,12 +8,12 @@ ## Why it reads flat -A section usually reads flat for reasons its neighbors have already solved. Look at what the rest of the page does that this section does not: the display type at full strength, the structural devices that carry meaning, the signature motif, the density and pacing, the specificity of the copy. A flat section is typically one that quietly opts out of the system's own strongest moves. The most reliable bolder pass brings the target up to the expressive level its neighbors already reach, in the system's own vocabulary rather than a new one. +A section usually reads flat for reasons its neighbors have already solved. Look at what the rest of the page does that this section does not: the display type at full strength, the structural devices that carry meaning, the signature motif, the density and pacing. A flat section is typically one that quietly opts out of the system's own strongest moves. The most reliable bolder pass brings the target up to the expressive level its neighbors already reach, in the system's own vocabulary rather than a new one. ## The amplification - **Amplify what the system already owns.** Reuse its motif and its type scale at full strength, turned up for this section rather than invented for it. The bolder version should look more like the same brand, not less. -- **Let the content carry the weight.** Flat copy makes a flat design, and no amount of size fixes a generic claim. Make every word specific enough to earn its place, and let the section's real evidence do the work that a decorative addition would otherwise fake. +- **Keep content true.** Existing claims are part of the scope: preserve them unless the user supplies replacements. If real evidence is essential to the direction but absent, ask for it. - **Commit, then clarify.** Half-measures read as noise. Make the one decisive move completely, then quiet everything around it so the move is legible. If every element got louder, the section got flatter. - **Give it its own rhythm.** The target should read as a peak in the scroll, a shift in density or pace from what surrounds it, not simply more of the same. diff --git a/.trae-cn/skills/impeccable/reference/clarify.md b/.trae-cn/skills/impeccable/reference/clarify.md index 00ee978c3..3047a9d82 100644 --- a/.trae-cn/skills/impeccable/reference/clarify.md +++ b/.trae-cn/skills/impeccable/reference/clarify.md @@ -1,288 +1,94 @@ -> **Additional context needed**: audience technical level and users' mental state in context. +> **Additional context needed**: audience knowledge and emotional state. -Find the unclear, confusing, or poorly written interface text and rewrite it. Vague copy creates support tickets and abandonment; specific copy gets users through the task. +Rewrite unclear interface text so users understand what happened, what matters, and what to do next. Preserve factual meaning, product terminology, and brand voice. +## Audit the language ---- +Read the entire interaction path, not isolated strings. Identify: -## Assess Current Copy +- ambiguous nouns, verbs, and actions; +- internal jargon or assumed knowledge; +- vague labels, outcomes, and system states; +- missing consequences, recovery, or timing; +- inconsistent terminology and capitalization; +- redundant headings, intros, helper text, and confirmations; +- text that breaks at realistic widths or in translation; +- tone that ignores stress, risk, success, or urgency. -Identify what makes the text unclear or ineffective: +Infer audience and task from product context and surrounding UI. Ask before changing factual claims, legal meaning, or a term that may be domain-specific. -1. **Find clarity problems**: - - **Jargon**: Technical terms users won't understand - - **Ambiguity**: Multiple interpretations possible - - **Passive voice**: "Your file has been uploaded" vs "We uploaded your file" - - **Length**: Too wordy or too terse - - **Assumptions**: Assuming user knowledge they don't have - - **Missing context**: Users don't know what to do or why - - **Tone mismatch**: Too formal, too casual, or inappropriate for situation +## Set the message hierarchy -2. **Understand the context**: - - Who's the audience? (Technical? General? First-time users?) - - What's the user's mental state? (Stressed during error? Confident during success?) - - What's the action? (What do we want users to do?) - - What's the constraint? (Character limits? Space limitations?) +For each state, decide: -**CRITICAL**: Clear copy helps users succeed. Unclear copy creates frustration, errors, and support tickets. +1. the one fact the user needs now; +2. the action available next; +3. supporting context that changes the decision; +4. the appropriate tone for this moment. -## Plan Copy Improvements +Say each idea once. If the heading already explains the state, the introduction should add new information or disappear. -Create a strategy for clearer communication: +## Rewrite by function -- **Primary message**: What's the ONE thing users need to know? -- **Action needed**: What should users do next (if anything)? -- **Tone**: How should this feel? (Helpful? Apologetic? Encouraging?) -- **Constraints**: Length limits, brand voice, localization considerations +### Actions and navigation -**IMPORTANT**: Good UX writing is invisible. Users should understand immediately without noticing the words. +Use a specific verb and object when the outcome is not already obvious. Labels should describe what will happen, not the gesture used to trigger it. Keep the same noun and verb for the same concept throughout the product. -## Improve Copy Systematically +For destructive actions, name the object and consequence. Prefer undo over confirmation when recovery is safe. When confirmation is necessary, name the action on both the message and button instead of using `Yes`, `No`, `OK`, or `Submit`. -Refine text across these common areas: +### Forms -### Error Messages -**Bad**: "Error 403: Forbidden" -**Good**: "You don't have permission to view this page. Contact your admin for access." +Use persistent labels; placeholders are examples, not labels. Put format and eligibility requirements before submission. Explain why information is requested only when it is not obvious. Required and optional treatment should be consistent. -**Bad**: "Invalid input" -**Good**: "Email addresses need an @ symbol. Try: name@example.com" +Validation says what needs attention and how to correct it without blaming the user. Keep related instructions near the field and announce errors accessibly. -**Principles**: -- Explain what went wrong in plain language -- Suggest how to fix it -- Don't blame the user -- Include examples when helpful -- Link to help/support if applicable - -### Form Labels & Instructions -**Bad**: "DOB (MM/DD/YYYY)" -**Good**: "Date of birth" (with placeholder showing format) - -**Bad**: "Enter value here" -**Good**: "Your email address" or "Company name" - -**Principles**: -- Use clear, specific labels (not generic placeholders) -- Show format expectations with examples -- Explain why you're asking (when not obvious) -- Put instructions before the field, not after -- Keep required field indicators clear - -### Button & CTA Text -**Bad**: "Click here" | "Submit" | "OK" -**Good**: "Create account" | "Save changes" | "Got it, thanks" +### Errors and permissions -**Principles**: -- Describe the action specifically -- Use active voice (verb + noun) -- Match user's mental model -- Be specific ("Save" is better than "OK") +An actionable error answers: -### Help Text & Tooltips -**Bad**: "This is the username field" -**Good**: "Choose a username. You can change this later in Settings." +1. what failed; +2. why, when known and useful; +3. how to recover or what alternative remains. -**Principles**: -- Add value (don't just repeat the label) -- Answer the implicit question ("What is this?" or "Why do you need this?") -- Keep it brief but complete -- Link to detailed docs if needed +Do not expose internal codes as the primary message. Do not promise a cause or resolution the system cannot know. Treat privacy, payment, deletion, access loss, and blocked work seriously; warmth is welcome, jokes are not. -### Empty States -**Bad**: "No items" -**Good**: "No projects yet. Create your first project to get started." +### Loading, empty, and success states -**Principles**: -- Explain why it's empty (if not obvious) -- Show next action clearly -- Make it welcoming, not dead-end +Loading text names the real operation and sets an honest expectation when the wait is meaningful. Show determinate progress when available; never invent progress. -### Success Messages -**Bad**: "Success" -**Good**: "Settings saved! Your changes will take effect immediately." +An empty state distinguishes first use, no results, filters, permissions, and failure. Explain the state and provide the next useful action. -**Principles**: -- Confirm what happened -- Explain what happens next (if relevant) -- Be brief but complete -- Match the user's emotional moment (celebrate big wins) - -### Loading States -**Bad**: "Loading..." (for 30+ seconds) -**Good**: "Analyzing your data... this usually takes 30-60 seconds" - -**Principles**: -- Set expectations (how long?) -- Explain what's happening (when it's not obvious) -- Show progress when possible -- Offer escape hatch if appropriate ("Cancel") +Success confirms the completed outcome and mentions the next consequence only when it changes what the user should do. Routine success should be brief. -### Confirmation Dialogs -**Bad**: "Are you sure?" -**Good**: "Delete 'Project Alpha'? This can't be undone." +### Help and instructional text -**Principles**: -- State the specific action -- Explain consequences (especially for destructive actions) -- Use clear button labels ("Delete project" not "Yes") -- Don't overuse confirmations (only for risky actions) +Helper text answers an implicit question instead of restating the control. Use progressive disclosure for uncommon detail. Link text must make sense out of context; icon-only controls need accessible names. -### Navigation & Wayfinding -**Bad**: Generic labels like "Items" | "Things" | "Stuff" -**Good**: Specific labels like "Your projects" | "Team members" | "Settings" +## Voice, accessibility, and localization -**Principles**: -- Be specific and descriptive -- Use language users understand (not internal jargon) -- Make hierarchy clear -- Consider information scent (breadcrumbs, current location) +Voice stays consistent; tone adapts to the moment. Use plain language without flattening terminology the audience genuinely knows. -## Apply Clarity Principles +- Write complete translatable messages rather than concatenated fragments. +- Keep variables and numbers structured so translators can reorder them. +- Allow expansion instead of abbreviating prematurely. +- Make alt text convey the image's information; use empty alt for decoration. +- Keep screen-reader names aligned with visible labels and outcomes. +- Do not rely on punctuation, color, or iconography to carry the message alone. -Every piece of copy should follow these rules: +Maintain a short terminology glossary when inconsistency spans the product. Do not vary words for literary effect in an interface. -1. **Be specific**: "Enter email" not "Enter value" -2. **Be concise**: Cut unnecessary words (but don't sacrifice clarity) -3. **Be active**: "Save changes" not "Changes will be saved" -4. **Be human**: "Oops, something went wrong" not "System error encountered" -5. **Tell users what to do**, not just what happened -6. **Be consistent**: Use same terms throughout (don't vary for variety) +## Verify -**NEVER**: -- Use jargon without explanation -- Blame users ("You made an error" → "This field is required") -- Be vague ("Something went wrong" without explanation) -- Use passive voice unnecessarily -- Write overly long explanations (be concise) -- Use humor for errors (be empathetic instead) -- Assume technical knowledge -- Vary terminology (pick one term and stick with it) -- Repeat information (headers restating intros, redundant explanations) -- Use placeholders as the only labels (they disappear when users type) +Read the flow in context and test: -## Verify Improvements +- comprehension without hidden product knowledge; +- actionability at errors, empty states, and decision points; +- factual accuracy and consistent terminology; +- scanability at target widths and 200% zoom; +- long names, localization expansion, pluralization, and dynamic values; +- accessible names and announced state changes; +- tone appropriate to consequence and emotional context. -Test that copy improvements work: +The final copy is as short as it can be without removing meaning or recovery. -- **Comprehension**: Can users understand without context? -- **Actionability**: Do users know what to do next? -- **Brevity**: Is it as short as possible while remaining clear? -- **Consistency**: Does it match terminology elsewhere? -- **Tone**: Is it appropriate for the situation? - -When the copy reads cleanly, hand off to `/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. +When the language reads cleanly, hand off to `/impeccable polish` for the final pass. diff --git a/.trae-cn/skills/impeccable/reference/codex.md b/.trae-cn/skills/impeccable/reference/codex.md index 18a924a91..6c550f1dc 100644 --- a/.trae-cn/skills/impeccable/reference/codex.md +++ b/.trae-cn/skills/impeccable/reference/codex.md @@ -1,105 +1,38 @@ -# Codex: Visual Direction & Asset Production +# Codex: Surface Probes & Asset Production -This file is loaded by `/impeccable craft` when the harness has native image generation (currently Codex via `image_gen`). Other harnesses skip it. It covers the two craft steps that depend on real image generation: landing the visual direction, and producing the raster assets the implementation will compose. +Load this from [new-work.md](new-work.md) only when the harness has native image generation and a substantial, high-fidelity surface would benefit from seeing the shortlisted concept before code. PRODUCT.md and DESIGN.md are preconditions. New-work has already resolved the visual world; this file must not reopen it. -Read this *before* generating any images. The order matters, and the per-step user pauses are what keep generated imagery from drifting away from the brief. +The purpose of a probe is to test composition, narrative, hierarchy, density, focal moment, signature use, and image requirements. It is not a second identity workshop. Keep DESIGN.md's palette, typography direction, material language, component character, imagery stance, and motion grammar fixed. -### Four stop points before code +## Generate the smallest useful probe set -Steps A through D each end with the user. Do not advance past any of them on your own read of the situation. +Generate one to three high-fidelity north-star comps using the native image-generation capability. Base them on the real content and the surface concepts already developed with the user. -1. **STOP after Step A questions.** Wait for answers. -2. **STOP after Step B palette generation.** Wait for "confirm palette." -3. **STOP after Step C mocks.** Wait for direction approval or delegation. -4. **Only after Step D approves a direction** do you return to the build (new-work.md's plan, then code). +- When the user shortlisted multiple concepts, show one clear expression of each. +- When one concept is already selected, vary only the structural uncertainty that the image can resolve: topology, sequence, density, hierarchy, focal composition, or interaction framing. +- Show enough beyond the opening moment to prove the concept can govern the whole requested surface. +- Do not generate a palette artifact, ask new atmosphere questions, introduce a different type voice, or invent a new motif. If the committed world cannot support the concept, return to the concept shortlist rather than changing the world. -Prior shape approval does **not** satisfy any of these. Shape's "confirm or override" advances you into Step A; it is not a substitute for it. +Treat each comp as a direction test, not a screenshot specification. Core UI text, responsive behavior, accessibility, semantics, and interaction states remain implementation responsibilities. -## Step A: Explore Directions with the User +## One approval point -Before generating anything, run a brief direction conversation grounded in the shape brief. +Show the probes together and ask what should carry forward, what feels false to the world, and whether the selected surface concept should be approved, combined, revised, or rejected. Then stop and wait. A structured simulated user counts as attended and receives the same question. -**Step A is required even when shape just produced a confirmed brief.** The shape questions and Step A questions cover different ground: shape pins purpose, content, scope; Step A pins palette, atmosphere, and named visual references for the comps you're about to generate. The only time you can skip Step A is when the user has already answered these exact palette/atmosphere/reference questions in the same session. +Do not begin code until the user approves a direction or explicitly delegates the choice. If they delegate, choose using the task brief, PRODUCT.md, and DESIGN.md, and state the evidence. Approval refines the task concept; it does not modify DESIGN.md. -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: +After approval, summarize the composition and the parts of the comp that must not be literalized. Return to new-work.md and write the direction contract from the approved surface concept before code. -- "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.)" +## Inventory implementation fidelity -**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. +Before building, inventory the approved comp's major visible ingredients and choose an implementation medium for each: semantic HTML/CSS/SVG, existing project asset, generated raster, sourced raster, icon library, canvas/WebGL, or accepted omission. -## Step B: Generate the Brand Palette First +Pay special attention to the dominant composition, signature use, image-native content, second-fold system, and any interaction the still image only implies. If the concept depends on a photograph, architectural scene, product object, portrait, or other raster-native material, do not silently replace it with generic CSS scenery. -Generate **one** palette artifact before any mocks. This is a small, focused image: typography pairing on the chosen background, primary + accent color swatches, one signature ornament or motif. Single image, single pass. +Treat the comp as a north star, not something to trace. Do not rasterize core UI text or controls. Do not substitute a different visual driver after approval without asking. -Why palette first: mocks generated against a vague color sense produce noise that drowns out the structural decisions. A confirmed palette is the first concrete contract for everything downstream. +## Produce only the assets the build needs -Show the palette to the user. Ask one question: "This is the palette I'm locking in for the mocks. Confirm, or call out what to shift?" +When clean raster ingredients are required and a scoped subagent is available and authorized, use `impeccable_asset_producer`. Give it the approved comp, output paths, required dimensions and formats, transparency needs, crop notes, and what must remain semantic code. Otherwise produce the minimum required assets with the native image-generation capability in the current thread. -**STOP and wait for confirmation.** Do not generate mocks against an unconfirmed palette. "Probably good enough" is the wrong call here; the palette is the contract for everything downstream. - -## Step C: Generate 1-3 Visual Mocks Against the Palette - -Once the palette is confirmed, generate **1 to 3** high-fidelity north-star comps. Each mock must use the confirmed palette and typography. Mocks differ in *structural* direction (hierarchy, topology, density, composition), not in color or motif. - -- Brand work: push visual identity, composition, mood, and signature motifs. -- Product work: push hierarchy, topology, density, tone, grounded in realistic product structure. -- Landing pages and long-form brand surfaces: show enough of the second fold to establish the system beyond the hero. - -Use the `image_gen` tool directly (or via the imagegen skill when available). Don't ask the user to install anything. - -## Step D: Approval Loop - -Show the comps. Ask what carries forward. Iterate until **one direction is approved** or the user explicitly delegates. - -**STOP and wait for the approval or the delegation.** Do not begin Step E or return to the build until a single direction is named. If the user delegates, pick the strongest direction and explain it from the brief, not personal taste. - -Before moving to assets, summarize what to carry into code and what *not* to literalize from the mock. This is the handoff between visual exploration and semantic implementation. - -## Step E: Mock Fidelity Inventory - -Inventory the approved mock's major visible ingredients. For each, decide implementation: semantic HTML/CSS/SVG, generated raster, sourced raster, icon library, canvas/WebGL, or accepted omission. - -Common ingredients to inventory: - -- Hero silhouette and dominant composition -- Signature motifs (planets, devices, portraits, charts, route lines, insets, badges, etc.) -- Nav and primary-action treatment (when the surface has one) -- Section sequence, especially the second fold -- Image-native content the concept depends on -- Typography, density, color/material treatment, motion cues - -Treat the mock as a north star, not a screenshot to trace. Don't rasterize core UI text. But if the live result lacks the mock's major ingredients, the implementation is wrong. - -If a photographic, architectural, product, or place-led mock becomes generic CSS scenery, decorative diagrams, bullets, or copy, stop and fix it. That's a broken implementation, not a harmless interpretation. - -Don't substitute a different hero composition or visual driver post-approval without user sign-off. - -## Step F: Asset Slicing via the Asset Producer - -Raster ingredients identified in Step E need clean production assets. Use the bundled `impeccable_asset_producer` subagent rather than producing inline. - -Spawn it as a scoped subagent. If you do not have explicit permission to use agents, stop and ask: - -```text -Asset production will work better as a scoped subagent job. Should I spawn the Impeccable asset producer subagent for this step? -``` - -Pass to the agent: - -- Approved mock path or screenshot reference -- Crop paths or a contact sheet with crop ids -- Output directory -- Required dimensions, format, transparency needs -- Avoid list -- Notes on what should remain semantic HTML/CSS/SVG instead of raster - -Attach image generation capability to the spawned agent when the harness supports it. Do **not** load image-generation reference material into the parent thread. - -Inline asset production is allowed only if the user declines subagents, the harness cannot spawn the authorized agent, or the user explicitly asks for single-thread mode. - -Prefer HTML/CSS/SVG/canvas when they can credibly reproduce an ingredient; reach for real, generated, or stock imagery when the mock or subject matter calls for actual visual content. - -## After This File - -Once Steps A through F are complete, return to the build: implement per new-work.md and SKILL.md's craft floor. The implementation builds against the confirmed palette, approved mock, and the assets the producer wrote. +Return to [new-work.md](new-work.md) for the direction contract, implementation, and finishing pass. diff --git a/.trae-cn/skills/impeccable/reference/colorize.md b/.trae-cn/skills/impeccable/reference/colorize.md index abee1acf8..dc45f88c5 100644 --- a/.trae-cn/skills/impeccable/reference/colorize.md +++ b/.trae-cn/skills/impeccable/reference/colorize.md @@ -1,257 +1,86 @@ > **Additional context needed**: existing brand colors. -Replace timid grayscale or single-accent designs with a strategic palette: pick a color strategy, choose a hue family that fits the brand, then apply color with intent. More color ≠ better. Strategic color beats rainbow vomit. +Introduce color as hierarchy, meaning, and atmosphere. Preserve confirmed brand and semantic conventions; do not replace a visual world under the guise of colorizing it. --- -## Register +## Visitor mode -Persuade + Experience: palette IS voice. Pick a color strategy first per SKILL.md (Restrained / Committed / Full palette / Drenched) and follow its dosage. Committed, Full palette, and Drenched deliberately exceed the ≤10% rule; that rule is Restrained only. Unexpected combinations are allowed; a dominant color can own the page when the chosen strategy calls for it. +- **Persuade + Experience:** color may carry the voice and own large regions when the selected world calls for it. +- **Operate + Read:** color primarily encodes action, selection, status, wayfinding, and reading hierarchy. Rarity gives an accent force. -Operate + Read: semantic-first and almost always Restrained. Accent color is reserved for primary action, current selection, and state indicators. Not decoration. Every color has a consistent meaning across every screen. +## Audit before choosing ---- +Read DESIGN.md, tokens, assets, current themes, and representative states. Identify: -## Assess Color Opportunity +- which colors are confirmed brand commitments; +- current surface, text, action, and semantic roles; +- places where grayscale obscures hierarchy or state; +- contrast failures and color-only communication; +- light/dark or data-visualization requirements; +- whether the task asks for more color or a new identity. -Analyze the current state and identify opportunities: +If a new identity is required, use [new-work.md](new-work.md). Ask only when a binding brand decision cannot be inferred. -1. **Understand current state**: - - **Color absence**: Pure grayscale? Limited neutrals? One timid accent? - - **Missed opportunities**: Where could color add meaning, hierarchy, or delight? - - **Context**: What's appropriate for this domain and audience? - - **Brand**: Are there existing brand colors we should use? +## Choose a strategy -2. **Identify where color adds value**: - - **Semantic meaning**: Success (green), error (red), warning (yellow/orange), info (blue) - - **Hierarchy**: Drawing attention to important elements - - **Categorization**: Different sections, types, or states - - **Emotional tone**: Warmth, energy, trust, creativity - - **Wayfinding**: Helping users navigate and understand structure - - **Delight**: Moments of visual interest and personality +Name the intended emotional temperature, dominant relationship, contrast range, and color dosage before editing. The strategy may be restrained or immersive; it must follow the brief and selected world rather than a fixed percentage rule. -If any of these are unclear from the codebase, ask the user directly to clarify what you cannot infer. +Build roles, not a bag of swatches: -**CRITICAL**: More color ≠ better. Strategic color beats rainbow vomit every time. Every color should have a purpose. +- canvas and elevated surfaces; +- primary and secondary text; +- action, focus, and selection; +- borders and separators; +- success, warning, error, and information; +- data categories or scales when needed. -## Plan Color Strategy +Use the project's existing color space. For a new web palette, prefer OKLCH because lightness and chroma can be adjusted predictably. Choose hue from product meaning and visual direction, never from a default category association. -Create a purposeful color introduction plan: +## Apply at system scale -- **Color palette**: What colors match the brand/context? (Choose 2-4 colors max beyond neutrals) -- **Dominant color**: Which color owns 60% of colored elements? -- **Accent colors**: Which colors provide contrast and highlights? (30% and 10%) -- **Application strategy**: Where does each color appear and why? +- Let the strongest color own a deliberate region or role instead of scattering tiny accents. +- Keep the primary action easy to find; do not spend its color on decoration. +- Tint neutrals only when the brand hue genuinely creates cohesion. Neutral gray is valid when it serves the world. +- On colored surfaces, derive secondary text from the foreground or surface hue rather than using washed-out generic gray. +- Keep semantic meanings consistent, but respect platform and domain conventions instead of assuming fixed hues. +- For data, use distinct lightness, chroma, shape, label, or pattern so color is not the only code. +- 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. -**IMPORTANT**: Color should enhance hierarchy and meaning, not create chaos. Less is more when it matters more. +Decoration without a relationship to hierarchy, state, content, or the visual world is not a color strategy. -## Introduce Color Strategically +## Contrast and perception -Add color systematically across these dimensions: +Verify computed foreground/background pairs: -### Semantic Color -- **State indicators**: - - Success: Green tones (emerald, forest, mint) - - Error: Red/pink tones (rose, crimson, coral) - - Warning: Orange/amber tones - - Info: Blue tones (sky, ocean, indigo) - - Neutral: Gray/slate for inactive states +| Content | WCAG AA minimum | +|---|---| +| body text | 4.5:1 | +| large text | 3:1 | +| controls, icons, focus indicators | 3:1 | -- **Status badges**: Colored backgrounds or borders for states (active, pending, completed, etc.) -- **Progress indicators**: Colored bars, rings, or charts showing completion or health +Do not rely on eyesight alone. Check interactive states, overlays, text on images, disabled content, and both themes. Simulate common vision deficiencies. Information conveyed by color also needs text, shape, iconography, or position. -### Accent Color Application -- **Primary actions**: Color the most important buttons/CTAs -- **Links**: Add color to clickable text (maintain accessibility) -- **Icons**: Colorize key icons for recognition and personality -- **Headers/titles**: Add color to section headers or key labels -- **Hover states**: Introduce color on interaction +When deriving OKLCH ramps, vary lightness and reduce chroma near white and black. Do not keep high chroma at extreme lightness merely to make the math uniform. Prefer explicit colors over chains of translucent overlays when alpha would make contrast context-dependent. -### Background & Surfaces -- **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 toward the brand, not "for warmth" by reflex +## Verify -**Use OKLCH for color**: It's perceptually uniform, meaning equal steps in lightness *look* equal. Great for generating harmonious scales. - -### Data Visualization -- **Charts & graphs**: Use color to encode categories or values -- **Heatmaps**: Color intensity shows density or importance -- **Comparison**: Color coding for different datasets or timeframes - -### Borders & Accents -- **Hairline borders**: 1px colored borders on full perimeter (not side-stripes; see the absolute ban on `border-left/right > 1px`) -- **Underlines**: Color underlines for emphasis or active states -- **Dividers**: Subtle colored dividers instead of gray lines -- **Focus rings**: Colored focus indicators matching brand -- **Surface tints**: A 4-8% background wash of the accent color instead of a stripe - -**NEVER**: `border-left` or `border-right` greater than 1px as a colored accent stripe. This is one of the three absolute bans in the parent skill. If you want to mark a card as "active" or "warning", use a full hairline border, a background tint, a leading glyph, or a numbered prefix. Not a side stripe. - -### Typography Color -- **Colored headings**: Use brand colors for section headings (maintain contrast) -- **Highlight text**: Color for emphasis or categories -- **Labels & tags**: Small colored labels for metadata or categories - -### Decorative Elements -- **Illustrations**: Add colored illustrations or icons -- **Shapes**: Geometric shapes in brand colors as background elements -- **Gradients**: Colorful gradient overlays or mesh backgrounds -- **Blobs/organic shapes**: Soft colored shapes for visual interest - -## Balance & Refinement - -Ensure color addition improves rather than overwhelms: - -### Maintain Hierarchy -- **Dominant color** (60%): Primary brand color or most used accent -- **Secondary color** (30%): Supporting color for variety -- **Accent color** (10%): High contrast for key moments -- **Neutrals** (remaining): Gray/black/white for structure - -### Accessibility -- **Contrast ratios**: Ensure WCAG compliance (4.5:1 for text, 3:1 for UI components) -- **Don't rely on color alone**: Use icons, labels, or patterns alongside color -- **Test for color blindness**: Verify red/green combinations work for all users - -### Cohesion -- **Consistent palette**: Use colors from defined palette, not arbitrary choices -- **Systematic application**: Same color meanings throughout (green always = success) -- **Temperature consistency**: Warm palette stays warm, cool stays cool - -**NEVER**: -- 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 -- Violate WCAG contrast requirements -- Use color as the only indicator (accessibility issue) -- Make everything colorful (defeats the purpose) -- Default to purple-blue gradients (AI slop aesthetic) - -## Verify Color Addition - -Test that colorization improves the experience: - -- **Better hierarchy**: Does color guide attention appropriately? -- **Clearer meaning**: Does color help users understand states/categories? -- **More engaging**: Does the interface feel warmer and more inviting? -- **Still accessible**: Do all color combinations meet WCAG standards? -- **Not overwhelming**: Is color balanced and purposeful? +- Every color has a stable role or a world-specific atmospheric purpose. +- Attention lands on the intended action, content, or state. +- The palette works across quiet, dense, interactive, error, and empty states. +- Light and dark themes are each composed, not mechanically inverted. +- Contrast and non-color cues pass in all relevant states. +- The result is recognizably this product, not a generic “colorful” treatment. When the palette earns its place, hand off to `/impeccable polish` for the final pass. ## Live-mode signature params -When invoked from live mode, each variant MUST declare a `color-amount` param so the user can dial between a restrained accent and a drenched surface without regeneration. Author the variant's CSS against `var(--p-color-amount, 0.5)`, typically as the alpha multiplier on backgrounds, or as a scaling factor on the chroma axis in an OKLCH expression. 0 = neutral/monochrome, 1 = full saturation / dominant coverage. +When invoked from live mode, every variant declares a `color-amount` parameter. Author CSS against `var(--p-color-amount, 0.5)` so the user can move from neutral to the variant's full color strategy without regeneration. ```json {"id":"color-amount","kind":"range","min":0,"max":1,"step":0.05,"default":0.5,"label":"Color amount"} ``` -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). +Add at most two variant-specific parameters, such as palette, temperature, or tint behavior. Follow [live.md](live.md)'s parameter contract. diff --git a/.trae-cn/skills/impeccable/reference/craft.md b/.trae-cn/skills/impeccable/reference/craft.md index cc7f98d37..dbbc9402c 100644 --- a/.trae-cn/skills/impeccable/reference/craft.md +++ b/.trae-cn/skills/impeccable/reference/craft.md @@ -1,3 +1,5 @@ # Craft (deprecated alias) -`craft` is a deprecated alias for the standard build flow. Builds route through SKILL.md's new-work gate; [new-work.md](new-work.md) carries the direction checkpoint ("Decide, then build"), the identity playbook, and the finishing pass. Invoking `craft` explicitly forces the attended checkpoints: pause at the stated direction for confirmation, and follow [codex.md](codex.md)'s mock flow when the harness generates images. Nothing else differs from a plain build request. +`craft` is a deprecated alias for an ordinary request to make new visual work. It adds no setup, interview, checkpoint, tool, or quality behavior. Apply SKILL.md's normal routing: create missing PRODUCT.md through [init.md](init.md), then follow [new-work.md](new-work.md) for visual authority, world and surface decisions, implementation, and finish. + +Do not tell users they need to invoke `craft`. Natural requests such as “build this feature,” “make a landing page,” or “redesign this screen” use the same flow. diff --git a/.trae-cn/skills/impeccable/reference/critique.md b/.trae-cn/skills/impeccable/reference/critique.md index 2ae773275..89ee9f3c0 100644 --- a/.trae-cn/skills/impeccable/reference/critique.md +++ b/.trae-cn/skills/impeccable/reference/critique.md @@ -43,13 +43,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 @@ -115,11 +115,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. @@ -176,7 +176,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. 2. **Pass the structured metadata** through `IMPECCABLE_CRITIQUE_META` (JSON), then run the write command: ```bash diff --git a/.trae-cn/skills/impeccable/reference/delight.md b/.trae-cn/skills/impeccable/reference/delight.md index 596cff09e..40aaddc34 100644 --- a/.trae-cn/skills/impeccable/reference/delight.md +++ b/.trae-cn/skills/impeccable/reference/delight.md @@ -1,302 +1,70 @@ -> **Additional context needed**: what's appropriate for the domain (playful vs professional vs quirky vs elegant). +> **Additional context needed**: the brand's emotional range. -Find the moments where personality and unexpected polish would turn a functional interface into one users remember and tell other people about. Add only where the moment earns it; delight everywhere reads as noise. +Make the experience memorable at moments that earn it. Delight is not a layer of generic whimsy; it is product character revealed through a useful interaction, a humane response, or an unexpectedly considered detail. --- -## Register +## Visitor mode -Persuade + Experience: delight can be distributed across copy voice, section transitions, discovery rewards, seasonal touches, personality across the whole surface. +- **Persuade + Experience:** personality may run through voice, composition, motion, and discovery, provided the artifact remains the focus. +- **Operate + Read:** concentrate delight at meaningful moments such as first use, completion, recovery, or mastery. Reliability carries everything else. -Operate + Read: delight at specific moments, not pages. Completion, first-time actions, error recovery, milestone crossings. Reliability and consistency carry the rest of the experience; delight pushed everywhere reads as noise. +## Find the opportunity ---- +Inspect the target, DESIGN.md, product voice, repeated-use frequency, and emotional context. Look for: -## Assess Delight Opportunities +- effort worth acknowledging; +- waiting that can become informative; +- an empty or first-use state that can orient; +- an error or recovery moment that needs empathy; +- an interaction whose physical or verbal response could express the brand; +- a useful capability people might enjoy discovering. -Identify where delight would enhance (not distract from) the experience: +Do not manufacture a celebration for an ordinary click. Ask only when the brand's emotional range or the stakes cannot be inferred. -1. **Find natural delight moments**: - - **Success states**: Completed actions (save, send, publish) - - **Empty states**: First-time experiences, onboarding - - **Loading states**: Waiting periods that could be entertaining - - **Achievements**: Milestones, streaks, completions - - **Interactions**: Hover states, clicks, drags - - **Errors**: Softening frustrating moments - - **Easter eggs**: Hidden discoveries for curious users +## Define one delight thesis -2. **Understand the context**: - - What's the brand personality? (Playful? Professional? Quirky? Elegant?) - - Who's the audience? (Tech-savvy? Creative? Corporate?) - - What's the emotional context? (Accomplishment? Exploration? Frustration?) - - What's appropriate? (Banking app ≠ gaming app) +State in one sentence what the user should feel and why that feeling belongs to this product. Then choose the smallest system that can deliver it: -3. **Define delight strategy**: - - **Subtle sophistication**: Refined micro-interactions (luxury brands) - - **Playful personality**: Whimsical illustrations and copy (consumer apps) - - **Helpful surprises**: Anticipating needs before users ask (productivity tools) - - **Sensory richness**: Satisfying sounds, smooth animations (creative tools) +- a distinctive response to a meaningful action; +- product-specific language that clarifies while carrying voice; +- an interaction or transition with a recognizable material behavior; +- an illustration, sound, haptic, or environmental detail grounded in the product world; +- a discovery reward that reveals real utility. -If any of these are unclear from the codebase, ask the user directly to clarify what you cannot infer. +Derive the treatment from product mechanism and visual world, not a stock catalog. -**CRITICAL**: Delight should enhance usability, never obscure it. If users notice the delight more than accomplishing their goal, you've gone too far. +## Build for the emotional moment -## Delight Principles +- **Success:** match the response to the effort and consequence. Major milestones can expand; routine saves should simply feel certain. +- **Waiting:** show truthful progress, useful context, or product-specific activity. Never fake work or delay completion to stage a flourish. +- **Empty and first use:** make the next action clear before adding personality. +- **Error and recovery:** lead with the problem and recovery. Warmth may reduce stress; jokes must not trivialize loss, money, privacy, or blocked work. +- **Repeated interaction:** keep the response satisfying after the hundredth use. Variation is useful only when it remains coherent and predictable enough to trust. +- **Discovery:** reward curiosity without hiding required functionality. -Follow these guidelines: +Copy must use the product's language. Generic whimsy is worse than neutral clarity. -### Delight Amplifies, Never Blocks -- Delight moments should be quick (< 1 second) -- Never delay core functionality for delight -- Make delight skippable or subtle -- Respect user's time and task focus +## Protect the experience -### Surprise and Discovery -- Hide delightful details for users to discover -- Reward exploration and curiosity -- Don't announce every delight moment -- Let users share discoveries with others +Delight must not: -### Appropriate to Context -- Match delight to emotional moment (celebrate success, empathize with errors) -- Respect the user's state (don't be playful during critical errors) -- Match brand personality and audience expectations -- Cultural sensitivity (what's delightful varies by culture) +- delay, block, or obscure the primary task; +- override platform conventions or accessibility; +- add unrequested factual claims; +- play sound without consent or ignore mute settings; +- become mandatory, unskippable, or exhausting on repeat; +- add a dependency or asset cost disproportionate to the moment. -### Compound Over Time -- Delight should remain fresh with repeated use -- Vary responses (not same animation every time) -- Reveal deeper layers with continued use -- Build anticipation through patterns +For authored motion, load [animate.md](animate.md). Respect reduced motion, screen readers, keyboard use, touch, localization, and cultural context. Nonessential loops stop when hidden. Make celebration intensity proportional to frequency and consequence. -## Delight Techniques +## Verify -Add personality and joy through these methods: +- The moment is specific enough that a neighboring product could not use it unchanged. +- It improves comprehension, confidence, motivation, or emotional recovery. +- The interface remains fast and obvious without the flourish. +- Repetition does not turn charm into friction. +- Reduced-motion, muted, keyboard, touch, and localized paths work. +- The result feels like the selected world, not a generic “delight” treatment. -### Micro-interactions & Animation - -**Button delight**: -```css -/* Satisfying button press */ -.button { - transition: transform 0.1s, box-shadow 0.1s; -} -.button:active { - transform: translateY(2px); - box-shadow: 0 2px 4px rgba(0,0,0,0.2); -} - -/* Ripple effect on click */ -/* Smooth lift on hover */ -.button:hover { - transform: translateY(-2px); - transition: transform 0.2s cubic-bezier(0.25, 1, 0.5, 1); /* ease-out-quart */ -} -``` - -**Loading delight**: -- Playful loading animations (not just spinners) -- Personality in loading messages (write product-specific ones, not generic AI filler) -- Progress indication with encouraging messages -- Skeleton screens with subtle animations - -**Success animations**: -- Checkmark draw animation -- Confetti burst for major achievements -- Gentle scale + fade for confirmation -- Satisfying sound effects (subtle) - -**Hover surprises**: -- Icons that animate on hover -- Color shifts or glow effects -- Tooltip reveals with personality -- Cursor changes (custom cursors for branded experiences) - -### Personality in Copy - -**Playful error messages**: -``` -"Error 404" -"This page is playing hide and seek. (And winning)" - -"Connection failed" -"Looks like the internet took a coffee break. Want to retry?" -``` - -**Encouraging empty states**: -``` -"No projects" -"Your canvas awaits. Create something amazing." - -"No saved articles" -"Nothing on the nightstand yet. Save a story for later." -``` - -**Playful labels & tooltips**: -``` -"Delete" -"Send to void" (for playful brand) - -"Help" -"Rescue me" (tooltip) -``` - -**IMPORTANT**: Match copy personality to brand. Banks shouldn't be wacky, but they can be warm. - -### Illustrations & Visual Personality - -**Custom illustrations**: -- Empty state illustrations (not stock icons) -- Error state illustrations (friendly monsters, quirky characters) -- Loading state illustrations (animated characters) -- Success state illustrations (celebrations) - -**Icon personality**: -- Custom icon set matching brand personality -- Animated icons (subtle motion on hover/click) -- Illustrative icons (more detailed than generic) -- Consistent style across all icons - -**Background effects**: -- Subtle particle effects -- Gradient mesh backgrounds -- Geometric patterns -- Parallax depth -- Time-of-day themes (morning vs night) - -### Satisfying Interactions - -**Drag and drop delight**: -- Lift effect on drag (shadow, scale) -- Snap animation when dropped -- Satisfying placement sound -- Undo toast ("Dropped in wrong place? [Undo]") - -**Toggle switches**: -- Smooth slide with spring physics -- Color transition -- Haptic feedback on mobile -- Optional sound effect - -**Progress & achievements** (Operate surfaces with recurring tasks only; gamification on editorial, portfolio, or one-visit surfaces reads as noise): -- Streak counters with celebratory milestones -- Progress bars that "celebrate" at 100% -- Badge unlocks with animation -- Playful stats ("You're on fire! 5 days in a row") - -**Form interactions**: -- Input fields that animate on focus -- Checkboxes with a satisfying scale pulse when checked -- Success state that celebrates valid input -- Auto-grow textareas - -### Sound Design - -**Subtle audio cues** (when appropriate): -- Notification sounds (distinctive but not annoying) -- Success sounds (satisfying "ding") -- Error sounds (empathetic, not harsh) -- Typing sounds for chat/messaging -- Ambient background audio (very subtle) - -**IMPORTANT**: -- Respect system sound settings -- Provide mute option -- Keep volumes quiet (subtle cues, not alarms) -- Don't play on every interaction (sound fatigue is real) - -### Easter Eggs & Hidden Delights - -**Discovery rewards**: -- Konami code unlocks special theme -- Hidden keyboard shortcuts (Cmd+K for special features) -- Hover reveals on logos or illustrations -- Alt text jokes on images (for screen reader users too!) -- A view-source or console colophon (a note on the typefaces, a thank-you, a hiring line) - -**Seasonal touches**: -- Holiday themes (subtle, tasteful) -- Seasonal color shifts -- Weather-based variations -- Time-based changes (dark at night, light during day) - -**Contextual personality**: -- Different messages based on time of day -- Responses to specific user actions -- Randomized variations (not same every time) -- Progressive reveals with continued use - -### Loading & Waiting States - -**Make waiting engaging**: -- Interesting loading messages that rotate -- Progress bars with personality -- Mini-games during long loads -- Fun facts or tips while waiting -- Countdown with encouraging messages - -``` -Loading messages: write ones specific to your product, not generic AI filler: -- "Syncing with your team's changes..." -- "Fetching this week's issue..." -- "Developing your photos..." -- "Checking tomorrow's tide tables..." -``` - -**WARNING**: Avoid cliched loading messages like "Herding pixels", "Teaching robots to dance", "Consulting the magic 8-ball", "Counting backwards from infinity". These are AI-slop copy, instantly recognizable as machine-generated. Write messages that are specific to what your product actually does. - -### Celebration Moments - -**Success celebrations**: -- Confetti for major milestones -- Animated checkmarks for completions -- Progress bar celebrations at 100% -- "Achievement unlocked" style notifications -- Personalized messages ("You published your 10th article!") - -**Milestone recognition** (same scoping as Progress & achievements: Operate surfaces with recurring tasks): -- First-time actions get special treatment -- Streak tracking and celebration -- Progress toward goals -- Anniversary celebrations - -## Implementation Patterns - -**Animation libraries**: -- Framer Motion (React) -- GSAP (universal) -- Lottie (After Effects animations) -- Canvas confetti (party effects) - -**Sound libraries**: -- Howler.js (audio management) -- Use-sound (React hook) - -**Physics libraries**: -- React Spring (spring physics) -- Popmotion (animation primitives) - -**IMPORTANT**: File size matters. Compress images, optimize animations, lazy load delight features. - -**NEVER**: -- Delay core functionality for delight -- Force users through delightful moments (make skippable) -- Use delight to hide poor UX -- Overdo it (less is more) -- Ignore accessibility (animate responsibly, provide alternatives) -- Make every interaction delightful (special moments should be special) -- Sacrifice performance for delight -- Be inappropriate for context (read the room) - -## Verify Delight Quality - -Test that delight actually delights: - -- **User reactions**: Do users smile? Share screenshots? -- **Doesn't annoy**: Still pleasant after 100th time? -- **Doesn't block**: Can users opt out or skip? -- **Performant**: No jank, no slowdown -- **Appropriate**: Matches brand and context -- **Accessible**: Works with reduced motion, screen readers - -When the moments feel earned, hand off to `/impeccable polish` for the final pass. +When the personality feels earned, hand off to `/impeccable polish` for the final pass. diff --git a/.trae-cn/skills/impeccable/reference/document.md b/.trae-cn/skills/impeccable/reference/document.md index 0d38eb568..121a00ab1 100644 --- a/.trae-cn/skills/impeccable/reference/document.md +++ b/.trae-cn/skills/impeccable/reference/document.md @@ -1,6 +1,6 @@ Generate a `DESIGN.md` file at the project root that captures the current visual design system, so AI agents generating new screens stay on-brand. -DESIGN.md follows the [official DESIGN.md format spec](https://raw.githubusercontent.com/google-labs-code/design.md/main/docs/spec.md): YAML frontmatter carrying machine-readable design tokens, followed by a markdown body with exactly six sections in a fixed order. **Tokens are normative; prose provides context for how to apply them.** Sections may be omitted when not relevant, but **do not reorder them and do not rename them**. Section headers must match the spec character-for-character so the file stays parseable by other DESIGN.md-aware tools (Stitch itself, awesome-design-md, skill-rest, etc.). +DESIGN.md follows the [official DESIGN.md format spec](https://raw.githubusercontent.com/google-labs-code/design.md/main/docs/spec.md): optional YAML frontmatter carrying machine-readable design tokens, followed by up to eight markdown sections in a fixed order. **Tokens are normative; prose provides context for how to apply them.** Sections may be omitted when not relevant, but those present stay in the specified order. Use the canonical headings below so the file remains portable across DESIGN.md-aware tools. ## The frontmatter: token schema @@ -43,26 +43,28 @@ components: 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. +- **Colors accept any valid CSS color string.** Hex is the recommended default for portability, but preserve an incumbent `rgb()`, `hsl()`, `oklch()`, wide-gamut, or mixed-color value when it is the project's normative source. 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 (`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) +## The markdown body: eight sections (canonical order) 1. `## Overview` 2. `## Colors` 3. `## Typography` -4. `## Elevation` -5. `## Components` -6. `## Do's and Don'ts` +4. `## Layout` +5. `## Elevation & Depth` +6. `## Shapes` +7. `## Components` +8. `## Do's and Don'ts` -Optional evocative subtitles are allowed in the form `## 2. Colors: The [Name] Palette` (Stitch's own outputs do this), but the literal word in each header (Overview, Colors, Typography, Elevation, Components, Do's and Don'ts) must be present. Do NOT add extra top-level sections (Layout Principles, Responsive Behavior, Motion, Agent Prompt Guide). Fold that content into the six spec sections where it naturally belongs. +Omit irrelevant sections rather than filling them with invented rules. Put responsive layout in Layout, depth in Elevation & Depth, radius and form language in Shapes, and per-component behavior in Components. Unknown sections are preserved by the format, but new visual guidance should use the canonical structure whenever it fits. ## When to run -- 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. +- New-work found a coherent incumbent visual system but no `DESIGN.md`. +- The first implementation of a new world is complete and its provisional decisions need to be carbonized. - An existing `DESIGN.md` is stale (the design has drifted). - Before a large redesign, to capture the current state as a reference. @@ -71,9 +73,9 @@ 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 init, nothing built yet). Interview for five high-level answers, write a minimal DESIGN.md marked ``. Re-run in scan mode once there's code. +- **Seed mode**: the project is pre-implementation. Ensure PRODUCT.md exists, then reuse new-work's visual-world workshop and write its directional DESIGN.md 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. +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` requests new-work's world workshop, but it does not authorize replacing coherent code: when an incumbent system exists, offer scan mode or route an explicit identity-replacement request through new-work. ## Scan mode (approach C: auto-extract, then confirm descriptive language) @@ -97,7 +99,8 @@ Build a structured draft from the discovered tokens. For each token class: - **Typography**: Map observed sizes and weights to the Material hierarchy (display / headline / title / body / label). Note font-family stacks and the scale ratio. - **Elevation**: Catalogue the shadow vocabulary. If the project is flat and uses tonal layering instead, that's a valid answer; state it explicitly. - **Components**: For each common component (button, card, input, chip, list item, tooltip, nav), extract shape (radius), color assignment, hover/focus treatment, internal padding. -- **Spacing + layout**: Fold into Overview or relevant Components. The spec does NOT have a Layout section. +- **Layout + spacing**: Extract grid, container, breakpoint, rhythm, and density behavior into Layout. +- **Shapes**: Extract radius, corner, border, clipping, and recurring form behavior into Shapes. ### Step 2b: Stage the frontmatter @@ -112,19 +115,19 @@ Skip anything the project doesn't have. Empty scale keys or fabricated tokens po ### Step 3: Ask the user for qualitative language -The following require creative input that cannot be auto-extracted. Group them into one `AskUserQuestion` interaction: +The following require creative input that cannot be auto-extracted. Ask them in two structured rounds of no more than three questions each (or the harness's lower limit), waiting between rounds: - **Creative North Star**: a single named metaphor for the whole system ("The Editorial Sanctuary", "The Golden State Curator", "The Lab Notebook"). Offer 2-3 options that honor PRODUCT.md's brand personality. -- **Overview voice**: mood adjectives, aesthetic philosophy in 2-3 sentences, anti-references (what the system should not feel like). +- **Overview voice**: mood adjectives, aesthetic philosophy in 2-3 sentences, and any confirmed visual anti-reference. - **Color character** (for auto-extracted colors): descriptive names ("Deep Muted Teal-Navy", not "blue-800"). Suggest 2-3 options per key color based on hue/saturation. - **Elevation philosophy**: flat/layered/lifted. If shadows exist, is their role ambient or structural? - **Component philosophy**: the feel of buttons, cards, inputs in one phrase ("tactile and confident" vs. "refined and restrained"). -Quote a line from PRODUCT.md when possible so the user sees their own strategic language carry forward. +Carry a line from PRODUCT.md only when it is a durable brand commitment that actually constrains the visual system. Page strategy and surface concepts do not belong here. ### Step 4: Write DESIGN.md -The file opens with the YAML frontmatter staged in Step 2b (schema documented at the top of this reference), then the markdown body using the structure below. Headers must match character-for-character. Optional evocative subtitles (e.g. `## 2. Colors: The Coastal Palette`) are allowed. +The file opens with the YAML frontmatter staged in Step 2b (schema documented at the top of this reference), then the markdown body using the canonical structure below. ```markdown --- @@ -136,13 +139,13 @@ colors: # Design System: [Project Title] -## 1. Overview +## Overview **Creative North Star: "[Named metaphor in quotes]"** -[2-3 paragraph holistic description: personality, density, aesthetic philosophy. Start from the North Star and work outward. State what this system explicitly rejects (pulled from PRODUCT.md's anti-references). End with a short **Key Characteristics:** bullet list.] +[2-3 paragraph holistic description: personality, density, and aesthetic philosophy. Start from the North Star and work outward. State only confirmed visual rejections. End with a short **Key Characteristics:** bullet list.] -## 2. Colors +## Colors [Describe the palette character in one sentence.] @@ -162,7 +165,7 @@ colors: ### Named Rules (optional, powerful) **The [Rule Name] Rule.** [Short, forceful prohibition or doctrine, e.g. "The One Voice Rule. The primary accent is used on ≤10% of any given screen. Its rarity is the point."] -## 3. Typography +## Typography **Display Font:** [Family] (with [fallback]) **Body Font:** [Family] (with [fallback]) @@ -180,7 +183,11 @@ colors: ### Named Rules (optional) **The [Rule Name] Rule.** [Short doctrine about type use.] -## 4. Elevation +## Layout + +[Describe the grid or spatial model, container behavior, density, responsive changes, and the spacing rhythm. Include exact values only when observed.] + +## Elevation & Depth [One paragraph: does this system use shadows, tonal layering, or a hybrid? If "no shadows", say so explicitly and describe how depth is conveyed instead.] @@ -191,7 +198,11 @@ colors: ### Named Rules (optional) **The [Rule Name] Rule.** [e.g. "The Flat-By-Default Rule. Surfaces are flat at rest. Shadows appear only as a response to state (hover, elevation, focus)."] -## 5. Components +## Shapes + +[Describe the form language: corner/radius strategy, borders, clipping, and any recurring silhouette or geometry.] + +## Components For each component, lead with a short character line, then specify shape, color assignment, states, and any distinctive behavior. @@ -223,16 +234,16 @@ For each component, lead with a short character line, then specify shape, color ### [Signature Component] (optional; if the project has a distinctive custom component worth documenting) [Description.] -## 6. Do's and Don'ts +## Do's and Don'ts -Concrete, forceful guardrails. Lead each with "Do" or "Don't". Be specific: include exact colors, pixel values, and named anti-patterns the user mentioned in PRODUCT.md. **Every anti-reference in PRODUCT.md should show up here as a "Don't" with the same language**, so the visual spec carries the strategic line through. Quote PRODUCT.md directly where possible: if PRODUCT.md says *"avoid dark mode with purple gradients, neon accents, glassmorphism"*, the Don'ts here should repeat that by name. +Concrete visual guardrails grounded in the incumbent implementation or the user's chosen world. Lead each with "Do" or "Don't" and include exact values only when established. Do not turn a task-specific concept or surface strategy into a system-wide prohibition. ### Do: - **Do** [specific prescription with exact values / named rule]. - **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** [...] ``` @@ -307,7 +318,7 @@ The `html` and `css` fields must be **self-contained, drop-in snippets** that re Aim for a tight set of **5-10 components** that best represent the visual system: - **Canonical primitives (always include if the project has them):** button (each variant as a separate component entry), input/text field, navigation, chip/tag, card. -- **Signature components (include if distinctive):** hero CTA, featured card, filter pill, a distinctive table or list-row treatment, any custom pattern the user mentioned as important in PRODUCT.md. +- **Signature components (include if distinctive):** the recurring custom patterns that actually define the implemented system. - **Skip the rest.** Utility components, form building blocks, wrapper layouts: not worth documenting unless visually distinctive. If the project has **no component library yet** (bare landing page, new project), synthesize canonical primitives from the tokens using best-practice defaults consistent with the DESIGN.md's rules. Every `.impeccable/design.json` has *something* to render, even on day zero. @@ -338,64 +349,40 @@ Your own write is the freshest source; subsequent commands in this session don't ## Seed mode -For projects with no visual system to extract yet. Produces a minimal scaffold, not a full spec. +For projects with no visual system to extract yet. Produces a user-chosen visual-world scaffold, not a fabricated token spec. -### Step 1: Confirm seed mode +### Step 1: Route through new-work's workshop -Before interviewing: "There's no existing visual system to scan. I'll ask five quick questions to seed a starter DESIGN.md. You can re-run `/impeccable document` once there's code, to capture the real tokens and components. OK?" +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 the user prefers to skip, stop. No file. +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. -### Step 2: Five questions +If new-work already completed the workshop in this session, use its chosen direction directly. Do not ask again. -Group into one `AskUserQuestion` interaction. Options must be concrete. +### Step 2: Write seed DESIGN.md -1. **Color strategy.** Pick one: - - Restrained: tinted neutrals + one accent ≤10% - - Committed: one saturated color carries 30–60% of the surface - - Full palette: 3–4 named color roles, each deliberate - - Drenched: the surface IS the color - - Then: one hue family or anchor reference ("deep teal", "mustard", "Klim #ff4500 orange"). - -2. **Typography direction.** Pick one (specific fonts come later): - - Serif display + sans body - - Single sans (warm / technical / geometric / humanist; pick a feel) - - Display + mono - - Mono-forward - - Editorial script + sans - -3. **Motion energy.** Pick one: - - Restrained: state changes only - - Responsive: feedback + transitions, no choreography - - Choreographed: orchestrated entrances, scroll-driven sequences - -4. **Three named references.** Brands, products, printed objects. Not adjectives. - -5. **One anti-reference.** What it should NOT feel like. Also named. - -### Step 3: Write seed DESIGN.md - -Use the six-section spec from Scan mode. Populate what the interview answers; leave the rest as honest placeholders. The seed is a scaffold, not a fabricated spec. +Use the canonical section order from Scan mode. Populate the selected workshop direction and leave unresolved implementation facts as honest placeholders. The seed commits a world and its invariants; it does not pretend implementation tokens already exist. Lead the file with: ```markdown - + ``` Per-section guidance in seed mode: -- **Overview**: Creative North Star and philosophy phrased from the answers (color strategy + motion energy + references). Reference the user's anti-reference directly. -- **Colors**: Color strategy as a Named Rule (e.g. *"The Drenched Rule. The surface IS the color."*). Hue family or anchor reference. No hex values; mark as `[to be resolved during implementation]`. -- **Typography**: the direction the user picked (e.g. "Serif display + sans body"). No font names yet: `[font pairing to be chosen at implementation]`. -- **Elevation**: inferred from motion energy. Restrained/Responsive → flat by default; Choreographed → layered. One sentence. +- **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. +- **Elevation & Depth**: the selected material and depth behavior, stated as an invariant rather than inferred from a generic preset. +- **Shapes**: the selected form and corner language. - **Components**: omit entirely; no components exist yet. -- **Do's and Don'ts**: carry PRODUCT.md's anti-references directly plus the anti-reference named in Q5. +- **Do's and Don'ts**: record the durable guardrails confirmed during the world choice, not task-local refusals. 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 +### Step 3: 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." @@ -405,15 +392,15 @@ Your own write is the freshest source; no reload needed. ## Style guidelines - **Frontmatter first, prose second.** Tokens go in the YAML frontmatter; prose contextualizes them. Don't redefine a token value in two places; the frontmatter is normative. -- **Cite PRODUCT.md anti-references by name** in the Do's and Don'ts section. If PRODUCT.md lists "SaaS landing-page clichés" or "generic AI tool marketing" as anti-references, the DESIGN.md Don'ts should repeat those phrases verbatim so the visual spec enforces the strategic line. -- **Match the spec, don't invent new sections.** The six section names are fixed. If you have Layout/Motion/Responsive content to document, fold it into Overview (philosophy-level rules) or Components (per-component behavior). +- **Carry only durable product constraints.** A binding logo, identity asset, accessibility need, or brand commitment from PRODUCT.md may constrain DESIGN.md. Surface strategy stays in its surface brief. +- **Match the spec.** Use its eight canonical sections in order and omit any that are irrelevant. Put motion guidance with the world or component it affects rather than creating a token group the schema does not support. - **Descriptive > technical**: "Gently curved edges (8px radius)" > "rounded-lg". Include the technical value in parens, lead with the description. - **Functional > decorative**: for each token, explain WHERE and WHY it's used, not just WHAT it is. - **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 forceful**. The voice of a design director. "Prohibited", "forbidden", "never", "always", not "consider", "might", "prefer". Match PRODUCT.md's tone. -- **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. -- **Reference PRODUCT.md**. The anti-references section of PRODUCT.md should directly inform the Do's and Don'ts section here. Quote or paraphrase. +- **Be decisive where evidence is decisive.** Use hard language for actual invariants and softer language for provisional guidance. +- **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. ## Pitfalls @@ -423,7 +410,7 @@ Your own write is the freshest source; no reload needed. - Don't invent components that don't exist. If the project only has buttons and cards, only document those. - Don't overwrite an existing DESIGN.md without asking. - Don't duplicate content from PRODUCT.md. DESIGN.md is strictly visual. -- Don't add a "Layout Principles" or "Motion" or "Responsive Behavior" top-level section. The spec has six, not nine. Fold that content where it belongs. +- Don't replace canonical sections with near-synonyms. Put layout and responsive behavior in `Layout`; put motion with the affected world or component. - Don't rename sections even slightly. "Colors" not "Color Palette & Roles". "Typography" not "Typography Rules". Tooling parsing depends on exact headers. - Don't duplicate token values between frontmatter and prose. If a color is in `colors.primary` as hex, the prose can name it and describe its role but should not reassert a different hex. The frontmatter is normative. - Don't invent frontmatter token groups outside Stitch's schema (no `motion:`, `breakpoints:`, `shadows:` at the top level). Stitch's Zod schema only accepts `colors`, `typography`, `rounded`, `spacing`, `components`. Anything else belongs in the sidecar's `extensions`. diff --git a/.trae-cn/skills/impeccable/reference/hooks.md b/.trae-cn/skills/impeccable/reference/hooks.md index 70163c075..d3cf15810 100644 --- a/.trae-cn/skills/impeccable/reference/hooks.md +++ b/.trae-cn/skills/impeccable/reference/hooks.md @@ -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. diff --git a/.trae-cn/skills/impeccable/reference/init.md b/.trae-cn/skills/impeccable/reference/init.md index d3be0fb9a..d3e3a1fbd 100644 --- a/.trae-cn/skills/impeccable/reference/init.md +++ b/.trae-cn/skills/impeccable/reference/init.md @@ -1,224 +1,114 @@ -# Init Flow +# Init flow -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 [DESIGN.md format spec](https://raw.githubusercontent.com/google-labs-code/design.md/main/docs/spec.md). Answers "how it looks". -- **`.impeccable/live/config.json`** (live mode): pre-configured so `/impeccable live` boots straight into variant mode with no first-time detour. - -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. +`init` captures durable product truth in PRODUCT.md. It does not invent a visual world and does not write DESIGN.md; [new-work.md](new-work.md) creates or expands one, and [document.md](document.md) records an incumbent one. Existing runnable web projects may also receive `.impeccable/live/config.json`. ## Step 1: Load current state -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). +Use the PRODUCT.md path resolved by context.mjs. Update it instead of creating a competing authority. In a child app inheriting root context, confirm shared versus app-specific scope before writing. -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. -- **PRODUCT.md exists, DESIGN.md missing**: skip to Step 5 and offer to run `/impeccable document` for DESIGN.md. -- **PRODUCT.md exists but has no `## Register` section (legacy)**: add it. Infer a hypothesis from the codebase (see Step 2), confirm with the user, write the field. -- **PRODUCT.md exists but has no `## Platform` section (legacy)**: add it the same way, but only when the project is native (`ios` / `android` / `adaptive`) or the user wants it explicit; a missing field already means `web`. -- **Both exist**: ask the user directly to clarify what you cannot infer. Ask which file to refresh. Skip the one the user doesn't want changed. -- **Just DESIGN.md exists (unusual)**: do Steps 2-4 to produce PRODUCT.md. +- **No PRODUCT.md:** explore, interview, and write it. +- **PRODUCT.md exists:** ask what product knowledge is stale or missing; do not reopen confirmed fields without a reason. +- **Legacy PRODUCT.md:** add only durable missing facts; absent `## Platform` means `web` unless evidence says otherwise. +- **Only DESIGN.md exists:** leave it untouched and create PRODUCT.md. +- **Redesign/rebrand request:** preserve confirmed product truth unless the user changes it. Visual replacement happens later in new-work, not here. -Never silently overwrite an existing file. Always confirm first. +Never silently overwrite an existing file or offer DESIGN.md during init. If another request invoked init, finish PRODUCT.md and resume it. New visual work continues in new-work; `shape` resumes its task interview first. -If init was invoked as a setup blocker by another command, such as `/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 project -## Step 2: Explore the codebase +Before asking, scan enough to avoid making the user repeat known facts: product docs and copy; package/config and app boundaries; features, workflows, routes, and roles; names, logos, legal/proof assets, and brand commitments; platform/accessibility signals; and the dev command/entry when live mode applies. -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: +Treat repository evidence as a hypothesis, not user approval. Note visual maturity without documenting, extending, or replacing the world. -- **README and docs**: Project purpose, target audience, any stated goals -- **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 -- **Any style guides or brand documentation** +Form a platform hypothesis: `web`, `ios`, `android`, or `adaptive` (one product that genuinely adapts its design language per OS). Mobile web remains `web`; a native wrapper around a website does not make its design language native. -Also form a **register hypothesis** from what you find: +## Step 3: Interview for product truth -- Brand signals: `/`, `/about`, `/pricing`, `/blog/*`, `/docs/*`, hero sections, big typography, scroll-driven sections, landing-page-shaped content. -- Product signals: `/app/*`, `/dashboard`, `/settings`, `/(auth)`, forms, data tables, side/top nav, app-shell components. +ask the user directly to clarify what you cannot infer. Ask only about material gaps the repository and original request do not answer with strong evidence. -Register is a hypothesis at this point, not a decision; Step 3 confirms it. +Use the structured question tool when available; otherwise ask and wait. Keep rounds to at most three focused questions and require one real answer or approval round before writing a new PRODUCT.md. Confirm inferences. -Also form a **platform hypothesis**: +Start with the unknowns that most change future product decisions: -- Native signals: React Native / Expo (`react-native`, `expo`), Flutter (`pubspec.yaml`, `flutter`), SwiftUI / UIKit (`.swift`, `.xcodeproj`, an `ios/` app target), Jetpack Compose / Android (`build.gradle`, an `android/` app module, `AndroidManifest.xml`). An `ios/` and/or `android/` directory that is a real app target, not just a Capacitor/Cordova wrapper around a website. -- Web signals (the default): a web framework (Vite, Next, Nuxt, SvelteKit, Astro), an HTML entry, a CSS/Tailwind setup, no native app target. +1. Who is the primary user, in what situation, and what job are they doing? +2. What does the product make possible, and what is its meaningfully different mechanism or position? +3. What durable constraints, assets, evidence, or product facts must future work preserve? -Values: `web` / `ios` / `android` / `adaptive` (one codebase, ships both, adapts per OS). Mobile web is still `web`. Like register, this is a hypothesis; Step 3 confirms it. +Confirm ambiguous platform separately. Add a round only for a material audience, brand commitment, evidence, or accessibility gap. Record undecided facts instead of inventing them. -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. +Do not ask for an aesthetic direction, emotional feel, visual references, colors, typography, or style during init. If the user volunteers a binding visual constraint, record it without expanding it. -## Step 3: Ask strategic questions (for PRODUCT.md) +### What belongs here -ask the user directly to clarify what you cannot infer. Ask about anything the codebase doesn't answer with strong, explicit evidence. +- users, jobs, workflows, purpose, success, positioning, and operating context; +- capabilities, constraints, terminology, evidence, platform, and accessibility; +- confirmed voice, assets, and brand commitments. -### Interview mode, not confirmation mode +### What does not belong here -If the repo is empty or the user's brief is sparse, run a short interview before proposing PRODUCT.md. Do **not** turn a one-sentence request into a complete inferred PRODUCT.md and ask for blanket confirmation. - -- Use the harness's structured question tool when one exists. Otherwise, ask directly in chat and stop: one question at a time, with lettered options where the crawl suggests likely answers, waiting for each answer before the next. -- Keep skill vocabulary (register, belief ladder, anti-references) out of question text; ask for the thing in words the user would use. For the brand register, ask like a magazine editor profiling the brand: curious and narrative, drawing out the story, the feel, and what a visitor should come to believe. -- Ask in focused rounds and wait for answers between them. Keep **one topic per question**; add rounds rather than fold several topics into one either-or choice. Options obey the same rule: an option answers only the question asked; never write a compound option that bundles a feeling with a business outcome or names an additional audience. -- Use inferred answers as hypotheses or options, not as finished facts. -- Complete at least one real user-answer round before drafting PRODUCT.md, unless every required answer is directly discoverable from repo docs. -- Round 1 should establish register, platform, users, purpose, positioning, and desired outcome. -- Round 2 should establish brand personality or references, anti-references, and accessibility needs, plus conversion & proof for Persuade surfaces. - -### Minimum viable interview - -Ask enough to complete PRODUCT.md. At minimum, cover register confirmation, **platform confirmation** (`web` / `ios` / `android` / `adaptive`), users, purpose, positioning, brand personality, anti-references, and accessibility needs (plus conversion & proof for Persuade surfaces) unless each answer is directly discoverable from repo context. Never let the template's default `web` stand unconfirmed for a native or cross-platform repo. After at least one interview round, you may propose inferred answers, but the user must confirm them before you write PRODUCT.md. Never synthesize PRODUCT.md from the original task prompt alone. - -### Register (ask first; it shapes everything below) - -Every design task serves one of four visitor modes (see SKILL.md's Registers): **Persuade** (marketing, landing, campaigns), **Experience** (portfolios, albums, bodies of work), **Operate** (app UI, admin, dashboards, tools), and **Read** (documentation, editorial, long-form). PRODUCT.md stores the two-value family that covers them: `brand` covers Persuade and Experience, `product` covers Operate and Read. The brief decides the exact mode per task; the stored register only sets the default family. - -If Step 2 produced a clear hypothesis, lead with it: *"From the codebase, this looks like a [brand / product] surface. Does that match your intent, or should we treat it differently?"* - -If the signal is genuinely split (e.g. a product with a big marketing landing), ask the user directly to clarify what you cannot infer. Ask which register describes the **primary** surface. The register can be overridden per task later, but PRODUCT.md carries one default. Settle the default before drafting any register-dependent questions; never batch Persuade-only questions (Conversion & proof) into the same round as the question that decides the register. - -### Platform (ask right after register) - -Every project targets **web** (includes responsive mobile web), **ios**, **android**, or **adaptive** (one codebase, ships both, adapts per OS: Flutter, React Native, KMP). Platform picks the native rulebook: HIG for `ios`, Material 3 for `android`, both for `adaptive`, none for `web`. - -If Step 2 produced a clear hypothesis, lead with it: *"From the codebase, this looks like a [web / ios / android / adaptive] project. Does that match?"* For cross-platform apps, decide by the **design language the app renders**, not the toolchain: one look on both platforms (Flutter's Material-everywhere default) takes that platform's value; genuine per-OS adaptation (Cupertino on iOS, Material on Android) is `adaptive`. When in doubt, `web`. - -A monorepo shipping both a website and a native app gets a PRODUCT.md per app, each with its own `## Platform`; the root PRODUCT.md carries the primary surface's platform. - -### Users & Purpose -- Who uses this? What's their context when using it? -- What job are they trying to get done? -- What is this for? A purpose stated in README or docs is a hypothesis, not strong evidence; confirm it, don't transcribe it. -- What does success look like? -- If more than one kind of user is plausible, confirm a primary and secondary audience; don't manufacture a split that isn't there. An audience implied by another answer (a success metric, a CTA) is still unconfirmed; ask before writing it as secondary. -- If the surface speaks to a different audience than the people who use the product, ask the user to name both. -- For brand: what emotions should the interface evoke? (confidence, delight, calm, urgency) Ask this standalone; don't fold emotions into the success question. -- For product: what workflow are they in? What's the primary task on any given screen? - -### Positioning -- In one line, what does this do that nothing else does? The single strategic claim every screen reinforces. - -### 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? - - 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? - -### Conversion & proof (Persuade surfaces only) - -Ask these only when the primary surface is a Persuade one (marketing, landing, campaigns). Experience and Read surfaces (portfolios, albums, long-form writing, documentation) get no CTA, belief-ladder, or proof questions even when the stored register is `brand`; skip this section and its PRODUCT.md counterpart. - -- What's the primary CTA? -- What's the secondary fallback, for visitors not ready for the primary? -- The one line a visitor should remember after 10 seconds. -- What must the visitor believe, in order, before taking the primary CTA? (The template's belief ladder.) -- What proof is on hand? Ask the user to hand over any testimonials, case studies, press, or client/partner logos they already have. If you can receive files directly, collect them; otherwise create `.impeccable/assets/proof/` and ask the user to add files there. Reference supplied files by path; record text proof inline. - -### Accessibility & Inclusion -- Specific accessibility requirements? (WCAG level, known user needs) -- Considerations for reduced motion, color blindness, or other accommodations? - -Skip questions where the answer is already clear. **Do NOT ask about colors, fonts, radii, or visual styling here.** Those belong in DESIGN.md, not PRODUCT.md. +- visual worlds, palettes, typography, components, or page concepts; +- visitor mode, narrative, CTA/proof sequence, or other surface strategy; +- invented testimonials, customers, benchmarks, pricing, licensing, or deployment claims; +- a requirement to decide every optional field. ## Step 4: Write PRODUCT.md -Write PRODUCT.md only after the user has confirmed the strategic answers from Step 3. If an inferred answer is uncertain or unconfirmed, ask before writing. Confirmed means what the user actually said yes to; do not pad a confirmed answer with extras they never picked (additional anti-references, audiences, roadmap claims, a WCAG level), whether drawn from the crawl, another answer, or your own option text. If an extra belongs in the doc, ask about it first. - -Synthesize into a strategic document: +Write only confirmed facts and explicitly marked open decisions. Omit irrelevant sections rather than filling them with generic prose. ```markdown # Product -## Register - -product - ## Platform web ## Users -[Who they are, their context, the job to be done. Primary audience; a secondary audience or a surface-vs-user split only when they apply.] +[Primary users, their situation, and job. Add other audiences only when confirmed.] ## Product Purpose -[What this product does, why it exists, what success looks like] +[What the product does, why it exists, and what success means.] ## Positioning -[The single strategic claim every screen reinforces. Not a visual rule, not an anti-reference.] +[The product mechanism or claim a neighboring product could not truthfully copy.] -## Conversion & proof -[Persuade surfaces only (marketing, landing, campaigns). Omit this section entirely, heading included, for the product register and for Experience or Read surfaces such as portfolios, albums, or long-form writing.] -- Primary and secondary CTA: [...] -- The line a visitor remembers after 10 seconds: [...] -- Belief ladder: [...] -- Proof on hand: [testimonials, case studies, press, or logos, referenced by path] +## Operating Context +[Workflows, environments, tools, documents, materials, and rituals that are factual parts of using or evaluating the product.] -## Brand Personality -[Voice, tone, 3-word personality, emotional goals] +## Capabilities and Constraints +[Confirmed functionality, technical constraints, terminology, and explicitly undecided product facts.] -## Anti-references -[What this should NOT look like. Specific bad-example sites or patterns to avoid.] +## Brand Commitments +[Existing name, voice, assets, personality, identity constraints, and references the user explicitly made binding. Omit when none exist.] -## Design Principles -[3-5 strategic principles derived from the conversation. Principles like "practice what you preach", "show, don't tell", "expert confidence". NOT visual rules like "use OKLCH" or "magenta accent".] +## Evidence on Hand +[Real content, data, demonstrations, testimonials, case studies, press, or assets, with paths where applicable. State absences that future work must not fabricate.] + +## Product Principles +[Three to five durable strategic principles derived from confirmed answers; no visual recipes.] ## Accessibility & Inclusion -[WCAG level, known user needs, considerations] +[Known user needs or required standard. Omit when no product-specific requirement was established.] ``` -Register is either `brand` or `product` as a bare value. No prose, no commentary. Platform is `web`, `ios`, `android`, or `adaptive`, also a bare value; omit the section only on legacy files you're leaving untouched, otherwise write `web` explicitly. +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. -Write fields as prose, and use bold sparingly: only where a word carries a decision, never as a label lead-in on every line. +### Completion gate -Write to `PROJECT_ROOT/PRODUCT.md`. If `.impeccable.md` existed, the loader already renamed it; merge into that content rather than starting from scratch. +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: Decide on DESIGN.md +## Step 5: Configure live mode when useful -Offer `/impeccable document` either way. Two paths: +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. -- **Code exists** (CSS tokens, components, a running site): "I can generate a DESIGN.md that captures your visual system (colors, typography, components) so variants stay on-brand. Want to do that now?" -- **Pre-implementation** (empty project): "I can seed a starter DESIGN.md from five quick questions about color strategy, type direction, motion energy, and references. You can re-run once there's code, to capture the real tokens. Want to do that now?" +## Step 6: Wrap up or resume -If the user agrees, delegate to `/impeccable document` (it auto-detects scan vs seed). Load its reference and follow that flow. +Summarize captured and deliberately undecided facts. Do not offer DESIGN.md merely because it is missing. -If the user prefers to skip, mention they can run `/impeccable document` any time later. +Recommend the next action from the actual project state: -## Step 6: Configure live mode (when code exists) +- Empty or early project: ask naturally for the surface to be built, or use `/impeccable shape ` when the user wants a confirmed brief without implementation. New-work will establish a visual world only when the requested work needs one. +- Existing coherent interface without DESIGN.md: `/impeccable document` if the user wants the incumbent system recorded independently of a new build. +- Existing surface needing work: name the most relevant scoped command. +- Web project ready for visual iteration: `/impeccable live` when configured. -**Skip this step when the platform is native** (`ios` / `android` / `adaptive`): live mode drives a browser overlay. A hybrid wrapper or Expo web target serving HTML doesn't change that. - -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 .trae-cn/skills/impeccable/scripts/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) and platform captured (web / ios / android / adaptive) -- 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 or live config is pending, one line on how to set it up later - -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 platform**, offer the 2-4 most relevant (not a menu dump), and give the exact command to type. Group by intent: - -- **Build something new**: `/impeccable craft ` (shape, then build end-to-end) or `/impeccable shape ` (plan first). Lead with this for empty or early-stage projects. -- **Improve what's there**: name the specific surface. `/impeccable critique ` for a scored UX review; `/impeccable audit ` for a11y / perf / responsive checks; `/impeccable polish ` 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** (web only): `/impeccable live` (configured in Step 6) to pick elements in the browser and generate variants in place. **Skip this group for native platforms.** - -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 the user directly to clarify what you cannot infer. Ask whether they'd like a brief summary of PRODUCT.md appended to RULES.md for easier agent reference. If yes, append a short **Design Context** pointer section there. +If init was invoked by another request, resume without rerunning context.mjs; new-work owns later visual decisions. diff --git a/.trae-cn/skills/impeccable/reference/ios.md b/.trae-cn/skills/impeccable/reference/ios.md index ef10ee0d2..ccef5d2c4 100644 --- a/.trae-cn/skills/impeccable/reference/ios.md +++ b/.trae-cn/skills/impeccable/reference/ios.md @@ -2,7 +2,7 @@ For native iOS / iPadOS apps: SwiftUI, UIKit, React Native, Expo, Flutter shipping to Apple hardware. -On native, register narrows. HIG conformance governs structure, navigation, and interaction whatever the register; brand expresses through the expressive layer the platform provides (tint, type, motion, content). Calm, Duolingo, and Spotify carry strong identity entirely inside HIG conventions. +On native, the visitor mode narrows what expression may override. HIG conformance governs structure, navigation, and interaction in every mode; brand expresses through the layer the platform leaves open (tint, type, motion, content). ## The iOS slop test diff --git a/.trae-cn/skills/impeccable/reference/layout.md b/.trae-cn/skills/impeccable/reference/layout.md index 6bc335b3c..db0f16300 100644 --- a/.trae-cn/skills/impeccable/reference/layout.md +++ b/.trae-cn/skills/impeccable/reference/layout.md @@ -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. --- -## Register +## 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, register, 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 .trae-cn/skills/impeccable/scripts/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 `/impeccable polish` for the final pass. +When the structure holds, hand off to `/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) * )`: 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. diff --git a/.trae-cn/skills/impeccable/reference/live.md b/.trae-cn/skills/impeccable/reference/live.md index ba78670e6..e8fa3a191 100644 --- a/.trae-cn/skills/impeccable/reference/live.md +++ b/.trae-cn/skills/impeccable/reference/live.md @@ -33,7 +33,7 @@ Chat is overhead. No recap, no tutorial output, no pasting PRODUCT / DESIGN bodi node .trae-cn/skills/impeccable/scripts/live.mjs ``` -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. +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, DESIGN.md, and any surface brief already loaded by Setup in mind for variant generation: **DESIGN.md wins on visual decisions; PRODUCT.md wins on durable product and voice decisions; the surface brief wins on this surface's strategy.** 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 requires the user's explicit redesign/replacement intent. `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). @@ -187,7 +187,7 @@ All three carry `fallback: "agent-driven"`. Follow **Handle fallback** below. ### 3. Load the action's reference -If `event.action` is `impeccable` (the default freeform action), use SKILL.md's shared laws plus the loaded register reference (`brand.md` or `product.md`). Do not load a sub-command reference. **Freeform is not a pass to skip parameters:** you still follow the composition budget and the freeform bias in **§7 Parameters** below. Sub-command files list MUST-have signature knobs; freeform has no such file, so sizing knobs from surface weight and primary axes is entirely on you. +If `event.action` is `impeccable` (the default freeform action), use SKILL.md's shared laws and decide the visitor mode from the selected surface. Do not load a sub-command reference. **Freeform is not a pass to skip parameters:** you still follow the composition budget and the freeform bias in **§7 Parameters** below. Sub-command files list MUST-have signature knobs; freeform has no such file, so sizing knobs from surface weight and primary axes is entirely on you. Any other `event.action` (`bolder`, `quieter`, `distill`, `polish`, `typeset`, `colorize`, `layout`, `adapt`, `animate`, `delight`, `overdrive`): Read `reference/.md` before planning. Each sub-command encodes a specific discipline; skipping its reference produces generic output. Those files may require specific params; layer them on top of the §7 budget, not instead of it. @@ -216,7 +216,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. @@ -224,10 +224,7 @@ This sentence is the **identity lock**. Every variant must be readable as the sa **Default mode**: the existing identity is preserved. Variants vary expression axes within it. *This is the right mode for ~90% of live sessions.* The user picked an element on a real product they're shipping; they expect variants of *their* hero, not three different brands' heroes. -**Departure mode**: the existing identity is rejected. Variants propose alternatives consistent with PRODUCT.md voice. Trigger only when at least one is true: - -- PRODUCT.md anti-references explicitly call out the current surface ("the current `index.html` is itself an example"; "diffuse away from this"; "the page on screen is the failure"). Generic anti-references that describe what to avoid in general do **not** trigger departure mode; only ones that point at *this* surface specifically. -- The user's freeform prompt explicitly asks for departure ("rebuild this from scratch", "what if it weren't editorial at all", "show me something completely different"). +**Departure mode**: the existing identity is rejected. Variants propose alternatives consistent with durable product and brand truth. Trigger only when the user explicitly asks for departure in the current request or freeform prompt ("redesign this", "rebuild this from scratch", "what if it weren't editorial at all", "show me something completely different"). A stale page critique or an old task note is not replacement authorization. If you're unsure, you're in default mode. The cost of being wrong about default is "three on-brand variants with similar feel": recoverable, the user picks none. The cost of being wrong about departure is "three off-brand variants": unrecoverable, the user is annoyed. @@ -246,13 +243,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 the brand's stated voice and register in PRODUCT.md. Do NOT pick from a fixed catalog of lane categories. The right three directions for this brand are not the same as the right three for another brand, 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. @@ -262,7 +259,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. @@ -289,7 +286,7 @@ In **default mode**, the prompt narrows the axes you choose, not the identity. * In **departure mode**, the prompt narrows the lanes you draw from, not the families. *"Make it feel like a newspaper front page"* would itself be a departure-mode prompt; honor it but pick three meaningfully different newspaper-adjacent lanes (broadsheet vs. tabloid vs. trade journal), and run the family pass to confirm they don't collapse into one. -When the prompt and PRODUCT.md anti-references conflict (the prompt asks for X, the anti-references ban X), the anti-references win; they describe the brand's standing position, the prompt is one moment. +When the prompt conflicts with a confirmed binding brand commitment or DESIGN.md invariant, preserve the invariant unless the user explicitly revokes or replaces it. Task-local strategy from the matching surface brief may change when the user changes that surface's goal. ### 6. Write all variants in a single edit diff --git a/.trae-cn/skills/impeccable/reference/new-work.md b/.trae-cn/skills/impeccable/reference/new-work.md index 6f3d47984..ee6b7bbb7 100644 --- a/.trae-cn/skills/impeccable/reference/new-work.md +++ b/.trae-cn/skills/impeccable/reference/new-work.md @@ -1,47 +1,178 @@ -# New identity work +# New visual work -Read this only when the project has no committed visual identity, or the user explicitly asked to replace the current one. A missing DESIGN.md is not enough: code, tokens, chosen type, components, and assets are incumbent design authority. If they exist, preserve and extend them unless the brief says to discard them. +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. -This playbook is task-scoped. PRODUCT.md owns durable product facts and DESIGN.md owns durable visual invariants; the direction for one page or feature belongs in the current task, not in either document. +## 1. Name the intent -## Choose the authority +- **Greenfield:** no coherent visual implementation. +- **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. -Classify the work before inventing anything: +A plain “redesign this page/site” authorizes replacement. “Redesign this within the current brand/system” means extension or refinement. Ask once only when the wording is genuinely ambiguous. -- **Extend**: an incumbent system exists and the task adds or improves a surface. Preserve its lineage, semantics, and affordances. -- **Surface redesign**: the current identity remains, but this surface may change its topology, hierarchy, and expression. -- **New identity**: nothing committed exists, or the user explicitly authorized a rebrand. +## 2. Resolve visual authority -When the evidence is mixed, preserve. Novelty is not permission to erase authored decisions. +Read DESIGN.md and representative code, tokens, components, and assets. Choose one path: -## Find the concept +### A. Explicit redesign -Name the subject, audience, page job, and visitor mode from SKILL.md. Then identify the product's unique mechanism: what it does, proves, or enables that a neighboring product cannot truthfully claim. +The old DESIGN.md and implementation are not authority. Keep only unrevoked product facts, content, function, native expectations, constraints, and brand commitments. Establish a replacement world. -Derive the concept from that mechanism through one primary transformation: information topology, evidence, temporal behavior, or spatial mapping. The transformation must change how the surface is organized or behaves, not merely how it is decorated. This produces stranger and more defensible work than imitating a culturally specific document or object. Borrowing a literal cultural form is allowed only when it is genuinely native to the product and audience, and never as costume. +### B. DESIGN.md covers this kind of surface -For **Operate** and **Read**, start with the task and information structure. Native controls remain native controls; expression comes from hierarchy, density, rhythm, state, and the system around them. For **Persuade** and **Experience** creating a new identity, explore more freely, but keep the product legible and the primary action obvious. +Use its invariants and normative tokens. Skip world-building and discover the surface. -Run `node .trae-cn/skills/impeccable/scripts/concept-seed.mjs` only for Persuade or Experience new-identity work when two or more directions remain equally credible. It is an entropy source, not an authority: never let the draw overrule the strongest fit. When color is genuinely unconstrained, `node .trae-cn/skills/impeccable/scripts/palette.mjs` may break a reflex palette; subject evidence and incumbent color still win. +### C. A coherent implementation exists but DESIGN.md does not -## State the direction +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. -Before code, write a task-scoped direction contract of at most 120 words in your reasoning: +### D. The brand exists, but a whole-surface family is unresolved -- the thesis this surface owns; -- the structural or behavioral transformation; -- what authority is preserved; -- the first viewport's composition and primary action; -- the signature element the surface will be remembered by. +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. -Do not embed this contract as a comment in the production artifact or turn it into a second design system. In an attended substantial build, confirm it once. In an unattended run, record it and continue. +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. -## Build once, commit fully +### E. No confirmed visual authority exists -Plan one compact token system and one layout source: the concept or the content's real structure. Build the strongest coherent direction once. Commitment means the transformation governs the whole surface; it does not mean every control must be rebuilt as a metaphor. +Establish a world. Scaffolds, framework defaults, and stray utilities are not identity. -Make the first viewport demonstrate the product's mechanism. Pace long surfaces through contrast in density and scale, and cut sections that only repeat claims. Briefs that depend on imagery ship real, verified imagery. Preserve semantic HTML, familiar interaction behavior, accessibility, and the project's technical conventions. +## 3. Discover the requested surface -## Finish proportionally +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. -Inspect desktop and mobile, write one honest critique against the brief and direction, patch material defects, then run the detector once. Repeat only when a real defect remains. A separate reviewer is optional when the harness already provides one and the extra cost is earned by the risk; it is not a default step and its presence is not evidence of quality. +Ask one attended round of at most three material questions without repeating durable facts. CTA hierarchy, proof sequence, content gaps, and interaction outcomes belong here, not PRODUCT.md. For a fully specified narrow request, state the interpretation and invite correction. + +When `shape` has already completed its discovery interview, reuse those confirmed answers and do not ask this round again. + +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. Each candidate joins a durable visual system (identity thesis, information/layout grammar, material and type behavior, color strategy, imagery, motion, reusable signature) to a concrete expression for this surface (topology, sequence, focal moment, and implementation consequence). Do not rank yet. +3. **Break the ranking rut once.** Run `node .trae-cn/skills/impeccable/scripts/concept-seed.mjs --scope direction`. Promote the assigned grounded pair and weigh its challengers only when they can become both a coherent system and a strong solution to this task. +4. **Test coupling and breadth.** Reject a pair if its surface could swap into another candidate unchanged, or if its world is merely the surface motif repeated. Test each survivor on the requested surface plus navigation, quiet and dense content, interaction/state, and one unlike future surface. Compare it with both the category habit and predictable contrarian response. +5. **Offer coupled choices.** Present two or three materially different pairs without recommendation cues. For each, show the world rules, this surface's 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. 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 .trae-cn/skills/impeccable/scripts/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 promoted candidate when a roll ran; otherwise use the strongest grounded structure. It must survive both tests. + +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). + +### Write or update DESIGN.md + +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: + +`` + +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 .trae-cn/skills/impeccable/scripts/surface-brief.mjs read ` + +Exit 0: preserve still-valid decisions and change only what the user changed. For redesign, retain valid product strategy, content, function, and open decisions; replace visual direction and contract. Exit 2: no brief. After the contract, write with `node .trae-cn/skills/impeccable/scripts/surface-brief.mjs write [related-target ...]`. + +The body is concise and contains: + +```markdown +# Surface brief: [name] + +## Scope +[Primary/related route or artifact, visitor mode, and what this surface owns.] + +## Product strategy +[Surface-specific audience and job, desired outcome, primary/secondary action or task, content and proof sequence, factual constraints, and explicitly open decisions.] + +## Selected direction +[Reference to the applicable DESIGN.md world or expression range, selected surface concept, focal moment, narrative/interaction sequence, and implementation consequence.] + +## Direction contract +[The six contract blocks below.] + +## Open decisions +[Only unresolved items that later work must not silently invent. Omit when empty.] +``` + +Commit `.impeccable/surfaces/.md` as stable later-work context. Exclude global truth, exact tokens, transient notes, and work logs. + +## 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. + +Before code, write a direction contract of at most 150 words in an opening HTML or framework comment. The first 200 characters must name `DIRECTION CONTRACT`. + +- `UNIQUE`: the task thesis tied to the product mechanism; +- `NOT-TEMPLATE`: the category-default arrangement this structure refuses; +- `OWN-WORLD`: the confirmed DESIGN.md invariants, tokens, and materials it uses; +- `STORY`: what the visitor understands, believes, and does; +- `FIRST VIEWPORT`: exact composition, hierarchy, action, and where the concept exceeds competent convention; +- `FORM`: chosen structure or behavior, signature, implementation consequence, and seed key. + +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. + +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. + +## 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. + +Build the strongest coherent direction once. Its grammar governs navigation, actions, controls, content, and transitions without disguising affordances. Give the focal form the scale that gives it force; do not trap it inside a standard hero panel. + +**Make the opening a thesis.** Demonstrate the mechanism immediately; leave an idea, interaction, or evidence, not merely mood. + +**Commit before correcting.** Land the hard move at full strength before refining it. In unattended work, safety is the known risk. + +**Commit at page scale.** Let color, material, image, or type own a region when the world calls for it. Scattered signature decoration is not commitment. + +**Pace the whole surface.** Vary density, scale, image, motion, and quiet inside one grammar. Cut repeated claims; prove the mechanism with real artifact, interaction, data, transformation, or content. + +**Author motion as material.** Motion expresses the world and task. Premium moments go beyond repeated transform/opacity through earned focus, depth, masks, continuity, light, or material change. Bound expensive effects, test in-browser, keep content visible by default, and design reduced motion. + +Preserve semantics, affordances, accessibility, performance, responsiveness, and project conventions. Operate/Read express through topology, hierarchy, density, rhythm, and state; Persuade/Experience may earn drama. + +## 8. Solidify the visual record + +After first implementation of a new/replacement world or approved expansion, refresh DESIGN.md from the build: + +- replace provisional direction with the exact type, color roles, tokens, spacing/radii, components, states, and motion that survived; +- add normative YAML tokens only for values the implementation actually uses; +- remove the directional-seed status once the record and implementation agree; +- preserve broader world invariants and expression ranges; +- do not promote the task's story, hero composition, or one-off motif into a global rule unless it is intentionally reusable. + +Ordinary extension does not rewrite DESIGN.md; only approved durable changes do. + +## 9. Finish like a 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. diff --git a/.trae-cn/skills/impeccable/reference/operate.md b/.trae-cn/skills/impeccable/reference/operate.md index 04ee9d7d2..f420cbaa2 100644 --- a/.trae-cn/skills/impeccable/reference/operate.md +++ b/.trae-cn/skills/impeccable/reference/operate.md @@ -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. - No orchestrated page-load sequences. Product loads into a task; users don't want to watch it load. -## Product bans (on top of the shared absolute bans) +## Product constraints - Decorative motion that doesn't convey state. - Inconsistent component vocabulary across screens. If the "save" button looks different in two places, one is wrong. @@ -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. diff --git a/.trae-cn/skills/impeccable/reference/polish.md b/.trae-cn/skills/impeccable/reference/polish.md index 0b281eb6e..eeae9d6a1 100644 --- a/.trae-cn/skills/impeccable/reference/polish.md +++ b/.trae-cn/skills/impeccable/reference/polish.md @@ -1,241 +1,98 @@ -> **Additional context needed**: quality bar (MVP vs flagship). +> **Additional context needed**: quality bar and shipping constraints. -Perform a meticulous final pass to catch all the small details that separate good work from great work. The difference between shipped and polished. +Polish is refinement, never concealed redesign. Preserve the incumbent visual world, content, behavior, and everything outside scope. If the concept itself is wrong, say so and recommend redesign or `bolder` instead of smuggling in a replacement. -Detector and automated QA output are defect evidence only. A clean script result is never proof that the design is strong; gather browser evidence and inspect the real interaction path. +A detector result is defect evidence, not proof of quality. Inspect the rendered experience and real interaction path. -## Design System Discovery +## 1. Establish the system -Aligning the feature to the design system is **not optional**. Polish without alignment is decoration on top of drift, and it makes the next person's job harder. Discovery comes before any other polish work. +Read DESIGN.md and representative tokens, shared components, patterns, and neighboring flows. If no formal system exists, use coherent project conventions. -1. **Find the design system**: Search for design system documentation, component libraries, style guides, or token definitions. Study the core patterns: design principles, target audience, color tokens, spacing scale, typography styles, component API, motion conventions. -2. **Note the conventions**: How are shared components imported? What spacing scale is used? Which colors come from tokens vs hard-coded values? What motion and interaction patterns are established? What flow shapes are used for comparable actions (modal vs full-page, inline vs route, save-on-blur vs explicit submit)? -3. **Identify drift, then name the root cause**: For every deviation, classify it as a **missing token** (the value should exist in the system but doesn't), a **one-off implementation** (a shared component already exists but wasn't used), or a **conceptual misalignment** (the feature's flow, IA, or hierarchy doesn't match neighboring features). The fix differs by category: patch the value, swap to the shared component, or rework the flow. Fixing the symptom without naming the cause is how drift compounds. +Classify each drift before fixing it: -If a design system exists, polish **must** align the feature with it. If none exists, polish against the conventions visible in the codebase. **If anything about the system is ambiguous, ask. Never guess at design system principles.** +- **missing token:** the system needs a reusable value; +- **one-off implementation:** an existing shared component or pattern should replace it; +- **conceptual mismatch:** the flow, information architecture, or hierarchy differs from comparable product areas; +- **local defect:** the implementation is simply incomplete or inconsistent. -## Pre-Polish Assessment +Fix the cause at the narrowest correct level. Ask when a binding system principle cannot be inferred. -Understand the current state and goals before touching anything: +## 2. Gather the evidence -1. **Review completeness**: - - Is it functionally complete? - - Are there known issues to preserve (mark with TODOs)? - - What's the quality bar? (MVP vs flagship feature?) - - When does it ship? (How much time for polish?) +Use the feature yourself at representative desktop and mobile sizes. Determine: -2. **Think experience-first**: Who actually uses this, and what's the best possible experience for them? Effective design beats decorative polish; a feature that looks beautiful but fights the user's flow is not polished. Walk the path from their perspective before opening DevTools. +- whether the path is functionally complete; +- the intended quality bar and time available; +- known constraints or deliberately unfinished work; +- the states, content lengths, roles, and input methods users will actually encounter. -3. **Identify polish areas**: - - Visual inconsistencies - - Spacing and alignment issues - - Interaction state gaps - - Copy inconsistencies - - Edge cases and error states - - Loading and transition smoothness - - Information architecture and flow drift (does this feature reveal complexity the way neighboring features do?) +If a prior critique exists, use it as one input: -4. **Pull in any prior critique** (optional signal): If `/impeccable critique` has been run on the same target, its priority issues are a useful prior for what to address first. Resolve the target to a file path or URL, then: - ```bash - slug=$(node .trae-cn/skills/impeccable/scripts/critique-storage.mjs slug "") - node .trae-cn/skills/impeccable/scripts/critique-storage.mjs latest "$slug" - ``` - Exit 0 with body = found; fold the P0/P1 items into your polish list and mention the snapshot path so the user sees what you read. Exit 2 = no snapshot, continue without it. The critique is one input among many. Do your own pass either way. +```bash +slug=$(node .trae-cn/skills/impeccable/scripts/critique-storage.mjs slug "") +node .trae-cn/skills/impeccable/scripts/critique-storage.mjs latest "$slug" +``` -5. **Triage cosmetic vs functional**: Classify each issue as **cosmetic** (looks off, doesn't impede the user) or **functional** (breaks, blocks, or confuses the experience). When polish time is tight, functional issues ship first; cosmetic ones can land in a follow-up. Quality should be consistent; never perfect one corner while leaving another rough. +Exit 0 returns the latest snapshot; incorporate relevant P0/P1 findings and name the snapshot read. Exit 2 means none exists. Perform an independent pass either way. -**CRITICAL**: Polish is the last step, not the first. Don't polish work that's not functionally complete. +## 3. Triage -## Polish Systematically +Separate functional defects from cosmetic ones and fix in this order: -Work through these dimensions methodically: +1. broken or blocked tasks, data loss, misleading state, and inaccessible paths; +2. missing loading, empty, error, success, disabled, and permission states; +3. flow, hierarchy, responsive, and design-system drift; +4. visual and motion inconsistencies; +5. code and asset cleanup. -### Visual Alignment & Spacing +Do not perfect one corner while leaving the rest below the same quality bar. -- **Pixel-perfect alignment**: Everything lines up to grid -- **Consistent spacing**: All gaps use spacing scale (no random 13px gaps) -- **Optical alignment**: Adjust for visual weight (icons may need offset for optical centering) -- **Responsive consistency**: Spacing and alignment work at all breakpoints -- **Grid adherence**: Elements snap to baseline grid +## 4. Polish the whole path -**Check**: -- Enable grid overlay and verify alignment -- Check spacing with browser inspector -- Test at multiple viewport sizes -- Look for elements that "feel" off +### Flow and hierarchy -### Information Architecture & Flow +- Match neighboring mental models, terminology, disclosure, routing, save behavior, and optimistic or pessimistic patterns. +- Make the primary task and current state obvious without flattening every element to equal weight. +- Ensure arrival, transition, empty, and recovery paths connect instead of behaving as isolated screens. -Visual polish on a misshapen flow is wasted work. Match the *shape* of the experience to the system, not just the surface. +### Layout and type -- **Progressive disclosure**: Match how much is revealed when, compared to neighboring features. A settings page exposing 40 fields when the rest of the app reveals 5 at a time is drift, even if every field is perfectly styled. -- **Established user flows**: Multi-step actions follow the same shape as comparable flows elsewhere: modal vs full-page, inline edit vs separate route, save-on-blur vs explicit submit, optimistic vs pessimistic updates. -- **Hierarchy & complexity**: The same conceptual weight gets the same visual weight throughout. Primary actions don't become tertiary in one corner of the product, and tertiary actions don't shout. -- **Empty, loading, and arrival transitions**: How content arrives, updates, and leaves matches how it does in adjacent features. -- **Naming and mental model**: The feature uses the same nouns and verbs as the rest of the system. A "Workspace" here shouldn't be a "Project" three screens away. +- Align to the project's grid and spacing scale; fix optical as well as mathematical alignment. +- Group related content tightly and separate distinct groups generously. +- Keep same-role typography consistent; test measure, wrapping, localization expansion, zoom, and font loading. +- Verify every supported viewport rather than correcting only the current screenshot. -### Typography Refinement +### Color, imagery, and icons -- **Hierarchy consistency**: Same elements use same sizes/weights throughout -- **Line length**: 45-75 characters for body text -- **Line height**: Appropriate for font size and context -- **Widows & orphans**: No single words on last line -- **Hyphenation**: Appropriate for language and column width -- **Kerning**: Adjust letter spacing where needed (especially headlines) -- **Font loading**: No FOUT/FOIT flashes +- Use semantic tokens and stable color meanings across themes. +- Verify text, control, and focus contrast in every state. +- Keep icon families, stroke/weight, sizing, and optical alignment coherent. +- Prevent image layout shift; use correct aspect ratios, responsive sources, and useful alt text. -### Color & Contrast +### Interaction and state -- **Contrast ratios**: All text meets WCAG standards -- **Consistent token usage**: No hard-coded colors, all use design tokens -- **Theme consistency**: Works in all theme variants -- **Color meaning**: Same colors mean same things throughout -- **Accessible focus**: Focus indicators visible with sufficient contrast -- **Gray on color**: Never put gray text on colored backgrounds; use a shade of that color or transparency +- Every control needs appropriate default, hover, focus, active, disabled, loading, error, and success behavior. +- Preserve visible keyboard focus, logical tab order, labels, and platform-appropriate touch targets. +- Keep motion coherent, interruptible, performant, and reduced-motion aware. Do not add animation merely to make polish visible. +- Validate long, missing, localized, offline, slow, and permission-limited content where the product can encounter it. -### Interaction States +### Content and code -Every interactive element needs all states: +- Keep terminology, capitalization, punctuation, and factual copy consistent. Ask before changing claims. +- Remove debug output, dead code, unused imports, obsolete styles, and polish-created duplication. +- Replace custom implementations with shared components where the system owns the pattern. +- Promote genuinely reusable values to tokens; do not create a system abstraction for one local exception. -- **Default**: Resting state -- **Hover**: Subtle feedback (color, scale, shadow) -- **Focus**: Keyboard focus indicator (never remove without replacement) -- **Active**: Click/tap feedback -- **Disabled**: Clearly non-interactive -- **Loading**: Async action feedback -- **Error**: Validation or error state -- **Success**: Successful completion +## 5. Verify and finish -**Missing states create confusion and broken experiences**. +Walk the complete path again with mouse, keyboard, and touch where applicable. Check: -### Micro-interactions & Transitions +- mobile, intermediate, and wide layouts; +- loading, empty, error, success, disabled, long-content, and missing-content states; +- zoom, contrast, focus, semantics, screen-reader names, and reduced motion; +- console errors, layout shift, interaction latency, image loading, and supported browsers; +- agreement with DESIGN.md, neighboring features, and the user's scope. -- **Smooth transitions**: All state changes animated appropriately (150-300ms) -- **Consistent easing**: Use ease-out-quart/quint/expo for natural deceleration. Never bounce or elastic; they feel dated. -- **No jank**: Smooth animations; use atmospheric blur/filter/mask/shadow effects when they add polish, but bound expensive paint areas and avoid casual layout-property animation -- **Appropriate motion**: Motion serves purpose, not decoration -- **Reduced motion**: Respects `prefers-reduced-motion` +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. -### Content & Copy - -- **Consistent terminology**: Same things called same names throughout -- **Consistent capitalization**: Title Case vs Sentence case applied consistently -- **Grammar & spelling**: No typos -- **Appropriate length**: Not too wordy, not too terse -- **Punctuation consistency**: Periods on sentences, not on labels (unless all labels have them) - -### Icons & Images - -- **Consistent style**: All icons from same family or matching style -- **Appropriate sizing**: Icons sized consistently for context -- **Proper alignment**: Icons align with adjacent text optically -- **Alt text**: All images have descriptive alt text -- **Loading states**: Images don't cause layout shift, proper aspect ratios -- **Retina support**: 2x assets for high-DPI screens - -### Forms & Inputs - -- **Label consistency**: All inputs properly labeled -- **Required indicators**: Clear and consistent -- **Error messages**: Helpful and consistent -- **Tab order**: Logical keyboard navigation -- **Auto-focus**: Appropriate (don't overuse) -- **Validation timing**: Consistent (on blur vs on submit) - -### Edge Cases & Error States - -- **Loading states**: All async actions have loading feedback -- **Empty states**: Helpful empty states, not just blank space -- **Error states**: Clear error messages with recovery paths -- **Success states**: Confirmation of successful actions -- **Long content**: Handles very long names, descriptions, etc. -- **No content**: Handles missing data gracefully -- **Offline**: Appropriate offline handling (if applicable) - -### Responsiveness - -- **All breakpoints**: Test mobile, tablet, desktop -- **Touch targets**: 44x44px minimum on touch devices -- **Readable text**: No text smaller than 14px on mobile -- **No horizontal scroll**: Content fits viewport -- **Appropriate reflow**: Content adapts logically - -### Performance - -- **Fast initial load**: Optimize critical path -- **No layout shift**: Elements don't jump after load (CLS) -- **Smooth interactions**: No lag or jank -- **Optimized images**: Appropriate formats and sizes -- **Lazy loading**: Off-screen content loads lazily - -### Code Quality - -- **Remove console logs**: No debug logging in production -- **Remove commented code**: Clean up dead code -- **Remove unused imports**: Clean up unused dependencies -- **Consistent naming**: Variables and functions follow conventions -- **Type safety**: No TypeScript `any` or ignored errors -- **Accessibility**: Proper ARIA labels and semantic HTML - -## Polish Checklist - -Go through systematically: - -- [ ] Aligned to the design system (drift named and resolved by root cause) -- [ ] Information architecture and flow shape match neighboring features -- [ ] Visual alignment perfect at all breakpoints -- [ ] Spacing uses design tokens consistently -- [ ] Typography hierarchy consistent -- [ ] All interactive states implemented -- [ ] All transitions smooth (60fps) -- [ ] Copy is consistent and polished -- [ ] Icons are consistent and properly sized -- [ ] All forms properly labeled and validated -- [ ] Error states are helpful -- [ ] Loading states are clear -- [ ] Empty states are welcoming -- [ ] Touch targets are 44x44px minimum -- [ ] Contrast ratios meet WCAG AA -- [ ] Keyboard navigation works -- [ ] Focus indicators visible -- [ ] No console errors or warnings -- [ ] No layout shift on load -- [ ] Works in all supported browsers -- [ ] Respects reduced motion preference -- [ ] Code is clean (no TODOs, console.logs, commented code) - -**IMPORTANT**: Polish is about details. Zoom in. Squint at it. Use it yourself. The little things add up. - -Sweat the details. Zoom in until the alignment is right and the spacing reads as deliberate. Then ship. - -**NEVER**: -- Polish before it's functionally complete -- Polish without aligning to the design system; that's decoration on drift -- Guess at design system principles instead of asking when something is ambiguous -- Spend hours on polish if it ships in 30 minutes (triage) -- Introduce bugs while polishing (test thoroughly) -- Ignore systematic issues (if spacing is off everywhere, fix the system, not just one screen) -- Perfect one thing while leaving others rough (consistent quality level) -- Create new one-off components when design system equivalents exist -- Hard-code values that should use design tokens -- Introduce new patterns or flows that diverge from established ones - -## Final Verification - -Before marking as done: - -- **Use it yourself**: Actually interact with the feature. -- **Test on real devices**: Not just browser DevTools. -- **Ask someone else to review**: Fresh eyes catch things. -- **Compare to design**: Match intended design. -- **Check all states**: Don't just test happy path. -- **Treat automation carefully**: Run detector or QA commands when they are available and relevant, fix their defects, but never cite a clean result as proof that the work is polished. - -## Clean Up - -After polishing, ensure code quality: - -- **Replace custom implementations**: If the design system provides a component you reimplemented, switch to the shared version. -- **Remove orphaned code**: Delete unused styles, components, or files made obsolete by polish. -- **Consolidate tokens**: If you introduced new values, check whether they should be tokens. -- **Verify DRYness**: Look for duplication introduced during polishing and consolidate. +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. diff --git a/.trae-cn/skills/impeccable/reference/quieter.md b/.trae-cn/skills/impeccable/reference/quieter.md index 55b00b6f3..bb163e40f 100644 --- a/.trae-cn/skills/impeccable/reference/quieter.md +++ b/.trae-cn/skills/impeccable/reference/quieter.md @@ -2,7 +2,7 @@ Quiet design is harder than bold design. Subtlety needs precision. Reduce visual --- -## Register +## Visitor mode Persuade + Experience: "quieter" means more restrained palette, more whitespace, more typographic air. Drama is reduced, not eliminated; the POV stays intact. diff --git a/.trae-cn/skills/impeccable/reference/routing.md b/.trae-cn/skills/impeccable/reference/routing.md index 1ac066499..4d37a18b6 100644 --- a/.trae-cn/skills/impeccable/reference/routing.md +++ b/.trae-cn/skills/impeccable/reference/routing.md @@ -11,7 +11,7 @@ Reason over the signals; there is no score to obey: - `critique.latest` with a low `score` or non-zero `p0` / `p1` → `polish` (it reads that snapshot as its backlog), or re-run `critique` if the snapshot looks stale. - `git.changedFiles` pointing at one surface → scope `audit` or `polish` to those files specifically, naming them. - `devServer.running` true → `live` is available for in-browser iteration; if false, don't lead with `live`. **`live` and the bundled `detect.mjs` are web-only.** If `setup.platform` is `ios`, `android`, or `adaptive`, don't lead with either; the browser overlay and the HTML rule engine don't apply to native app code. -- Otherwise group by intent exactly as init's "Recommend starting points" step does (build new / improve what's there / iterate visually), tailored to `setup.register`. +- Otherwise group by intent (build new / improve what's there / iterate visually), tailored to the current surface and `setup.platform`. **If `scan.targets` is non-empty and `setup.platform` is not `ios`/`android`/`adaptive`, run `node .trae-cn/skills/impeccable/scripts/detect.mjs --json ` once** (the bundled detector over local files: no network, no npx; it reads HTML/CSS, so skip it for native projects). `scan.via` tells you what they are: `git-changes` (the markup/style files in your dirty tree, the most relevant set), `source-dir` (e.g. `src`, `app`), `html`, or `root`. Fold the hits into your picks: many quality / contrast hits → `audit` or `polish`; a specific slop family → the matching command (gradient text or eyebrows → `quieter` / `typeset`, flat or gray palette → `colorize`, and so on). It's a real, current signal that beats guessing. If detect errors or the tree is large and slow, skip it and recommend the user run `audit` themselves; never block the suggestion on it. diff --git a/.trae-cn/skills/impeccable/reference/shape.md b/.trae-cn/skills/impeccable/reference/shape.md index 40804f6e4..acbb26e5b 100644 --- a/.trae-cn/skills/impeccable/reference/shape.md +++ b/.trae-cn/skills/impeccable/reference/shape.md @@ -1,167 +1,61 @@ -Shape the UX and UI for a feature before any code is written. This command produces a **design brief**: a structured artifact that guides implementation through discovery, not guesswork. +# Shape -**Scope**: Design planning only. This command does NOT write code. It produces the thinking that makes code good. +Discover what should be made and how it should work, then return a confirmed design brief without code. -**Output**: A design brief that can be handed off to /impeccable craft, or directly to /impeccable for freeform implementation. When visual direction probes are used, the images are supporting artifacts, not the primary output. +**Product gate:** when context reports that PRODUCT.md is missing, load and complete [init.md](init.md) before Phase 1. Do not substitute shape questions for the product interview. Once PRODUCT.md exists, return here; product context does not replace task discovery. -## Philosophy +## Phase 1: Discovery interview -Most AI-generated UIs fail not because of bad code, but because of skipped thinking. They jump to "here's a card grid" without asking "what is the user trying to accomplish?" This command inverts that: understand deeply first, so implementation is precise. +Do not write code or choose visual direction yet. -## Phase 1: Discovery Interview +### Cadence -**Do NOT write any code or make any design decisions during this phase.** Your only job is to understand the feature deeply enough to make excellent design decisions later. +- Use the structured question tool when available; otherwise ask and stop. +- Ask two or three related questions per round, then wait. One round is the default; add a second only when the answers expose a material gap. +- Do not dump a questionnaire, repeat settled facts, or turn obvious facts into menus. Assert the likely reading and invite correction. +- A sparse prompt requires at least one answer round. A precise prompt may need only a compact confirmation. -**Unattended runs:** when no user can respond (a one-shot task, an automated run, or an explicit instruction not to ask questions), answer the interview questions yourself from the brief, record the answers as the design brief, and continue without pausing at any confirmation gate in this file. The thinking is still required; only the waiting is waived. +### Round 1: purpose, people, and outcome -This is a required interaction, not optional guidance. Ask these questions in conversation, adapting based on answers. Don't dump them all at once; have a natural dialogue. ask the user directly to clarify what you cannot infer. +Choose the two or three questions that most change the result: -### Interview cadence +- What is this surface or feature for, and what problem must it solve? +- Who specifically reaches it, in what situation and state of mind? +- What is the primary thing they must understand or do? What would success look like? +- What is uniquely true here that a neighboring product or generic template could not claim? -Discovery includes at least one user-answer round unless PRODUCT.md, DESIGN.md, or an already-confirmed brief directly answers the needed inputs. With a sparse prompt, do **not** synthesize a complete brief for confirmation on the first response. +### Round 2: material, behavior, and boundaries -- Use the harness's structured question tool when one exists. Otherwise, ask directly in chat and stop. -- Ask **2-3 questions per round**, then wait for answers. -- Treat PRODUCT.md and DESIGN.md as anchors; they reduce repeated questions but do **not** replace shape for craft. Shape is task-specific. -- One round is the default. Add a second only if the first answers leave material gaps. Don't run a second round just to feel thorough. -- Round 1 should clarify purpose, audience/context, content/scope, and (for brand) visual direction. -- Round 2, when needed, fills in whatever's still genuinely missing. +Run only for material unresolved decisions: -**Assert-then-confirm, not menu-with-escape.** When PRODUCT.md and the user's prompt make one option obvious, name it and ask the user to confirm or override. Don't enumerate "Restrained / Committed / Or something else?" as a real choice; "This reads as Restrained, confirm?" beats a four-option menu when the answer is already clear. +- What real content, evidence, data, and assets must the experience carry? What are realistic minimum, typical, and maximum ranges? +- Which states and transitions matter: first-run, empty, loading, error, success, permissions, overflow, or expert use? +- What is the intended fidelity, breadth, and interactivity: exploration, production-ready screen, full flow, or broader surface? +- What must remain untouched? What would make the result feel wrong even if it looked polished? +- Which platform, framework, performance, accessibility, localization, or delivery constraints are binding? -### Purpose & Context -- What is this feature for? What problem does it solve? -- Who specifically will use it? (Not "users"; be specific: role, context, frequency) -- What does success look like? How will you know this feature is working? -- What's the user's state of mind when they reach this feature? (Rushed? Exploring? Anxious? Focused?) +Never ask for CSS values or canned aesthetic lanes. New-work owns visual-world and concept choices. -### Content & Data -- What content or data does this feature display or collect? -- What are the realistic ranges? (Minimum, typical, maximum, e.g., 0 items, 5 items, 500 items) -- What are the edge cases? (Empty state, error state, first-time use, power user) -- Is any content dynamic? What changes and how often? -- What visual assets are real content here? Note required images, product shots, illustrations, maps, textures, diagrams, generated objects, or existing project assets. +## Phase 2: Resolve the design direction -### Design Direction +For new surfaces, brand expansion, or replacement, follow [new-work.md](new-work.md) through visual authority, any world workshop, and concept choice. Reuse discovery, then return before its contract, persistence, or implementation. Inside an established world, use its concept process only when composition or interaction remains materially open. -Force a visual decision on three fronts. Skip anything PRODUCT.md or DESIGN.md already answers; ask only what's missing. +## Phase 3: Write the brief -- **Color strategy for this surface.** Pick one: Restrained / Committed / Full palette / Drenched. Can override the project default if the surface earns it (e.g. a drenched hero inside an otherwise Restrained product). -- **Theme via scene sentence.** Write one sentence of physical context for this surface: who uses it, where, under what ambient light, in what mood. The sentence forces dark vs light. If it doesn't, add detail until it does. -- **Two or three named anchor references.** Specific products, brands, objects. Not adjectives like "modern" or "clean." +Write the smallest useful brief: -### Scope +1. **Job and audience:** who arrives, their context, need, and visitor mode. +2. **Outcome and proof:** primary task/action, success, real evidence, and product-specific truth. +3. **Selected direction:** visual authority, structural/interaction thesis, sequence, focal moment, and implementation consequence. +4. **Scope and boundaries:** fidelity, breadth, interactivity, named target, what remains untouched, and explicit anti-goals. +5. **States and ranges:** realistic content/data ranges and material states. +6. **Interaction and layout:** hierarchy, topology, responsiveness, affordances, feedback, and transitions; intent, not CSS. +7. **Constraints and open decisions:** platform, delivery, accessibility, localization, reusable components, and choices a builder must not invent. -Always ask. Sketch quality and shipped quality are different outputs; don't guess between them. +Use three to five bullets when the task is settled; use the full structure only for ambiguous, multi-screen, or standalone planning. Do not restate the conversation. -- **Fidelity.** Sketch / mid-fi / high-fi / production-ready? -- **Breadth.** One screen / a flow / a whole surface? -- **Interactivity.** Static visual / interactive prototype / shipped-quality component? -- **Time intent.** Quick exploration, or polish until it ships? +## Confirm and stop -Scope answers are task-scoped. Don't write them to PRODUCT.md or DESIGN.md; carry them through the design brief only. +Present the brief for explicit confirmation or one correction round, then stop: shape never writes code or a direction contract. -### Constraints -- Are there technical constraints? (Framework, performance budget, browser support) -- Are there content constraints? (Localization, dynamic text length, user-generated content) -- Mobile/responsive requirements? -- Accessibility requirements beyond WCAG AA? - -### Anti-Goals -- What should this NOT be? What would be a wrong direction? -- What's the biggest risk of getting this wrong? - -## Phase 1.5: Visual Direction Probe (Capability-Gated) - -After the discovery interview, generate a small set of visual direction probes **before** writing the final brief when all of these are true: - -- The work is **net-new** or directionally ambiguous enough that visual exploration will clarify the brief. -- The requested fidelity is **mid-fi, high-fi, or production-ready**. Skip for sketch-only planning. -- The current harness gives you native image generation (Codex's `image_gen`, an equivalent MCP tool, or similar). Don't ask the user to install APIs or tooling. - -When those conditions are met, this step is mandatory. If image generation isn't natively available, do not ask the user to install APIs or tooling. State in one line that the image step is skipped because the harness lacks native image generation, then proceed. The one-line announcement is required, not optional; it forces a conscious decision instead of letting the step quietly evaporate. - -Use probes to explore visual lanes, not to replace the brief. - -Do not skip probes because the final UI will be semantic, editable, code-native, responsive, or accessible. Those are implementation requirements, not reasons to avoid visual exploration. - -### What to generate - -Generate **2 to 4** distinct direction probes based on the discovery answers, especially: - -- Color strategy -- Theme scene sentence -- Named anchor references -- Scope and fidelity - -The probes should differ in primary visual direction (hierarchy, topology, density, typographic voice, or color strategy), not just palette tweaks. - -### How to use the probes - -- Treat them as **direction tests**, not final designs. -- Use them to pressure-test whether the brief is pointing at the right lane. -- Ask the user which direction feels closest, what feels off, and what should carry forward. -- If the probes reveal a mismatch, revise the brief inputs before finalizing the brief. - -### Important limits - -- Do **not** skip discovery because image generation is available. -- Do **not** treat generated imagery as final UX specification, final copy, or final accessibility behavior. -- Do **not** use this step for minor refinements of existing work. It's for shaping a new surface or clarifying a big directional choice. - -If image generation isn't natively available, announce the skip in one line and proceed to the design brief. - -## Phase 2: Design Brief - -After the interview and any required probes, present a brief and **end your response**. The user must confirm before any implementation runs. Do not present a brief and then continue to code in the same response, even if the brief feels obvious to you. The user's confirmation is the gate. - -**Choose the brief shape based on how clear the answers are:** - -- **Compact form (3-5 bullets)** when discovery was crisp and the original prompt + PRODUCT.md already pinned scope, content, and direction. State what you're building, the visual lane, and end with one or two specific questions or a clear "confirm or override?" prompt. This is the default for typical craft requests with a clear prompt. -- **Full structured form (sections below)** when the task is genuinely ambiguous, multi-screen, or when the user asked for shape as a standalone step. Use this when the discipline of structure earns its weight. - -Don't pad a clear brief into a long one to look thorough. A 70-line brief restating answers the user just gave is noise, not rigor. Equally, don't skip the confirmation pause to look efficient: the pause is the point. - -Present the brief, then **stop and wait for explicit confirmation**. You are not the judge of whether the user already approved. Even when the brief feels obviously right, ask once and wait. The pause is what separates shape from premature implementation. - -### Brief Structure - -**1. Feature Summary** (2-3 sentences) -What this is, who it's for, what it needs to accomplish. - -**2. Primary User Action** -The single most important thing a user should do or understand here. - -**3. Design Direction** -Color strategy (Restrained / Committed / Full palette / Drenched) + the theme scene sentence + 2–3 named anchor references. Reference PRODUCT.md and DESIGN.md where they already answer, and note any per-surface overrides. - -If you ran the Visual Direction Probe step, name which probe direction won and what changed in the brief because of it. - -**4. Scope** -Fidelity, breadth, interactivity, and time intent from the Scope section of the interview. Task-scoped; these don't persist beyond the brief. - -**5. Layout Strategy** -High-level spatial approach: what gets emphasis, what's secondary, how information flows. Describe the visual hierarchy and rhythm, not specific CSS. - -**6. Key States** -List every state the feature needs: default, empty, loading, error, success, edge cases. For each, note what the user needs to see and feel. - -**7. Interaction Model** -How users interact with this feature. What happens on click, hover, scroll? What feedback do they get? What's the flow from entry to completion? - -**8. Content Requirements** -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., 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. - ---- - -ask the user directly to clarify what you cannot infer. Ask for explicit confirmation of the brief before finishing. - -If the user disagrees with any part, revisit the relevant discovery questions. A shape run is incomplete until the user confirms direction. - -Once confirmed, the brief is complete. The user can now hand it to /impeccable, or use it to guide any other implementation approach. (If the user wants the full discovery-then-build flow in one step, they should use /impeccable craft instead, which runs this command internally.) +When no human or structured answer mechanism exists, mark assumptions plainly, return the brief, and stop. diff --git a/.trae-cn/skills/impeccable/reference/typeset.md b/.trae-cn/skills/impeccable/reference/typeset.md index 4595fe854..12100375e 100644 --- a/.trae-cn/skills/impeccable/reference/typeset.md +++ b/.trae-cn/skills/impeccable/reference/typeset.md @@ -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. --- -## Register +## Visitor mode -New identity work: run the font selection procedure in [new-work.md](new-work.md). Fluid `clamp()` scale, ≥1.25 ratio between steps. +- **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, register, **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 .trae-cn/skills/impeccable/scripts/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 `/impeccable polish` for the final pass. +When the hierarchy holds, hand off to `/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) * )` 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 - -The tactical selection procedure and the reflex-reject list live in [new-work.md](new-work.md) (loaded for new identity work). 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 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. diff --git a/.trae-cn/skills/impeccable/scripts/command-metadata.json b/.trae-cn/skills/impeccable/scripts/command-metadata.json index 83796d54b..dad8ef2e0 100644 --- a/.trae-cn/skills/impeccable/scripts/command-metadata.json +++ b/.trae-cn/skills/impeccable/scripts/command-metadata.json @@ -1,6 +1,6 @@ { "craft": { - "description": "Full confirmed-brief-then-build flow. Runs multi-round shape discovery first, resolves visual probe and north-star mock gates when available, then builds and visually iterates. Use when building a new feature end-to-end.", + "description": "Deprecated compatibility alias for an ordinary Impeccable new-work request. It adds no behavior; natural build and redesign requests use the same flow.", "argumentHint": "[feature description]" }, "init": { diff --git a/.trae-cn/skills/impeccable/scripts/concept-seed.mjs b/.trae-cn/skills/impeccable/scripts/concept-seed.mjs index 1f141a80f..25f20d9ab 100644 --- a/.trae-cn/skills/impeccable/scripts/concept-seed.mjs +++ b/.trae-cn/skills/impeccable/scripts/concept-seed.mjs @@ -1,6 +1,7 @@ #!/usr/bin/env node /** - * Concept-seed picker: the dice half of the new-work concept procedure. + * External concept seed: the dice half of new-work's coupled-direction and + * established-world surface procedures. * * The model derives a grounded shortlist of candidate FORMS from the * audience's world and the subject's cultural home (see @@ -12,10 +13,9 @@ * * This script rolls them from outside, the same trick that made the * palette seed work: - * - BUILD INDEX (2-5): which entry of the model's own resonance-ordered - * shortlist to build. The dice never choose an ungrounded ingredient; - * they only refuse the argmax rut. (Index 1 is excluded: that's the - * concept every run would ship anyway.) + * - PROMOTED INDEX: which entry of the model's own resonance-ordered + * shortlist must be taken seriously beside its favorites. The dice never + * choose an ungrounded ingredient; they only refuse the argmax rut. * - CHALLENGERS (3): outside forms from concept-ingredients.json, weighed * against the derived candidates on exactly two axes — audience * identification and product clarity. They win only when they beat the @@ -23,8 +23,8 @@ * material and win over thin categories, which is the intended shape. * * Usage: - * node scripts/concept-seed.mjs # roll at random - * node scripts/concept-seed.mjs --from # deterministic (hash key) + * node scripts/concept-seed.mjs --scope direction + * node scripts/concept-seed.mjs --scope surface --from * * Env vars: * IMPECCABLE_CONCEPT_SEED — same as --from; for reproducible eval runs. @@ -40,6 +40,12 @@ const pool = JSON.parse(readFileSync(join(here, 'concept-ingredients.json'), 'ut const args = process.argv.slice(2); const fromIdx = args.indexOf('--from'); +const scopeIdx = args.indexOf('--scope'); +const scope = scopeIdx !== -1 ? args[scopeIdx + 1] : 'surface'; +if (scope !== 'surface' && scope !== 'direction') { + process.stderr.write('concept-seed: --scope must be direction or surface\n'); + process.exit(1); +} // When no key is supplied, generate one and print it: a user reporting a // bad outcome can hand us the key and we replay the exact roll. const key = fromIdx !== -1 @@ -47,7 +53,7 @@ const key = fromIdx !== -1 : (process.env.IMPECCABLE_CONCEPT_SEED || crypto.randomBytes(4).toString('hex')); function hashUnit(k, salt) { - const h = crypto.createHash('sha256').update(`${salt}:${k}`).digest(); + const h = crypto.createHash('sha256').update(`${scope}:${salt}:${k}`).digest(); return h.readUInt32BE(0) / 0xffffffff; } const unit = (salt) => hashUnit(key, salt); @@ -67,22 +73,48 @@ for (let i = 0; picks.length < 3 && i < 60; i++) { } } -process.stdout.write(`CONCEPT SEED (key: ${key}; rerun with --from ${key} to reproduce this roll) -BUILD INDEX: ${buildIndex} - After ordering your derived candidates by resonance, build the page whose - form comes from candidate number ${buildIndex}, exactly as if it had ranked - first: full commitment. Your top-ranked candidate is what every run in this - category would ship; the assignment exists to refuse that rut, not to - punish it. -CHALLENGERS (weigh against your derived candidates on the same two axes, -audience identification and product clarity; a challenger wins only when -it beats the grounded list on both): +const promotedInstruction = scope === 'direction' + ? `After ordering the grounded coupled directions by product fit, promote + candidate ${buildIndex} into the serious shortlist. Each candidate must join + a durable visual system to a concrete expression for the requested first + surface; select or revise that pair as one decision. It must survive the + current task plus navigation, quiet and dense content, interaction and state, + and a substantially different future surface.` + : `After ordering the task's grounded structural candidates by resonance, + promote candidate ${buildIndex} into the serious shortlist. In an attended + run, present it beside the strongest materially different candidates and + let the user select or revise the surface concept. In a truly unattended + run, use it when it survives audience identification and product clarity.`; + +const challengerInstruction = scope === 'direction' + ? `A challenger enters only when it can supply both reusable identity grammar + and a strong first-surface structure, not a one-page costume or a style pasted + onto an unrelated layout. Weigh audience identification, product clarity, + current-surface force, and cross-surface system breadth.` + : `A challenger wins only when it beats the grounded list on both audience + identification and product clarity. It may change task topology or + interaction, but never the committed visual identity.`; + +const authorityInstruction = scope === 'direction' + ? `PRODUCT.md and explicit incumbent brand commitments constrain every coupled +direction. The seed never chooses exact colors, fonts, tokens, or a user +preference, and it never permits the world and first surface to be selected +independently.` + : `PRODUCT.md and DESIGN.md constrain every surface candidate's identity +vocabulary; they do not cancel task-level composition. The seed never +authorizes a new palette, type system, material world, or unfamiliar control +behavior.`; + +process.stdout.write(`${scope.toUpperCase()} CONCEPT SEED (key: ${key}; rerun with --scope ${scope} --from ${key} to reproduce this roll) +PROMOTED INDEX: ${buildIndex} + ${promotedInstruction} + The promotion exists to refuse the model's ranking rut, not to outrank the + user or the brief. +CHALLENGERS: 1. ${picks[0]} 2. ${picks[1]} 3. ${picks[2]} -If a challenger wins, it replaces the assigned candidate. If the surface is -an existing world whose incumbent carries a deliberate, ownable idea, the -incumbent IS the chosen candidate: intensify its lineage and ignore the -roll entirely. The same override applies when the user, PRODUCT.md, or -DESIGN.md pins a direction: pinned direction beats the roll, always. +${challengerInstruction} +${authorityInstruction} +A user- or brief-pinned decision beats the roll, always. `); diff --git a/.trae-cn/skills/impeccable/scripts/context-signals.mjs b/.trae-cn/skills/impeccable/scripts/context-signals.mjs index 2fc27bea7..d296320d7 100644 --- a/.trae-cn/skills/impeccable/scripts/context-signals.mjs +++ b/.trae-cn/skills/impeccable/scripts/context-signals.mjs @@ -11,7 +11,7 @@ * output is always valid JSON. * * Signals: - * - setup: PRODUCT.md / DESIGN.md presence, register, whether code exists + * - setup: PRODUCT.md / DESIGN.md presence and whether code exists * - critique: the latest cached critique score (.impeccable/critique) * - git: branch + files changed vs the default branch (a scope hint) * - devServer: whether a local dev server answers on a common port (gates live) @@ -21,7 +21,7 @@ import net from 'node:net'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { execFileSync } from 'node:child_process'; -import { loadContext, extractRegister, extractPlatform } from './context.mjs'; +import { loadContext, extractPlatform } from './context.mjs'; import { getCritiqueDir } from './lib/impeccable-paths.mjs'; /** Is there code here at all, or just context files / an empty repo? */ @@ -196,7 +196,6 @@ export async function gatherSignals(cwd = process.cwd()) { hasDesign: ctx.hasDesign, designPath: ctx.designPath, hasCode: hasCode(cwd), - register: extractRegister(ctx.product), platform: extractPlatform(ctx.product), }, critique: { latest: latestCritique(cwd) }, diff --git a/.trae-cn/skills/impeccable/scripts/context.mjs b/.trae-cn/skills/impeccable/scripts/context.mjs index 2ef2d4ce8..080e7ee3a 100644 --- a/.trae-cn/skills/impeccable/scripts/context.mjs +++ b/.trae-cn/skills/impeccable/scripts/context.mjs @@ -1,8 +1,10 @@ /** - * Context loader: prints PRODUCT.md (and DESIGN.md if present) as one - * markdown block on stdout, or prints a `NO_PRODUCT_MD:` message when no + * Context loader: prints PRODUCT.md, DESIGN.md when present, the matching + * persisted surface brief when one can be resolved, and native-platform + * guidance selected from PRODUCT.md. It prints a + * `NO_PRODUCT_MD:` message when no * PRODUCT.md is found anywhere. The skill keys off that message to branch: - * from-scratch build commands (init / teach / craft / shape) and clear + * from-scratch build requests (plus init / teach / shape) and clear * build/shape intent divert into the init flow, while scoped commands proceed * using the existing code as context. * @@ -23,10 +25,13 @@ import os from 'node:os'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { parseTargetOptions } from './lib/target-args.mjs'; -import { IMPECCABLE_COMMAND } from './lib/provider.mjs'; +import { IMPECCABLE_COMMAND, IMPECCABLE_PROVIDER_ID } from './lib/provider.mjs'; +import { renderLlmOnlySlopReview } from './lib/slop-review.mjs'; +import { resolveSurfaceBrief } from './lib/surface-briefs.mjs'; const PRODUCT_NAMES = ['PRODUCT.md', 'Product.md', 'product.md']; const DESIGN_NAMES = ['DESIGN.md', 'Design.md', 'design.md']; +const SKILL_REFERENCE_DIR = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', 'reference'); const FALLBACK_DIRS = ['.agents/context', 'docs']; const MONOREPO_MARKER_FILES = ['pnpm-workspace.yaml', 'turbo.json', 'nx.json', 'lerna.json']; const MONOREPO_FALLBACK_PROJECT_DIRS = ['apps', 'packages']; @@ -41,6 +46,8 @@ const WORKSPACE_DISCOVERY_IGNORED_DIRS = new Set([ '.turbo', '.cache', 'coverage', + 'vendor', + 'vendors', ]); const VISUAL_SOURCE_DIRS = ['src', 'app', 'pages', 'components', 'site', 'public', 'styles']; const STYLE_EXTENSIONS = new Set(['.css', '.scss', '.sass', '.less', '.styl']); @@ -73,6 +80,12 @@ export function loadContext(cwd = process.cwd(), options = {}) { const designPath = resolved.designPath; const product = productPath ? safeRead(productPath) : null; const design = designPath ? safeRead(designPath) : null; + const platform = extractPlatform(product); + const surfaceResolution = resolveSurfaceBrief( + resolved.projectRoot, + hasTargetOption(options) ? options.targetPath : null, + ); + const surfaceBrief = surfaceResolution.brief; return { hasProduct: !!product, product, @@ -83,7 +96,18 @@ export function loadContext(cwd = process.cwd(), options = {}) { contextDir: resolved.contextDir, productContextDir: productPath ? path.dirname(productPath) : null, designContextDir: designPath ? path.dirname(designPath) : null, + hasSurfaceBrief: !!surfaceBrief, + surfaceBrief: surfaceBrief?.text ?? null, + surfaceBriefPath: surfaceBrief?.path ? path.relative(absCwd, surfaceBrief.path) : null, + surfaceBriefReason: surfaceResolution.reason, + surfaceBriefCandidates: surfaceResolution.candidates.map((brief) => ({ + slug: brief.slug, + path: path.relative(absCwd, brief.path), + primaryTarget: brief.primaryTarget, + relatedTargets: brief.relatedTargets, + })), hasVisualImplementation: hasVisualImplementation(resolved.projectRoot), + platform, projectRoot: resolved.projectRoot, repoRoot: resolved.repoRoot, isMonorepo: resolved.isMonorepo, @@ -696,6 +720,19 @@ function safeRead(p) { } } +function loadNativePlatformReferences(platform) { + const names = platform === 'adaptive' + ? ['ios', 'android'] + : platform === 'ios' || platform === 'android' + ? [platform] + : []; + return names.flatMap((name) => { + const filePath = path.join(SKILL_REFERENCE_DIR, `${name}.md`); + const content = safeRead(filePath); + return content ? [{ name, filePath, content }] : []; + }); +} + /** * Best-effort evidence that the project already has an incumbent visual * implementation. DESIGN.md is documentation, not the only source of design @@ -719,9 +756,11 @@ export function hasVisualImplementation(projectRoot) { let styledComponents = 0; const inspectFile = (filePath) => { - if (scannedFiles++ >= VISUAL_SCAN_FILE_LIMIT) return false; const ext = path.extname(filePath).toLowerCase(); if (!STYLE_EXTENSIONS.has(ext) && !UI_EXTENSIONS.has(ext)) return false; + const base = path.basename(filePath).toLowerCase(); + if (/\.min\.[a-z]+$/.test(base)) return false; + if (scannedFiles++ >= VISUAL_SCAN_FILE_LIMIT) return false; let body; try { body = fs.readFileSync(filePath, 'utf-8').slice(0, 64 * 1024); @@ -729,18 +768,28 @@ export function hasVisualImplementation(projectRoot) { return false; } - const base = path.basename(filePath).toLowerCase(); + const evidence = body + .replace(/\/\*[\s\S]*?\*\//g, '') + .replace(//g, '') + .replace(/^\s*\/\/.*$/gm, ''); if (STYLE_EXTENSIONS.has(ext)) { - const customProperties = body.match(/--[a-z0-9_-]+\s*:/gi)?.length ?? 0; - const visualDeclarations = body.match(/\b(?:color|background(?:-color)?|border(?:-color)?|font-family)\s*:/gi)?.length ?? 0; - if (/\b(?:tokens?|theme|design-system)\b/.test(base) && body.trim().length > 80) return true; + const customProperties = evidence.match(/--[a-z0-9_-]+\s*:/gi)?.length ?? 0; + const visualDeclarations = evidence.match(/\b(?:color|background(?:-color)?|border(?:-color)?|font-family)\s*:/gi)?.length ?? 0; + if (/\b(?:tokens?|theme|design-system)\b/.test(base) && evidence.trim().length > 80) return true; if (customProperties >= 3 || visualDeclarations >= 5) return true; } - if ((ext === '.html' || ext === '.htm') && body.length > 600 && /]+stylesheet/i.test(body)) { + if ((ext === '.html' || ext === '.htm') && evidence.length > 600 && /]+stylesheet/i.test(evidence)) { return true; } - if (!['.html', '.htm'].includes(ext) && body.length > 300 && /class(?:Name)?\s*=|style\s*=|styled\(|css`/i.test(body)) { + if (!['.html', '.htm'].includes(ext) && evidence.length > 300) { + const embeddedCustomProperties = evidence.match(/--[a-z0-9_-]+\s*:/gi)?.length ?? 0; + const embeddedVisualDeclarations = evidence.match(/\b(?:color|background(?:-color)?|border(?:-color)?|font-family)\s*:/gi)?.length ?? 0; + const classTokens = [...evidence.matchAll(/class(?:Name)?\s*=\s*["'`]([^"'`]+)["'`]/gi)] + .reduce((count, match) => count + match[1].trim().split(/\s+/).length, 0); + if ((embeddedCustomProperties >= 3 && embeddedVisualDeclarations >= 3) || embeddedVisualDeclarations >= 5 || classTokens >= 12) return true; + } + if (!['.html', '.htm'].includes(ext) && evidence.length > 300 && /class(?:Name)?\s*=|style\s*=|styled\(|css`/i.test(evidence)) { styledComponents += 1; if (styledComponents >= 3) return true; } @@ -781,9 +830,9 @@ function escapeRegExp(value) { /** * Read the first non-empty line under a bare `## ` section of - * PRODUCT.md (e.g. `## Register`, `## Platform`). Returns null when the + * PRODUCT.md (for example `## Platform`). Returns null when the * section is absent. The heading match is exact (`\s*$`) so near-miss - * headings like `## Register guidelines` don't shadow the real field. + * near-miss headings don't shadow the real field. */ export function extractSectionValue(product, heading) { if (!product) return null; @@ -802,16 +851,6 @@ export function extractSectionValue(product, heading) { return null; } -/** - * Pull the register (`brand` or `product`) out of PRODUCT.md by looking - * for a `## Register` section and reading the first non-empty line that - * follows it. Returns null when the file is legacy / register-less. - */ -export function extractRegister(product) { - const word = (extractSectionValue(product, 'Register') || '').toLowerCase(); - return word === 'brand' || word === 'product' ? word : null; -} - /** * Pull the platform (`web`, `ios`, `android`, or `adaptive`) out of PRODUCT.md * by looking for a `## Platform` section and reading the first non-empty line @@ -985,30 +1024,35 @@ async function cli() { const parts = ctx.hasVisualImplementation ? [ 'NO_PRODUCT_MD: This project has no PRODUCT.md yet, but it does have an incumbent visual implementation. ' + - 'For `init` or `teach`, load reference/init.md and create PRODUCT.md. For `craft`, `shape`, or other work ' + - 'against the existing product, read its CSS, tokens, components, and assets first and proceed without blocking; ' + - `offer \`${IMPECCABLE_COMMAND} init\` as a follow-up. Only treat the work as greenfield when the user explicitly ` + - 'asks to discard or replace the current identity.', - 'EXISTING_VISUAL_SYSTEM: Code and assets are the incumbent design authority. Missing DESIGN.md is a documentation ' + - 'gap, not permission to invent a replacement identity. Preserve and extend the implementation unless the user ' + - 'explicitly requests a rebrand, or inspection proves the detected files contain no coherent visual decisions.', + 'For `init`, `teach`, `shape`, or any request to create a new surface or replacement visual world, load reference/init.md and create PRODUCT.md with the user first. ' + + 'After init writes PRODUCT.md, reference/new-work.md preserves and documents the incumbent system for an ' + + 'extension or replaces it with the user for a redesign/rebrand. Other ' + + 'narrow refinement commands may read the CSS, tokens, components, and assets and proceed without blocking, then ' + + `offer \`${IMPECCABLE_COMMAND} init\` as a follow-up.`, + 'BUILD_INIT_REQUIRED: Before shape or any new-surface/redesign flow, init must capture PRODUCT.md with the human or structured ' + + 'simulated user. Init writes product truth only; reference/new-work.md owns every visual decision.', + 'SCOPED_EXISTING_ALLOWED: Narrow refinement commands may use the incumbent implementation as authority without ' + + 'blocking on context setup; they must preserve it and offer init afterward.', + 'EXISTING_VISUAL_SYSTEM: For refinement or extension, code and assets are incumbent design authority and missing ' + + 'DESIGN.md is a documentation gap. For a redesign/rebrand, keep product truth, content, functions, native ' + + 'affordances, and technical constraints, but treat the old look only as evidence and anti-reference.', ] : [ 'NO_PRODUCT_MD: This project has no PRODUCT.md yet. ' + - 'For `init`, `teach`, `craft`, `shape`, ' + + 'For `init`, `teach`, `shape`, ' + 'or wording that clearly maps to a from-scratch build/shape flow, load ' + - 'reference/init.md and write PRODUCT.md first, unless no user can ' + - 'respond (a one-shot or automated run, or the user said not to ask): ' + - 'then write a one-paragraph understanding of the product, audience, ' + - 'and the page\'s job from the brief, and continue. For any other ' + + 'reference/init.md, complete its human or structured simulated-user interview, and write PRODUCT.md before ' + + 'designing. If no answer mechanism truly exists, init may infer only from the explicit brief and must label its ' + + 'assumptions. It never writes DESIGN.md. For any other ' + '(scoped) command against existing code, proceed using the code as ' + `context and offer \`${IMPECCABLE_COMMAND} init\` as a suggestion (do not block).`, - 'NEW_WORK: No committed design context was found. If this task produces ' + - 'new design (a build from scratch, or a redesign that discards the ' + - 'current look), you MUST read reference/new-work.md before making any ' + - 'design decision. Scoped fixes to existing code do not need it.', + 'PRODUCT_INIT_REQUIRED: No product context or visual authority was found. New builds and redesigns ' + + 'must finish reference/init.md for PRODUCT.md, then reference/new-work.md establishes the world and surface. Scoped ' + + 'fixes to existing code do not need the new-surface flow.', ]; + appendSurfaceBriefContext(parts, ctx); parts.push(buildResolvedContextDirective(ctx, cliOptions, { targetExists })); + appendHookFallback(parts, ctx); if (shouldWarnMissingTarget(ctx, targetProvided, targetExists)) { parts.push(buildMissingTargetDirective()); } @@ -1020,37 +1064,29 @@ async function cli() { if (ctx.hasDesign) { parts.push(`# DESIGN.md\n\n${ctx.design.trim()}`); } + appendSurfaceBriefContext(parts, ctx); parts.push(buildResolvedContextDirective(ctx, cliOptions, { targetExists })); + appendHookFallback(parts, ctx); if (shouldWarnMissingTarget(ctx, targetProvided, targetExists)) { parts.push(buildMissingTargetDirective()); } - const register = extractRegister(ctx.product); - // The register field survives as a family hint (brand = Persuade/Experience, - // product = Operate/Read); SKILL.md's mode section carries the essentials - // inline, so no register-file read is mandated here. What IS mandated: - // the new-work playbook when no committed design system exists yet. if (!ctx.hasDesign) { parts.push(ctx.hasVisualImplementation - ? 'EXISTING_VISUAL_SYSTEM: PRODUCT.md exists and DESIGN.md is missing, but code contains incumbent visual decisions. ' + - 'Treat CSS, tokens, components, and assets as design authority. Do not route to new identity work merely because the ' + - 'document is absent; preserve and extend the implementation unless the user explicitly asks to discard it.' - : 'NEW_WORK: PRODUCT.md exists but no DESIGN.md or incumbent visual implementation was found. If this task produces ' + - 'new design or a redesign that discards the current look, you MUST read reference/new-work.md before making any ' + - 'design decision. Scoped fixes to existing code do not need it.'); + ? 'INCUMBENT_WORLD_UNDOCUMENTED: PRODUCT.md exists and DESIGN.md is missing, but code contains incumbent visual decisions. ' + + 'For shape or a new-surface/redesign request, load reference/new-work.md: an extension documents and preserves the code-defined world; ' + + 'a redesign replaces it with the user and uses the old look only as evidence and anti-reference. Narrow refinement ' + + 'commands may proceed using the implementation directly.' + : 'WORLD_DISCOVERY_REQUIRED: PRODUCT.md exists but no DESIGN.md or incumbent visual implementation was found. ' + + 'For a new build or redesign, load reference/new-work.md and establish the visual world with the human or structured ' + + 'simulated user before developing the task concept. Scoped fixes to existing code do not need this flow.'); } - if (register) { - parts.push(`REGISTER: \`${register}\` (family hint: brand = Persuade/Experience surfaces, product = Operate/Read). Derive the visitor's mode per SKILL.md.`); - } - const platform = extractPlatform(ctx.product); - const nativeRefs = - platform === 'adaptive' ? ['ios', 'android'] : platform === 'ios' || platform === 'android' ? [platform] : []; - if (nativeRefs.length) { - const refList = nativeRefs.map(p => `\`reference/${p}.md\``).join(' and '); - const label = platform === 'adaptive' ? '`adaptive` (both iOS and Android)' : `\`${platform}\``; + const platformReferences = loadNativePlatformReferences(ctx.platform); + for (const reference of platformReferences) { parts.push( - `NEXT STEP: This project targets ${label}. Also read ${refList} for native conventions, in addition to SKILL.md's mode guidance.`, + `# NATIVE PLATFORM REFERENCE: ${reference.name.toUpperCase()} (reference/${reference.name}.md)\n\n${reference.content.trim()}`, ); - } else if (!platform) { + } + if (!ctx.platform) { // A `## Platform` section that names something we don't recognize (a // toolchain like `flutter`, a typo) would otherwise silently fall back to // web — the wrong default exactly when the user tried to say "native". @@ -1078,6 +1114,72 @@ function pathExistsForTarget(cwd, targetPath) { return fs.existsSync(abs); } +const HOOK_MANIFESTS_BY_PROVIDER = Object.freeze({ + 'claude-code': ['.claude/settings.local.json', '.claude/settings.json'], + codex: ['.codex/hooks.json'], + agents: ['.codex/hooks.json'], + cursor: ['.cursor/hooks.json'], + github: ['.github/hooks/impeccable.json'], +}); + +function truthyEnv(value) { + return typeof value === 'string' && /^(1|true|yes|on)$/i.test(value.trim()); +} + +function valueHasHookMarker(value) { + if (typeof value === 'string') { + return value.includes('skills/impeccable/scripts/hook.mjs') + || value.includes('skills/impeccable/scripts/hook-before-edit.mjs'); + } + if (Array.isArray(value)) return value.some(valueHasHookMarker); + if (value && typeof value === 'object') return Object.values(value).some(valueHasHookMarker); + return false; +} + +function hookEnabledAt(root) { + if (truthyEnv(process.env.IMPECCABLE_HOOK_DISABLED)) return false; + let enabled = true; + for (const name of ['.impeccable/config.json', '.impeccable/config.local.json']) { + const raw = readJson(path.join(root, name)); + if (raw?.hook && Object.prototype.hasOwnProperty.call(raw.hook, 'enabled')) { + enabled = raw.hook.enabled !== false; + } + } + return enabled; +} + +const STOP_REVIEW_PROVIDERS = new Set(['claude-code', 'codex', 'agents']); + +function automaticHookMode(ctx) { + if (ctx.platform === 'ios' || ctx.platform === 'android' || ctx.platform === 'adaptive') { + return 'none'; + } + const activeRoot = path.resolve(ctx.projectRoot || process.cwd()); + if (!hookEnabledAt(activeRoot)) return 'none'; + const manifests = HOOK_MANIFESTS_BY_PROVIDER[IMPECCABLE_PROVIDER_ID] || []; + const roots = [...new Set([process.cwd(), ctx.projectRoot, ctx.repoRoot].filter(Boolean).map((root) => path.resolve(root)))]; + for (const root of roots) { + for (const rel of manifests) { + const raw = readJson(path.join(root, rel)); + if (raw?.hooks && valueHasHookMarker(raw.hooks)) { + return STOP_REVIEW_PROVIDERS.has(IMPECCABLE_PROVIDER_ID) ? 'stop' : 'per-edit'; + } + } + } + return 'none'; +} + +function appendHookFallback(parts, ctx) { + const hookMode = automaticHookMode(ctx); + if (hookMode === 'stop') return; + const native = ctx.platform === 'ios' || ctx.platform === 'android' || ctx.platform === 'adaptive'; + parts.push(renderLlmOnlySlopReview({ + automaticDetector: hookMode === 'per-edit', + manualDetector: hookMode === 'none' && !native, + scriptsPath: path.dirname(fileURLToPath(import.meta.url)), + })); +} + function buildResolvedContextDirective(ctx, options, { targetExists = null } = {}) { const targetPath = hasTargetOption(options) ? options.targetPath : null; return `RESOLVED_CONTEXT:\n${JSON.stringify({ @@ -1087,10 +1189,29 @@ function buildResolvedContextDirective(ctx, options, { targetExists = null } = { repoRoot: ctx.repoRoot, productPath: ctx.productPath, designPath: ctx.designPath, + surfaceBriefPath: ctx.surfaceBriefPath, + surfaceBriefReason: ctx.surfaceBriefReason, + surfaceBriefCandidates: ctx.surfaceBriefCandidates, hasVisualImplementation: ctx.hasVisualImplementation, + platform: ctx.platform, }, null, 2)}`; } +function appendSurfaceBriefContext(parts, ctx) { + if (ctx.hasSurfaceBrief && ctx.surfaceBrief) { + parts.push(`# SURFACE BRIEF (${ctx.surfaceBriefPath})\n\n${ctx.surfaceBrief.trim()}`); + return; + } + if (!ctx.surfaceBriefCandidates?.length) return; + const helper = path.join(path.dirname(fileURLToPath(import.meta.url)), 'surface-brief.mjs'); + parts.push( + 'SURFACE_CONTEXT_AVAILABLE: Persisted surface briefs exist, but none was selected unambiguously for this invocation. ' + + 'Resolve the requested surface to its concrete primary or related source path, then run ' + + `\`node ${helper} read \` once before changing that surface. Candidates:\n` + + JSON.stringify(ctx.surfaceBriefCandidates, null, 2), + ); +} + function shouldWarnMissingTarget(ctx, targetProvided, targetExists = null) { if (ctx.isMonorepo && targetProvided && targetExists === false) return true; return !!( diff --git a/.trae-cn/skills/impeccable/scripts/critique-storage.mjs b/.trae-cn/skills/impeccable/scripts/critique-storage.mjs index 6b4d225cf..38674285a 100644 --- a/.trae-cn/skills/impeccable/scripts/critique-storage.mjs +++ b/.trae-cn/skills/impeccable/scripts/critique-storage.mjs @@ -29,8 +29,9 @@ import fs from 'node:fs'; import path from 'node:path'; import { fileURLToPath, pathToFileURL } from 'node:url'; import { getCritiqueDir } from './lib/impeccable-paths.mjs'; +import { slugFromTarget } from './lib/target-slug.mjs'; -const SLUG_MAX = 50; +export { slugFromTarget } from './lib/target-slug.mjs'; /** * Mechanically derive a slug from a resolved target. Returns null if the @@ -40,46 +41,6 @@ const SLUG_MAX = 50; * concrete artifact before calling this — we never slug a natural-language * phrase. */ -export function slugFromTarget(resolved, { cwd = process.cwd() } = {}) { - if (!resolved || typeof resolved !== 'string') return null; - const trimmed = resolved.trim(); - if (!trimmed) return null; - - // URL - if (/^https?:\/\//i.test(trimmed)) { - let url; - try { url = new URL(trimmed); } catch { return null; } - const hostPath = `${url.hostname}${url.pathname}`; - return kebab(hostPath); - } - - // File path. Make it project-relative so two devs critiquing the same - // checkout get the same slug regardless of where their repo is cloned. - const abs = path.isAbsolute(trimmed) ? trimmed : path.resolve(cwd, trimmed); - let rel = path.relative(cwd, abs); - // If the target is outside cwd, fall back to the basename so we still - // produce a stable slug (vs the absolute path, which would include - // home dirs / usernames). - if (rel.startsWith('..') || path.isAbsolute(rel)) { - rel = path.basename(abs); - } - if (!rel || rel === '.' || rel === '') return null; - return kebab(rel); -} - -function kebab(s) { - const slug = s - .toLowerCase() - .replace(/[/\\.]+/g, '-') - .replace(/[^a-z0-9-]+/g, '-') - .replace(/-+/g, '-') - .replace(/^-|-$/g, ''); - if (!slug) return null; - // Cap from the tail — the tail (filename) is more identifying than the - // top-level directory. - return slug.length <= SLUG_MAX ? slug : slug.slice(slug.length - SLUG_MAX).replace(/^-/, ''); -} - /** * Filename-safe UTC ISO timestamp: hyphens for separators, trailing Z. * Plain colons aren't allowed on Windows filesystems. diff --git a/.trae-cn/skills/impeccable/scripts/detector/detect-antipatterns-browser.js b/.trae-cn/skills/impeccable/scripts/detector/detect-antipatterns-browser.js index 033573e36..76bf4ddeb 100644 --- a/.trae-cn/skills/impeccable/scripts/detector/detect-antipatterns-browser.js +++ b/.trae-cn/skills/impeccable/scripts/detector/detect-antipatterns-browser.js @@ -312,17 +312,6 @@ const ANTIPATTERNS = [ skillSection: 'Layout & Space', skillGuideline: 'numbered section markers', }, - { - id: 'numbered-section-markers', - category: 'slop', - scopes: ['layout'], - severity: 'advisory', - name: 'Numbered section markers (01 / 02 / 03)', - description: - 'Numbered display markers as section labels (01, 02, 03) are the AI editorial scaffold one tier deeper than tracked eyebrow chips. If you find yourself reaching for them, choose a different section cadence.', - skillSection: 'Layout & Space', - skillGuideline: 'numbered section markers', - }, { id: 'em-dash-overuse', category: 'slop', diff --git a/.trae-cn/skills/impeccable/scripts/detector/engines/regex/detect-text.mjs b/.trae-cn/skills/impeccable/scripts/detector/engines/regex/detect-text.mjs index 81c30ab99..f641d5917 100644 --- a/.trae-cn/skills/impeccable/scripts/detector/engines/regex/detect-text.mjs +++ b/.trae-cn/skills/impeccable/scripts/detector/engines/regex/detect-text.mjs @@ -280,22 +280,6 @@ const REGEX_ANALYZERS = [ if (count === 0) return []; return [finding('marketing-buzzword', filePath, `${count} buzzword phrase${count === 1 ? '' : 's'}: "${firstSample}"`)]; }, - // Numbered section markers (01 / 02 / 03 ...) - (content, filePath) => { - const text = stripHtmlToText(content); - const re = /\b(0[1-9]|1[0-2])\b/g; - const seen = new Set(); - let m; - while ((m = re.exec(text)) !== null) seen.add(m[1]); - if (seen.size < 3) return []; - const sorted = [...seen].sort(); - let sequential = 0; - for (let i = 1; i < sorted.length; i++) { - if (parseInt(sorted[i], 10) === parseInt(sorted[i - 1], 10) + 1) sequential++; - } - if (sequential < 2) return []; - return [finding('numbered-section-markers', filePath, `Sequence: ${sorted.slice(0, 6).join(', ')}`)]; - }, // Aphoristic cadence: manufactured-contrast + short-rebuttal (content, filePath) => { const text = stripHtmlToText(content); @@ -430,21 +414,20 @@ function runRegexMatchers(lines, filePath, lineOffset = 0, blockContext = null, } /** Page-level analyzers that scan rendered text content (em-dash use, - * buzzword phrases, numbered section markers, aphoristic cadence). + * buzzword phrases, aphoristic cadence). * These are detector-agnostic — they work on any HTML/text source * and don't need a parsed DOM. Exported so detectHtml can call them * for `.html` files (which otherwise skip the regex engine). */ const TEXT_CONTENT_ANALYZER_IDS = [ 'em-dash-overuse', 'marketing-buzzword', - 'numbered-section-markers', 'aphoristic-cadence', ]; function runTextContentAnalyzers(content, filePath, options = {}) { const profile = options?.profile; if (!shouldRunPageAnalyzers(content, filePath)) return []; - // The 4 text-content analyzers are at indices 3-6 in REGEX_ANALYZERS. + // The 3 text-content analyzers are at indices 3-5 in REGEX_ANALYZERS. const findings = []; for (let i = 0; i < TEXT_CONTENT_ANALYZER_IDS.length; i++) { const analyzer = REGEX_ANALYZERS[3 + i]; @@ -535,7 +518,6 @@ function detectText(content, filePath, options = {}) { 'monotonous-spacing', 'em-dash-overuse', 'marketing-buzzword', - 'numbered-section-markers', 'aphoristic-cadence', 'dark-glow', ]; diff --git a/.trae-cn/skills/impeccable/scripts/detector/registry/antipatterns.mjs b/.trae-cn/skills/impeccable/scripts/detector/registry/antipatterns.mjs index b85f7e62f..84c69b0b8 100644 --- a/.trae-cn/skills/impeccable/scripts/detector/registry/antipatterns.mjs +++ b/.trae-cn/skills/impeccable/scripts/detector/registry/antipatterns.mjs @@ -210,17 +210,6 @@ const ANTIPATTERNS = [ skillSection: 'Layout & Space', skillGuideline: 'numbered section markers', }, - { - id: 'numbered-section-markers', - category: 'slop', - scopes: ['layout'], - severity: 'advisory', - name: 'Numbered section markers (01 / 02 / 03)', - description: - 'Numbered display markers as section labels (01, 02, 03) are the AI editorial scaffold one tier deeper than tracked eyebrow chips. If you find yourself reaching for them, choose a different section cadence.', - skillSection: 'Layout & Space', - skillGuideline: 'numbered section markers', - }, { id: 'em-dash-overuse', category: 'slop', diff --git a/.trae-cn/skills/impeccable/scripts/detector/rules/checks.mjs b/.trae-cn/skills/impeccable/scripts/detector/rules/checks.mjs index 3fdd6b2dd..9bace11b3 100644 --- a/.trae-cn/skills/impeccable/scripts/detector/rules/checks.mjs +++ b/.trae-cn/skills/impeccable/scripts/detector/rules/checks.mjs @@ -352,27 +352,57 @@ function isAccentColor(cssColor) { return false; } +function resolveHeroHeadingSizePx(value) { + const input = String(value || '').trim().toLowerCase(); + if (!input) return 0; + + const simpleLengthPx = (token) => { + const match = /^(-?\d*\.?\d+)\s*(px|rem|em|%)?$/.exec(String(token || '').trim()); + if (!match) return null; + const amount = Number(match[1]); + if (!Number.isFinite(amount)) return null; + if (match[2] === 'rem' || match[2] === 'em') return amount * 16; + if (match[2] === '%') return amount * 0.16; + return amount; + }; + + const direct = simpleLengthPx(input); + if (direct !== null) return direct; + + // Static CSS engines cannot resolve viewport units, but clamp's min/max + // bounds still tell us whether the heading can ever reach hero scale. + const clamp = /^clamp\((.*)\)$/.exec(input); + if (clamp) { + const parts = clamp[1].split(','); + if (parts.length === 3) { + const bounds = [simpleLengthPx(parts[0]), simpleLengthPx(parts[2])] + .filter((candidate) => candidate !== null); + if (bounds.length > 0) return Math.max(...bounds); + } + } + + return 0; +} + // Sibling-relationship rule. Anchor on a hero-scale h1, look at the // previousElementSibling, and gate on EITHER the classic tracked- // uppercase eyebrow OR the modern accent-colored bold eyebrow. function checkHeroEyebrow(opts) { const { headingTag, headingText, headingFontSize, + headingInApplicationContext, siblingTag, siblingText, siblingTextTransform, siblingFontSize, siblingLetterSpacing, siblingFontWeight, siblingColor, siblingHasAccentDashPseudo, } = opts; if (headingTag !== 'h1') return []; - // We previously gated on headingFontSize >= 48 to anchor "hero scale". - // But modern hero h1s use clamp() / vw / var(--text-*), none of which - // jsdom can resolve — the computed value comes back as "2em" or - // "var(--text-9xl)" and parseFloat returns 2 or NaN. The gate fails - // on virtually every Tailwind v4 / framework build. The other gates - // (sibling text 2-60 chars, font-size ≤ 14px, accent-bold OR - // tracked-caps) are tight enough to avoid false positives on non- - // hero h1s — a tiny tan label directly above any h1 is the - // antipattern regardless of how big the h1 ends up. + // This is specifically a marketing-hero cliché, not a ban on compact + // context labels in product UI (for example, a station name inside a tab + // panel). Browser-computed sizes are reliable; the static adapter also + // resolves ordinary px/rem/em and clamp() bounds before reaching here. + if (headingInApplicationContext) return []; + if (!(headingFontSize >= 48)) return []; if (!siblingTag) return []; // An h2 above an h1 is a different anti-pattern (heading hierarchy / dual // headings) — never an eyebrow. @@ -1938,6 +1968,7 @@ function checkElementHeroEyebrowDOM(el) { headingTag: tag, headingText: el.textContent || '', headingFontSize: parseFloat(headStyle.fontSize) || 0, + headingInApplicationContext: !!el.closest('[role="tabpanel"], [role="dialog"], [role="application"], dialog'), siblingTag: sibling.tagName.toLowerCase(), siblingText: sibling.textContent || '', siblingTextTransform: sibStyle.textTransform || '', @@ -3367,7 +3398,8 @@ function checkElementHeroEyebrow(el, style, tag, window, customPropMap) { return checkHeroEyebrow({ headingTag: tag, headingText: el.textContent || '', - headingFontSize: parseFloat(headingFontSizeRaw) || 0, + headingFontSize: resolveHeroHeadingSizePx(headingFontSizeRaw), + headingInApplicationContext: !!el.closest?.('[role="tabpanel"], [role="dialog"], [role="application"], dialog'), siblingTag: sibling.tagName.toLowerCase(), siblingText: sibling.textContent || '', siblingTextTransform: sibStyle.textTransform || '', diff --git a/.trae-cn/skills/impeccable/scripts/hook-lib.mjs b/.trae-cn/skills/impeccable/scripts/hook-lib.mjs index e61ea692b..50aa2f8fb 100644 --- a/.trae-cn/skills/impeccable/scripts/hook-lib.mjs +++ b/.trae-cn/skills/impeccable/scripts/hook-lib.mjs @@ -46,6 +46,7 @@ import path from 'node:path'; import { pathToFileURL, fileURLToPath } from 'node:url'; import { extractPlatform, loadContext } from './context.mjs'; import { IMPECCABLE_COMMAND } from './lib/provider.mjs'; +import { renderStopSlopReview } from './lib/slop-review.mjs'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); @@ -1868,22 +1869,42 @@ export const CONTRACT_MAX_CHARS = 1800; // Cap contract sections per Stop emission so many touched artifacts cannot // stack an unbounded message. export const CONTRACT_AUDIT_MAX_FILES = 3; +export const CONTRACT_EXTS = new Set(['.html', '.htm', '.astro', '.svelte', '.vue', '.jsx', '.tsx']); +export const CONTRACT_REQUIRED_FIELDS = ['UNIQUE', 'NOT-TEMPLATE', 'OWN-WORLD', 'STORY', 'FIRST VIEWPORT', 'FORM']; /** - * Extract the artifact's own direction-contract comment: the first HTML - * comment in the head of the file, when its opening chars identify it as a - * contract/concept block. Returns the trimmed, length-capped body, or null - * when the file carries none (no comment, unclosed comment, marker missing, - * or the comment starts past the head window). + * Extract the artifact's own direction-contract comment from the head of an + * HTML or component file. Supports HTML-family comments and JSX block + * comments so the contract works in the Astro/Svelte/Vue/React scaffolds the + * skill actually builds. Returns the trimmed, length-capped body, or null + * when the file carries no valid contract block. */ export function extractDirectionContract(content) { if (typeof content !== 'string' || !content) return null; const head = content.slice(0, CONTRACT_HEAD_CHARS); - const m = //.exec(head); - if (!m) return null; - const body = m[1].trim(); - if (!body || !/contract|concept/i.test(body.slice(0, CONTRACT_MARKER_CHARS))) return null; - return body.slice(0, CONTRACT_MAX_CHARS); + const candidates = []; + for (const pattern of [//g, /\{\/\*([\s\S]*?)\*\/\}/g]) { + for (const match of head.matchAll(pattern)) { + const index = match.index ?? 0; + const linePrefix = head.slice(head.lastIndexOf('\n', index - 1) + 1, index).trim(); + if (linePrefix.startsWith('//')) continue; + candidates.push({ index, body: match[1].trim() }); + } + } + candidates.sort((a, b) => a.index - b.index); + for (const candidate of candidates) { + if (!candidate.body || !/direction\s+contract|concept\s+contract/i.test(candidate.body.slice(0, CONTRACT_MARKER_CHARS))) continue; + return candidate.body.slice(0, CONTRACT_MAX_CHARS); + } + return null; +} + +export function missingDirectionContractFields(contract) { + const body = typeof contract === 'string' ? contract : ''; + return CONTRACT_REQUIRED_FIELDS.filter((field) => { + const label = field.replace(/[.*+?^${}()|[\]\\]/g, '\\$&').replace(/\s+/g, '\\s+'); + return !new RegExp(`\\b${label}\\s*:`, 'i').test(body); + }); } /** @@ -1899,7 +1920,11 @@ export function renderContractAudit(entries, opts = {}) { const shown = entries.slice(0, CONTRACT_AUDIT_MAX_FILES); const blocks = shown.map(({ filePath, contract }) => { const display = relativize(filePath, cwd); - return `${display} opens with this direction contract, written when the direction was decided:\n\n${contract}`; + const missing = missingDirectionContractFields(contract); + const integrity = missing.length > 0 + ? `\n\nContract integrity defect: missing ${missing.join(', ')}. Repair the contract and the implementation together.` + : ''; + return `${display} opens with this direction contract, written when the direction was decided:\n\n${contract}${integrity}`; }); return [ `${ENVELOPE_PREFIX} Direction-contract audit. Before finishing, audit the rendered page against the contract it opens with, promise by promise.`, @@ -1984,9 +2009,22 @@ export async function runStopHook({ stdinJson, env = {}, cwd = process.cwd(), no return result({ skipped: 'native-platform', platform, durationMs: Date.now() - started }); } + const session = ensureSession(cache, sessionId); + const needsSlopReview = session.llmSlopReviewed !== true; + const det = detector || await loadDetector(); if (!det || typeof det.detectText !== 'function') { - return result({ skipped: 'detector-missing', durationMs: Date.now() - started }); + if (!needsSlopReview) return result({ skipped: 'detector-missing', durationMs: Date.now() - started }); + session.llmSlopReviewed = true; + session.updatedAt = Date.now(); + persistCache(projectCwd, cache); + const text = renderStopSlopReview(); + return { + exitCode: 0, + stdout: payload(text, 'Stop', harness), + emission: { kind: 'stop-llm-slop-review', llmSlopReview: true }, + audit: { ...audit, emitted: true, detectorMissing: true, llmSlopReview: true, chars: text.length, durationMs: Date.now() - started }, + }; } const scanOptions = designSystemOptions(config, det, projectCwd); @@ -2013,10 +2051,11 @@ export async function runStopHook({ stdinJson, env = {}, cwd = process.cwd(), no ? configuredExt.engine === 'html' : (ext === '.html' || ext === '.htm'); - // Direction-contract audit: HTML artifacts only, at most once per file + // Direction-contract audit: HTML and component artifacts, at most once per file // per session. The flag lives on the same session cache entry the // finding dedupe uses, so a second Stop fire stays quiet about it. - if (useHtmlEngine) { + const contractCapable = useHtmlEngine || CONTRACT_EXTS.has(ext); + if (contractCapable) { const fileEntry = ensureFile(cache, sessionId, filePath); if (!fileEntry.contractAudited) { const contract = extractDirectionContract(content); @@ -2046,10 +2085,15 @@ export async function runStopHook({ stdinJson, env = {}, cwd = process.cwd(), no } audit.scannedFiles = scanned; - if (freshGroups.length === 0 && contractEntries.length === 0) { + if (freshGroups.length === 0 && contractEntries.length === 0 && !needsSlopReview) { return result({ emitted: false, skipped: 'stop-clean', durationMs: Date.now() - started }); } + if (needsSlopReview) { + session.llmSlopReviewed = true; + session.updatedAt = Date.now(); + } + // Fresh findings and first-time contract audits earn the cache write; // both mark this batch as surfaced so the next Stop fire is silent // unless new issues appear. @@ -2064,6 +2108,9 @@ export async function runStopHook({ stdinJson, env = {}, cwd = process.cwd(), no if (contractEntries.length > 0) { parts.push(renderContractAudit(contractEntries, { cwd: projectCwd })); } + if (needsSlopReview) { + parts.push(renderStopSlopReview()); + } const text = appendDesignSystemNote(parts.join('\n\n'), scanOptions); return { exitCode: 0, @@ -2074,6 +2121,7 @@ export async function runStopHook({ stdinJson, env = {}, cwd = process.cwd(), no ...(contractEntries.length > 0 ? { contractFiles: contractEntries.map((entry) => entry.filePath) } : {}), + ...(needsSlopReview ? { llmSlopReview: true } : {}), }, audit: { ...audit, @@ -2081,6 +2129,7 @@ export async function runStopHook({ stdinJson, env = {}, cwd = process.cwd(), no freshFiles: freshGroups.length, freshFindings: freshGroups.reduce((sum, group) => sum + group.findings.length, 0), ...(contractEntries.length > 0 ? { contractAudits: contractEntries.length } : {}), + ...(needsSlopReview ? { llmSlopReview: true } : {}), chars: text.length, durationMs: Date.now() - started, }, diff --git a/.trae-cn/skills/impeccable/scripts/lib/provider.mjs b/.trae-cn/skills/impeccable/scripts/lib/provider.mjs index 7fa928a9c..fa593733a 100644 --- a/.trae-cn/skills/impeccable/scripts/lib/provider.mjs +++ b/.trae-cn/skills/impeccable/scripts/lib/provider.mjs @@ -1,4 +1,5 @@ // Source scripts default to slash commands. The provider build replaces only // this exact declaration, avoiding heuristic rewrites across executable code. export const IMPECCABLE_COMMAND_PREFIX = "/"; +export const IMPECCABLE_PROVIDER_ID = "trae-cn"; export const IMPECCABLE_COMMAND = `${IMPECCABLE_COMMAND_PREFIX}impeccable`; diff --git a/.trae-cn/skills/impeccable/scripts/lib/slop-review.mjs b/.trae-cn/skills/impeccable/scripts/lib/slop-review.mjs new file mode 100644 index 000000000..30f03e34d --- /dev/null +++ b/.trae-cn/skills/impeccable/scripts/lib/slop-review.mjs @@ -0,0 +1,30 @@ +export const LLM_ONLY_SLOP_ITEMS = Object.freeze([ + 'Monospace used merely to signal “technical” or “developer.”', + 'Light or dark mode chosen by category habit rather than the actual use scene.', + 'Everything wrapped in cards, or identical icon-heading-text cards repeated as the page structure.', + 'Hero-metric scaffolds: one oversized number, a small label, supporting stats, and an accent treatment.', + 'Decorative glassmorphism, meaningless sparklines, or generic rounded rectangles with drop shadows.', + 'A modal chosen by reflex when the task does not require interruption or protected focus.', +]); + +export function renderLlmOnlySlopReview({ manualDetector = false, automaticDetector = false, scriptsPath = null } = {}) { + const lines = [ + automaticDetector + ? 'AI_SLOP_REVIEW_REQUIRED: The automatic detector covers mechanical rules, but this harness has no reliable late review. Before finishing changed UI, inspect the authored result for detector-blind model reflexes:' + : 'AI_SLOP_REVIEW_REQUIRED: The automatic Impeccable design hook is not available for this session. Before finishing changed UI, inspect the authored result for detector-blind model reflexes:', + ...LLM_ONLY_SLOP_ITEMS.map((item) => `- ${item}`), + 'Fix reflexes, not intentional choices required by the brief or established visual authority.', + ]; + if (manualDetector && scriptsPath) { + lines.push(`Then run the mechanical detector once over the changed web UI: \`node ${scriptsPath}/detect.mjs --json \`. Do not run it earlier during concept selection.`); + } + return lines.join('\n'); +} + +export function renderStopSlopReview() { + return [ + '[impeccable@1] Detector-blind AI-slop review. The mechanical pass is complete; now inspect the rendered result for model reflexes it cannot reliably detect:', + ...LLM_ONLY_SLOP_ITEMS.map((item) => `- ${item}`), + 'Fix reflexes, not intentional choices required by the brief or established visual authority. Do not rerun the detector; this is the authored judgment layer.', + ].join('\n'); +} diff --git a/.trae-cn/skills/impeccable/scripts/lib/surface-briefs.mjs b/.trae-cn/skills/impeccable/scripts/lib/surface-briefs.mjs new file mode 100644 index 000000000..f83416f69 --- /dev/null +++ b/.trae-cn/skills/impeccable/scripts/lib/surface-briefs.mjs @@ -0,0 +1,151 @@ +import fs from 'node:fs'; +import path from 'node:path'; +import { slugFromTarget } from './target-slug.mjs'; + +export const SURFACE_BRIEF_VERSION = 1; + +export function getSurfaceBriefDir(projectRoot) { + return path.join(projectRoot, '.impeccable', 'surfaces'); +} + +export function normalizeSurfaceTarget(target, { projectRoot = process.cwd() } = {}) { + if (!target || typeof target !== 'string' || !target.trim()) return null; + const trimmed = target.trim(); + if (/^https?:\/\//i.test(trimmed)) { + try { + const url = new URL(trimmed); + url.hash = ''; + url.search = ''; + return url.toString().replace(/\/$/, '') || url.origin; + } catch { + return null; + } + } + if (/^route:/i.test(trimmed)) { + const route = trimmed.slice(trimmed.indexOf(':') + 1).trim(); + if (!route.startsWith('/') || route.includes('..')) return null; + const normalizedRoute = route.split(/[?#]/, 1)[0].replace(/\/{2,}/g, '/').replace(/\/$/, '') || '/'; + return `route:${normalizedRoute}`; + } + if (trimmed === '/') return 'route:/'; + if (trimmed.startsWith('/')) { + const absolute = path.resolve(trimmed); + const relativeToProject = path.relative(projectRoot, absolute); + const isProjectFile = relativeToProject && !relativeToProject.startsWith('..') && !path.isAbsolute(relativeToProject); + if (!isProjectFile && !fs.existsSync(absolute) && !trimmed.includes('..')) { + const normalizedRoute = trimmed.split(/[?#]/, 1)[0].replace(/\/{2,}/g, '/').replace(/\/$/, '') || '/'; + return `route:${normalizedRoute}`; + } + } + const abs = path.isAbsolute(trimmed) ? trimmed : path.resolve(projectRoot, trimmed); + const rel = path.relative(projectRoot, abs); + if (!rel || rel === '.' || rel.startsWith('..') || path.isAbsolute(rel)) return null; + return rel.split(path.sep).join('/'); +} + +export function surfaceBriefPathForTarget(target, { projectRoot = process.cwd() } = {}) { + const normalized = normalizeSurfaceTarget(target, { projectRoot }); + if (!normalized) return null; + const slugInput = normalized.startsWith('route:') ? `route${normalized.slice('route:'.length)}` : normalized; + const slug = slugFromTarget(slugInput, { cwd: projectRoot }); + return slug ? path.join(getSurfaceBriefDir(projectRoot), `${slug}.md`) : null; +} + +export function parseSurfaceBrief(text, filePath = null) { + const match = String(text || '').match(/^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/); + const meta = {}; + if (match) { + for (const line of match[1].split(/\r?\n/)) { + const colon = line.indexOf(':'); + if (colon < 0) continue; + const key = line.slice(0, colon).trim(); + const raw = line.slice(colon + 1).trim(); + if (!key) continue; + if (/^(?:\[|\{|\")/.test(raw) || /^(?:true|false|null|-?\d+(?:\.\d+)?)$/.test(raw)) { + try { meta[key] = JSON.parse(raw); continue; } catch { /* keep string */ } + } + meta[key] = raw.replace(/^['"]|['"]$/g, ''); + } + } + const primaryTarget = typeof meta.primary_target === 'string' ? meta.primary_target : null; + const relatedTargets = Array.isArray(meta.related_targets) + ? meta.related_targets.filter((value) => typeof value === 'string') + : []; + return { + path: filePath, + text: String(text || ''), + body: match ? String(text || '').slice(match[0].length).trim() : String(text || '').trim(), + meta, + slug: typeof meta.slug === 'string' ? meta.slug : filePath ? path.basename(filePath, '.md') : null, + primaryTarget, + relatedTargets, + targets: [primaryTarget, ...relatedTargets].filter(Boolean), + }; +} + +export function listSurfaceBriefs(projectRoot = process.cwd()) { + const dir = getSurfaceBriefDir(projectRoot); + let names; + try { + names = fs.readdirSync(dir).filter((name) => name.endsWith('.md')).sort(); + } catch { + return []; + } + return names.flatMap((name) => { + const filePath = path.join(dir, name); + try { + return [parseSurfaceBrief(fs.readFileSync(filePath, 'utf-8'), filePath)]; + } catch { + return []; + } + }); +} + +export function resolveSurfaceBrief(projectRoot = process.cwd(), target = null) { + const briefs = listSurfaceBriefs(projectRoot); + if (!target) { + return { + brief: briefs.length === 1 ? briefs[0] : null, + candidates: briefs, + reason: briefs.length === 1 ? 'only-brief' : briefs.length > 1 ? 'ambiguous' : 'none', + }; + } + + const normalized = normalizeSurfaceTarget(target, { projectRoot }); + if (!normalized) return { brief: null, candidates: briefs, reason: 'invalid-target' }; + const exactPath = surfaceBriefPathForTarget(normalized, { projectRoot }); + const exact = briefs.find((brief) => brief.path === exactPath && (!brief.targets.length || brief.targets.includes(normalized))); + if (exact) return { brief: exact, candidates: briefs, reason: 'slug' }; + const mapped = briefs.filter((brief) => brief.targets.includes(normalized)); + return { + brief: mapped.length === 1 ? mapped[0] : null, + candidates: mapped.length > 1 ? mapped : briefs, + reason: mapped.length === 1 ? 'mapping' : mapped.length > 1 ? 'ambiguous-target' : 'not-found', + }; +} + +export function writeSurfaceBrief({ + projectRoot = process.cwd(), + primaryTarget, + relatedTargets = [], + body, +}) { + const normalizedPrimary = normalizeSurfaceTarget(primaryTarget, { projectRoot }); + if (!normalizedPrimary) throw new Error('surface brief requires a concrete project-relative primary target or URL'); + const normalizedRelated = [...new Set(relatedTargets + .map((target) => normalizeSurfaceTarget(target, { projectRoot })) + .filter((target) => target && target !== normalizedPrimary))]; + const slug = slugFromTarget(normalizedPrimary, { cwd: projectRoot }); + const filePath = surfaceBriefPathForTarget(normalizedPrimary, { projectRoot }); + fs.mkdirSync(path.dirname(filePath), { recursive: true }); + const frontmatter = [ + '---', + `version: ${SURFACE_BRIEF_VERSION}`, + `slug: ${JSON.stringify(slug)}`, + `primary_target: ${JSON.stringify(normalizedPrimary)}`, + `related_targets: ${JSON.stringify(normalizedRelated)}`, + '---', + ].join('\n'); + fs.writeFileSync(filePath, `${frontmatter}\n\n${String(body || '').trim()}\n`, 'utf-8'); + return filePath; +} diff --git a/.trae-cn/skills/impeccable/scripts/lib/target-slug.mjs b/.trae-cn/skills/impeccable/scripts/lib/target-slug.mjs new file mode 100644 index 000000000..025915ad5 --- /dev/null +++ b/.trae-cn/skills/impeccable/scripts/lib/target-slug.mjs @@ -0,0 +1,33 @@ +import path from 'node:path'; + +const SLUG_MAX = 50; + +/** Derive one clone-stable slug from a concrete file path or URL. */ +export function slugFromTarget(resolved, { cwd = process.cwd() } = {}) { + if (!resolved || typeof resolved !== 'string') return null; + const trimmed = resolved.trim(); + if (!trimmed) return null; + + if (/^https?:\/\//i.test(trimmed)) { + let url; + try { url = new URL(trimmed); } catch { return null; } + return kebab(`${url.hostname}${url.pathname}`); + } + + const abs = path.isAbsolute(trimmed) ? trimmed : path.resolve(cwd, trimmed); + let rel = path.relative(cwd, abs); + if (rel.startsWith('..') || path.isAbsolute(rel)) rel = path.basename(abs); + if (!rel || rel === '.') return null; + return kebab(rel); +} + +export function kebab(value) { + const slug = String(value || '') + .toLowerCase() + .replace(/[/\\.]+/g, '-') + .replace(/[^a-z0-9-]+/g, '-') + .replace(/-+/g, '-') + .replace(/^-|-$/g, ''); + if (!slug) return null; + return slug.length <= SLUG_MAX ? slug : slug.slice(slug.length - SLUG_MAX).replace(/^-/, ''); +} diff --git a/.trae-cn/skills/impeccable/scripts/live-browser.js b/.trae-cn/skills/impeccable/scripts/live-browser.js index 616f1290a..47bbfefbf 100644 --- a/.trae-cn/skills/impeccable/scripts/live-browser.js +++ b/.trae-cn/skills/impeccable/scripts/live-browser.js @@ -2592,7 +2592,7 @@ minWidth: '16px', height: '16px', padding: '0 4px', borderRadius: '999px', background: tuneOpen ? C.brand : BP.hairline, - color: tuneOpen ? 'oklch(98% 0 0)' : 'inherit', + color: tuneOpen ? C.ink : 'inherit', fontFamily: MONO, fontSize: '9.5px', fontWeight: '600', lineHeight: '1', boxSizing: 'border-box', @@ -3131,7 +3131,7 @@ position: 'absolute', top: '2px', left: initial ? '18px' : '2px', width: '16px', height: '16px', borderRadius: '50%', - background: 'oklch(98% 0 0)', + background: C.ink, transition: 'left 0.18s ' + EASE, boxShadow: '0 1px 2px oklch(0% 0 0 / 0.2)', }); @@ -3165,7 +3165,7 @@ const b = el('button', { padding: '5px 4px', border: 'none', borderRadius: '3px', background: active ? C.brand : 'transparent', - color: active ? 'oklch(98% 0 0)' : P.text, + color: active ? C.ink : P.text, fontFamily: FONT, fontSize: '10.5px', fontWeight: '500', cursor: 'pointer', whiteSpace: 'nowrap', transition: 'background 0.1s ease, color 0.1s ease', @@ -3178,7 +3178,7 @@ segBtns.forEach(({ btn, val }) => { const on = val === o.value; btn.style.background = on ? C.brand : 'transparent'; - btn.style.color = on ? 'oklch(98% 0 0)' : P.text; + btn.style.color = on ? C.ink : P.text; }); applyParamValue(variantEl, p, o.value); queueCheckpoint('param_changed'); diff --git a/.trae-cn/skills/impeccable/scripts/surface-brief.mjs b/.trae-cn/skills/impeccable/scripts/surface-brief.mjs new file mode 100644 index 000000000..723f7c1b4 --- /dev/null +++ b/.trae-cn/skills/impeccable/scripts/surface-brief.mjs @@ -0,0 +1,74 @@ +#!/usr/bin/env node +import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath, pathToFileURL } from 'node:url'; +import { resolveProjectRoot } from './context.mjs'; +import { + listSurfaceBriefs, + resolveSurfaceBrief, + surfaceBriefPathForTarget, + writeSurfaceBrief, +} from './lib/surface-briefs.mjs'; + +function summary(brief, projectRoot) { + return { + slug: brief.slug, + path: path.relative(projectRoot, brief.path).split(path.sep).join('/'), + primaryTarget: brief.primaryTarget, + relatedTargets: brief.relatedTargets, + }; +} + +function main(argv) { + const [command, target, bodyFile, ...relatedTargets] = argv; + const projectRoot = resolveProjectRoot(process.cwd(), target ? { targetPath: target } : {}); + if (command === 'path') { + const filePath = surfaceBriefPathForTarget(target, { projectRoot }); + if (!filePath) throw new Error('surface brief path requires a concrete target'); + process.stdout.write(`${path.relative(process.cwd(), filePath) || filePath}\n`); + return; + } + if (command === 'list') { + process.stdout.write(`${JSON.stringify(listSurfaceBriefs(projectRoot).map((brief) => summary(brief, projectRoot)), null, 2)}\n`); + return; + } + if (command === 'read') { + const result = resolveSurfaceBrief(projectRoot, target || null); + if (result.brief) { + process.stdout.write(result.brief.text); + return; + } + if (result.candidates.length) process.stderr.write(`${JSON.stringify(result.candidates.map((brief) => summary(brief, projectRoot)), null, 2)}\n`); + process.exit(2); + } + if (command === 'write') { + if (!target || !bodyFile) throw new Error('usage: surface-brief.mjs write '); + const filePath = writeSurfaceBrief({ + projectRoot, + primaryTarget: target, + relatedTargets, + body: fs.readFileSync(bodyFile, 'utf-8'), + }); + process.stdout.write(`${path.relative(process.cwd(), filePath) || filePath}\n`); + return; + } + throw new Error('usage: surface-brief.mjs [target] [body-file] [related-target ...]'); +} + +function isMainModule() { + if (!process.argv[1]) return false; + try { + return fs.realpathSync(fileURLToPath(import.meta.url)) === fs.realpathSync(process.argv[1]); + } catch { + return import.meta.url === pathToFileURL(process.argv[1]).href; + } +} + +if (isMainModule()) { + try { + main(process.argv.slice(2)); + } catch (error) { + process.stderr.write(`${error?.message || error}\n`); + process.exit(1); + } +} diff --git a/.trae/skills/impeccable/SKILL.md b/.trae/skills/impeccable/SKILL.md index e21baa7db..d6265851e 100644 --- a/.trae/skills/impeccable/SKILL.md +++ b/.trae/skills/impeccable/SKILL.md @@ -1,62 +1,61 @@ --- name: impeccable description: Use when the user wants to design, redesign, shape, critique, audit, polish, clarify, distill, harden, optimize, adapt, animate, colorize, extract, or otherwise improve a frontend interface. Covers websites, landing pages, dashboards, product UI, app shells, components, forms, settings, onboarding, and empty states. Handles UX review, visual hierarchy, information architecture, cognitive load, accessibility, performance, responsive behavior, theming, anti-patterns, typography, fonts, spacing, layout, alignment, color, motion, micro-interactions, UX copy, error states, edge cases, i18n, and reusable design systems or tokens. Also use for bland designs that need to become bolder or more delightful, loud designs that should become quieter, live browser iteration on UI elements, or ambitious visual effects that should feel technically extraordinary. Not for backend-only or non-UI tasks. -version: 4.0.0-alpha.9 +version: 4.0.0-alpha.10 user-invocable: true argument-hint: "[craft|shape · audit|critique · animate|bolder|colorize|delight|layout|overdrive|quieter|typeset · adapt|clarify|distill · harden|onboard|optimize|polish · init|document|extract|live] [target]" license: Apache 2.0 --- -Designs and iterates production-grade frontend interfaces. Real working code, committed design choices, exceptional craft. +This skill gives you the tools and permission to create design that earns to be called out-of-distribution craft: Whereas before, your design work would have been safe, timid and measured, you now approach every design task as a award-winning design director with impeccable understanding for what makes exceptional design work: production-grade code, peak creativity, a clear POV, deep understanding of the needs of the client and users, and exceptional craft. -Approach every design task as the design lead at a small studio known for giving every client a visual identity that could not be mistaken for anyone else's. The client has already rejected work that felt templated; they are paying for a point of view. the model is capable of extraordinary work. Don't hold back. +Core principles: +- Go all out. No hedging, no shortcuts. The deliverable must be complete (except assets the user must provide). +- Dream big and bold. Distinct, beautiful, outstanding and highly inspiring work. +- Iterate with tools available to you (e.g. visual understanding, browser screenshots) until you think this meets the bar. ## Setup -1. Run `node .trae/skills/impeccable/scripts/context.mjs` once per session (if the runtime shows this skill's loaded base directory, run `node /scripts/context.mjs`; keep cwd at the user's project). It prints the project's context and its directives; follow what it prints. Once its output is in the conversation, never rerun it on a later turn. -2. `craft` and `shape` are build-path exceptions: the new-work gate below owns their flow, and on unattended runs its checkpoints resolve without pausing. For any other invoked sub-command (`audit`, `polish`, `live`, ...), immediately read **`reference/.md`** after `context.mjs` (the `.native` variant from the Commands table when the platform is `ios`/`android`/`adaptive`) and follow it. This read is a hard gate: do not inspect the target, run command-specific scripts, or edit files until the reference is loaded. -3. Read at least one project file (CSS / tokens / theme / a representative component) to learn what world you're in. If PRODUCT.md's `## Platform` is `ios` or `android`, also read `reference/.md` (`adaptive` reads both). +1. Run `node .trae/skills/impeccable/scripts/context.mjs` once per session (if the runtime shows this skill's loaded base directory, run `node /scripts/context.mjs`; keep cwd at the user's project). Pass a named source file or route as `--target `. It loads PRODUCT.md, DESIGN.md, the matching surface brief, and native-platform guidance when applicable; follow its directives and do not rerun it. +2. Before acting, load the one playbook that owns the request: the Commands table's reference for an explicit or clearly implied sub-command, or [reference/new-work.md](reference/new-work.md) for a new surface or replacement visual world. Then inspect the target and at least one representative source of incumbent visual truth (tokens, theme, CSS, component, or asset) before editing. ## How to design -**The brief wins.** Where the brief pins down a direction (a named aesthetic, an era, a place, a material, a specific font or palette), follow it exactly, including when it asks for a look this skill warns is saturated. Redirecting a pinned direction toward your own taste is a failure, not a save. - -**Existing worlds are sacred.** Most of impeccable's work happens inside a site or app that already exists. When the surface has a committed design system (real tokens, deliberately chosen faces, a palette the brand owns), work inside that world: extend it, sharpen it, leave it unmistakably the same brand, and never degrade a working page's performance. Inventing parallel colors, fonts, or styles on an existing surface is a defect, not creativity. A scoped refinement keeps the named scope's content and media footprint unless the brief explicitly changes them; build the added emphasis by recomposing what is already there through hierarchy, proportion, rhythm, and the system's own motifs. - -**New identity work reads the playbook first.** When nothing committed exists (greenfield, or a codebase with no real tokens or chosen faces), or the user explicitly asks for a redesign that discards the current look, you MUST read [reference/new-work.md](reference/new-work.md) before making any design decision. Missing DESIGN.md alone does not make work greenfield: code, tokens, chosen type, components, and assets are incumbent design authority. Preserve and extend them unless the user asked to replace them. Producing a genuinely new identity without the playbook yields the generic default this skill exists to prevent. Scoped fixes inside an existing world don't need it; the craft floor below governs them. `context.mjs` prints the appropriate authority directive. +- **The brief wins.** Honor pinned aesthetics, eras, materials, fonts, and palettes even when they conflict with a saturated-pattern warning. Redirecting a clear brief toward your taste is failure. +- **Refinement preserves; redesign replaces.** Refinement keeps the incumbent identity, behavior, copy, and everything outside scope. Ask before replacing factual copy or adding claims. Redesign keeps product truth, content, function, native affordances, and constraints, but treats the old look as evidence and anti-reference; choose a replacement world in new-work and replace DESIGN.md. Never split the difference into polish on the discarded look. +- **Visual authority is evidence, not a filename.** Missing DESIGN.md alone does not make a project greenfield; new-work decides whether to preserve, expand, or replace the incumbent world. ## Modes -Name the visitor's mode before designing; the page's grammar follows from it, and most ruined pages are one mode wearing another mode's grammar. **The mode belongs to the surface, not the subject**: a landing page for a dense technical tool is still Persuade, with Persuade's full permission to be striking; a docs page for a fashion house is still Read. Deciding a page can be plain because its subject is workmanlike is the same category error in reverse. The brief and the surface decide the mode; PRODUCT.md's `register` field survives only as a family hint (`brand` covers Persuade and Experience, `product` covers Operate and Read). Depth beyond the paragraphs below: [reference/new-work.md](reference/new-work.md) when inventing identity, [reference/operate.md](reference/operate.md) for substantial Operate and Read work. +Choose the mode from the requested surface, not the product, and persist it only in that surface brief. A tool's landing page is still Persuade; a fashion house's documentation is still Read. See [new-work.md](reference/new-work.md) for new surfaces and [operate.md](reference/operate.md) for deeper Operate/Read guidance. -**Persuade** (the surface exists to win someone over; design IS the product). The deliverable is an impression that stops the scroll, earns the click, converts. Spans every genre; don't collapse them into one look. On new surfaces, briefs that imply imagery must ship real, verified imagery; a colored rectangle where a photo belongs reads as incomplete. New Persuade surfaces take their typeface procedure and reject list from [reference/new-work.md](reference/new-work.md). - -**Operate** (the surface is a tool someone works in; design SERVES the task). A person getting something done: scanability and consistency outrank expressiveness. These surfaces earn trust by feeling native to their platform: system font stacks and workhorse UI faces are legitimate and often correct here (the Persuade reject list does not apply). The brand lives in the details: focus states, empty states, microcopy, one owned accent. The usage scene is part of the spec: an interface read outdoors, in motion, or at a glance must survive its real ambient light, and the theme follows the scene, not the category's habit. - -**Read** (the surface exists to be understood; long-form, reference, guidance). The deliverable is comprehension, and comprehension is earned twice: a structure the reader can hold in their head with nothing standing between them and the answer, and a reading experience good enough to stay in, through typographic quality and whatever visual or interactive support genuinely helps the reader follow. The brand lives in type, spacing, and small accents. - -**Experience** (the surface presents a body of work; the page IS the work). The artifact leads, the interface recedes, and the visitor meets the work itself in the first viewport at every screen size. Boldness here means trusting the work. +- **Persuade:** win someone over; design is the product. Earn attention and action. Ship real imagery when the brief needs it; follow the committed world, not category habit. +- **Operate:** help someone do work. Scanability, consistency, native expectations, and the real usage scene outrank expression. Brand lives in precise details. +- **Read:** make something understood. Structure for comprehension, then make the reading experience worth staying in. +- **Experience:** present a body of work. Let the artifact lead from the first viewport; the interface recedes. ## Craft floor -Build to this floor without announcing it. The design detector (the project hook, `node .trae/skills/impeccable/scripts/detect.mjs --json `, or `audit`) verifies most of it mechanically; resolve every finding before finalizing. Fix real defects, but use context judgment rather than distorting intentional design to appease a false positive. Classify any intentional exception explicitly and use the hook system's narrowest appropriate waiver when it must persist. +Build to this floor without announcing it. -- Contrast: body text ≥4.5:1 against its background (placeholders too); large text ≥3:1. Gray text on a colored background looks washed out: use a darker shade of the background's own hue, or a transparency of the text color. -- Shadows describe real light: an offset and a soft blur. A zero-offset colored halo is decoration announcing itself. -- Spacing has rhythm: generous separations, tight groupings; cramped padding reads as broken; the space above a heading exceeds the space below it. Verify computed spacing, not intended spacing. -- Type: body line length 65-75ch; display clamp() max ≤6rem; letter-spacing ≥-0.04em; `text-wrap: balance` on headings; modular scale ≥1.25 between steps; light-on-dark adds 0.05-0.1 line-height. Pair faces on a contrast axis, never two similar-but-not-identical ones; one family with committed weight contrast beats a timid pair. Test headings at every breakpoint; overflow means reduce the clamp or rewrite the copy. -- Motion is part of the build: one orchestrated moment beats scattered effects; ease-out exponential curves; reveals enhance an already-visible default (content gated on a class-triggered transition ships blank in hidden tabs and headless renderers). Responsive down to mobile and visible keyboard focus are part of the floor. -- Ship real content (no placeholders, dead links, or fake controls) and cover the interaction states people will actually hit (hover, focus, disabled, loading, error, empty). -- Copy is design material: name things the way the page's own people speak, make every control say what it does, and make every error say what happened and what to do next. -- Before finishing, re-read the brief: every requirement it names must exist on the page, findable in seconds. A beautiful page missing an asked-for feature is unfinished. +- **Contrast:** body and placeholder text ≥4.5:1; large text ≥3:1. On colored surfaces, tint secondary text from that hue or the foreground instead of using gray. +- **Depth:** shadows describe light with offset and soft blur; zero-offset colored halos are decoration. +- **Spacing:** tight groups, generous separation, no cramped containers; space above a heading exceeds space below. Verify computed values. +- **Type:** body measure 65–75ch; display max 6rem and tracking floor -0.04em; balance headings; use clear scale/weight contrast; test overflow at every breakpoint. +- **Motion:** author one coherent moment instead of scattered effects. Use exponential ease-out and an already-visible default. Premium motion is not transform/opacity alone: it may add focus, depth, masks, light, or material change through blur/filter, backdrop-filter, clip-path/masks, or shadow when smooth. Always provide reduced motion. +- **Shipping:** real content, working controls, responsive composition, keyboard focus, and the states users hit: hover, disabled, loading, error, and empty. +- **Copy:** use the product's language; controls name their action, errors name the problem and recovery. +- **Coverage:** every brief requirement must exist and be findable within seconds. + +Before finishing changed UI, follow the quality guidance supplied by `context.mjs` and hooks. Context requests a manual scan only when no automatic detector is active; never add a second detector pass. ## Commands | Command | Category | Description | Reference | |---|---|---|---| -| `craft [feature]` | Build | The standard build flow with attended checkpoints | [reference/new-work.md](reference/new-work.md) | +| `craft [feature]` | Build | Deprecated alias for an ordinary new-work request | [reference/craft.md](reference/craft.md) | | `shape [feature]` | Build | Plan UX/UI before writing code | [reference/shape.md](reference/shape.md) | -| `init` | Build | Set up project context: PRODUCT.md, DESIGN.md, live config, next steps | [reference/init.md](reference/init.md) | +| `init` | Build | Capture durable product context in PRODUCT.md | [reference/init.md](reference/init.md) | | `document` | Build | Generate DESIGN.md from existing project code | [reference/document.md](reference/document.md) | | `extract [target]` | Build | Pull reusable tokens and components into design system | [reference/extract.md](reference/extract.md) | | `critique [target]` | Evaluate | UX design review with heuristic scoring | [reference/critique.md](reference/critique.md) | @@ -78,7 +77,14 @@ Build to this floor without announcing it. The design detector (the project hook | `optimize [target]` | Fix | Diagnose and fix UI performance | [reference/optimize.md](reference/optimize.md) | | `live` | Iterate | Visual variant mode: pick elements in the browser, generate alternatives | [reference/live.md](reference/live.md) | -Routing: **no argument** → read [reference/routing.md](reference/routing.md) and present the context-aware menu (never auto-run a command). **First word matches a command** (or `pin` / `unpin` / `hooks`) → load its reference (native variant on native platforms) and follow it; everything after the command name is the target. **Intent clearly maps to one command** ("fix the spacing" → `layout`, "rewrite this error" → `clarify`) → same; if two fit, ask once. **Otherwise** → general design invocation: apply Setup and this file's guidance; builds flow through the new-work gate above, whose playbook carries the direction checkpoint and the finishing pass. `teach` routes to `init`, and `craft` routes to the standard build flow with attended checkpoints. If setup diverted into `init` for a build request, finish init, refresh context, then resume. +Routing: + +- **No argument:** read [routing.md](reference/routing.md) and present its context-aware menu; never auto-run a command. +- **Explicit or clearly implied command:** load its reference (native variant on native platforms) and follow it. Ask once if two commands fit. +- **Otherwise:** treat the request as general design work. Missing PRODUCT.md routes through init; new surfaces and replacement worlds use new-work. +- `teach` aliases `init`. `craft` is a deprecated alias for ordinary new-work and adds nothing. `shape` owns task discovery, then enters new-work only for visual-world and surface-concept decisions. + +After init writes PRODUCT.md, resume without rerunning `context.mjs`. **Pin / Unpin:** `node .trae/skills/impeccable/scripts/pin.mjs ` creates or removes a standalone `/` shortcut. Report the script's result concisely; relay stderr verbatim on error. diff --git a/.trae/skills/impeccable/reference/android.md b/.trae/skills/impeccable/reference/android.md index 3575c31fb..6337b9018 100644 --- a/.trae/skills/impeccable/reference/android.md +++ b/.trae/skills/impeccable/reference/android.md @@ -2,7 +2,7 @@ For native Android apps: Jetpack Compose, Android Views, React Native, Expo, Flutter shipping to Android hardware. -On native, register narrows. Material Design 3 governs structure, navigation, and interaction whatever the register; brand expresses through Material's theming (color roles, type scale, shape, motion). A Material-everywhere cross-platform app that also ships to iPhone still owes iOS its OS guarantees on that hardware: safe-area insets, Reduce Motion, edge-swipe back. +On native, the visitor mode narrows what expression may override. Material Design 3 governs structure, navigation, and interaction in every mode; brand expresses through Material's theming (color roles, type scale, shape, motion). A Material-everywhere cross-platform app that also ships to iPhone still owes iOS its OS guarantees on that hardware: safe-area insets, Reduce Motion, edge-swipe back. ## The Android slop test diff --git a/.trae/skills/impeccable/reference/animate.md b/.trae/skills/impeccable/reference/animate.md index 1a1098a5b..d51bb6d63 100644 --- a/.trae/skills/impeccable/reference/animate.md +++ b/.trae/skills/impeccable/reference/animate.md @@ -1,203 +1,88 @@ > **Additional context needed**: performance constraints. -Add motion that conveys state, gives feedback, and clarifies hierarchy. Cut motion that exists only for decoration. Animation fatigue is a real cost; spend the budget on the moments that need it. +Use motion to explain state, relationship, and hierarchy, or to create one authored moment the surface has earned. Decoration without purpose is animation debt. --- -## Register +## Visitor mode -Persuade + Experience: 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. +- **Persuade + Experience:** motion may carry the voice. Prefer one rehearsed focal sequence to repeated section reveals. +- **Operate + Read:** motion serves feedback, state, and continuity. Keep routine transitions fast and do not make users wait through page-load choreography. +- **Native (`ios` / `android` / `adaptive`):** follow the Motion section of [ios.md](ios.md) or [android.md](android.md), including the platform's Reduce Motion behavior. Do not apply the web tooling below. -Operate + Read: 150–250 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. +## Find the job -Native (`ios` / `android` / `adaptive`): implementation follows the Motion section of [ios.md](ios.md) / [android.md](android.md) (read it first if Setup hasn't already): system transitions and OS Reduce Motion, never the web tooling below. +Inspect the existing motion language, interaction states, target devices, and performance budget. Find only the places where motion would: ---- +- acknowledge an action; +- make a state change or spatial relationship legible; +- preserve continuity through navigation or layout change; +- direct attention at a meaningful moment; +- embody the selected visual world. -## Assess Animation Opportunities +Ask only when a material constraint cannot be inferred. Do not animate a static area merely because it exists. -Analyze where motion would improve the experience: +## Set the motion thesis -1. **Identify static areas**: - - **Missing feedback**: Actions without visual acknowledgment (button clicks, form submission, etc.) - - **Jarring transitions**: Instant state changes that feel abrupt (show/hide, page loads, route changes) - - **Unclear relationships**: Spatial or hierarchical relationships that aren't obvious - - **Lack of delight**: Functional but joyless interactions - - **Missed guidance**: Opportunities to direct attention or explain behavior +Write a short plan before implementation: -2. **Understand the context**: - - What's the personality? (Playful vs serious, energetic vs calm) - - What's the performance budget? (Mobile-first? Complex page?) - - Who's the audience? (Motion-sensitive users? Power users who want speed?) - - What matters most? (One hero animation vs many micro-interactions?) +- **Focal moment:** the one sequence or interaction that deserves authorship, if any. +- **Continuity:** the state, layout, or navigation changes that need explanation. +- **Feedback:** the controls and outcomes that need acknowledgment. +- **Budget:** which effects may be expensive, how often they run, and the reduced-motion equivalent. -If any of these are unclear from the codebase, ask the user directly to clarify what you cannot infer. +The focal moment must come from this product and surface concept. A generic fade-and-rise, hover lift, parallax layer, or scroll reveal is not a thesis. -**CRITICAL**: Respect `prefers-reduced-motion`. Always provide non-animated alternatives for users who need them. +## Choose material by meaning -## Plan Animation Strategy +Transform and opacity are reliable foundations, not the entire palette. Choose properties for what the transition communicates: -Create a purposeful animation plan: +- **Continuity and relationship:** shared-element motion, FLIP-style transforms, view transitions, or deliberate spatial movement. +- **Focus and depth:** bounded blur, filter, backdrop, light, or shadow changes. +- **Reveal and composition:** masks, clip paths, cropping, or controlled occlusion. +- **Material and energy:** color, gradient position, texture, distortion, or shader effects when the world and runtime support them. +- **State and feedback:** the smallest change that makes cause and result unmistakable. -- **Feedback layer**: Which interactions need acknowledgment? -- **Transition layer**: Which state changes need smoothing? -- **Entrance moment**: The ONE entrance worth rehearsing, where the mode invites it. Not every surface wants one. -- **Delight layer**: Where can we surprise and delight? +Do not stack techniques for spectacle. One strong material idea, carried through the focal sequence and quiet supporting states, is usually enough. -**IMPORTANT**: One well-orchestrated experience beats scattered animations everywhere. Focus on high-impact moments. +Sibling stagger is appropriate when a list appears as a list. Cap the total delay, and never reinterpret every scrolled section as a staggered list. -## Implement Animations +## Timing and easing -Add motion systematically across these categories: +Timing should express distance and consequence: -### Micro-interactions -- **Button feedback**: - - Hover: Subtle scale (1.02-1.05), color shift, shadow increase - - Click: Quick scale down then up (0.95 → 1), ripple effect - - Loading: Spinner or pulse state -- **Form interactions**: - - Input focus: Border color transition, slight scale or glow - - Validation: Shake on error, check mark on success, smooth color transitions -- **Toggle switches**: Smooth slide + color transition (200-300ms) -- **Checkboxes/radio**: Check mark animation, ripple effect -- **Like/favorite**: Scale + rotation, particle effects, color transition +| Duration | Typical use | +|---|---| +| 100–150 ms | immediate feedback | +| 150–300 ms | routine state change | +| 300–500 ms | layout, overlay, or view transition | +| 500–800 ms | a deliberately authored focal entrance | -### State Transitions -- **Show/hide**: Fade + slide (not instant), appropriate timing (200-300ms) -- **Expand/collapse**: Height transition with overflow handling, icon rotation -- **Loading states**: Skeleton screen fades, spinner animations, progress bars -- **Success/error**: Color transitions, icon animations, gentle scale pulse -- **Enable/disable**: Opacity transitions, cursor changes +Exit faster than entrance. Use natural deceleration such as `cubic-bezier(0.16, 1, 0.3, 1)` for confident arrivals; do not use bounce or elastic curves by reflex. Long feedback feels like latency. -### Entrance Animations -- **The one entrance moment** (where the mode invites it): a committed entrance for primary content (scale, parallax, or a creative effect) -- **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. +## Implement to the runtime - 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. +- Use CSS transitions and keyframes for declarative state and bounded sequences. +- Use Web Animations API or the project's existing motion library for interruption, sequencing, and dynamic values. +- Use View Transitions or shared-element techniques when continuity across states is the point. +- Use scroll-driven motion only when the scroll relationship itself carries meaning, with a robust fallback. +- Do not add a dependency for an effect the existing stack can express cleanly. -### Navigation & Flow -- **Page transitions**: Crossfade between routes, shared element transitions -- **Tab switching**: Slide indicator, content fade/slide -- **Carousel/slider**: Smooth transforms, snap points, momentum -- **Scroll effects**: Parallax layers, sticky headers with state changes, scroll progress indicators +Keep content visible in the default state so failed scripts do not hide the page. Avoid casually animating layout-driving properties such as `width`, `height`, `top`, `left`, and margins; use FLIP, transforms, or grid techniques when appropriate. Bound blur, filter, shadow, canvas, and shader work to isolated regions. Apply `will-change` only during known animation. Measure on target viewports and devices rather than assuming transform means fast. -### Feedback & Guidance -- **Hover hints**: Tooltip fade-ins, cursor changes, element highlights -- **Drag & drop**: Lift effect (shadow + scale), drop zone highlights, smooth repositioning -- **Copy/paste**: Brief highlight flash on paste, "copied" confirmation -- **Focus flow**: Highlight path through form or workflow +## Accessibility and control -### Delight Moments -- **Empty states**: Subtle floating animations on illustrations -- **Completed actions**: Confetti, check mark flourish, success celebrations -- **Easter eggs**: Hidden interactions for discovery -- **Contextual animation**: Weather effects, time-of-day themes, seasonal touches +Honor `prefers-reduced-motion` with an intentional alternative. Preserve state change and hierarchy while removing travel, parallax, flashing, or prolonged sequences. Do not replace every animation globally with `0.01ms` if that destroys useful feedback. Motion must never block focus, interaction, reading, or task completion. -## Technical Implementation +Respect autoplay and sound preferences. Any nonessential loop must stop when offscreen or hidden. -Use appropriate techniques for each animation: +## Verify -### Timing & Easing +- The focal motion is specific to the selected world and surface. +- Every supporting animation explains feedback, state, or relationship. +- Interruption and repeated use behave correctly. +- Desktop, mobile, keyboard, and reduced-motion paths remain usable. +- Expensive effects stay smooth on the target device. +- Removing an animation would lose meaning or authored character, not merely decoration. -**Duration: the 100/300/500 rule.** Timing matters more than easing for "feels right": - -| Duration | Use Case | Examples | -|----------|----------|----------| -| **100–150ms** | Instant feedback | Button press, toggle, color change | -| **200–300ms** | State changes | Menu open, tooltip, hover state | -| **300–500ms** | Layout changes | Accordion, modal, drawer | -| **500–800ms** | Entrance animations | Page load, hero reveal | - -**Easing curves (use these, not CSS defaults):** -```css -/* Recommended: natural deceleration */ ---ease-out-quart: cubic-bezier(0.25, 1, 0.5, 1); /* Smooth */ ---ease-out-quint: cubic-bezier(0.22, 1, 0.36, 1); /* Slightly snappier */ ---ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1); /* Confident, decisive */ - -/* AVOID: feel dated and tacky */ -/* bounce: cubic-bezier(0.34, 1.56, 0.64, 1); */ -/* elastic: cubic-bezier(0.68, -0.6, 0.32, 1.6); */ -``` - -**Exit animations are faster than entrances.** Use ~75% of enter duration. - -### CSS Animations -```css -/* Prefer for simple, declarative animations */ -- transitions for state changes -- @keyframes for complex sequences -- transform and opacity for reliable movement -- blur, filters, masks, clip paths, shadows, and color shifts for premium atmospheric effects when verified smooth -``` - -### JavaScript Animation -```javascript -/* Use for complex, interactive animations */ -- Web Animations API for programmatic control -- Framer Motion for React -- 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 -- **Layout safety**: Avoid casual animation of layout-driving properties (`width`, `height`, `top`, `left`, margins) -- **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) { - * { - animation-duration: 0.01ms !important; - animation-iteration-count: 1 !important; - transition-duration: 0.01ms !important; - } -} -``` - -**NEVER**: -- Use bounce or elastic easing curves; they feel dated and draw attention to the animation itself -- Animate layout properties casually (`width`, `height`, `top`, `left`, margins) when transform, FLIP, or grid-based techniques would work -- Use durations over 500ms for feedback (it feels laggy) -- Animate without purpose (every animation needs a reason) -- Ignore `prefers-reduced-motion` (this is an accessibility violation) -- Animate everything (animation fatigue makes interfaces feel exhausting) -- Block interaction during animations unless intentional - -## Verify Quality - -Test animations thoroughly: - -- **Smooth at 60fps**: No jank on target devices -- **Feels natural**: Easing curves feel organic, not robotic -- **Appropriate timing**: Not too fast (jarring) or too slow (laggy) -- **Reduced motion works**: Animations disabled or simplified appropriately -- **Doesn't block**: Users can interact during/after animations -- **Adds value**: Makes interface clearer or more delightful - -When the motion clarifies state instead of decorating it, hand off to `/impeccable polish` for the final pass. +When motion earns its place, hand off to `/impeccable polish` for the final pass. diff --git a/.trae/skills/impeccable/reference/audit.md b/.trae/skills/impeccable/reference/audit.md index 72815af69..70d80771a 100644 --- a/.trae/skills/impeccable/reference/audit.md +++ b/.trae/skills/impeccable/reference/audit.md @@ -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 - diff --git a/.trae/skills/impeccable/reference/audit.native.md b/.trae/skills/impeccable/reference/audit.native.md index 737d50e4f..0126fa157 100644 --- a/.trae/skills/impeccable/reference/audit.native.md +++ b/.trae/skills/impeccable/reference/audit.native.md @@ -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) diff --git a/.trae/skills/impeccable/reference/bolder.md b/.trae/skills/impeccable/reference/bolder.md index 6b44f0e38..78f5e4811 100644 --- a/.trae/skills/impeccable/reference/bolder.md +++ b/.trae/skills/impeccable/reference/bolder.md @@ -8,12 +8,12 @@ ## Why it reads flat -A section usually reads flat for reasons its neighbors have already solved. Look at what the rest of the page does that this section does not: the display type at full strength, the structural devices that carry meaning, the signature motif, the density and pacing, the specificity of the copy. A flat section is typically one that quietly opts out of the system's own strongest moves. The most reliable bolder pass brings the target up to the expressive level its neighbors already reach, in the system's own vocabulary rather than a new one. +A section usually reads flat for reasons its neighbors have already solved. Look at what the rest of the page does that this section does not: the display type at full strength, the structural devices that carry meaning, the signature motif, the density and pacing. A flat section is typically one that quietly opts out of the system's own strongest moves. The most reliable bolder pass brings the target up to the expressive level its neighbors already reach, in the system's own vocabulary rather than a new one. ## The amplification - **Amplify what the system already owns.** Reuse its motif and its type scale at full strength, turned up for this section rather than invented for it. The bolder version should look more like the same brand, not less. -- **Let the content carry the weight.** Flat copy makes a flat design, and no amount of size fixes a generic claim. Make every word specific enough to earn its place, and let the section's real evidence do the work that a decorative addition would otherwise fake. +- **Keep content true.** Existing claims are part of the scope: preserve them unless the user supplies replacements. If real evidence is essential to the direction but absent, ask for it. - **Commit, then clarify.** Half-measures read as noise. Make the one decisive move completely, then quiet everything around it so the move is legible. If every element got louder, the section got flatter. - **Give it its own rhythm.** The target should read as a peak in the scroll, a shift in density or pace from what surrounds it, not simply more of the same. diff --git a/.trae/skills/impeccable/reference/clarify.md b/.trae/skills/impeccable/reference/clarify.md index 00ee978c3..3047a9d82 100644 --- a/.trae/skills/impeccable/reference/clarify.md +++ b/.trae/skills/impeccable/reference/clarify.md @@ -1,288 +1,94 @@ -> **Additional context needed**: audience technical level and users' mental state in context. +> **Additional context needed**: audience knowledge and emotional state. -Find the unclear, confusing, or poorly written interface text and rewrite it. Vague copy creates support tickets and abandonment; specific copy gets users through the task. +Rewrite unclear interface text so users understand what happened, what matters, and what to do next. Preserve factual meaning, product terminology, and brand voice. +## Audit the language ---- +Read the entire interaction path, not isolated strings. Identify: -## Assess Current Copy +- ambiguous nouns, verbs, and actions; +- internal jargon or assumed knowledge; +- vague labels, outcomes, and system states; +- missing consequences, recovery, or timing; +- inconsistent terminology and capitalization; +- redundant headings, intros, helper text, and confirmations; +- text that breaks at realistic widths or in translation; +- tone that ignores stress, risk, success, or urgency. -Identify what makes the text unclear or ineffective: +Infer audience and task from product context and surrounding UI. Ask before changing factual claims, legal meaning, or a term that may be domain-specific. -1. **Find clarity problems**: - - **Jargon**: Technical terms users won't understand - - **Ambiguity**: Multiple interpretations possible - - **Passive voice**: "Your file has been uploaded" vs "We uploaded your file" - - **Length**: Too wordy or too terse - - **Assumptions**: Assuming user knowledge they don't have - - **Missing context**: Users don't know what to do or why - - **Tone mismatch**: Too formal, too casual, or inappropriate for situation +## Set the message hierarchy -2. **Understand the context**: - - Who's the audience? (Technical? General? First-time users?) - - What's the user's mental state? (Stressed during error? Confident during success?) - - What's the action? (What do we want users to do?) - - What's the constraint? (Character limits? Space limitations?) +For each state, decide: -**CRITICAL**: Clear copy helps users succeed. Unclear copy creates frustration, errors, and support tickets. +1. the one fact the user needs now; +2. the action available next; +3. supporting context that changes the decision; +4. the appropriate tone for this moment. -## Plan Copy Improvements +Say each idea once. If the heading already explains the state, the introduction should add new information or disappear. -Create a strategy for clearer communication: +## Rewrite by function -- **Primary message**: What's the ONE thing users need to know? -- **Action needed**: What should users do next (if anything)? -- **Tone**: How should this feel? (Helpful? Apologetic? Encouraging?) -- **Constraints**: Length limits, brand voice, localization considerations +### Actions and navigation -**IMPORTANT**: Good UX writing is invisible. Users should understand immediately without noticing the words. +Use a specific verb and object when the outcome is not already obvious. Labels should describe what will happen, not the gesture used to trigger it. Keep the same noun and verb for the same concept throughout the product. -## Improve Copy Systematically +For destructive actions, name the object and consequence. Prefer undo over confirmation when recovery is safe. When confirmation is necessary, name the action on both the message and button instead of using `Yes`, `No`, `OK`, or `Submit`. -Refine text across these common areas: +### Forms -### Error Messages -**Bad**: "Error 403: Forbidden" -**Good**: "You don't have permission to view this page. Contact your admin for access." +Use persistent labels; placeholders are examples, not labels. Put format and eligibility requirements before submission. Explain why information is requested only when it is not obvious. Required and optional treatment should be consistent. -**Bad**: "Invalid input" -**Good**: "Email addresses need an @ symbol. Try: name@example.com" +Validation says what needs attention and how to correct it without blaming the user. Keep related instructions near the field and announce errors accessibly. -**Principles**: -- Explain what went wrong in plain language -- Suggest how to fix it -- Don't blame the user -- Include examples when helpful -- Link to help/support if applicable - -### Form Labels & Instructions -**Bad**: "DOB (MM/DD/YYYY)" -**Good**: "Date of birth" (with placeholder showing format) - -**Bad**: "Enter value here" -**Good**: "Your email address" or "Company name" - -**Principles**: -- Use clear, specific labels (not generic placeholders) -- Show format expectations with examples -- Explain why you're asking (when not obvious) -- Put instructions before the field, not after -- Keep required field indicators clear - -### Button & CTA Text -**Bad**: "Click here" | "Submit" | "OK" -**Good**: "Create account" | "Save changes" | "Got it, thanks" +### Errors and permissions -**Principles**: -- Describe the action specifically -- Use active voice (verb + noun) -- Match user's mental model -- Be specific ("Save" is better than "OK") +An actionable error answers: -### Help Text & Tooltips -**Bad**: "This is the username field" -**Good**: "Choose a username. You can change this later in Settings." +1. what failed; +2. why, when known and useful; +3. how to recover or what alternative remains. -**Principles**: -- Add value (don't just repeat the label) -- Answer the implicit question ("What is this?" or "Why do you need this?") -- Keep it brief but complete -- Link to detailed docs if needed +Do not expose internal codes as the primary message. Do not promise a cause or resolution the system cannot know. Treat privacy, payment, deletion, access loss, and blocked work seriously; warmth is welcome, jokes are not. -### Empty States -**Bad**: "No items" -**Good**: "No projects yet. Create your first project to get started." +### Loading, empty, and success states -**Principles**: -- Explain why it's empty (if not obvious) -- Show next action clearly -- Make it welcoming, not dead-end +Loading text names the real operation and sets an honest expectation when the wait is meaningful. Show determinate progress when available; never invent progress. -### Success Messages -**Bad**: "Success" -**Good**: "Settings saved! Your changes will take effect immediately." +An empty state distinguishes first use, no results, filters, permissions, and failure. Explain the state and provide the next useful action. -**Principles**: -- Confirm what happened -- Explain what happens next (if relevant) -- Be brief but complete -- Match the user's emotional moment (celebrate big wins) - -### Loading States -**Bad**: "Loading..." (for 30+ seconds) -**Good**: "Analyzing your data... this usually takes 30-60 seconds" - -**Principles**: -- Set expectations (how long?) -- Explain what's happening (when it's not obvious) -- Show progress when possible -- Offer escape hatch if appropriate ("Cancel") +Success confirms the completed outcome and mentions the next consequence only when it changes what the user should do. Routine success should be brief. -### Confirmation Dialogs -**Bad**: "Are you sure?" -**Good**: "Delete 'Project Alpha'? This can't be undone." +### Help and instructional text -**Principles**: -- State the specific action -- Explain consequences (especially for destructive actions) -- Use clear button labels ("Delete project" not "Yes") -- Don't overuse confirmations (only for risky actions) +Helper text answers an implicit question instead of restating the control. Use progressive disclosure for uncommon detail. Link text must make sense out of context; icon-only controls need accessible names. -### Navigation & Wayfinding -**Bad**: Generic labels like "Items" | "Things" | "Stuff" -**Good**: Specific labels like "Your projects" | "Team members" | "Settings" +## Voice, accessibility, and localization -**Principles**: -- Be specific and descriptive -- Use language users understand (not internal jargon) -- Make hierarchy clear -- Consider information scent (breadcrumbs, current location) +Voice stays consistent; tone adapts to the moment. Use plain language without flattening terminology the audience genuinely knows. -## Apply Clarity Principles +- Write complete translatable messages rather than concatenated fragments. +- Keep variables and numbers structured so translators can reorder them. +- Allow expansion instead of abbreviating prematurely. +- Make alt text convey the image's information; use empty alt for decoration. +- Keep screen-reader names aligned with visible labels and outcomes. +- Do not rely on punctuation, color, or iconography to carry the message alone. -Every piece of copy should follow these rules: +Maintain a short terminology glossary when inconsistency spans the product. Do not vary words for literary effect in an interface. -1. **Be specific**: "Enter email" not "Enter value" -2. **Be concise**: Cut unnecessary words (but don't sacrifice clarity) -3. **Be active**: "Save changes" not "Changes will be saved" -4. **Be human**: "Oops, something went wrong" not "System error encountered" -5. **Tell users what to do**, not just what happened -6. **Be consistent**: Use same terms throughout (don't vary for variety) +## Verify -**NEVER**: -- Use jargon without explanation -- Blame users ("You made an error" → "This field is required") -- Be vague ("Something went wrong" without explanation) -- Use passive voice unnecessarily -- Write overly long explanations (be concise) -- Use humor for errors (be empathetic instead) -- Assume technical knowledge -- Vary terminology (pick one term and stick with it) -- Repeat information (headers restating intros, redundant explanations) -- Use placeholders as the only labels (they disappear when users type) +Read the flow in context and test: -## Verify Improvements +- comprehension without hidden product knowledge; +- actionability at errors, empty states, and decision points; +- factual accuracy and consistent terminology; +- scanability at target widths and 200% zoom; +- long names, localization expansion, pluralization, and dynamic values; +- accessible names and announced state changes; +- tone appropriate to consequence and emotional context. -Test that copy improvements work: +The final copy is as short as it can be without removing meaning or recovery. -- **Comprehension**: Can users understand without context? -- **Actionability**: Do users know what to do next? -- **Brevity**: Is it as short as possible while remaining clear? -- **Consistency**: Does it match terminology elsewhere? -- **Tone**: Is it appropriate for the situation? - -When the copy reads cleanly, hand off to `/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. +When the language reads cleanly, hand off to `/impeccable polish` for the final pass. diff --git a/.trae/skills/impeccable/reference/codex.md b/.trae/skills/impeccable/reference/codex.md index 18a924a91..6c550f1dc 100644 --- a/.trae/skills/impeccable/reference/codex.md +++ b/.trae/skills/impeccable/reference/codex.md @@ -1,105 +1,38 @@ -# Codex: Visual Direction & Asset Production +# Codex: Surface Probes & Asset Production -This file is loaded by `/impeccable craft` when the harness has native image generation (currently Codex via `image_gen`). Other harnesses skip it. It covers the two craft steps that depend on real image generation: landing the visual direction, and producing the raster assets the implementation will compose. +Load this from [new-work.md](new-work.md) only when the harness has native image generation and a substantial, high-fidelity surface would benefit from seeing the shortlisted concept before code. PRODUCT.md and DESIGN.md are preconditions. New-work has already resolved the visual world; this file must not reopen it. -Read this *before* generating any images. The order matters, and the per-step user pauses are what keep generated imagery from drifting away from the brief. +The purpose of a probe is to test composition, narrative, hierarchy, density, focal moment, signature use, and image requirements. It is not a second identity workshop. Keep DESIGN.md's palette, typography direction, material language, component character, imagery stance, and motion grammar fixed. -### Four stop points before code +## Generate the smallest useful probe set -Steps A through D each end with the user. Do not advance past any of them on your own read of the situation. +Generate one to three high-fidelity north-star comps using the native image-generation capability. Base them on the real content and the surface concepts already developed with the user. -1. **STOP after Step A questions.** Wait for answers. -2. **STOP after Step B palette generation.** Wait for "confirm palette." -3. **STOP after Step C mocks.** Wait for direction approval or delegation. -4. **Only after Step D approves a direction** do you return to the build (new-work.md's plan, then code). +- When the user shortlisted multiple concepts, show one clear expression of each. +- When one concept is already selected, vary only the structural uncertainty that the image can resolve: topology, sequence, density, hierarchy, focal composition, or interaction framing. +- Show enough beyond the opening moment to prove the concept can govern the whole requested surface. +- Do not generate a palette artifact, ask new atmosphere questions, introduce a different type voice, or invent a new motif. If the committed world cannot support the concept, return to the concept shortlist rather than changing the world. -Prior shape approval does **not** satisfy any of these. Shape's "confirm or override" advances you into Step A; it is not a substitute for it. +Treat each comp as a direction test, not a screenshot specification. Core UI text, responsive behavior, accessibility, semantics, and interaction states remain implementation responsibilities. -## Step A: Explore Directions with the User +## One approval point -Before generating anything, run a brief direction conversation grounded in the shape brief. +Show the probes together and ask what should carry forward, what feels false to the world, and whether the selected surface concept should be approved, combined, revised, or rejected. Then stop and wait. A structured simulated user counts as attended and receives the same question. -**Step A is required even when shape just produced a confirmed brief.** The shape questions and Step A questions cover different ground: shape pins purpose, content, scope; Step A pins palette, atmosphere, and named visual references for the comps you're about to generate. The only time you can skip Step A is when the user has already answered these exact palette/atmosphere/reference questions in the same session. +Do not begin code until the user approves a direction or explicitly delegates the choice. If they delegate, choose using the task brief, PRODUCT.md, and DESIGN.md, and state the evidence. Approval refines the task concept; it does not modify DESIGN.md. -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: +After approval, summarize the composition and the parts of the comp that must not be literalized. Return to new-work.md and write the direction contract from the approved surface concept before code. -- "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.)" +## Inventory implementation fidelity -**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. +Before building, inventory the approved comp's major visible ingredients and choose an implementation medium for each: semantic HTML/CSS/SVG, existing project asset, generated raster, sourced raster, icon library, canvas/WebGL, or accepted omission. -## Step B: Generate the Brand Palette First +Pay special attention to the dominant composition, signature use, image-native content, second-fold system, and any interaction the still image only implies. If the concept depends on a photograph, architectural scene, product object, portrait, or other raster-native material, do not silently replace it with generic CSS scenery. -Generate **one** palette artifact before any mocks. This is a small, focused image: typography pairing on the chosen background, primary + accent color swatches, one signature ornament or motif. Single image, single pass. +Treat the comp as a north star, not something to trace. Do not rasterize core UI text or controls. Do not substitute a different visual driver after approval without asking. -Why palette first: mocks generated against a vague color sense produce noise that drowns out the structural decisions. A confirmed palette is the first concrete contract for everything downstream. +## Produce only the assets the build needs -Show the palette to the user. Ask one question: "This is the palette I'm locking in for the mocks. Confirm, or call out what to shift?" +When clean raster ingredients are required and a scoped subagent is available and authorized, use `impeccable_asset_producer`. Give it the approved comp, output paths, required dimensions and formats, transparency needs, crop notes, and what must remain semantic code. Otherwise produce the minimum required assets with the native image-generation capability in the current thread. -**STOP and wait for confirmation.** Do not generate mocks against an unconfirmed palette. "Probably good enough" is the wrong call here; the palette is the contract for everything downstream. - -## Step C: Generate 1-3 Visual Mocks Against the Palette - -Once the palette is confirmed, generate **1 to 3** high-fidelity north-star comps. Each mock must use the confirmed palette and typography. Mocks differ in *structural* direction (hierarchy, topology, density, composition), not in color or motif. - -- Brand work: push visual identity, composition, mood, and signature motifs. -- Product work: push hierarchy, topology, density, tone, grounded in realistic product structure. -- Landing pages and long-form brand surfaces: show enough of the second fold to establish the system beyond the hero. - -Use the `image_gen` tool directly (or via the imagegen skill when available). Don't ask the user to install anything. - -## Step D: Approval Loop - -Show the comps. Ask what carries forward. Iterate until **one direction is approved** or the user explicitly delegates. - -**STOP and wait for the approval or the delegation.** Do not begin Step E or return to the build until a single direction is named. If the user delegates, pick the strongest direction and explain it from the brief, not personal taste. - -Before moving to assets, summarize what to carry into code and what *not* to literalize from the mock. This is the handoff between visual exploration and semantic implementation. - -## Step E: Mock Fidelity Inventory - -Inventory the approved mock's major visible ingredients. For each, decide implementation: semantic HTML/CSS/SVG, generated raster, sourced raster, icon library, canvas/WebGL, or accepted omission. - -Common ingredients to inventory: - -- Hero silhouette and dominant composition -- Signature motifs (planets, devices, portraits, charts, route lines, insets, badges, etc.) -- Nav and primary-action treatment (when the surface has one) -- Section sequence, especially the second fold -- Image-native content the concept depends on -- Typography, density, color/material treatment, motion cues - -Treat the mock as a north star, not a screenshot to trace. Don't rasterize core UI text. But if the live result lacks the mock's major ingredients, the implementation is wrong. - -If a photographic, architectural, product, or place-led mock becomes generic CSS scenery, decorative diagrams, bullets, or copy, stop and fix it. That's a broken implementation, not a harmless interpretation. - -Don't substitute a different hero composition or visual driver post-approval without user sign-off. - -## Step F: Asset Slicing via the Asset Producer - -Raster ingredients identified in Step E need clean production assets. Use the bundled `impeccable_asset_producer` subagent rather than producing inline. - -Spawn it as a scoped subagent. If you do not have explicit permission to use agents, stop and ask: - -```text -Asset production will work better as a scoped subagent job. Should I spawn the Impeccable asset producer subagent for this step? -``` - -Pass to the agent: - -- Approved mock path or screenshot reference -- Crop paths or a contact sheet with crop ids -- Output directory -- Required dimensions, format, transparency needs -- Avoid list -- Notes on what should remain semantic HTML/CSS/SVG instead of raster - -Attach image generation capability to the spawned agent when the harness supports it. Do **not** load image-generation reference material into the parent thread. - -Inline asset production is allowed only if the user declines subagents, the harness cannot spawn the authorized agent, or the user explicitly asks for single-thread mode. - -Prefer HTML/CSS/SVG/canvas when they can credibly reproduce an ingredient; reach for real, generated, or stock imagery when the mock or subject matter calls for actual visual content. - -## After This File - -Once Steps A through F are complete, return to the build: implement per new-work.md and SKILL.md's craft floor. The implementation builds against the confirmed palette, approved mock, and the assets the producer wrote. +Return to [new-work.md](new-work.md) for the direction contract, implementation, and finishing pass. diff --git a/.trae/skills/impeccable/reference/colorize.md b/.trae/skills/impeccable/reference/colorize.md index abee1acf8..dc45f88c5 100644 --- a/.trae/skills/impeccable/reference/colorize.md +++ b/.trae/skills/impeccable/reference/colorize.md @@ -1,257 +1,86 @@ > **Additional context needed**: existing brand colors. -Replace timid grayscale or single-accent designs with a strategic palette: pick a color strategy, choose a hue family that fits the brand, then apply color with intent. More color ≠ better. Strategic color beats rainbow vomit. +Introduce color as hierarchy, meaning, and atmosphere. Preserve confirmed brand and semantic conventions; do not replace a visual world under the guise of colorizing it. --- -## Register +## Visitor mode -Persuade + Experience: palette IS voice. Pick a color strategy first per SKILL.md (Restrained / Committed / Full palette / Drenched) and follow its dosage. Committed, Full palette, and Drenched deliberately exceed the ≤10% rule; that rule is Restrained only. Unexpected combinations are allowed; a dominant color can own the page when the chosen strategy calls for it. +- **Persuade + Experience:** color may carry the voice and own large regions when the selected world calls for it. +- **Operate + Read:** color primarily encodes action, selection, status, wayfinding, and reading hierarchy. Rarity gives an accent force. -Operate + Read: semantic-first and almost always Restrained. Accent color is reserved for primary action, current selection, and state indicators. Not decoration. Every color has a consistent meaning across every screen. +## Audit before choosing ---- +Read DESIGN.md, tokens, assets, current themes, and representative states. Identify: -## Assess Color Opportunity +- which colors are confirmed brand commitments; +- current surface, text, action, and semantic roles; +- places where grayscale obscures hierarchy or state; +- contrast failures and color-only communication; +- light/dark or data-visualization requirements; +- whether the task asks for more color or a new identity. -Analyze the current state and identify opportunities: +If a new identity is required, use [new-work.md](new-work.md). Ask only when a binding brand decision cannot be inferred. -1. **Understand current state**: - - **Color absence**: Pure grayscale? Limited neutrals? One timid accent? - - **Missed opportunities**: Where could color add meaning, hierarchy, or delight? - - **Context**: What's appropriate for this domain and audience? - - **Brand**: Are there existing brand colors we should use? +## Choose a strategy -2. **Identify where color adds value**: - - **Semantic meaning**: Success (green), error (red), warning (yellow/orange), info (blue) - - **Hierarchy**: Drawing attention to important elements - - **Categorization**: Different sections, types, or states - - **Emotional tone**: Warmth, energy, trust, creativity - - **Wayfinding**: Helping users navigate and understand structure - - **Delight**: Moments of visual interest and personality +Name the intended emotional temperature, dominant relationship, contrast range, and color dosage before editing. The strategy may be restrained or immersive; it must follow the brief and selected world rather than a fixed percentage rule. -If any of these are unclear from the codebase, ask the user directly to clarify what you cannot infer. +Build roles, not a bag of swatches: -**CRITICAL**: More color ≠ better. Strategic color beats rainbow vomit every time. Every color should have a purpose. +- canvas and elevated surfaces; +- primary and secondary text; +- action, focus, and selection; +- borders and separators; +- success, warning, error, and information; +- data categories or scales when needed. -## Plan Color Strategy +Use the project's existing color space. For a new web palette, prefer OKLCH because lightness and chroma can be adjusted predictably. Choose hue from product meaning and visual direction, never from a default category association. -Create a purposeful color introduction plan: +## Apply at system scale -- **Color palette**: What colors match the brand/context? (Choose 2-4 colors max beyond neutrals) -- **Dominant color**: Which color owns 60% of colored elements? -- **Accent colors**: Which colors provide contrast and highlights? (30% and 10%) -- **Application strategy**: Where does each color appear and why? +- Let the strongest color own a deliberate region or role instead of scattering tiny accents. +- Keep the primary action easy to find; do not spend its color on decoration. +- Tint neutrals only when the brand hue genuinely creates cohesion. Neutral gray is valid when it serves the world. +- On colored surfaces, derive secondary text from the foreground or surface hue rather than using washed-out generic gray. +- Keep semantic meanings consistent, but respect platform and domain conventions instead of assuming fixed hues. +- For data, use distinct lightness, chroma, shape, label, or pattern so color is not the only code. +- 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. -**IMPORTANT**: Color should enhance hierarchy and meaning, not create chaos. Less is more when it matters more. +Decoration without a relationship to hierarchy, state, content, or the visual world is not a color strategy. -## Introduce Color Strategically +## Contrast and perception -Add color systematically across these dimensions: +Verify computed foreground/background pairs: -### Semantic Color -- **State indicators**: - - Success: Green tones (emerald, forest, mint) - - Error: Red/pink tones (rose, crimson, coral) - - Warning: Orange/amber tones - - Info: Blue tones (sky, ocean, indigo) - - Neutral: Gray/slate for inactive states +| Content | WCAG AA minimum | +|---|---| +| body text | 4.5:1 | +| large text | 3:1 | +| controls, icons, focus indicators | 3:1 | -- **Status badges**: Colored backgrounds or borders for states (active, pending, completed, etc.) -- **Progress indicators**: Colored bars, rings, or charts showing completion or health +Do not rely on eyesight alone. Check interactive states, overlays, text on images, disabled content, and both themes. Simulate common vision deficiencies. Information conveyed by color also needs text, shape, iconography, or position. -### Accent Color Application -- **Primary actions**: Color the most important buttons/CTAs -- **Links**: Add color to clickable text (maintain accessibility) -- **Icons**: Colorize key icons for recognition and personality -- **Headers/titles**: Add color to section headers or key labels -- **Hover states**: Introduce color on interaction +When deriving OKLCH ramps, vary lightness and reduce chroma near white and black. Do not keep high chroma at extreme lightness merely to make the math uniform. Prefer explicit colors over chains of translucent overlays when alpha would make contrast context-dependent. -### Background & Surfaces -- **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 toward the brand, not "for warmth" by reflex +## Verify -**Use OKLCH for color**: It's perceptually uniform, meaning equal steps in lightness *look* equal. Great for generating harmonious scales. - -### Data Visualization -- **Charts & graphs**: Use color to encode categories or values -- **Heatmaps**: Color intensity shows density or importance -- **Comparison**: Color coding for different datasets or timeframes - -### Borders & Accents -- **Hairline borders**: 1px colored borders on full perimeter (not side-stripes; see the absolute ban on `border-left/right > 1px`) -- **Underlines**: Color underlines for emphasis or active states -- **Dividers**: Subtle colored dividers instead of gray lines -- **Focus rings**: Colored focus indicators matching brand -- **Surface tints**: A 4-8% background wash of the accent color instead of a stripe - -**NEVER**: `border-left` or `border-right` greater than 1px as a colored accent stripe. This is one of the three absolute bans in the parent skill. If you want to mark a card as "active" or "warning", use a full hairline border, a background tint, a leading glyph, or a numbered prefix. Not a side stripe. - -### Typography Color -- **Colored headings**: Use brand colors for section headings (maintain contrast) -- **Highlight text**: Color for emphasis or categories -- **Labels & tags**: Small colored labels for metadata or categories - -### Decorative Elements -- **Illustrations**: Add colored illustrations or icons -- **Shapes**: Geometric shapes in brand colors as background elements -- **Gradients**: Colorful gradient overlays or mesh backgrounds -- **Blobs/organic shapes**: Soft colored shapes for visual interest - -## Balance & Refinement - -Ensure color addition improves rather than overwhelms: - -### Maintain Hierarchy -- **Dominant color** (60%): Primary brand color or most used accent -- **Secondary color** (30%): Supporting color for variety -- **Accent color** (10%): High contrast for key moments -- **Neutrals** (remaining): Gray/black/white for structure - -### Accessibility -- **Contrast ratios**: Ensure WCAG compliance (4.5:1 for text, 3:1 for UI components) -- **Don't rely on color alone**: Use icons, labels, or patterns alongside color -- **Test for color blindness**: Verify red/green combinations work for all users - -### Cohesion -- **Consistent palette**: Use colors from defined palette, not arbitrary choices -- **Systematic application**: Same color meanings throughout (green always = success) -- **Temperature consistency**: Warm palette stays warm, cool stays cool - -**NEVER**: -- 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 -- Violate WCAG contrast requirements -- Use color as the only indicator (accessibility issue) -- Make everything colorful (defeats the purpose) -- Default to purple-blue gradients (AI slop aesthetic) - -## Verify Color Addition - -Test that colorization improves the experience: - -- **Better hierarchy**: Does color guide attention appropriately? -- **Clearer meaning**: Does color help users understand states/categories? -- **More engaging**: Does the interface feel warmer and more inviting? -- **Still accessible**: Do all color combinations meet WCAG standards? -- **Not overwhelming**: Is color balanced and purposeful? +- Every color has a stable role or a world-specific atmospheric purpose. +- Attention lands on the intended action, content, or state. +- The palette works across quiet, dense, interactive, error, and empty states. +- Light and dark themes are each composed, not mechanically inverted. +- Contrast and non-color cues pass in all relevant states. +- The result is recognizably this product, not a generic “colorful” treatment. When the palette earns its place, hand off to `/impeccable polish` for the final pass. ## Live-mode signature params -When invoked from live mode, each variant MUST declare a `color-amount` param so the user can dial between a restrained accent and a drenched surface without regeneration. Author the variant's CSS against `var(--p-color-amount, 0.5)`, typically as the alpha multiplier on backgrounds, or as a scaling factor on the chroma axis in an OKLCH expression. 0 = neutral/monochrome, 1 = full saturation / dominant coverage. +When invoked from live mode, every variant declares a `color-amount` parameter. Author CSS against `var(--p-color-amount, 0.5)` so the user can move from neutral to the variant's full color strategy without regeneration. ```json {"id":"color-amount","kind":"range","min":0,"max":1,"step":0.05,"default":0.5,"label":"Color amount"} ``` -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). +Add at most two variant-specific parameters, such as palette, temperature, or tint behavior. Follow [live.md](live.md)'s parameter contract. diff --git a/.trae/skills/impeccable/reference/craft.md b/.trae/skills/impeccable/reference/craft.md index cc7f98d37..dbbc9402c 100644 --- a/.trae/skills/impeccable/reference/craft.md +++ b/.trae/skills/impeccable/reference/craft.md @@ -1,3 +1,5 @@ # Craft (deprecated alias) -`craft` is a deprecated alias for the standard build flow. Builds route through SKILL.md's new-work gate; [new-work.md](new-work.md) carries the direction checkpoint ("Decide, then build"), the identity playbook, and the finishing pass. Invoking `craft` explicitly forces the attended checkpoints: pause at the stated direction for confirmation, and follow [codex.md](codex.md)'s mock flow when the harness generates images. Nothing else differs from a plain build request. +`craft` is a deprecated alias for an ordinary request to make new visual work. It adds no setup, interview, checkpoint, tool, or quality behavior. Apply SKILL.md's normal routing: create missing PRODUCT.md through [init.md](init.md), then follow [new-work.md](new-work.md) for visual authority, world and surface decisions, implementation, and finish. + +Do not tell users they need to invoke `craft`. Natural requests such as “build this feature,” “make a landing page,” or “redesign this screen” use the same flow. diff --git a/.trae/skills/impeccable/reference/critique.md b/.trae/skills/impeccable/reference/critique.md index 0e43b1a41..e809e5b29 100644 --- a/.trae/skills/impeccable/reference/critique.md +++ b/.trae/skills/impeccable/reference/critique.md @@ -43,13 +43,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 @@ -115,11 +115,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. @@ -176,7 +176,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. 2. **Pass the structured metadata** through `IMPECCABLE_CRITIQUE_META` (JSON), then run the write command: ```bash diff --git a/.trae/skills/impeccable/reference/delight.md b/.trae/skills/impeccable/reference/delight.md index 596cff09e..40aaddc34 100644 --- a/.trae/skills/impeccable/reference/delight.md +++ b/.trae/skills/impeccable/reference/delight.md @@ -1,302 +1,70 @@ -> **Additional context needed**: what's appropriate for the domain (playful vs professional vs quirky vs elegant). +> **Additional context needed**: the brand's emotional range. -Find the moments where personality and unexpected polish would turn a functional interface into one users remember and tell other people about. Add only where the moment earns it; delight everywhere reads as noise. +Make the experience memorable at moments that earn it. Delight is not a layer of generic whimsy; it is product character revealed through a useful interaction, a humane response, or an unexpectedly considered detail. --- -## Register +## Visitor mode -Persuade + Experience: delight can be distributed across copy voice, section transitions, discovery rewards, seasonal touches, personality across the whole surface. +- **Persuade + Experience:** personality may run through voice, composition, motion, and discovery, provided the artifact remains the focus. +- **Operate + Read:** concentrate delight at meaningful moments such as first use, completion, recovery, or mastery. Reliability carries everything else. -Operate + Read: delight at specific moments, not pages. Completion, first-time actions, error recovery, milestone crossings. Reliability and consistency carry the rest of the experience; delight pushed everywhere reads as noise. +## Find the opportunity ---- +Inspect the target, DESIGN.md, product voice, repeated-use frequency, and emotional context. Look for: -## Assess Delight Opportunities +- effort worth acknowledging; +- waiting that can become informative; +- an empty or first-use state that can orient; +- an error or recovery moment that needs empathy; +- an interaction whose physical or verbal response could express the brand; +- a useful capability people might enjoy discovering. -Identify where delight would enhance (not distract from) the experience: +Do not manufacture a celebration for an ordinary click. Ask only when the brand's emotional range or the stakes cannot be inferred. -1. **Find natural delight moments**: - - **Success states**: Completed actions (save, send, publish) - - **Empty states**: First-time experiences, onboarding - - **Loading states**: Waiting periods that could be entertaining - - **Achievements**: Milestones, streaks, completions - - **Interactions**: Hover states, clicks, drags - - **Errors**: Softening frustrating moments - - **Easter eggs**: Hidden discoveries for curious users +## Define one delight thesis -2. **Understand the context**: - - What's the brand personality? (Playful? Professional? Quirky? Elegant?) - - Who's the audience? (Tech-savvy? Creative? Corporate?) - - What's the emotional context? (Accomplishment? Exploration? Frustration?) - - What's appropriate? (Banking app ≠ gaming app) +State in one sentence what the user should feel and why that feeling belongs to this product. Then choose the smallest system that can deliver it: -3. **Define delight strategy**: - - **Subtle sophistication**: Refined micro-interactions (luxury brands) - - **Playful personality**: Whimsical illustrations and copy (consumer apps) - - **Helpful surprises**: Anticipating needs before users ask (productivity tools) - - **Sensory richness**: Satisfying sounds, smooth animations (creative tools) +- a distinctive response to a meaningful action; +- product-specific language that clarifies while carrying voice; +- an interaction or transition with a recognizable material behavior; +- an illustration, sound, haptic, or environmental detail grounded in the product world; +- a discovery reward that reveals real utility. -If any of these are unclear from the codebase, ask the user directly to clarify what you cannot infer. +Derive the treatment from product mechanism and visual world, not a stock catalog. -**CRITICAL**: Delight should enhance usability, never obscure it. If users notice the delight more than accomplishing their goal, you've gone too far. +## Build for the emotional moment -## Delight Principles +- **Success:** match the response to the effort and consequence. Major milestones can expand; routine saves should simply feel certain. +- **Waiting:** show truthful progress, useful context, or product-specific activity. Never fake work or delay completion to stage a flourish. +- **Empty and first use:** make the next action clear before adding personality. +- **Error and recovery:** lead with the problem and recovery. Warmth may reduce stress; jokes must not trivialize loss, money, privacy, or blocked work. +- **Repeated interaction:** keep the response satisfying after the hundredth use. Variation is useful only when it remains coherent and predictable enough to trust. +- **Discovery:** reward curiosity without hiding required functionality. -Follow these guidelines: +Copy must use the product's language. Generic whimsy is worse than neutral clarity. -### Delight Amplifies, Never Blocks -- Delight moments should be quick (< 1 second) -- Never delay core functionality for delight -- Make delight skippable or subtle -- Respect user's time and task focus +## Protect the experience -### Surprise and Discovery -- Hide delightful details for users to discover -- Reward exploration and curiosity -- Don't announce every delight moment -- Let users share discoveries with others +Delight must not: -### Appropriate to Context -- Match delight to emotional moment (celebrate success, empathize with errors) -- Respect the user's state (don't be playful during critical errors) -- Match brand personality and audience expectations -- Cultural sensitivity (what's delightful varies by culture) +- delay, block, or obscure the primary task; +- override platform conventions or accessibility; +- add unrequested factual claims; +- play sound without consent or ignore mute settings; +- become mandatory, unskippable, or exhausting on repeat; +- add a dependency or asset cost disproportionate to the moment. -### Compound Over Time -- Delight should remain fresh with repeated use -- Vary responses (not same animation every time) -- Reveal deeper layers with continued use -- Build anticipation through patterns +For authored motion, load [animate.md](animate.md). Respect reduced motion, screen readers, keyboard use, touch, localization, and cultural context. Nonessential loops stop when hidden. Make celebration intensity proportional to frequency and consequence. -## Delight Techniques +## Verify -Add personality and joy through these methods: +- The moment is specific enough that a neighboring product could not use it unchanged. +- It improves comprehension, confidence, motivation, or emotional recovery. +- The interface remains fast and obvious without the flourish. +- Repetition does not turn charm into friction. +- Reduced-motion, muted, keyboard, touch, and localized paths work. +- The result feels like the selected world, not a generic “delight” treatment. -### Micro-interactions & Animation - -**Button delight**: -```css -/* Satisfying button press */ -.button { - transition: transform 0.1s, box-shadow 0.1s; -} -.button:active { - transform: translateY(2px); - box-shadow: 0 2px 4px rgba(0,0,0,0.2); -} - -/* Ripple effect on click */ -/* Smooth lift on hover */ -.button:hover { - transform: translateY(-2px); - transition: transform 0.2s cubic-bezier(0.25, 1, 0.5, 1); /* ease-out-quart */ -} -``` - -**Loading delight**: -- Playful loading animations (not just spinners) -- Personality in loading messages (write product-specific ones, not generic AI filler) -- Progress indication with encouraging messages -- Skeleton screens with subtle animations - -**Success animations**: -- Checkmark draw animation -- Confetti burst for major achievements -- Gentle scale + fade for confirmation -- Satisfying sound effects (subtle) - -**Hover surprises**: -- Icons that animate on hover -- Color shifts or glow effects -- Tooltip reveals with personality -- Cursor changes (custom cursors for branded experiences) - -### Personality in Copy - -**Playful error messages**: -``` -"Error 404" -"This page is playing hide and seek. (And winning)" - -"Connection failed" -"Looks like the internet took a coffee break. Want to retry?" -``` - -**Encouraging empty states**: -``` -"No projects" -"Your canvas awaits. Create something amazing." - -"No saved articles" -"Nothing on the nightstand yet. Save a story for later." -``` - -**Playful labels & tooltips**: -``` -"Delete" -"Send to void" (for playful brand) - -"Help" -"Rescue me" (tooltip) -``` - -**IMPORTANT**: Match copy personality to brand. Banks shouldn't be wacky, but they can be warm. - -### Illustrations & Visual Personality - -**Custom illustrations**: -- Empty state illustrations (not stock icons) -- Error state illustrations (friendly monsters, quirky characters) -- Loading state illustrations (animated characters) -- Success state illustrations (celebrations) - -**Icon personality**: -- Custom icon set matching brand personality -- Animated icons (subtle motion on hover/click) -- Illustrative icons (more detailed than generic) -- Consistent style across all icons - -**Background effects**: -- Subtle particle effects -- Gradient mesh backgrounds -- Geometric patterns -- Parallax depth -- Time-of-day themes (morning vs night) - -### Satisfying Interactions - -**Drag and drop delight**: -- Lift effect on drag (shadow, scale) -- Snap animation when dropped -- Satisfying placement sound -- Undo toast ("Dropped in wrong place? [Undo]") - -**Toggle switches**: -- Smooth slide with spring physics -- Color transition -- Haptic feedback on mobile -- Optional sound effect - -**Progress & achievements** (Operate surfaces with recurring tasks only; gamification on editorial, portfolio, or one-visit surfaces reads as noise): -- Streak counters with celebratory milestones -- Progress bars that "celebrate" at 100% -- Badge unlocks with animation -- Playful stats ("You're on fire! 5 days in a row") - -**Form interactions**: -- Input fields that animate on focus -- Checkboxes with a satisfying scale pulse when checked -- Success state that celebrates valid input -- Auto-grow textareas - -### Sound Design - -**Subtle audio cues** (when appropriate): -- Notification sounds (distinctive but not annoying) -- Success sounds (satisfying "ding") -- Error sounds (empathetic, not harsh) -- Typing sounds for chat/messaging -- Ambient background audio (very subtle) - -**IMPORTANT**: -- Respect system sound settings -- Provide mute option -- Keep volumes quiet (subtle cues, not alarms) -- Don't play on every interaction (sound fatigue is real) - -### Easter Eggs & Hidden Delights - -**Discovery rewards**: -- Konami code unlocks special theme -- Hidden keyboard shortcuts (Cmd+K for special features) -- Hover reveals on logos or illustrations -- Alt text jokes on images (for screen reader users too!) -- A view-source or console colophon (a note on the typefaces, a thank-you, a hiring line) - -**Seasonal touches**: -- Holiday themes (subtle, tasteful) -- Seasonal color shifts -- Weather-based variations -- Time-based changes (dark at night, light during day) - -**Contextual personality**: -- Different messages based on time of day -- Responses to specific user actions -- Randomized variations (not same every time) -- Progressive reveals with continued use - -### Loading & Waiting States - -**Make waiting engaging**: -- Interesting loading messages that rotate -- Progress bars with personality -- Mini-games during long loads -- Fun facts or tips while waiting -- Countdown with encouraging messages - -``` -Loading messages: write ones specific to your product, not generic AI filler: -- "Syncing with your team's changes..." -- "Fetching this week's issue..." -- "Developing your photos..." -- "Checking tomorrow's tide tables..." -``` - -**WARNING**: Avoid cliched loading messages like "Herding pixels", "Teaching robots to dance", "Consulting the magic 8-ball", "Counting backwards from infinity". These are AI-slop copy, instantly recognizable as machine-generated. Write messages that are specific to what your product actually does. - -### Celebration Moments - -**Success celebrations**: -- Confetti for major milestones -- Animated checkmarks for completions -- Progress bar celebrations at 100% -- "Achievement unlocked" style notifications -- Personalized messages ("You published your 10th article!") - -**Milestone recognition** (same scoping as Progress & achievements: Operate surfaces with recurring tasks): -- First-time actions get special treatment -- Streak tracking and celebration -- Progress toward goals -- Anniversary celebrations - -## Implementation Patterns - -**Animation libraries**: -- Framer Motion (React) -- GSAP (universal) -- Lottie (After Effects animations) -- Canvas confetti (party effects) - -**Sound libraries**: -- Howler.js (audio management) -- Use-sound (React hook) - -**Physics libraries**: -- React Spring (spring physics) -- Popmotion (animation primitives) - -**IMPORTANT**: File size matters. Compress images, optimize animations, lazy load delight features. - -**NEVER**: -- Delay core functionality for delight -- Force users through delightful moments (make skippable) -- Use delight to hide poor UX -- Overdo it (less is more) -- Ignore accessibility (animate responsibly, provide alternatives) -- Make every interaction delightful (special moments should be special) -- Sacrifice performance for delight -- Be inappropriate for context (read the room) - -## Verify Delight Quality - -Test that delight actually delights: - -- **User reactions**: Do users smile? Share screenshots? -- **Doesn't annoy**: Still pleasant after 100th time? -- **Doesn't block**: Can users opt out or skip? -- **Performant**: No jank, no slowdown -- **Appropriate**: Matches brand and context -- **Accessible**: Works with reduced motion, screen readers - -When the moments feel earned, hand off to `/impeccable polish` for the final pass. +When the personality feels earned, hand off to `/impeccable polish` for the final pass. diff --git a/.trae/skills/impeccable/reference/document.md b/.trae/skills/impeccable/reference/document.md index 0d38eb568..121a00ab1 100644 --- a/.trae/skills/impeccable/reference/document.md +++ b/.trae/skills/impeccable/reference/document.md @@ -1,6 +1,6 @@ Generate a `DESIGN.md` file at the project root that captures the current visual design system, so AI agents generating new screens stay on-brand. -DESIGN.md follows the [official DESIGN.md format spec](https://raw.githubusercontent.com/google-labs-code/design.md/main/docs/spec.md): YAML frontmatter carrying machine-readable design tokens, followed by a markdown body with exactly six sections in a fixed order. **Tokens are normative; prose provides context for how to apply them.** Sections may be omitted when not relevant, but **do not reorder them and do not rename them**. Section headers must match the spec character-for-character so the file stays parseable by other DESIGN.md-aware tools (Stitch itself, awesome-design-md, skill-rest, etc.). +DESIGN.md follows the [official DESIGN.md format spec](https://raw.githubusercontent.com/google-labs-code/design.md/main/docs/spec.md): optional YAML frontmatter carrying machine-readable design tokens, followed by up to eight markdown sections in a fixed order. **Tokens are normative; prose provides context for how to apply them.** Sections may be omitted when not relevant, but those present stay in the specified order. Use the canonical headings below so the file remains portable across DESIGN.md-aware tools. ## The frontmatter: token schema @@ -43,26 +43,28 @@ components: 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. +- **Colors accept any valid CSS color string.** Hex is the recommended default for portability, but preserve an incumbent `rgb()`, `hsl()`, `oklch()`, wide-gamut, or mixed-color value when it is the project's normative source. 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 (`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) +## The markdown body: eight sections (canonical order) 1. `## Overview` 2. `## Colors` 3. `## Typography` -4. `## Elevation` -5. `## Components` -6. `## Do's and Don'ts` +4. `## Layout` +5. `## Elevation & Depth` +6. `## Shapes` +7. `## Components` +8. `## Do's and Don'ts` -Optional evocative subtitles are allowed in the form `## 2. Colors: The [Name] Palette` (Stitch's own outputs do this), but the literal word in each header (Overview, Colors, Typography, Elevation, Components, Do's and Don'ts) must be present. Do NOT add extra top-level sections (Layout Principles, Responsive Behavior, Motion, Agent Prompt Guide). Fold that content into the six spec sections where it naturally belongs. +Omit irrelevant sections rather than filling them with invented rules. Put responsive layout in Layout, depth in Elevation & Depth, radius and form language in Shapes, and per-component behavior in Components. Unknown sections are preserved by the format, but new visual guidance should use the canonical structure whenever it fits. ## When to run -- 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. +- New-work found a coherent incumbent visual system but no `DESIGN.md`. +- The first implementation of a new world is complete and its provisional decisions need to be carbonized. - An existing `DESIGN.md` is stale (the design has drifted). - Before a large redesign, to capture the current state as a reference. @@ -71,9 +73,9 @@ 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 init, nothing built yet). Interview for five high-level answers, write a minimal DESIGN.md marked ``. Re-run in scan mode once there's code. +- **Seed mode**: the project is pre-implementation. Ensure PRODUCT.md exists, then reuse new-work's visual-world workshop and write its directional DESIGN.md 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. +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` requests new-work's world workshop, but it does not authorize replacing coherent code: when an incumbent system exists, offer scan mode or route an explicit identity-replacement request through new-work. ## Scan mode (approach C: auto-extract, then confirm descriptive language) @@ -97,7 +99,8 @@ Build a structured draft from the discovered tokens. For each token class: - **Typography**: Map observed sizes and weights to the Material hierarchy (display / headline / title / body / label). Note font-family stacks and the scale ratio. - **Elevation**: Catalogue the shadow vocabulary. If the project is flat and uses tonal layering instead, that's a valid answer; state it explicitly. - **Components**: For each common component (button, card, input, chip, list item, tooltip, nav), extract shape (radius), color assignment, hover/focus treatment, internal padding. -- **Spacing + layout**: Fold into Overview or relevant Components. The spec does NOT have a Layout section. +- **Layout + spacing**: Extract grid, container, breakpoint, rhythm, and density behavior into Layout. +- **Shapes**: Extract radius, corner, border, clipping, and recurring form behavior into Shapes. ### Step 2b: Stage the frontmatter @@ -112,19 +115,19 @@ Skip anything the project doesn't have. Empty scale keys or fabricated tokens po ### Step 3: Ask the user for qualitative language -The following require creative input that cannot be auto-extracted. Group them into one `AskUserQuestion` interaction: +The following require creative input that cannot be auto-extracted. Ask them in two structured rounds of no more than three questions each (or the harness's lower limit), waiting between rounds: - **Creative North Star**: a single named metaphor for the whole system ("The Editorial Sanctuary", "The Golden State Curator", "The Lab Notebook"). Offer 2-3 options that honor PRODUCT.md's brand personality. -- **Overview voice**: mood adjectives, aesthetic philosophy in 2-3 sentences, anti-references (what the system should not feel like). +- **Overview voice**: mood adjectives, aesthetic philosophy in 2-3 sentences, and any confirmed visual anti-reference. - **Color character** (for auto-extracted colors): descriptive names ("Deep Muted Teal-Navy", not "blue-800"). Suggest 2-3 options per key color based on hue/saturation. - **Elevation philosophy**: flat/layered/lifted. If shadows exist, is their role ambient or structural? - **Component philosophy**: the feel of buttons, cards, inputs in one phrase ("tactile and confident" vs. "refined and restrained"). -Quote a line from PRODUCT.md when possible so the user sees their own strategic language carry forward. +Carry a line from PRODUCT.md only when it is a durable brand commitment that actually constrains the visual system. Page strategy and surface concepts do not belong here. ### Step 4: Write DESIGN.md -The file opens with the YAML frontmatter staged in Step 2b (schema documented at the top of this reference), then the markdown body using the structure below. Headers must match character-for-character. Optional evocative subtitles (e.g. `## 2. Colors: The Coastal Palette`) are allowed. +The file opens with the YAML frontmatter staged in Step 2b (schema documented at the top of this reference), then the markdown body using the canonical structure below. ```markdown --- @@ -136,13 +139,13 @@ colors: # Design System: [Project Title] -## 1. Overview +## Overview **Creative North Star: "[Named metaphor in quotes]"** -[2-3 paragraph holistic description: personality, density, aesthetic philosophy. Start from the North Star and work outward. State what this system explicitly rejects (pulled from PRODUCT.md's anti-references). End with a short **Key Characteristics:** bullet list.] +[2-3 paragraph holistic description: personality, density, and aesthetic philosophy. Start from the North Star and work outward. State only confirmed visual rejections. End with a short **Key Characteristics:** bullet list.] -## 2. Colors +## Colors [Describe the palette character in one sentence.] @@ -162,7 +165,7 @@ colors: ### Named Rules (optional, powerful) **The [Rule Name] Rule.** [Short, forceful prohibition or doctrine, e.g. "The One Voice Rule. The primary accent is used on ≤10% of any given screen. Its rarity is the point."] -## 3. Typography +## Typography **Display Font:** [Family] (with [fallback]) **Body Font:** [Family] (with [fallback]) @@ -180,7 +183,11 @@ colors: ### Named Rules (optional) **The [Rule Name] Rule.** [Short doctrine about type use.] -## 4. Elevation +## Layout + +[Describe the grid or spatial model, container behavior, density, responsive changes, and the spacing rhythm. Include exact values only when observed.] + +## Elevation & Depth [One paragraph: does this system use shadows, tonal layering, or a hybrid? If "no shadows", say so explicitly and describe how depth is conveyed instead.] @@ -191,7 +198,11 @@ colors: ### Named Rules (optional) **The [Rule Name] Rule.** [e.g. "The Flat-By-Default Rule. Surfaces are flat at rest. Shadows appear only as a response to state (hover, elevation, focus)."] -## 5. Components +## Shapes + +[Describe the form language: corner/radius strategy, borders, clipping, and any recurring silhouette or geometry.] + +## Components For each component, lead with a short character line, then specify shape, color assignment, states, and any distinctive behavior. @@ -223,16 +234,16 @@ For each component, lead with a short character line, then specify shape, color ### [Signature Component] (optional; if the project has a distinctive custom component worth documenting) [Description.] -## 6. Do's and Don'ts +## Do's and Don'ts -Concrete, forceful guardrails. Lead each with "Do" or "Don't". Be specific: include exact colors, pixel values, and named anti-patterns the user mentioned in PRODUCT.md. **Every anti-reference in PRODUCT.md should show up here as a "Don't" with the same language**, so the visual spec carries the strategic line through. Quote PRODUCT.md directly where possible: if PRODUCT.md says *"avoid dark mode with purple gradients, neon accents, glassmorphism"*, the Don'ts here should repeat that by name. +Concrete visual guardrails grounded in the incumbent implementation or the user's chosen world. Lead each with "Do" or "Don't" and include exact values only when established. Do not turn a task-specific concept or surface strategy into a system-wide prohibition. ### Do: - **Do** [specific prescription with exact values / named rule]. - **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** [...] ``` @@ -307,7 +318,7 @@ The `html` and `css` fields must be **self-contained, drop-in snippets** that re Aim for a tight set of **5-10 components** that best represent the visual system: - **Canonical primitives (always include if the project has them):** button (each variant as a separate component entry), input/text field, navigation, chip/tag, card. -- **Signature components (include if distinctive):** hero CTA, featured card, filter pill, a distinctive table or list-row treatment, any custom pattern the user mentioned as important in PRODUCT.md. +- **Signature components (include if distinctive):** the recurring custom patterns that actually define the implemented system. - **Skip the rest.** Utility components, form building blocks, wrapper layouts: not worth documenting unless visually distinctive. If the project has **no component library yet** (bare landing page, new project), synthesize canonical primitives from the tokens using best-practice defaults consistent with the DESIGN.md's rules. Every `.impeccable/design.json` has *something* to render, even on day zero. @@ -338,64 +349,40 @@ Your own write is the freshest source; subsequent commands in this session don't ## Seed mode -For projects with no visual system to extract yet. Produces a minimal scaffold, not a full spec. +For projects with no visual system to extract yet. Produces a user-chosen visual-world scaffold, not a fabricated token spec. -### Step 1: Confirm seed mode +### Step 1: Route through new-work's workshop -Before interviewing: "There's no existing visual system to scan. I'll ask five quick questions to seed a starter DESIGN.md. You can re-run `/impeccable document` once there's code, to capture the real tokens and components. OK?" +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 the user prefers to skip, stop. No file. +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. -### Step 2: Five questions +If new-work already completed the workshop in this session, use its chosen direction directly. Do not ask again. -Group into one `AskUserQuestion` interaction. Options must be concrete. +### Step 2: Write seed DESIGN.md -1. **Color strategy.** Pick one: - - Restrained: tinted neutrals + one accent ≤10% - - Committed: one saturated color carries 30–60% of the surface - - Full palette: 3–4 named color roles, each deliberate - - Drenched: the surface IS the color - - Then: one hue family or anchor reference ("deep teal", "mustard", "Klim #ff4500 orange"). - -2. **Typography direction.** Pick one (specific fonts come later): - - Serif display + sans body - - Single sans (warm / technical / geometric / humanist; pick a feel) - - Display + mono - - Mono-forward - - Editorial script + sans - -3. **Motion energy.** Pick one: - - Restrained: state changes only - - Responsive: feedback + transitions, no choreography - - Choreographed: orchestrated entrances, scroll-driven sequences - -4. **Three named references.** Brands, products, printed objects. Not adjectives. - -5. **One anti-reference.** What it should NOT feel like. Also named. - -### Step 3: Write seed DESIGN.md - -Use the six-section spec from Scan mode. Populate what the interview answers; leave the rest as honest placeholders. The seed is a scaffold, not a fabricated spec. +Use the canonical section order from Scan mode. Populate the selected workshop direction and leave unresolved implementation facts as honest placeholders. The seed commits a world and its invariants; it does not pretend implementation tokens already exist. Lead the file with: ```markdown - + ``` Per-section guidance in seed mode: -- **Overview**: Creative North Star and philosophy phrased from the answers (color strategy + motion energy + references). Reference the user's anti-reference directly. -- **Colors**: Color strategy as a Named Rule (e.g. *"The Drenched Rule. The surface IS the color."*). Hue family or anchor reference. No hex values; mark as `[to be resolved during implementation]`. -- **Typography**: the direction the user picked (e.g. "Serif display + sans body"). No font names yet: `[font pairing to be chosen at implementation]`. -- **Elevation**: inferred from motion energy. Restrained/Responsive → flat by default; Choreographed → layered. One sentence. +- **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. +- **Elevation & Depth**: the selected material and depth behavior, stated as an invariant rather than inferred from a generic preset. +- **Shapes**: the selected form and corner language. - **Components**: omit entirely; no components exist yet. -- **Do's and Don'ts**: carry PRODUCT.md's anti-references directly plus the anti-reference named in Q5. +- **Do's and Don'ts**: record the durable guardrails confirmed during the world choice, not task-local refusals. 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 +### Step 3: 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." @@ -405,15 +392,15 @@ Your own write is the freshest source; no reload needed. ## Style guidelines - **Frontmatter first, prose second.** Tokens go in the YAML frontmatter; prose contextualizes them. Don't redefine a token value in two places; the frontmatter is normative. -- **Cite PRODUCT.md anti-references by name** in the Do's and Don'ts section. If PRODUCT.md lists "SaaS landing-page clichés" or "generic AI tool marketing" as anti-references, the DESIGN.md Don'ts should repeat those phrases verbatim so the visual spec enforces the strategic line. -- **Match the spec, don't invent new sections.** The six section names are fixed. If you have Layout/Motion/Responsive content to document, fold it into Overview (philosophy-level rules) or Components (per-component behavior). +- **Carry only durable product constraints.** A binding logo, identity asset, accessibility need, or brand commitment from PRODUCT.md may constrain DESIGN.md. Surface strategy stays in its surface brief. +- **Match the spec.** Use its eight canonical sections in order and omit any that are irrelevant. Put motion guidance with the world or component it affects rather than creating a token group the schema does not support. - **Descriptive > technical**: "Gently curved edges (8px radius)" > "rounded-lg". Include the technical value in parens, lead with the description. - **Functional > decorative**: for each token, explain WHERE and WHY it's used, not just WHAT it is. - **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 forceful**. The voice of a design director. "Prohibited", "forbidden", "never", "always", not "consider", "might", "prefer". Match PRODUCT.md's tone. -- **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. -- **Reference PRODUCT.md**. The anti-references section of PRODUCT.md should directly inform the Do's and Don'ts section here. Quote or paraphrase. +- **Be decisive where evidence is decisive.** Use hard language for actual invariants and softer language for provisional guidance. +- **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. ## Pitfalls @@ -423,7 +410,7 @@ Your own write is the freshest source; no reload needed. - Don't invent components that don't exist. If the project only has buttons and cards, only document those. - Don't overwrite an existing DESIGN.md without asking. - Don't duplicate content from PRODUCT.md. DESIGN.md is strictly visual. -- Don't add a "Layout Principles" or "Motion" or "Responsive Behavior" top-level section. The spec has six, not nine. Fold that content where it belongs. +- Don't replace canonical sections with near-synonyms. Put layout and responsive behavior in `Layout`; put motion with the affected world or component. - Don't rename sections even slightly. "Colors" not "Color Palette & Roles". "Typography" not "Typography Rules". Tooling parsing depends on exact headers. - Don't duplicate token values between frontmatter and prose. If a color is in `colors.primary` as hex, the prose can name it and describe its role but should not reassert a different hex. The frontmatter is normative. - Don't invent frontmatter token groups outside Stitch's schema (no `motion:`, `breakpoints:`, `shadows:` at the top level). Stitch's Zod schema only accepts `colors`, `typography`, `rounded`, `spacing`, `components`. Anything else belongs in the sidecar's `extensions`. diff --git a/.trae/skills/impeccable/reference/hooks.md b/.trae/skills/impeccable/reference/hooks.md index 2a547f360..9e3afd258 100644 --- a/.trae/skills/impeccable/reference/hooks.md +++ b/.trae/skills/impeccable/reference/hooks.md @@ -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. diff --git a/.trae/skills/impeccable/reference/init.md b/.trae/skills/impeccable/reference/init.md index 499f60055..d3e3a1fbd 100644 --- a/.trae/skills/impeccable/reference/init.md +++ b/.trae/skills/impeccable/reference/init.md @@ -1,224 +1,114 @@ -# Init Flow +# Init flow -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 [DESIGN.md format spec](https://raw.githubusercontent.com/google-labs-code/design.md/main/docs/spec.md). Answers "how it looks". -- **`.impeccable/live/config.json`** (live mode): pre-configured so `/impeccable live` boots straight into variant mode with no first-time detour. - -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. +`init` captures durable product truth in PRODUCT.md. It does not invent a visual world and does not write DESIGN.md; [new-work.md](new-work.md) creates or expands one, and [document.md](document.md) records an incumbent one. Existing runnable web projects may also receive `.impeccable/live/config.json`. ## Step 1: Load current state -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). +Use the PRODUCT.md path resolved by context.mjs. Update it instead of creating a competing authority. In a child app inheriting root context, confirm shared versus app-specific scope before writing. -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. -- **PRODUCT.md exists, DESIGN.md missing**: skip to Step 5 and offer to run `/impeccable document` for DESIGN.md. -- **PRODUCT.md exists but has no `## Register` section (legacy)**: add it. Infer a hypothesis from the codebase (see Step 2), confirm with the user, write the field. -- **PRODUCT.md exists but has no `## Platform` section (legacy)**: add it the same way, but only when the project is native (`ios` / `android` / `adaptive`) or the user wants it explicit; a missing field already means `web`. -- **Both exist**: ask the user directly to clarify what you cannot infer. Ask which file to refresh. Skip the one the user doesn't want changed. -- **Just DESIGN.md exists (unusual)**: do Steps 2-4 to produce PRODUCT.md. +- **No PRODUCT.md:** explore, interview, and write it. +- **PRODUCT.md exists:** ask what product knowledge is stale or missing; do not reopen confirmed fields without a reason. +- **Legacy PRODUCT.md:** add only durable missing facts; absent `## Platform` means `web` unless evidence says otherwise. +- **Only DESIGN.md exists:** leave it untouched and create PRODUCT.md. +- **Redesign/rebrand request:** preserve confirmed product truth unless the user changes it. Visual replacement happens later in new-work, not here. -Never silently overwrite an existing file. Always confirm first. +Never silently overwrite an existing file or offer DESIGN.md during init. If another request invoked init, finish PRODUCT.md and resume it. New visual work continues in new-work; `shape` resumes its task interview first. -If init was invoked as a setup blocker by another command, such as `/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 project -## Step 2: Explore the codebase +Before asking, scan enough to avoid making the user repeat known facts: product docs and copy; package/config and app boundaries; features, workflows, routes, and roles; names, logos, legal/proof assets, and brand commitments; platform/accessibility signals; and the dev command/entry when live mode applies. -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: +Treat repository evidence as a hypothesis, not user approval. Note visual maturity without documenting, extending, or replacing the world. -- **README and docs**: Project purpose, target audience, any stated goals -- **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 -- **Any style guides or brand documentation** +Form a platform hypothesis: `web`, `ios`, `android`, or `adaptive` (one product that genuinely adapts its design language per OS). Mobile web remains `web`; a native wrapper around a website does not make its design language native. -Also form a **register hypothesis** from what you find: +## Step 3: Interview for product truth -- Brand signals: `/`, `/about`, `/pricing`, `/blog/*`, `/docs/*`, hero sections, big typography, scroll-driven sections, landing-page-shaped content. -- Product signals: `/app/*`, `/dashboard`, `/settings`, `/(auth)`, forms, data tables, side/top nav, app-shell components. +ask the user directly to clarify what you cannot infer. Ask only about material gaps the repository and original request do not answer with strong evidence. -Register is a hypothesis at this point, not a decision; Step 3 confirms it. +Use the structured question tool when available; otherwise ask and wait. Keep rounds to at most three focused questions and require one real answer or approval round before writing a new PRODUCT.md. Confirm inferences. -Also form a **platform hypothesis**: +Start with the unknowns that most change future product decisions: -- Native signals: React Native / Expo (`react-native`, `expo`), Flutter (`pubspec.yaml`, `flutter`), SwiftUI / UIKit (`.swift`, `.xcodeproj`, an `ios/` app target), Jetpack Compose / Android (`build.gradle`, an `android/` app module, `AndroidManifest.xml`). An `ios/` and/or `android/` directory that is a real app target, not just a Capacitor/Cordova wrapper around a website. -- Web signals (the default): a web framework (Vite, Next, Nuxt, SvelteKit, Astro), an HTML entry, a CSS/Tailwind setup, no native app target. +1. Who is the primary user, in what situation, and what job are they doing? +2. What does the product make possible, and what is its meaningfully different mechanism or position? +3. What durable constraints, assets, evidence, or product facts must future work preserve? -Values: `web` / `ios` / `android` / `adaptive` (one codebase, ships both, adapts per OS). Mobile web is still `web`. Like register, this is a hypothesis; Step 3 confirms it. +Confirm ambiguous platform separately. Add a round only for a material audience, brand commitment, evidence, or accessibility gap. Record undecided facts instead of inventing them. -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. +Do not ask for an aesthetic direction, emotional feel, visual references, colors, typography, or style during init. If the user volunteers a binding visual constraint, record it without expanding it. -## Step 3: Ask strategic questions (for PRODUCT.md) +### What belongs here -ask the user directly to clarify what you cannot infer. Ask about anything the codebase doesn't answer with strong, explicit evidence. +- users, jobs, workflows, purpose, success, positioning, and operating context; +- capabilities, constraints, terminology, evidence, platform, and accessibility; +- confirmed voice, assets, and brand commitments. -### Interview mode, not confirmation mode +### What does not belong here -If the repo is empty or the user's brief is sparse, run a short interview before proposing PRODUCT.md. Do **not** turn a one-sentence request into a complete inferred PRODUCT.md and ask for blanket confirmation. - -- Use the harness's structured question tool when one exists. Otherwise, ask directly in chat and stop: one question at a time, with lettered options where the crawl suggests likely answers, waiting for each answer before the next. -- Keep skill vocabulary (register, belief ladder, anti-references) out of question text; ask for the thing in words the user would use. For the brand register, ask like a magazine editor profiling the brand: curious and narrative, drawing out the story, the feel, and what a visitor should come to believe. -- Ask in focused rounds and wait for answers between them. Keep **one topic per question**; add rounds rather than fold several topics into one either-or choice. Options obey the same rule: an option answers only the question asked; never write a compound option that bundles a feeling with a business outcome or names an additional audience. -- Use inferred answers as hypotheses or options, not as finished facts. -- Complete at least one real user-answer round before drafting PRODUCT.md, unless every required answer is directly discoverable from repo docs. -- Round 1 should establish register, platform, users, purpose, positioning, and desired outcome. -- Round 2 should establish brand personality or references, anti-references, and accessibility needs, plus conversion & proof for Persuade surfaces. - -### Minimum viable interview - -Ask enough to complete PRODUCT.md. At minimum, cover register confirmation, **platform confirmation** (`web` / `ios` / `android` / `adaptive`), users, purpose, positioning, brand personality, anti-references, and accessibility needs (plus conversion & proof for Persuade surfaces) unless each answer is directly discoverable from repo context. Never let the template's default `web` stand unconfirmed for a native or cross-platform repo. After at least one interview round, you may propose inferred answers, but the user must confirm them before you write PRODUCT.md. Never synthesize PRODUCT.md from the original task prompt alone. - -### Register (ask first; it shapes everything below) - -Every design task serves one of four visitor modes (see SKILL.md's Registers): **Persuade** (marketing, landing, campaigns), **Experience** (portfolios, albums, bodies of work), **Operate** (app UI, admin, dashboards, tools), and **Read** (documentation, editorial, long-form). PRODUCT.md stores the two-value family that covers them: `brand` covers Persuade and Experience, `product` covers Operate and Read. The brief decides the exact mode per task; the stored register only sets the default family. - -If Step 2 produced a clear hypothesis, lead with it: *"From the codebase, this looks like a [brand / product] surface. Does that match your intent, or should we treat it differently?"* - -If the signal is genuinely split (e.g. a product with a big marketing landing), ask the user directly to clarify what you cannot infer. Ask which register describes the **primary** surface. The register can be overridden per task later, but PRODUCT.md carries one default. Settle the default before drafting any register-dependent questions; never batch Persuade-only questions (Conversion & proof) into the same round as the question that decides the register. - -### Platform (ask right after register) - -Every project targets **web** (includes responsive mobile web), **ios**, **android**, or **adaptive** (one codebase, ships both, adapts per OS: Flutter, React Native, KMP). Platform picks the native rulebook: HIG for `ios`, Material 3 for `android`, both for `adaptive`, none for `web`. - -If Step 2 produced a clear hypothesis, lead with it: *"From the codebase, this looks like a [web / ios / android / adaptive] project. Does that match?"* For cross-platform apps, decide by the **design language the app renders**, not the toolchain: one look on both platforms (Flutter's Material-everywhere default) takes that platform's value; genuine per-OS adaptation (Cupertino on iOS, Material on Android) is `adaptive`. When in doubt, `web`. - -A monorepo shipping both a website and a native app gets a PRODUCT.md per app, each with its own `## Platform`; the root PRODUCT.md carries the primary surface's platform. - -### Users & Purpose -- Who uses this? What's their context when using it? -- What job are they trying to get done? -- What is this for? A purpose stated in README or docs is a hypothesis, not strong evidence; confirm it, don't transcribe it. -- What does success look like? -- If more than one kind of user is plausible, confirm a primary and secondary audience; don't manufacture a split that isn't there. An audience implied by another answer (a success metric, a CTA) is still unconfirmed; ask before writing it as secondary. -- If the surface speaks to a different audience than the people who use the product, ask the user to name both. -- For brand: what emotions should the interface evoke? (confidence, delight, calm, urgency) Ask this standalone; don't fold emotions into the success question. -- For product: what workflow are they in? What's the primary task on any given screen? - -### Positioning -- In one line, what does this do that nothing else does? The single strategic claim every screen reinforces. - -### 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? - - 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? - -### Conversion & proof (Persuade surfaces only) - -Ask these only when the primary surface is a Persuade one (marketing, landing, campaigns). Experience and Read surfaces (portfolios, albums, long-form writing, documentation) get no CTA, belief-ladder, or proof questions even when the stored register is `brand`; skip this section and its PRODUCT.md counterpart. - -- What's the primary CTA? -- What's the secondary fallback, for visitors not ready for the primary? -- The one line a visitor should remember after 10 seconds. -- What must the visitor believe, in order, before taking the primary CTA? (The template's belief ladder.) -- What proof is on hand? Ask the user to hand over any testimonials, case studies, press, or client/partner logos they already have. If you can receive files directly, collect them; otherwise create `.impeccable/assets/proof/` and ask the user to add files there. Reference supplied files by path; record text proof inline. - -### Accessibility & Inclusion -- Specific accessibility requirements? (WCAG level, known user needs) -- Considerations for reduced motion, color blindness, or other accommodations? - -Skip questions where the answer is already clear. **Do NOT ask about colors, fonts, radii, or visual styling here.** Those belong in DESIGN.md, not PRODUCT.md. +- visual worlds, palettes, typography, components, or page concepts; +- visitor mode, narrative, CTA/proof sequence, or other surface strategy; +- invented testimonials, customers, benchmarks, pricing, licensing, or deployment claims; +- a requirement to decide every optional field. ## Step 4: Write PRODUCT.md -Write PRODUCT.md only after the user has confirmed the strategic answers from Step 3. If an inferred answer is uncertain or unconfirmed, ask before writing. Confirmed means what the user actually said yes to; do not pad a confirmed answer with extras they never picked (additional anti-references, audiences, roadmap claims, a WCAG level), whether drawn from the crawl, another answer, or your own option text. If an extra belongs in the doc, ask about it first. - -Synthesize into a strategic document: +Write only confirmed facts and explicitly marked open decisions. Omit irrelevant sections rather than filling them with generic prose. ```markdown # Product -## Register - -product - ## Platform web ## Users -[Who they are, their context, the job to be done. Primary audience; a secondary audience or a surface-vs-user split only when they apply.] +[Primary users, their situation, and job. Add other audiences only when confirmed.] ## Product Purpose -[What this product does, why it exists, what success looks like] +[What the product does, why it exists, and what success means.] ## Positioning -[The single strategic claim every screen reinforces. Not a visual rule, not an anti-reference.] +[The product mechanism or claim a neighboring product could not truthfully copy.] -## Conversion & proof -[Persuade surfaces only (marketing, landing, campaigns). Omit this section entirely, heading included, for the product register and for Experience or Read surfaces such as portfolios, albums, or long-form writing.] -- Primary and secondary CTA: [...] -- The line a visitor remembers after 10 seconds: [...] -- Belief ladder: [...] -- Proof on hand: [testimonials, case studies, press, or logos, referenced by path] +## Operating Context +[Workflows, environments, tools, documents, materials, and rituals that are factual parts of using or evaluating the product.] -## Brand Personality -[Voice, tone, 3-word personality, emotional goals] +## Capabilities and Constraints +[Confirmed functionality, technical constraints, terminology, and explicitly undecided product facts.] -## Anti-references -[What this should NOT look like. Specific bad-example sites or patterns to avoid.] +## Brand Commitments +[Existing name, voice, assets, personality, identity constraints, and references the user explicitly made binding. Omit when none exist.] -## Design Principles -[3-5 strategic principles derived from the conversation. Principles like "practice what you preach", "show, don't tell", "expert confidence". NOT visual rules like "use OKLCH" or "magenta accent".] +## Evidence on Hand +[Real content, data, demonstrations, testimonials, case studies, press, or assets, with paths where applicable. State absences that future work must not fabricate.] + +## Product Principles +[Three to five durable strategic principles derived from confirmed answers; no visual recipes.] ## Accessibility & Inclusion -[WCAG level, known user needs, considerations] +[Known user needs or required standard. Omit when no product-specific requirement was established.] ``` -Register is either `brand` or `product` as a bare value. No prose, no commentary. Platform is `web`, `ios`, `android`, or `adaptive`, also a bare value; omit the section only on legacy files you're leaving untouched, otherwise write `web` explicitly. +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. -Write fields as prose, and use bold sparingly: only where a word carries a decision, never as a label lead-in on every line. +### Completion gate -Write to `PROJECT_ROOT/PRODUCT.md`. If `.impeccable.md` existed, the loader already renamed it; merge into that content rather than starting from scratch. +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: Decide on DESIGN.md +## Step 5: Configure live mode when useful -Offer `/impeccable document` either way. Two paths: +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. -- **Code exists** (CSS tokens, components, a running site): "I can generate a DESIGN.md that captures your visual system (colors, typography, components) so variants stay on-brand. Want to do that now?" -- **Pre-implementation** (empty project): "I can seed a starter DESIGN.md from five quick questions about color strategy, type direction, motion energy, and references. You can re-run once there's code, to capture the real tokens. Want to do that now?" +## Step 6: Wrap up or resume -If the user agrees, delegate to `/impeccable document` (it auto-detects scan vs seed). Load its reference and follow that flow. +Summarize captured and deliberately undecided facts. Do not offer DESIGN.md merely because it is missing. -If the user prefers to skip, mention they can run `/impeccable document` any time later. +Recommend the next action from the actual project state: -## Step 6: Configure live mode (when code exists) +- Empty or early project: ask naturally for the surface to be built, or use `/impeccable shape ` when the user wants a confirmed brief without implementation. New-work will establish a visual world only when the requested work needs one. +- Existing coherent interface without DESIGN.md: `/impeccable document` if the user wants the incumbent system recorded independently of a new build. +- Existing surface needing work: name the most relevant scoped command. +- Web project ready for visual iteration: `/impeccable live` when configured. -**Skip this step when the platform is native** (`ios` / `android` / `adaptive`): live mode drives a browser overlay. A hybrid wrapper or Expo web target serving HTML doesn't change that. - -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 .trae/skills/impeccable/scripts/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) and platform captured (web / ios / android / adaptive) -- 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 or live config is pending, one line on how to set it up later - -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 platform**, offer the 2-4 most relevant (not a menu dump), and give the exact command to type. Group by intent: - -- **Build something new**: `/impeccable craft ` (shape, then build end-to-end) or `/impeccable shape ` (plan first). Lead with this for empty or early-stage projects. -- **Improve what's there**: name the specific surface. `/impeccable critique ` for a scored UX review; `/impeccable audit ` for a11y / perf / responsive checks; `/impeccable polish ` 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** (web only): `/impeccable live` (configured in Step 6) to pick elements in the browser and generate variants in place. **Skip this group for native platforms.** - -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 the user directly to clarify what you cannot infer. Ask whether they'd like a brief summary of PRODUCT.md appended to RULES.md for easier agent reference. If yes, append a short **Design Context** pointer section there. +If init was invoked by another request, resume without rerunning context.mjs; new-work owns later visual decisions. diff --git a/.trae/skills/impeccable/reference/ios.md b/.trae/skills/impeccable/reference/ios.md index ef10ee0d2..ccef5d2c4 100644 --- a/.trae/skills/impeccable/reference/ios.md +++ b/.trae/skills/impeccable/reference/ios.md @@ -2,7 +2,7 @@ For native iOS / iPadOS apps: SwiftUI, UIKit, React Native, Expo, Flutter shipping to Apple hardware. -On native, register narrows. HIG conformance governs structure, navigation, and interaction whatever the register; brand expresses through the expressive layer the platform provides (tint, type, motion, content). Calm, Duolingo, and Spotify carry strong identity entirely inside HIG conventions. +On native, the visitor mode narrows what expression may override. HIG conformance governs structure, navigation, and interaction in every mode; brand expresses through the layer the platform leaves open (tint, type, motion, content). ## The iOS slop test diff --git a/.trae/skills/impeccable/reference/layout.md b/.trae/skills/impeccable/reference/layout.md index ecd958443..24882b90f 100644 --- a/.trae/skills/impeccable/reference/layout.md +++ b/.trae/skills/impeccable/reference/layout.md @@ -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. --- -## Register +## 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, register, 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 .trae/skills/impeccable/scripts/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 `/impeccable polish` for the final pass. +When the structure holds, hand off to `/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) * )`: 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. diff --git a/.trae/skills/impeccable/reference/live.md b/.trae/skills/impeccable/reference/live.md index c8279f19c..077f3541d 100644 --- a/.trae/skills/impeccable/reference/live.md +++ b/.trae/skills/impeccable/reference/live.md @@ -33,7 +33,7 @@ Chat is overhead. No recap, no tutorial output, no pasting PRODUCT / DESIGN bodi node .trae/skills/impeccable/scripts/live.mjs ``` -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. +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, DESIGN.md, and any surface brief already loaded by Setup in mind for variant generation: **DESIGN.md wins on visual decisions; PRODUCT.md wins on durable product and voice decisions; the surface brief wins on this surface's strategy.** 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 requires the user's explicit redesign/replacement intent. `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). @@ -187,7 +187,7 @@ All three carry `fallback: "agent-driven"`. Follow **Handle fallback** below. ### 3. Load the action's reference -If `event.action` is `impeccable` (the default freeform action), use SKILL.md's shared laws plus the loaded register reference (`brand.md` or `product.md`). Do not load a sub-command reference. **Freeform is not a pass to skip parameters:** you still follow the composition budget and the freeform bias in **§7 Parameters** below. Sub-command files list MUST-have signature knobs; freeform has no such file, so sizing knobs from surface weight and primary axes is entirely on you. +If `event.action` is `impeccable` (the default freeform action), use SKILL.md's shared laws and decide the visitor mode from the selected surface. Do not load a sub-command reference. **Freeform is not a pass to skip parameters:** you still follow the composition budget and the freeform bias in **§7 Parameters** below. Sub-command files list MUST-have signature knobs; freeform has no such file, so sizing knobs from surface weight and primary axes is entirely on you. Any other `event.action` (`bolder`, `quieter`, `distill`, `polish`, `typeset`, `colorize`, `layout`, `adapt`, `animate`, `delight`, `overdrive`): Read `reference/.md` before planning. Each sub-command encodes a specific discipline; skipping its reference produces generic output. Those files may require specific params; layer them on top of the §7 budget, not instead of it. @@ -216,7 +216,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. @@ -224,10 +224,7 @@ This sentence is the **identity lock**. Every variant must be readable as the sa **Default mode**: the existing identity is preserved. Variants vary expression axes within it. *This is the right mode for ~90% of live sessions.* The user picked an element on a real product they're shipping; they expect variants of *their* hero, not three different brands' heroes. -**Departure mode**: the existing identity is rejected. Variants propose alternatives consistent with PRODUCT.md voice. Trigger only when at least one is true: - -- PRODUCT.md anti-references explicitly call out the current surface ("the current `index.html` is itself an example"; "diffuse away from this"; "the page on screen is the failure"). Generic anti-references that describe what to avoid in general do **not** trigger departure mode; only ones that point at *this* surface specifically. -- The user's freeform prompt explicitly asks for departure ("rebuild this from scratch", "what if it weren't editorial at all", "show me something completely different"). +**Departure mode**: the existing identity is rejected. Variants propose alternatives consistent with durable product and brand truth. Trigger only when the user explicitly asks for departure in the current request or freeform prompt ("redesign this", "rebuild this from scratch", "what if it weren't editorial at all", "show me something completely different"). A stale page critique or an old task note is not replacement authorization. If you're unsure, you're in default mode. The cost of being wrong about default is "three on-brand variants with similar feel": recoverable, the user picks none. The cost of being wrong about departure is "three off-brand variants": unrecoverable, the user is annoyed. @@ -246,13 +243,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 the brand's stated voice and register in PRODUCT.md. Do NOT pick from a fixed catalog of lane categories. The right three directions for this brand are not the same as the right three for another brand, 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. @@ -262,7 +259,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. @@ -289,7 +286,7 @@ In **default mode**, the prompt narrows the axes you choose, not the identity. * In **departure mode**, the prompt narrows the lanes you draw from, not the families. *"Make it feel like a newspaper front page"* would itself be a departure-mode prompt; honor it but pick three meaningfully different newspaper-adjacent lanes (broadsheet vs. tabloid vs. trade journal), and run the family pass to confirm they don't collapse into one. -When the prompt and PRODUCT.md anti-references conflict (the prompt asks for X, the anti-references ban X), the anti-references win; they describe the brand's standing position, the prompt is one moment. +When the prompt conflicts with a confirmed binding brand commitment or DESIGN.md invariant, preserve the invariant unless the user explicitly revokes or replaces it. Task-local strategy from the matching surface brief may change when the user changes that surface's goal. ### 6. Write all variants in a single edit diff --git a/.trae/skills/impeccable/reference/new-work.md b/.trae/skills/impeccable/reference/new-work.md index 809b5bd4c..4c113e1bb 100644 --- a/.trae/skills/impeccable/reference/new-work.md +++ b/.trae/skills/impeccable/reference/new-work.md @@ -1,47 +1,178 @@ -# New identity work +# New visual work -Read this only when the project has no committed visual identity, or the user explicitly asked to replace the current one. A missing DESIGN.md is not enough: code, tokens, chosen type, components, and assets are incumbent design authority. If they exist, preserve and extend them unless the brief says to discard them. +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. -This playbook is task-scoped. PRODUCT.md owns durable product facts and DESIGN.md owns durable visual invariants; the direction for one page or feature belongs in the current task, not in either document. +## 1. Name the intent -## Choose the authority +- **Greenfield:** no coherent visual implementation. +- **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. -Classify the work before inventing anything: +A plain “redesign this page/site” authorizes replacement. “Redesign this within the current brand/system” means extension or refinement. Ask once only when the wording is genuinely ambiguous. -- **Extend**: an incumbent system exists and the task adds or improves a surface. Preserve its lineage, semantics, and affordances. -- **Surface redesign**: the current identity remains, but this surface may change its topology, hierarchy, and expression. -- **New identity**: nothing committed exists, or the user explicitly authorized a rebrand. +## 2. Resolve visual authority -When the evidence is mixed, preserve. Novelty is not permission to erase authored decisions. +Read DESIGN.md and representative code, tokens, components, and assets. Choose one path: -## Find the concept +### A. Explicit redesign -Name the subject, audience, page job, and visitor mode from SKILL.md. Then identify the product's unique mechanism: what it does, proves, or enables that a neighboring product cannot truthfully claim. +The old DESIGN.md and implementation are not authority. Keep only unrevoked product facts, content, function, native expectations, constraints, and brand commitments. Establish a replacement world. -Derive the concept from that mechanism through one primary transformation: information topology, evidence, temporal behavior, or spatial mapping. The transformation must change how the surface is organized or behaves, not merely how it is decorated. This produces stranger and more defensible work than imitating a culturally specific document or object. Borrowing a literal cultural form is allowed only when it is genuinely native to the product and audience, and never as costume. +### B. DESIGN.md covers this kind of surface -For **Operate** and **Read**, start with the task and information structure. Native controls remain native controls; expression comes from hierarchy, density, rhythm, state, and the system around them. For **Persuade** and **Experience** creating a new identity, explore more freely, but keep the product legible and the primary action obvious. +Use its invariants and normative tokens. Skip world-building and discover the surface. -Run `node .trae/skills/impeccable/scripts/concept-seed.mjs` only for Persuade or Experience new-identity work when two or more directions remain equally credible. It is an entropy source, not an authority: never let the draw overrule the strongest fit. When color is genuinely unconstrained, `node .trae/skills/impeccable/scripts/palette.mjs` may break a reflex palette; subject evidence and incumbent color still win. +### C. A coherent implementation exists but DESIGN.md does not -## State the direction +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. -Before code, write a task-scoped direction contract of at most 120 words in your reasoning: +### D. The brand exists, but a whole-surface family is unresolved -- the thesis this surface owns; -- the structural or behavioral transformation; -- what authority is preserved; -- the first viewport's composition and primary action; -- the signature element the surface will be remembered by. +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. -Do not embed this contract as a comment in the production artifact or turn it into a second design system. In an attended substantial build, confirm it once. In an unattended run, record it and continue. +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. -## Build once, commit fully +### E. No confirmed visual authority exists -Plan one compact token system and one layout source: the concept or the content's real structure. Build the strongest coherent direction once. Commitment means the transformation governs the whole surface; it does not mean every control must be rebuilt as a metaphor. +Establish a world. Scaffolds, framework defaults, and stray utilities are not identity. -Make the first viewport demonstrate the product's mechanism. Pace long surfaces through contrast in density and scale, and cut sections that only repeat claims. Briefs that depend on imagery ship real, verified imagery. Preserve semantic HTML, familiar interaction behavior, accessibility, and the project's technical conventions. +## 3. Discover the requested surface -## Finish proportionally +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. -Inspect desktop and mobile, write one honest critique against the brief and direction, patch material defects, then run the detector once. Repeat only when a real defect remains. A separate reviewer is optional when the harness already provides one and the extra cost is earned by the risk; it is not a default step and its presence is not evidence of quality. +Ask one attended round of at most three material questions without repeating durable facts. CTA hierarchy, proof sequence, content gaps, and interaction outcomes belong here, not PRODUCT.md. For a fully specified narrow request, state the interpretation and invite correction. + +When `shape` has already completed its discovery interview, reuse those confirmed answers and do not ask this round again. + +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. Each candidate joins a durable visual system (identity thesis, information/layout grammar, material and type behavior, color strategy, imagery, motion, reusable signature) to a concrete expression for this surface (topology, sequence, focal moment, and implementation consequence). Do not rank yet. +3. **Break the ranking rut once.** Run `node .trae/skills/impeccable/scripts/concept-seed.mjs --scope direction`. Promote the assigned grounded pair and weigh its challengers only when they can become both a coherent system and a strong solution to this task. +4. **Test coupling and breadth.** Reject a pair if its surface could swap into another candidate unchanged, or if its world is merely the surface motif repeated. Test each survivor on the requested surface plus navigation, quiet and dense content, interaction/state, and one unlike future surface. Compare it with both the category habit and predictable contrarian response. +5. **Offer coupled choices.** Present two or three materially different pairs without recommendation cues. For each, show the world rules, this surface's 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. 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 .trae/skills/impeccable/scripts/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 promoted candidate when a roll ran; otherwise use the strongest grounded structure. It must survive both tests. + +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). + +### Write or update DESIGN.md + +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: + +`` + +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 .trae/skills/impeccable/scripts/surface-brief.mjs read ` + +Exit 0: preserve still-valid decisions and change only what the user changed. For redesign, retain valid product strategy, content, function, and open decisions; replace visual direction and contract. Exit 2: no brief. After the contract, write with `node .trae/skills/impeccable/scripts/surface-brief.mjs write [related-target ...]`. + +The body is concise and contains: + +```markdown +# Surface brief: [name] + +## Scope +[Primary/related route or artifact, visitor mode, and what this surface owns.] + +## Product strategy +[Surface-specific audience and job, desired outcome, primary/secondary action or task, content and proof sequence, factual constraints, and explicitly open decisions.] + +## Selected direction +[Reference to the applicable DESIGN.md world or expression range, selected surface concept, focal moment, narrative/interaction sequence, and implementation consequence.] + +## Direction contract +[The six contract blocks below.] + +## Open decisions +[Only unresolved items that later work must not silently invent. Omit when empty.] +``` + +Commit `.impeccable/surfaces/.md` as stable later-work context. Exclude global truth, exact tokens, transient notes, and work logs. + +## 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. + +Before code, write a direction contract of at most 150 words in an opening HTML or framework comment. The first 200 characters must name `DIRECTION CONTRACT`. + +- `UNIQUE`: the task thesis tied to the product mechanism; +- `NOT-TEMPLATE`: the category-default arrangement this structure refuses; +- `OWN-WORLD`: the confirmed DESIGN.md invariants, tokens, and materials it uses; +- `STORY`: what the visitor understands, believes, and does; +- `FIRST VIEWPORT`: exact composition, hierarchy, action, and where the concept exceeds competent convention; +- `FORM`: chosen structure or behavior, signature, implementation consequence, and seed key. + +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. + +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. + +## 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. + +Build the strongest coherent direction once. Its grammar governs navigation, actions, controls, content, and transitions without disguising affordances. Give the focal form the scale that gives it force; do not trap it inside a standard hero panel. + +**Make the opening a thesis.** Demonstrate the mechanism immediately; leave an idea, interaction, or evidence, not merely mood. + +**Commit before correcting.** Land the hard move at full strength before refining it. In unattended work, safety is the known risk. + +**Commit at page scale.** Let color, material, image, or type own a region when the world calls for it. Scattered signature decoration is not commitment. + +**Pace the whole surface.** Vary density, scale, image, motion, and quiet inside one grammar. Cut repeated claims; prove the mechanism with real artifact, interaction, data, transformation, or content. + +**Author motion as material.** Motion expresses the world and task. Premium moments go beyond repeated transform/opacity through earned focus, depth, masks, continuity, light, or material change. Bound expensive effects, test in-browser, keep content visible by default, and design reduced motion. + +Preserve semantics, affordances, accessibility, performance, responsiveness, and project conventions. Operate/Read express through topology, hierarchy, density, rhythm, and state; Persuade/Experience may earn drama. + +## 8. Solidify the visual record + +After first implementation of a new/replacement world or approved expansion, refresh DESIGN.md from the build: + +- replace provisional direction with the exact type, color roles, tokens, spacing/radii, components, states, and motion that survived; +- add normative YAML tokens only for values the implementation actually uses; +- remove the directional-seed status once the record and implementation agree; +- preserve broader world invariants and expression ranges; +- do not promote the task's story, hero composition, or one-off motif into a global rule unless it is intentionally reusable. + +Ordinary extension does not rewrite DESIGN.md; only approved durable changes do. + +## 9. Finish like a 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. diff --git a/.trae/skills/impeccable/reference/operate.md b/.trae/skills/impeccable/reference/operate.md index 04ee9d7d2..f420cbaa2 100644 --- a/.trae/skills/impeccable/reference/operate.md +++ b/.trae/skills/impeccable/reference/operate.md @@ -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. - No orchestrated page-load sequences. Product loads into a task; users don't want to watch it load. -## Product bans (on top of the shared absolute bans) +## Product constraints - Decorative motion that doesn't convey state. - Inconsistent component vocabulary across screens. If the "save" button looks different in two places, one is wrong. @@ -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. diff --git a/.trae/skills/impeccable/reference/polish.md b/.trae/skills/impeccable/reference/polish.md index 8541f43ac..6f7a86217 100644 --- a/.trae/skills/impeccable/reference/polish.md +++ b/.trae/skills/impeccable/reference/polish.md @@ -1,241 +1,98 @@ -> **Additional context needed**: quality bar (MVP vs flagship). +> **Additional context needed**: quality bar and shipping constraints. -Perform a meticulous final pass to catch all the small details that separate good work from great work. The difference between shipped and polished. +Polish is refinement, never concealed redesign. Preserve the incumbent visual world, content, behavior, and everything outside scope. If the concept itself is wrong, say so and recommend redesign or `bolder` instead of smuggling in a replacement. -Detector and automated QA output are defect evidence only. A clean script result is never proof that the design is strong; gather browser evidence and inspect the real interaction path. +A detector result is defect evidence, not proof of quality. Inspect the rendered experience and real interaction path. -## Design System Discovery +## 1. Establish the system -Aligning the feature to the design system is **not optional**. Polish without alignment is decoration on top of drift, and it makes the next person's job harder. Discovery comes before any other polish work. +Read DESIGN.md and representative tokens, shared components, patterns, and neighboring flows. If no formal system exists, use coherent project conventions. -1. **Find the design system**: Search for design system documentation, component libraries, style guides, or token definitions. Study the core patterns: design principles, target audience, color tokens, spacing scale, typography styles, component API, motion conventions. -2. **Note the conventions**: How are shared components imported? What spacing scale is used? Which colors come from tokens vs hard-coded values? What motion and interaction patterns are established? What flow shapes are used for comparable actions (modal vs full-page, inline vs route, save-on-blur vs explicit submit)? -3. **Identify drift, then name the root cause**: For every deviation, classify it as a **missing token** (the value should exist in the system but doesn't), a **one-off implementation** (a shared component already exists but wasn't used), or a **conceptual misalignment** (the feature's flow, IA, or hierarchy doesn't match neighboring features). The fix differs by category: patch the value, swap to the shared component, or rework the flow. Fixing the symptom without naming the cause is how drift compounds. +Classify each drift before fixing it: -If a design system exists, polish **must** align the feature with it. If none exists, polish against the conventions visible in the codebase. **If anything about the system is ambiguous, ask. Never guess at design system principles.** +- **missing token:** the system needs a reusable value; +- **one-off implementation:** an existing shared component or pattern should replace it; +- **conceptual mismatch:** the flow, information architecture, or hierarchy differs from comparable product areas; +- **local defect:** the implementation is simply incomplete or inconsistent. -## Pre-Polish Assessment +Fix the cause at the narrowest correct level. Ask when a binding system principle cannot be inferred. -Understand the current state and goals before touching anything: +## 2. Gather the evidence -1. **Review completeness**: - - Is it functionally complete? - - Are there known issues to preserve (mark with TODOs)? - - What's the quality bar? (MVP vs flagship feature?) - - When does it ship? (How much time for polish?) +Use the feature yourself at representative desktop and mobile sizes. Determine: -2. **Think experience-first**: Who actually uses this, and what's the best possible experience for them? Effective design beats decorative polish; a feature that looks beautiful but fights the user's flow is not polished. Walk the path from their perspective before opening DevTools. +- whether the path is functionally complete; +- the intended quality bar and time available; +- known constraints or deliberately unfinished work; +- the states, content lengths, roles, and input methods users will actually encounter. -3. **Identify polish areas**: - - Visual inconsistencies - - Spacing and alignment issues - - Interaction state gaps - - Copy inconsistencies - - Edge cases and error states - - Loading and transition smoothness - - Information architecture and flow drift (does this feature reveal complexity the way neighboring features do?) +If a prior critique exists, use it as one input: -4. **Pull in any prior critique** (optional signal): If `/impeccable critique` has been run on the same target, its priority issues are a useful prior for what to address first. Resolve the target to a file path or URL, then: - ```bash - slug=$(node .trae/skills/impeccable/scripts/critique-storage.mjs slug "") - node .trae/skills/impeccable/scripts/critique-storage.mjs latest "$slug" - ``` - Exit 0 with body = found; fold the P0/P1 items into your polish list and mention the snapshot path so the user sees what you read. Exit 2 = no snapshot, continue without it. The critique is one input among many. Do your own pass either way. +```bash +slug=$(node .trae/skills/impeccable/scripts/critique-storage.mjs slug "") +node .trae/skills/impeccable/scripts/critique-storage.mjs latest "$slug" +``` -5. **Triage cosmetic vs functional**: Classify each issue as **cosmetic** (looks off, doesn't impede the user) or **functional** (breaks, blocks, or confuses the experience). When polish time is tight, functional issues ship first; cosmetic ones can land in a follow-up. Quality should be consistent; never perfect one corner while leaving another rough. +Exit 0 returns the latest snapshot; incorporate relevant P0/P1 findings and name the snapshot read. Exit 2 means none exists. Perform an independent pass either way. -**CRITICAL**: Polish is the last step, not the first. Don't polish work that's not functionally complete. +## 3. Triage -## Polish Systematically +Separate functional defects from cosmetic ones and fix in this order: -Work through these dimensions methodically: +1. broken or blocked tasks, data loss, misleading state, and inaccessible paths; +2. missing loading, empty, error, success, disabled, and permission states; +3. flow, hierarchy, responsive, and design-system drift; +4. visual and motion inconsistencies; +5. code and asset cleanup. -### Visual Alignment & Spacing +Do not perfect one corner while leaving the rest below the same quality bar. -- **Pixel-perfect alignment**: Everything lines up to grid -- **Consistent spacing**: All gaps use spacing scale (no random 13px gaps) -- **Optical alignment**: Adjust for visual weight (icons may need offset for optical centering) -- **Responsive consistency**: Spacing and alignment work at all breakpoints -- **Grid adherence**: Elements snap to baseline grid +## 4. Polish the whole path -**Check**: -- Enable grid overlay and verify alignment -- Check spacing with browser inspector -- Test at multiple viewport sizes -- Look for elements that "feel" off +### Flow and hierarchy -### Information Architecture & Flow +- Match neighboring mental models, terminology, disclosure, routing, save behavior, and optimistic or pessimistic patterns. +- Make the primary task and current state obvious without flattening every element to equal weight. +- Ensure arrival, transition, empty, and recovery paths connect instead of behaving as isolated screens. -Visual polish on a misshapen flow is wasted work. Match the *shape* of the experience to the system, not just the surface. +### Layout and type -- **Progressive disclosure**: Match how much is revealed when, compared to neighboring features. A settings page exposing 40 fields when the rest of the app reveals 5 at a time is drift, even if every field is perfectly styled. -- **Established user flows**: Multi-step actions follow the same shape as comparable flows elsewhere: modal vs full-page, inline edit vs separate route, save-on-blur vs explicit submit, optimistic vs pessimistic updates. -- **Hierarchy & complexity**: The same conceptual weight gets the same visual weight throughout. Primary actions don't become tertiary in one corner of the product, and tertiary actions don't shout. -- **Empty, loading, and arrival transitions**: How content arrives, updates, and leaves matches how it does in adjacent features. -- **Naming and mental model**: The feature uses the same nouns and verbs as the rest of the system. A "Workspace" here shouldn't be a "Project" three screens away. +- Align to the project's grid and spacing scale; fix optical as well as mathematical alignment. +- Group related content tightly and separate distinct groups generously. +- Keep same-role typography consistent; test measure, wrapping, localization expansion, zoom, and font loading. +- Verify every supported viewport rather than correcting only the current screenshot. -### Typography Refinement +### Color, imagery, and icons -- **Hierarchy consistency**: Same elements use same sizes/weights throughout -- **Line length**: 45-75 characters for body text -- **Line height**: Appropriate for font size and context -- **Widows & orphans**: No single words on last line -- **Hyphenation**: Appropriate for language and column width -- **Kerning**: Adjust letter spacing where needed (especially headlines) -- **Font loading**: No FOUT/FOIT flashes +- Use semantic tokens and stable color meanings across themes. +- Verify text, control, and focus contrast in every state. +- Keep icon families, stroke/weight, sizing, and optical alignment coherent. +- Prevent image layout shift; use correct aspect ratios, responsive sources, and useful alt text. -### Color & Contrast +### Interaction and state -- **Contrast ratios**: All text meets WCAG standards -- **Consistent token usage**: No hard-coded colors, all use design tokens -- **Theme consistency**: Works in all theme variants -- **Color meaning**: Same colors mean same things throughout -- **Accessible focus**: Focus indicators visible with sufficient contrast -- **Gray on color**: Never put gray text on colored backgrounds; use a shade of that color or transparency +- Every control needs appropriate default, hover, focus, active, disabled, loading, error, and success behavior. +- Preserve visible keyboard focus, logical tab order, labels, and platform-appropriate touch targets. +- Keep motion coherent, interruptible, performant, and reduced-motion aware. Do not add animation merely to make polish visible. +- Validate long, missing, localized, offline, slow, and permission-limited content where the product can encounter it. -### Interaction States +### Content and code -Every interactive element needs all states: +- Keep terminology, capitalization, punctuation, and factual copy consistent. Ask before changing claims. +- Remove debug output, dead code, unused imports, obsolete styles, and polish-created duplication. +- Replace custom implementations with shared components where the system owns the pattern. +- Promote genuinely reusable values to tokens; do not create a system abstraction for one local exception. -- **Default**: Resting state -- **Hover**: Subtle feedback (color, scale, shadow) -- **Focus**: Keyboard focus indicator (never remove without replacement) -- **Active**: Click/tap feedback -- **Disabled**: Clearly non-interactive -- **Loading**: Async action feedback -- **Error**: Validation or error state -- **Success**: Successful completion +## 5. Verify and finish -**Missing states create confusion and broken experiences**. +Walk the complete path again with mouse, keyboard, and touch where applicable. Check: -### Micro-interactions & Transitions +- mobile, intermediate, and wide layouts; +- loading, empty, error, success, disabled, long-content, and missing-content states; +- zoom, contrast, focus, semantics, screen-reader names, and reduced motion; +- console errors, layout shift, interaction latency, image loading, and supported browsers; +- agreement with DESIGN.md, neighboring features, and the user's scope. -- **Smooth transitions**: All state changes animated appropriately (150-300ms) -- **Consistent easing**: Use ease-out-quart/quint/expo for natural deceleration. Never bounce or elastic; they feel dated. -- **No jank**: Smooth animations; use atmospheric blur/filter/mask/shadow effects when they add polish, but bound expensive paint areas and avoid casual layout-property animation -- **Appropriate motion**: Motion serves purpose, not decoration -- **Reduced motion**: Respects `prefers-reduced-motion` +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. -### Content & Copy - -- **Consistent terminology**: Same things called same names throughout -- **Consistent capitalization**: Title Case vs Sentence case applied consistently -- **Grammar & spelling**: No typos -- **Appropriate length**: Not too wordy, not too terse -- **Punctuation consistency**: Periods on sentences, not on labels (unless all labels have them) - -### Icons & Images - -- **Consistent style**: All icons from same family or matching style -- **Appropriate sizing**: Icons sized consistently for context -- **Proper alignment**: Icons align with adjacent text optically -- **Alt text**: All images have descriptive alt text -- **Loading states**: Images don't cause layout shift, proper aspect ratios -- **Retina support**: 2x assets for high-DPI screens - -### Forms & Inputs - -- **Label consistency**: All inputs properly labeled -- **Required indicators**: Clear and consistent -- **Error messages**: Helpful and consistent -- **Tab order**: Logical keyboard navigation -- **Auto-focus**: Appropriate (don't overuse) -- **Validation timing**: Consistent (on blur vs on submit) - -### Edge Cases & Error States - -- **Loading states**: All async actions have loading feedback -- **Empty states**: Helpful empty states, not just blank space -- **Error states**: Clear error messages with recovery paths -- **Success states**: Confirmation of successful actions -- **Long content**: Handles very long names, descriptions, etc. -- **No content**: Handles missing data gracefully -- **Offline**: Appropriate offline handling (if applicable) - -### Responsiveness - -- **All breakpoints**: Test mobile, tablet, desktop -- **Touch targets**: 44x44px minimum on touch devices -- **Readable text**: No text smaller than 14px on mobile -- **No horizontal scroll**: Content fits viewport -- **Appropriate reflow**: Content adapts logically - -### Performance - -- **Fast initial load**: Optimize critical path -- **No layout shift**: Elements don't jump after load (CLS) -- **Smooth interactions**: No lag or jank -- **Optimized images**: Appropriate formats and sizes -- **Lazy loading**: Off-screen content loads lazily - -### Code Quality - -- **Remove console logs**: No debug logging in production -- **Remove commented code**: Clean up dead code -- **Remove unused imports**: Clean up unused dependencies -- **Consistent naming**: Variables and functions follow conventions -- **Type safety**: No TypeScript `any` or ignored errors -- **Accessibility**: Proper ARIA labels and semantic HTML - -## Polish Checklist - -Go through systematically: - -- [ ] Aligned to the design system (drift named and resolved by root cause) -- [ ] Information architecture and flow shape match neighboring features -- [ ] Visual alignment perfect at all breakpoints -- [ ] Spacing uses design tokens consistently -- [ ] Typography hierarchy consistent -- [ ] All interactive states implemented -- [ ] All transitions smooth (60fps) -- [ ] Copy is consistent and polished -- [ ] Icons are consistent and properly sized -- [ ] All forms properly labeled and validated -- [ ] Error states are helpful -- [ ] Loading states are clear -- [ ] Empty states are welcoming -- [ ] Touch targets are 44x44px minimum -- [ ] Contrast ratios meet WCAG AA -- [ ] Keyboard navigation works -- [ ] Focus indicators visible -- [ ] No console errors or warnings -- [ ] No layout shift on load -- [ ] Works in all supported browsers -- [ ] Respects reduced motion preference -- [ ] Code is clean (no TODOs, console.logs, commented code) - -**IMPORTANT**: Polish is about details. Zoom in. Squint at it. Use it yourself. The little things add up. - -Sweat the details. Zoom in until the alignment is right and the spacing reads as deliberate. Then ship. - -**NEVER**: -- Polish before it's functionally complete -- Polish without aligning to the design system; that's decoration on drift -- Guess at design system principles instead of asking when something is ambiguous -- Spend hours on polish if it ships in 30 minutes (triage) -- Introduce bugs while polishing (test thoroughly) -- Ignore systematic issues (if spacing is off everywhere, fix the system, not just one screen) -- Perfect one thing while leaving others rough (consistent quality level) -- Create new one-off components when design system equivalents exist -- Hard-code values that should use design tokens -- Introduce new patterns or flows that diverge from established ones - -## Final Verification - -Before marking as done: - -- **Use it yourself**: Actually interact with the feature. -- **Test on real devices**: Not just browser DevTools. -- **Ask someone else to review**: Fresh eyes catch things. -- **Compare to design**: Match intended design. -- **Check all states**: Don't just test happy path. -- **Treat automation carefully**: Run detector or QA commands when they are available and relevant, fix their defects, but never cite a clean result as proof that the work is polished. - -## Clean Up - -After polishing, ensure code quality: - -- **Replace custom implementations**: If the design system provides a component you reimplemented, switch to the shared version. -- **Remove orphaned code**: Delete unused styles, components, or files made obsolete by polish. -- **Consolidate tokens**: If you introduced new values, check whether they should be tokens. -- **Verify DRYness**: Look for duplication introduced during polishing and consolidate. +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. diff --git a/.trae/skills/impeccable/reference/quieter.md b/.trae/skills/impeccable/reference/quieter.md index 55b00b6f3..bb163e40f 100644 --- a/.trae/skills/impeccable/reference/quieter.md +++ b/.trae/skills/impeccable/reference/quieter.md @@ -2,7 +2,7 @@ Quiet design is harder than bold design. Subtlety needs precision. Reduce visual --- -## Register +## Visitor mode Persuade + Experience: "quieter" means more restrained palette, more whitespace, more typographic air. Drama is reduced, not eliminated; the POV stays intact. diff --git a/.trae/skills/impeccable/reference/routing.md b/.trae/skills/impeccable/reference/routing.md index a7e40fc4a..57c81ff86 100644 --- a/.trae/skills/impeccable/reference/routing.md +++ b/.trae/skills/impeccable/reference/routing.md @@ -11,7 +11,7 @@ Reason over the signals; there is no score to obey: - `critique.latest` with a low `score` or non-zero `p0` / `p1` → `polish` (it reads that snapshot as its backlog), or re-run `critique` if the snapshot looks stale. - `git.changedFiles` pointing at one surface → scope `audit` or `polish` to those files specifically, naming them. - `devServer.running` true → `live` is available for in-browser iteration; if false, don't lead with `live`. **`live` and the bundled `detect.mjs` are web-only.** If `setup.platform` is `ios`, `android`, or `adaptive`, don't lead with either; the browser overlay and the HTML rule engine don't apply to native app code. -- Otherwise group by intent exactly as init's "Recommend starting points" step does (build new / improve what's there / iterate visually), tailored to `setup.register`. +- Otherwise group by intent (build new / improve what's there / iterate visually), tailored to the current surface and `setup.platform`. **If `scan.targets` is non-empty and `setup.platform` is not `ios`/`android`/`adaptive`, run `node .trae/skills/impeccable/scripts/detect.mjs --json ` once** (the bundled detector over local files: no network, no npx; it reads HTML/CSS, so skip it for native projects). `scan.via` tells you what they are: `git-changes` (the markup/style files in your dirty tree, the most relevant set), `source-dir` (e.g. `src`, `app`), `html`, or `root`. Fold the hits into your picks: many quality / contrast hits → `audit` or `polish`; a specific slop family → the matching command (gradient text or eyebrows → `quieter` / `typeset`, flat or gray palette → `colorize`, and so on). It's a real, current signal that beats guessing. If detect errors or the tree is large and slow, skip it and recommend the user run `audit` themselves; never block the suggestion on it. diff --git a/.trae/skills/impeccable/reference/shape.md b/.trae/skills/impeccable/reference/shape.md index 40804f6e4..acbb26e5b 100644 --- a/.trae/skills/impeccable/reference/shape.md +++ b/.trae/skills/impeccable/reference/shape.md @@ -1,167 +1,61 @@ -Shape the UX and UI for a feature before any code is written. This command produces a **design brief**: a structured artifact that guides implementation through discovery, not guesswork. +# Shape -**Scope**: Design planning only. This command does NOT write code. It produces the thinking that makes code good. +Discover what should be made and how it should work, then return a confirmed design brief without code. -**Output**: A design brief that can be handed off to /impeccable craft, or directly to /impeccable for freeform implementation. When visual direction probes are used, the images are supporting artifacts, not the primary output. +**Product gate:** when context reports that PRODUCT.md is missing, load and complete [init.md](init.md) before Phase 1. Do not substitute shape questions for the product interview. Once PRODUCT.md exists, return here; product context does not replace task discovery. -## Philosophy +## Phase 1: Discovery interview -Most AI-generated UIs fail not because of bad code, but because of skipped thinking. They jump to "here's a card grid" without asking "what is the user trying to accomplish?" This command inverts that: understand deeply first, so implementation is precise. +Do not write code or choose visual direction yet. -## Phase 1: Discovery Interview +### Cadence -**Do NOT write any code or make any design decisions during this phase.** Your only job is to understand the feature deeply enough to make excellent design decisions later. +- Use the structured question tool when available; otherwise ask and stop. +- Ask two or three related questions per round, then wait. One round is the default; add a second only when the answers expose a material gap. +- Do not dump a questionnaire, repeat settled facts, or turn obvious facts into menus. Assert the likely reading and invite correction. +- A sparse prompt requires at least one answer round. A precise prompt may need only a compact confirmation. -**Unattended runs:** when no user can respond (a one-shot task, an automated run, or an explicit instruction not to ask questions), answer the interview questions yourself from the brief, record the answers as the design brief, and continue without pausing at any confirmation gate in this file. The thinking is still required; only the waiting is waived. +### Round 1: purpose, people, and outcome -This is a required interaction, not optional guidance. Ask these questions in conversation, adapting based on answers. Don't dump them all at once; have a natural dialogue. ask the user directly to clarify what you cannot infer. +Choose the two or three questions that most change the result: -### Interview cadence +- What is this surface or feature for, and what problem must it solve? +- Who specifically reaches it, in what situation and state of mind? +- What is the primary thing they must understand or do? What would success look like? +- What is uniquely true here that a neighboring product or generic template could not claim? -Discovery includes at least one user-answer round unless PRODUCT.md, DESIGN.md, or an already-confirmed brief directly answers the needed inputs. With a sparse prompt, do **not** synthesize a complete brief for confirmation on the first response. +### Round 2: material, behavior, and boundaries -- Use the harness's structured question tool when one exists. Otherwise, ask directly in chat and stop. -- Ask **2-3 questions per round**, then wait for answers. -- Treat PRODUCT.md and DESIGN.md as anchors; they reduce repeated questions but do **not** replace shape for craft. Shape is task-specific. -- One round is the default. Add a second only if the first answers leave material gaps. Don't run a second round just to feel thorough. -- Round 1 should clarify purpose, audience/context, content/scope, and (for brand) visual direction. -- Round 2, when needed, fills in whatever's still genuinely missing. +Run only for material unresolved decisions: -**Assert-then-confirm, not menu-with-escape.** When PRODUCT.md and the user's prompt make one option obvious, name it and ask the user to confirm or override. Don't enumerate "Restrained / Committed / Or something else?" as a real choice; "This reads as Restrained, confirm?" beats a four-option menu when the answer is already clear. +- What real content, evidence, data, and assets must the experience carry? What are realistic minimum, typical, and maximum ranges? +- Which states and transitions matter: first-run, empty, loading, error, success, permissions, overflow, or expert use? +- What is the intended fidelity, breadth, and interactivity: exploration, production-ready screen, full flow, or broader surface? +- What must remain untouched? What would make the result feel wrong even if it looked polished? +- Which platform, framework, performance, accessibility, localization, or delivery constraints are binding? -### Purpose & Context -- What is this feature for? What problem does it solve? -- Who specifically will use it? (Not "users"; be specific: role, context, frequency) -- What does success look like? How will you know this feature is working? -- What's the user's state of mind when they reach this feature? (Rushed? Exploring? Anxious? Focused?) +Never ask for CSS values or canned aesthetic lanes. New-work owns visual-world and concept choices. -### Content & Data -- What content or data does this feature display or collect? -- What are the realistic ranges? (Minimum, typical, maximum, e.g., 0 items, 5 items, 500 items) -- What are the edge cases? (Empty state, error state, first-time use, power user) -- Is any content dynamic? What changes and how often? -- What visual assets are real content here? Note required images, product shots, illustrations, maps, textures, diagrams, generated objects, or existing project assets. +## Phase 2: Resolve the design direction -### Design Direction +For new surfaces, brand expansion, or replacement, follow [new-work.md](new-work.md) through visual authority, any world workshop, and concept choice. Reuse discovery, then return before its contract, persistence, or implementation. Inside an established world, use its concept process only when composition or interaction remains materially open. -Force a visual decision on three fronts. Skip anything PRODUCT.md or DESIGN.md already answers; ask only what's missing. +## Phase 3: Write the brief -- **Color strategy for this surface.** Pick one: Restrained / Committed / Full palette / Drenched. Can override the project default if the surface earns it (e.g. a drenched hero inside an otherwise Restrained product). -- **Theme via scene sentence.** Write one sentence of physical context for this surface: who uses it, where, under what ambient light, in what mood. The sentence forces dark vs light. If it doesn't, add detail until it does. -- **Two or three named anchor references.** Specific products, brands, objects. Not adjectives like "modern" or "clean." +Write the smallest useful brief: -### Scope +1. **Job and audience:** who arrives, their context, need, and visitor mode. +2. **Outcome and proof:** primary task/action, success, real evidence, and product-specific truth. +3. **Selected direction:** visual authority, structural/interaction thesis, sequence, focal moment, and implementation consequence. +4. **Scope and boundaries:** fidelity, breadth, interactivity, named target, what remains untouched, and explicit anti-goals. +5. **States and ranges:** realistic content/data ranges and material states. +6. **Interaction and layout:** hierarchy, topology, responsiveness, affordances, feedback, and transitions; intent, not CSS. +7. **Constraints and open decisions:** platform, delivery, accessibility, localization, reusable components, and choices a builder must not invent. -Always ask. Sketch quality and shipped quality are different outputs; don't guess between them. +Use three to five bullets when the task is settled; use the full structure only for ambiguous, multi-screen, or standalone planning. Do not restate the conversation. -- **Fidelity.** Sketch / mid-fi / high-fi / production-ready? -- **Breadth.** One screen / a flow / a whole surface? -- **Interactivity.** Static visual / interactive prototype / shipped-quality component? -- **Time intent.** Quick exploration, or polish until it ships? +## Confirm and stop -Scope answers are task-scoped. Don't write them to PRODUCT.md or DESIGN.md; carry them through the design brief only. +Present the brief for explicit confirmation or one correction round, then stop: shape never writes code or a direction contract. -### Constraints -- Are there technical constraints? (Framework, performance budget, browser support) -- Are there content constraints? (Localization, dynamic text length, user-generated content) -- Mobile/responsive requirements? -- Accessibility requirements beyond WCAG AA? - -### Anti-Goals -- What should this NOT be? What would be a wrong direction? -- What's the biggest risk of getting this wrong? - -## Phase 1.5: Visual Direction Probe (Capability-Gated) - -After the discovery interview, generate a small set of visual direction probes **before** writing the final brief when all of these are true: - -- The work is **net-new** or directionally ambiguous enough that visual exploration will clarify the brief. -- The requested fidelity is **mid-fi, high-fi, or production-ready**. Skip for sketch-only planning. -- The current harness gives you native image generation (Codex's `image_gen`, an equivalent MCP tool, or similar). Don't ask the user to install APIs or tooling. - -When those conditions are met, this step is mandatory. If image generation isn't natively available, do not ask the user to install APIs or tooling. State in one line that the image step is skipped because the harness lacks native image generation, then proceed. The one-line announcement is required, not optional; it forces a conscious decision instead of letting the step quietly evaporate. - -Use probes to explore visual lanes, not to replace the brief. - -Do not skip probes because the final UI will be semantic, editable, code-native, responsive, or accessible. Those are implementation requirements, not reasons to avoid visual exploration. - -### What to generate - -Generate **2 to 4** distinct direction probes based on the discovery answers, especially: - -- Color strategy -- Theme scene sentence -- Named anchor references -- Scope and fidelity - -The probes should differ in primary visual direction (hierarchy, topology, density, typographic voice, or color strategy), not just palette tweaks. - -### How to use the probes - -- Treat them as **direction tests**, not final designs. -- Use them to pressure-test whether the brief is pointing at the right lane. -- Ask the user which direction feels closest, what feels off, and what should carry forward. -- If the probes reveal a mismatch, revise the brief inputs before finalizing the brief. - -### Important limits - -- Do **not** skip discovery because image generation is available. -- Do **not** treat generated imagery as final UX specification, final copy, or final accessibility behavior. -- Do **not** use this step for minor refinements of existing work. It's for shaping a new surface or clarifying a big directional choice. - -If image generation isn't natively available, announce the skip in one line and proceed to the design brief. - -## Phase 2: Design Brief - -After the interview and any required probes, present a brief and **end your response**. The user must confirm before any implementation runs. Do not present a brief and then continue to code in the same response, even if the brief feels obvious to you. The user's confirmation is the gate. - -**Choose the brief shape based on how clear the answers are:** - -- **Compact form (3-5 bullets)** when discovery was crisp and the original prompt + PRODUCT.md already pinned scope, content, and direction. State what you're building, the visual lane, and end with one or two specific questions or a clear "confirm or override?" prompt. This is the default for typical craft requests with a clear prompt. -- **Full structured form (sections below)** when the task is genuinely ambiguous, multi-screen, or when the user asked for shape as a standalone step. Use this when the discipline of structure earns its weight. - -Don't pad a clear brief into a long one to look thorough. A 70-line brief restating answers the user just gave is noise, not rigor. Equally, don't skip the confirmation pause to look efficient: the pause is the point. - -Present the brief, then **stop and wait for explicit confirmation**. You are not the judge of whether the user already approved. Even when the brief feels obviously right, ask once and wait. The pause is what separates shape from premature implementation. - -### Brief Structure - -**1. Feature Summary** (2-3 sentences) -What this is, who it's for, what it needs to accomplish. - -**2. Primary User Action** -The single most important thing a user should do or understand here. - -**3. Design Direction** -Color strategy (Restrained / Committed / Full palette / Drenched) + the theme scene sentence + 2–3 named anchor references. Reference PRODUCT.md and DESIGN.md where they already answer, and note any per-surface overrides. - -If you ran the Visual Direction Probe step, name which probe direction won and what changed in the brief because of it. - -**4. Scope** -Fidelity, breadth, interactivity, and time intent from the Scope section of the interview. Task-scoped; these don't persist beyond the brief. - -**5. Layout Strategy** -High-level spatial approach: what gets emphasis, what's secondary, how information flows. Describe the visual hierarchy and rhythm, not specific CSS. - -**6. Key States** -List every state the feature needs: default, empty, loading, error, success, edge cases. For each, note what the user needs to see and feel. - -**7. Interaction Model** -How users interact with this feature. What happens on click, hover, scroll? What feedback do they get? What's the flow from entry to completion? - -**8. Content Requirements** -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., 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. - ---- - -ask the user directly to clarify what you cannot infer. Ask for explicit confirmation of the brief before finishing. - -If the user disagrees with any part, revisit the relevant discovery questions. A shape run is incomplete until the user confirms direction. - -Once confirmed, the brief is complete. The user can now hand it to /impeccable, or use it to guide any other implementation approach. (If the user wants the full discovery-then-build flow in one step, they should use /impeccable craft instead, which runs this command internally.) +When no human or structured answer mechanism exists, mark assumptions plainly, return the brief, and stop. diff --git a/.trae/skills/impeccable/reference/typeset.md b/.trae/skills/impeccable/reference/typeset.md index 1b9743b91..004e031ff 100644 --- a/.trae/skills/impeccable/reference/typeset.md +++ b/.trae/skills/impeccable/reference/typeset.md @@ -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. --- -## Register +## Visitor mode -New identity work: run the font selection procedure in [new-work.md](new-work.md). Fluid `clamp()` scale, ≥1.25 ratio between steps. +- **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, register, **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 .trae/skills/impeccable/scripts/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 `/impeccable polish` for the final pass. +When the hierarchy holds, hand off to `/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) * )` 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 - -The tactical selection procedure and the reflex-reject list live in [new-work.md](new-work.md) (loaded for new identity work). 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 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. diff --git a/.trae/skills/impeccable/scripts/command-metadata.json b/.trae/skills/impeccable/scripts/command-metadata.json index 83796d54b..dad8ef2e0 100644 --- a/.trae/skills/impeccable/scripts/command-metadata.json +++ b/.trae/skills/impeccable/scripts/command-metadata.json @@ -1,6 +1,6 @@ { "craft": { - "description": "Full confirmed-brief-then-build flow. Runs multi-round shape discovery first, resolves visual probe and north-star mock gates when available, then builds and visually iterates. Use when building a new feature end-to-end.", + "description": "Deprecated compatibility alias for an ordinary Impeccable new-work request. It adds no behavior; natural build and redesign requests use the same flow.", "argumentHint": "[feature description]" }, "init": { diff --git a/.trae/skills/impeccable/scripts/concept-seed.mjs b/.trae/skills/impeccable/scripts/concept-seed.mjs index 1f141a80f..25f20d9ab 100644 --- a/.trae/skills/impeccable/scripts/concept-seed.mjs +++ b/.trae/skills/impeccable/scripts/concept-seed.mjs @@ -1,6 +1,7 @@ #!/usr/bin/env node /** - * Concept-seed picker: the dice half of the new-work concept procedure. + * External concept seed: the dice half of new-work's coupled-direction and + * established-world surface procedures. * * The model derives a grounded shortlist of candidate FORMS from the * audience's world and the subject's cultural home (see @@ -12,10 +13,9 @@ * * This script rolls them from outside, the same trick that made the * palette seed work: - * - BUILD INDEX (2-5): which entry of the model's own resonance-ordered - * shortlist to build. The dice never choose an ungrounded ingredient; - * they only refuse the argmax rut. (Index 1 is excluded: that's the - * concept every run would ship anyway.) + * - PROMOTED INDEX: which entry of the model's own resonance-ordered + * shortlist must be taken seriously beside its favorites. The dice never + * choose an ungrounded ingredient; they only refuse the argmax rut. * - CHALLENGERS (3): outside forms from concept-ingredients.json, weighed * against the derived candidates on exactly two axes — audience * identification and product clarity. They win only when they beat the @@ -23,8 +23,8 @@ * material and win over thin categories, which is the intended shape. * * Usage: - * node scripts/concept-seed.mjs # roll at random - * node scripts/concept-seed.mjs --from # deterministic (hash key) + * node scripts/concept-seed.mjs --scope direction + * node scripts/concept-seed.mjs --scope surface --from * * Env vars: * IMPECCABLE_CONCEPT_SEED — same as --from; for reproducible eval runs. @@ -40,6 +40,12 @@ const pool = JSON.parse(readFileSync(join(here, 'concept-ingredients.json'), 'ut const args = process.argv.slice(2); const fromIdx = args.indexOf('--from'); +const scopeIdx = args.indexOf('--scope'); +const scope = scopeIdx !== -1 ? args[scopeIdx + 1] : 'surface'; +if (scope !== 'surface' && scope !== 'direction') { + process.stderr.write('concept-seed: --scope must be direction or surface\n'); + process.exit(1); +} // When no key is supplied, generate one and print it: a user reporting a // bad outcome can hand us the key and we replay the exact roll. const key = fromIdx !== -1 @@ -47,7 +53,7 @@ const key = fromIdx !== -1 : (process.env.IMPECCABLE_CONCEPT_SEED || crypto.randomBytes(4).toString('hex')); function hashUnit(k, salt) { - const h = crypto.createHash('sha256').update(`${salt}:${k}`).digest(); + const h = crypto.createHash('sha256').update(`${scope}:${salt}:${k}`).digest(); return h.readUInt32BE(0) / 0xffffffff; } const unit = (salt) => hashUnit(key, salt); @@ -67,22 +73,48 @@ for (let i = 0; picks.length < 3 && i < 60; i++) { } } -process.stdout.write(`CONCEPT SEED (key: ${key}; rerun with --from ${key} to reproduce this roll) -BUILD INDEX: ${buildIndex} - After ordering your derived candidates by resonance, build the page whose - form comes from candidate number ${buildIndex}, exactly as if it had ranked - first: full commitment. Your top-ranked candidate is what every run in this - category would ship; the assignment exists to refuse that rut, not to - punish it. -CHALLENGERS (weigh against your derived candidates on the same two axes, -audience identification and product clarity; a challenger wins only when -it beats the grounded list on both): +const promotedInstruction = scope === 'direction' + ? `After ordering the grounded coupled directions by product fit, promote + candidate ${buildIndex} into the serious shortlist. Each candidate must join + a durable visual system to a concrete expression for the requested first + surface; select or revise that pair as one decision. It must survive the + current task plus navigation, quiet and dense content, interaction and state, + and a substantially different future surface.` + : `After ordering the task's grounded structural candidates by resonance, + promote candidate ${buildIndex} into the serious shortlist. In an attended + run, present it beside the strongest materially different candidates and + let the user select or revise the surface concept. In a truly unattended + run, use it when it survives audience identification and product clarity.`; + +const challengerInstruction = scope === 'direction' + ? `A challenger enters only when it can supply both reusable identity grammar + and a strong first-surface structure, not a one-page costume or a style pasted + onto an unrelated layout. Weigh audience identification, product clarity, + current-surface force, and cross-surface system breadth.` + : `A challenger wins only when it beats the grounded list on both audience + identification and product clarity. It may change task topology or + interaction, but never the committed visual identity.`; + +const authorityInstruction = scope === 'direction' + ? `PRODUCT.md and explicit incumbent brand commitments constrain every coupled +direction. The seed never chooses exact colors, fonts, tokens, or a user +preference, and it never permits the world and first surface to be selected +independently.` + : `PRODUCT.md and DESIGN.md constrain every surface candidate's identity +vocabulary; they do not cancel task-level composition. The seed never +authorizes a new palette, type system, material world, or unfamiliar control +behavior.`; + +process.stdout.write(`${scope.toUpperCase()} CONCEPT SEED (key: ${key}; rerun with --scope ${scope} --from ${key} to reproduce this roll) +PROMOTED INDEX: ${buildIndex} + ${promotedInstruction} + The promotion exists to refuse the model's ranking rut, not to outrank the + user or the brief. +CHALLENGERS: 1. ${picks[0]} 2. ${picks[1]} 3. ${picks[2]} -If a challenger wins, it replaces the assigned candidate. If the surface is -an existing world whose incumbent carries a deliberate, ownable idea, the -incumbent IS the chosen candidate: intensify its lineage and ignore the -roll entirely. The same override applies when the user, PRODUCT.md, or -DESIGN.md pins a direction: pinned direction beats the roll, always. +${challengerInstruction} +${authorityInstruction} +A user- or brief-pinned decision beats the roll, always. `); diff --git a/.trae/skills/impeccable/scripts/context-signals.mjs b/.trae/skills/impeccable/scripts/context-signals.mjs index 2fc27bea7..d296320d7 100644 --- a/.trae/skills/impeccable/scripts/context-signals.mjs +++ b/.trae/skills/impeccable/scripts/context-signals.mjs @@ -11,7 +11,7 @@ * output is always valid JSON. * * Signals: - * - setup: PRODUCT.md / DESIGN.md presence, register, whether code exists + * - setup: PRODUCT.md / DESIGN.md presence and whether code exists * - critique: the latest cached critique score (.impeccable/critique) * - git: branch + files changed vs the default branch (a scope hint) * - devServer: whether a local dev server answers on a common port (gates live) @@ -21,7 +21,7 @@ import net from 'node:net'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { execFileSync } from 'node:child_process'; -import { loadContext, extractRegister, extractPlatform } from './context.mjs'; +import { loadContext, extractPlatform } from './context.mjs'; import { getCritiqueDir } from './lib/impeccable-paths.mjs'; /** Is there code here at all, or just context files / an empty repo? */ @@ -196,7 +196,6 @@ export async function gatherSignals(cwd = process.cwd()) { hasDesign: ctx.hasDesign, designPath: ctx.designPath, hasCode: hasCode(cwd), - register: extractRegister(ctx.product), platform: extractPlatform(ctx.product), }, critique: { latest: latestCritique(cwd) }, diff --git a/.trae/skills/impeccable/scripts/context.mjs b/.trae/skills/impeccable/scripts/context.mjs index 2ef2d4ce8..080e7ee3a 100644 --- a/.trae/skills/impeccable/scripts/context.mjs +++ b/.trae/skills/impeccable/scripts/context.mjs @@ -1,8 +1,10 @@ /** - * Context loader: prints PRODUCT.md (and DESIGN.md if present) as one - * markdown block on stdout, or prints a `NO_PRODUCT_MD:` message when no + * Context loader: prints PRODUCT.md, DESIGN.md when present, the matching + * persisted surface brief when one can be resolved, and native-platform + * guidance selected from PRODUCT.md. It prints a + * `NO_PRODUCT_MD:` message when no * PRODUCT.md is found anywhere. The skill keys off that message to branch: - * from-scratch build commands (init / teach / craft / shape) and clear + * from-scratch build requests (plus init / teach / shape) and clear * build/shape intent divert into the init flow, while scoped commands proceed * using the existing code as context. * @@ -23,10 +25,13 @@ import os from 'node:os'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { parseTargetOptions } from './lib/target-args.mjs'; -import { IMPECCABLE_COMMAND } from './lib/provider.mjs'; +import { IMPECCABLE_COMMAND, IMPECCABLE_PROVIDER_ID } from './lib/provider.mjs'; +import { renderLlmOnlySlopReview } from './lib/slop-review.mjs'; +import { resolveSurfaceBrief } from './lib/surface-briefs.mjs'; const PRODUCT_NAMES = ['PRODUCT.md', 'Product.md', 'product.md']; const DESIGN_NAMES = ['DESIGN.md', 'Design.md', 'design.md']; +const SKILL_REFERENCE_DIR = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', 'reference'); const FALLBACK_DIRS = ['.agents/context', 'docs']; const MONOREPO_MARKER_FILES = ['pnpm-workspace.yaml', 'turbo.json', 'nx.json', 'lerna.json']; const MONOREPO_FALLBACK_PROJECT_DIRS = ['apps', 'packages']; @@ -41,6 +46,8 @@ const WORKSPACE_DISCOVERY_IGNORED_DIRS = new Set([ '.turbo', '.cache', 'coverage', + 'vendor', + 'vendors', ]); const VISUAL_SOURCE_DIRS = ['src', 'app', 'pages', 'components', 'site', 'public', 'styles']; const STYLE_EXTENSIONS = new Set(['.css', '.scss', '.sass', '.less', '.styl']); @@ -73,6 +80,12 @@ export function loadContext(cwd = process.cwd(), options = {}) { const designPath = resolved.designPath; const product = productPath ? safeRead(productPath) : null; const design = designPath ? safeRead(designPath) : null; + const platform = extractPlatform(product); + const surfaceResolution = resolveSurfaceBrief( + resolved.projectRoot, + hasTargetOption(options) ? options.targetPath : null, + ); + const surfaceBrief = surfaceResolution.brief; return { hasProduct: !!product, product, @@ -83,7 +96,18 @@ export function loadContext(cwd = process.cwd(), options = {}) { contextDir: resolved.contextDir, productContextDir: productPath ? path.dirname(productPath) : null, designContextDir: designPath ? path.dirname(designPath) : null, + hasSurfaceBrief: !!surfaceBrief, + surfaceBrief: surfaceBrief?.text ?? null, + surfaceBriefPath: surfaceBrief?.path ? path.relative(absCwd, surfaceBrief.path) : null, + surfaceBriefReason: surfaceResolution.reason, + surfaceBriefCandidates: surfaceResolution.candidates.map((brief) => ({ + slug: brief.slug, + path: path.relative(absCwd, brief.path), + primaryTarget: brief.primaryTarget, + relatedTargets: brief.relatedTargets, + })), hasVisualImplementation: hasVisualImplementation(resolved.projectRoot), + platform, projectRoot: resolved.projectRoot, repoRoot: resolved.repoRoot, isMonorepo: resolved.isMonorepo, @@ -696,6 +720,19 @@ function safeRead(p) { } } +function loadNativePlatformReferences(platform) { + const names = platform === 'adaptive' + ? ['ios', 'android'] + : platform === 'ios' || platform === 'android' + ? [platform] + : []; + return names.flatMap((name) => { + const filePath = path.join(SKILL_REFERENCE_DIR, `${name}.md`); + const content = safeRead(filePath); + return content ? [{ name, filePath, content }] : []; + }); +} + /** * Best-effort evidence that the project already has an incumbent visual * implementation. DESIGN.md is documentation, not the only source of design @@ -719,9 +756,11 @@ export function hasVisualImplementation(projectRoot) { let styledComponents = 0; const inspectFile = (filePath) => { - if (scannedFiles++ >= VISUAL_SCAN_FILE_LIMIT) return false; const ext = path.extname(filePath).toLowerCase(); if (!STYLE_EXTENSIONS.has(ext) && !UI_EXTENSIONS.has(ext)) return false; + const base = path.basename(filePath).toLowerCase(); + if (/\.min\.[a-z]+$/.test(base)) return false; + if (scannedFiles++ >= VISUAL_SCAN_FILE_LIMIT) return false; let body; try { body = fs.readFileSync(filePath, 'utf-8').slice(0, 64 * 1024); @@ -729,18 +768,28 @@ export function hasVisualImplementation(projectRoot) { return false; } - const base = path.basename(filePath).toLowerCase(); + const evidence = body + .replace(/\/\*[\s\S]*?\*\//g, '') + .replace(//g, '') + .replace(/^\s*\/\/.*$/gm, ''); if (STYLE_EXTENSIONS.has(ext)) { - const customProperties = body.match(/--[a-z0-9_-]+\s*:/gi)?.length ?? 0; - const visualDeclarations = body.match(/\b(?:color|background(?:-color)?|border(?:-color)?|font-family)\s*:/gi)?.length ?? 0; - if (/\b(?:tokens?|theme|design-system)\b/.test(base) && body.trim().length > 80) return true; + const customProperties = evidence.match(/--[a-z0-9_-]+\s*:/gi)?.length ?? 0; + const visualDeclarations = evidence.match(/\b(?:color|background(?:-color)?|border(?:-color)?|font-family)\s*:/gi)?.length ?? 0; + if (/\b(?:tokens?|theme|design-system)\b/.test(base) && evidence.trim().length > 80) return true; if (customProperties >= 3 || visualDeclarations >= 5) return true; } - if ((ext === '.html' || ext === '.htm') && body.length > 600 && /]+stylesheet/i.test(body)) { + if ((ext === '.html' || ext === '.htm') && evidence.length > 600 && /]+stylesheet/i.test(evidence)) { return true; } - if (!['.html', '.htm'].includes(ext) && body.length > 300 && /class(?:Name)?\s*=|style\s*=|styled\(|css`/i.test(body)) { + if (!['.html', '.htm'].includes(ext) && evidence.length > 300) { + const embeddedCustomProperties = evidence.match(/--[a-z0-9_-]+\s*:/gi)?.length ?? 0; + const embeddedVisualDeclarations = evidence.match(/\b(?:color|background(?:-color)?|border(?:-color)?|font-family)\s*:/gi)?.length ?? 0; + const classTokens = [...evidence.matchAll(/class(?:Name)?\s*=\s*["'`]([^"'`]+)["'`]/gi)] + .reduce((count, match) => count + match[1].trim().split(/\s+/).length, 0); + if ((embeddedCustomProperties >= 3 && embeddedVisualDeclarations >= 3) || embeddedVisualDeclarations >= 5 || classTokens >= 12) return true; + } + if (!['.html', '.htm'].includes(ext) && evidence.length > 300 && /class(?:Name)?\s*=|style\s*=|styled\(|css`/i.test(evidence)) { styledComponents += 1; if (styledComponents >= 3) return true; } @@ -781,9 +830,9 @@ function escapeRegExp(value) { /** * Read the first non-empty line under a bare `## ` section of - * PRODUCT.md (e.g. `## Register`, `## Platform`). Returns null when the + * PRODUCT.md (for example `## Platform`). Returns null when the * section is absent. The heading match is exact (`\s*$`) so near-miss - * headings like `## Register guidelines` don't shadow the real field. + * near-miss headings don't shadow the real field. */ export function extractSectionValue(product, heading) { if (!product) return null; @@ -802,16 +851,6 @@ export function extractSectionValue(product, heading) { return null; } -/** - * Pull the register (`brand` or `product`) out of PRODUCT.md by looking - * for a `## Register` section and reading the first non-empty line that - * follows it. Returns null when the file is legacy / register-less. - */ -export function extractRegister(product) { - const word = (extractSectionValue(product, 'Register') || '').toLowerCase(); - return word === 'brand' || word === 'product' ? word : null; -} - /** * Pull the platform (`web`, `ios`, `android`, or `adaptive`) out of PRODUCT.md * by looking for a `## Platform` section and reading the first non-empty line @@ -985,30 +1024,35 @@ async function cli() { const parts = ctx.hasVisualImplementation ? [ 'NO_PRODUCT_MD: This project has no PRODUCT.md yet, but it does have an incumbent visual implementation. ' + - 'For `init` or `teach`, load reference/init.md and create PRODUCT.md. For `craft`, `shape`, or other work ' + - 'against the existing product, read its CSS, tokens, components, and assets first and proceed without blocking; ' + - `offer \`${IMPECCABLE_COMMAND} init\` as a follow-up. Only treat the work as greenfield when the user explicitly ` + - 'asks to discard or replace the current identity.', - 'EXISTING_VISUAL_SYSTEM: Code and assets are the incumbent design authority. Missing DESIGN.md is a documentation ' + - 'gap, not permission to invent a replacement identity. Preserve and extend the implementation unless the user ' + - 'explicitly requests a rebrand, or inspection proves the detected files contain no coherent visual decisions.', + 'For `init`, `teach`, `shape`, or any request to create a new surface or replacement visual world, load reference/init.md and create PRODUCT.md with the user first. ' + + 'After init writes PRODUCT.md, reference/new-work.md preserves and documents the incumbent system for an ' + + 'extension or replaces it with the user for a redesign/rebrand. Other ' + + 'narrow refinement commands may read the CSS, tokens, components, and assets and proceed without blocking, then ' + + `offer \`${IMPECCABLE_COMMAND} init\` as a follow-up.`, + 'BUILD_INIT_REQUIRED: Before shape or any new-surface/redesign flow, init must capture PRODUCT.md with the human or structured ' + + 'simulated user. Init writes product truth only; reference/new-work.md owns every visual decision.', + 'SCOPED_EXISTING_ALLOWED: Narrow refinement commands may use the incumbent implementation as authority without ' + + 'blocking on context setup; they must preserve it and offer init afterward.', + 'EXISTING_VISUAL_SYSTEM: For refinement or extension, code and assets are incumbent design authority and missing ' + + 'DESIGN.md is a documentation gap. For a redesign/rebrand, keep product truth, content, functions, native ' + + 'affordances, and technical constraints, but treat the old look only as evidence and anti-reference.', ] : [ 'NO_PRODUCT_MD: This project has no PRODUCT.md yet. ' + - 'For `init`, `teach`, `craft`, `shape`, ' + + 'For `init`, `teach`, `shape`, ' + 'or wording that clearly maps to a from-scratch build/shape flow, load ' + - 'reference/init.md and write PRODUCT.md first, unless no user can ' + - 'respond (a one-shot or automated run, or the user said not to ask): ' + - 'then write a one-paragraph understanding of the product, audience, ' + - 'and the page\'s job from the brief, and continue. For any other ' + + 'reference/init.md, complete its human or structured simulated-user interview, and write PRODUCT.md before ' + + 'designing. If no answer mechanism truly exists, init may infer only from the explicit brief and must label its ' + + 'assumptions. It never writes DESIGN.md. For any other ' + '(scoped) command against existing code, proceed using the code as ' + `context and offer \`${IMPECCABLE_COMMAND} init\` as a suggestion (do not block).`, - 'NEW_WORK: No committed design context was found. If this task produces ' + - 'new design (a build from scratch, or a redesign that discards the ' + - 'current look), you MUST read reference/new-work.md before making any ' + - 'design decision. Scoped fixes to existing code do not need it.', + 'PRODUCT_INIT_REQUIRED: No product context or visual authority was found. New builds and redesigns ' + + 'must finish reference/init.md for PRODUCT.md, then reference/new-work.md establishes the world and surface. Scoped ' + + 'fixes to existing code do not need the new-surface flow.', ]; + appendSurfaceBriefContext(parts, ctx); parts.push(buildResolvedContextDirective(ctx, cliOptions, { targetExists })); + appendHookFallback(parts, ctx); if (shouldWarnMissingTarget(ctx, targetProvided, targetExists)) { parts.push(buildMissingTargetDirective()); } @@ -1020,37 +1064,29 @@ async function cli() { if (ctx.hasDesign) { parts.push(`# DESIGN.md\n\n${ctx.design.trim()}`); } + appendSurfaceBriefContext(parts, ctx); parts.push(buildResolvedContextDirective(ctx, cliOptions, { targetExists })); + appendHookFallback(parts, ctx); if (shouldWarnMissingTarget(ctx, targetProvided, targetExists)) { parts.push(buildMissingTargetDirective()); } - const register = extractRegister(ctx.product); - // The register field survives as a family hint (brand = Persuade/Experience, - // product = Operate/Read); SKILL.md's mode section carries the essentials - // inline, so no register-file read is mandated here. What IS mandated: - // the new-work playbook when no committed design system exists yet. if (!ctx.hasDesign) { parts.push(ctx.hasVisualImplementation - ? 'EXISTING_VISUAL_SYSTEM: PRODUCT.md exists and DESIGN.md is missing, but code contains incumbent visual decisions. ' + - 'Treat CSS, tokens, components, and assets as design authority. Do not route to new identity work merely because the ' + - 'document is absent; preserve and extend the implementation unless the user explicitly asks to discard it.' - : 'NEW_WORK: PRODUCT.md exists but no DESIGN.md or incumbent visual implementation was found. If this task produces ' + - 'new design or a redesign that discards the current look, you MUST read reference/new-work.md before making any ' + - 'design decision. Scoped fixes to existing code do not need it.'); + ? 'INCUMBENT_WORLD_UNDOCUMENTED: PRODUCT.md exists and DESIGN.md is missing, but code contains incumbent visual decisions. ' + + 'For shape or a new-surface/redesign request, load reference/new-work.md: an extension documents and preserves the code-defined world; ' + + 'a redesign replaces it with the user and uses the old look only as evidence and anti-reference. Narrow refinement ' + + 'commands may proceed using the implementation directly.' + : 'WORLD_DISCOVERY_REQUIRED: PRODUCT.md exists but no DESIGN.md or incumbent visual implementation was found. ' + + 'For a new build or redesign, load reference/new-work.md and establish the visual world with the human or structured ' + + 'simulated user before developing the task concept. Scoped fixes to existing code do not need this flow.'); } - if (register) { - parts.push(`REGISTER: \`${register}\` (family hint: brand = Persuade/Experience surfaces, product = Operate/Read). Derive the visitor's mode per SKILL.md.`); - } - const platform = extractPlatform(ctx.product); - const nativeRefs = - platform === 'adaptive' ? ['ios', 'android'] : platform === 'ios' || platform === 'android' ? [platform] : []; - if (nativeRefs.length) { - const refList = nativeRefs.map(p => `\`reference/${p}.md\``).join(' and '); - const label = platform === 'adaptive' ? '`adaptive` (both iOS and Android)' : `\`${platform}\``; + const platformReferences = loadNativePlatformReferences(ctx.platform); + for (const reference of platformReferences) { parts.push( - `NEXT STEP: This project targets ${label}. Also read ${refList} for native conventions, in addition to SKILL.md's mode guidance.`, + `# NATIVE PLATFORM REFERENCE: ${reference.name.toUpperCase()} (reference/${reference.name}.md)\n\n${reference.content.trim()}`, ); - } else if (!platform) { + } + if (!ctx.platform) { // A `## Platform` section that names something we don't recognize (a // toolchain like `flutter`, a typo) would otherwise silently fall back to // web — the wrong default exactly when the user tried to say "native". @@ -1078,6 +1114,72 @@ function pathExistsForTarget(cwd, targetPath) { return fs.existsSync(abs); } +const HOOK_MANIFESTS_BY_PROVIDER = Object.freeze({ + 'claude-code': ['.claude/settings.local.json', '.claude/settings.json'], + codex: ['.codex/hooks.json'], + agents: ['.codex/hooks.json'], + cursor: ['.cursor/hooks.json'], + github: ['.github/hooks/impeccable.json'], +}); + +function truthyEnv(value) { + return typeof value === 'string' && /^(1|true|yes|on)$/i.test(value.trim()); +} + +function valueHasHookMarker(value) { + if (typeof value === 'string') { + return value.includes('skills/impeccable/scripts/hook.mjs') + || value.includes('skills/impeccable/scripts/hook-before-edit.mjs'); + } + if (Array.isArray(value)) return value.some(valueHasHookMarker); + if (value && typeof value === 'object') return Object.values(value).some(valueHasHookMarker); + return false; +} + +function hookEnabledAt(root) { + if (truthyEnv(process.env.IMPECCABLE_HOOK_DISABLED)) return false; + let enabled = true; + for (const name of ['.impeccable/config.json', '.impeccable/config.local.json']) { + const raw = readJson(path.join(root, name)); + if (raw?.hook && Object.prototype.hasOwnProperty.call(raw.hook, 'enabled')) { + enabled = raw.hook.enabled !== false; + } + } + return enabled; +} + +const STOP_REVIEW_PROVIDERS = new Set(['claude-code', 'codex', 'agents']); + +function automaticHookMode(ctx) { + if (ctx.platform === 'ios' || ctx.platform === 'android' || ctx.platform === 'adaptive') { + return 'none'; + } + const activeRoot = path.resolve(ctx.projectRoot || process.cwd()); + if (!hookEnabledAt(activeRoot)) return 'none'; + const manifests = HOOK_MANIFESTS_BY_PROVIDER[IMPECCABLE_PROVIDER_ID] || []; + const roots = [...new Set([process.cwd(), ctx.projectRoot, ctx.repoRoot].filter(Boolean).map((root) => path.resolve(root)))]; + for (const root of roots) { + for (const rel of manifests) { + const raw = readJson(path.join(root, rel)); + if (raw?.hooks && valueHasHookMarker(raw.hooks)) { + return STOP_REVIEW_PROVIDERS.has(IMPECCABLE_PROVIDER_ID) ? 'stop' : 'per-edit'; + } + } + } + return 'none'; +} + +function appendHookFallback(parts, ctx) { + const hookMode = automaticHookMode(ctx); + if (hookMode === 'stop') return; + const native = ctx.platform === 'ios' || ctx.platform === 'android' || ctx.platform === 'adaptive'; + parts.push(renderLlmOnlySlopReview({ + automaticDetector: hookMode === 'per-edit', + manualDetector: hookMode === 'none' && !native, + scriptsPath: path.dirname(fileURLToPath(import.meta.url)), + })); +} + function buildResolvedContextDirective(ctx, options, { targetExists = null } = {}) { const targetPath = hasTargetOption(options) ? options.targetPath : null; return `RESOLVED_CONTEXT:\n${JSON.stringify({ @@ -1087,10 +1189,29 @@ function buildResolvedContextDirective(ctx, options, { targetExists = null } = { repoRoot: ctx.repoRoot, productPath: ctx.productPath, designPath: ctx.designPath, + surfaceBriefPath: ctx.surfaceBriefPath, + surfaceBriefReason: ctx.surfaceBriefReason, + surfaceBriefCandidates: ctx.surfaceBriefCandidates, hasVisualImplementation: ctx.hasVisualImplementation, + platform: ctx.platform, }, null, 2)}`; } +function appendSurfaceBriefContext(parts, ctx) { + if (ctx.hasSurfaceBrief && ctx.surfaceBrief) { + parts.push(`# SURFACE BRIEF (${ctx.surfaceBriefPath})\n\n${ctx.surfaceBrief.trim()}`); + return; + } + if (!ctx.surfaceBriefCandidates?.length) return; + const helper = path.join(path.dirname(fileURLToPath(import.meta.url)), 'surface-brief.mjs'); + parts.push( + 'SURFACE_CONTEXT_AVAILABLE: Persisted surface briefs exist, but none was selected unambiguously for this invocation. ' + + 'Resolve the requested surface to its concrete primary or related source path, then run ' + + `\`node ${helper} read \` once before changing that surface. Candidates:\n` + + JSON.stringify(ctx.surfaceBriefCandidates, null, 2), + ); +} + function shouldWarnMissingTarget(ctx, targetProvided, targetExists = null) { if (ctx.isMonorepo && targetProvided && targetExists === false) return true; return !!( diff --git a/.trae/skills/impeccable/scripts/critique-storage.mjs b/.trae/skills/impeccable/scripts/critique-storage.mjs index 6b4d225cf..38674285a 100644 --- a/.trae/skills/impeccable/scripts/critique-storage.mjs +++ b/.trae/skills/impeccable/scripts/critique-storage.mjs @@ -29,8 +29,9 @@ import fs from 'node:fs'; import path from 'node:path'; import { fileURLToPath, pathToFileURL } from 'node:url'; import { getCritiqueDir } from './lib/impeccable-paths.mjs'; +import { slugFromTarget } from './lib/target-slug.mjs'; -const SLUG_MAX = 50; +export { slugFromTarget } from './lib/target-slug.mjs'; /** * Mechanically derive a slug from a resolved target. Returns null if the @@ -40,46 +41,6 @@ const SLUG_MAX = 50; * concrete artifact before calling this — we never slug a natural-language * phrase. */ -export function slugFromTarget(resolved, { cwd = process.cwd() } = {}) { - if (!resolved || typeof resolved !== 'string') return null; - const trimmed = resolved.trim(); - if (!trimmed) return null; - - // URL - if (/^https?:\/\//i.test(trimmed)) { - let url; - try { url = new URL(trimmed); } catch { return null; } - const hostPath = `${url.hostname}${url.pathname}`; - return kebab(hostPath); - } - - // File path. Make it project-relative so two devs critiquing the same - // checkout get the same slug regardless of where their repo is cloned. - const abs = path.isAbsolute(trimmed) ? trimmed : path.resolve(cwd, trimmed); - let rel = path.relative(cwd, abs); - // If the target is outside cwd, fall back to the basename so we still - // produce a stable slug (vs the absolute path, which would include - // home dirs / usernames). - if (rel.startsWith('..') || path.isAbsolute(rel)) { - rel = path.basename(abs); - } - if (!rel || rel === '.' || rel === '') return null; - return kebab(rel); -} - -function kebab(s) { - const slug = s - .toLowerCase() - .replace(/[/\\.]+/g, '-') - .replace(/[^a-z0-9-]+/g, '-') - .replace(/-+/g, '-') - .replace(/^-|-$/g, ''); - if (!slug) return null; - // Cap from the tail — the tail (filename) is more identifying than the - // top-level directory. - return slug.length <= SLUG_MAX ? slug : slug.slice(slug.length - SLUG_MAX).replace(/^-/, ''); -} - /** * Filename-safe UTC ISO timestamp: hyphens for separators, trailing Z. * Plain colons aren't allowed on Windows filesystems. diff --git a/.trae/skills/impeccable/scripts/detector/detect-antipatterns-browser.js b/.trae/skills/impeccable/scripts/detector/detect-antipatterns-browser.js index 033573e36..76bf4ddeb 100644 --- a/.trae/skills/impeccable/scripts/detector/detect-antipatterns-browser.js +++ b/.trae/skills/impeccable/scripts/detector/detect-antipatterns-browser.js @@ -312,17 +312,6 @@ const ANTIPATTERNS = [ skillSection: 'Layout & Space', skillGuideline: 'numbered section markers', }, - { - id: 'numbered-section-markers', - category: 'slop', - scopes: ['layout'], - severity: 'advisory', - name: 'Numbered section markers (01 / 02 / 03)', - description: - 'Numbered display markers as section labels (01, 02, 03) are the AI editorial scaffold one tier deeper than tracked eyebrow chips. If you find yourself reaching for them, choose a different section cadence.', - skillSection: 'Layout & Space', - skillGuideline: 'numbered section markers', - }, { id: 'em-dash-overuse', category: 'slop', diff --git a/.trae/skills/impeccable/scripts/detector/engines/regex/detect-text.mjs b/.trae/skills/impeccable/scripts/detector/engines/regex/detect-text.mjs index 81c30ab99..f641d5917 100644 --- a/.trae/skills/impeccable/scripts/detector/engines/regex/detect-text.mjs +++ b/.trae/skills/impeccable/scripts/detector/engines/regex/detect-text.mjs @@ -280,22 +280,6 @@ const REGEX_ANALYZERS = [ if (count === 0) return []; return [finding('marketing-buzzword', filePath, `${count} buzzword phrase${count === 1 ? '' : 's'}: "${firstSample}"`)]; }, - // Numbered section markers (01 / 02 / 03 ...) - (content, filePath) => { - const text = stripHtmlToText(content); - const re = /\b(0[1-9]|1[0-2])\b/g; - const seen = new Set(); - let m; - while ((m = re.exec(text)) !== null) seen.add(m[1]); - if (seen.size < 3) return []; - const sorted = [...seen].sort(); - let sequential = 0; - for (let i = 1; i < sorted.length; i++) { - if (parseInt(sorted[i], 10) === parseInt(sorted[i - 1], 10) + 1) sequential++; - } - if (sequential < 2) return []; - return [finding('numbered-section-markers', filePath, `Sequence: ${sorted.slice(0, 6).join(', ')}`)]; - }, // Aphoristic cadence: manufactured-contrast + short-rebuttal (content, filePath) => { const text = stripHtmlToText(content); @@ -430,21 +414,20 @@ function runRegexMatchers(lines, filePath, lineOffset = 0, blockContext = null, } /** Page-level analyzers that scan rendered text content (em-dash use, - * buzzword phrases, numbered section markers, aphoristic cadence). + * buzzword phrases, aphoristic cadence). * These are detector-agnostic — they work on any HTML/text source * and don't need a parsed DOM. Exported so detectHtml can call them * for `.html` files (which otherwise skip the regex engine). */ const TEXT_CONTENT_ANALYZER_IDS = [ 'em-dash-overuse', 'marketing-buzzword', - 'numbered-section-markers', 'aphoristic-cadence', ]; function runTextContentAnalyzers(content, filePath, options = {}) { const profile = options?.profile; if (!shouldRunPageAnalyzers(content, filePath)) return []; - // The 4 text-content analyzers are at indices 3-6 in REGEX_ANALYZERS. + // The 3 text-content analyzers are at indices 3-5 in REGEX_ANALYZERS. const findings = []; for (let i = 0; i < TEXT_CONTENT_ANALYZER_IDS.length; i++) { const analyzer = REGEX_ANALYZERS[3 + i]; @@ -535,7 +518,6 @@ function detectText(content, filePath, options = {}) { 'monotonous-spacing', 'em-dash-overuse', 'marketing-buzzword', - 'numbered-section-markers', 'aphoristic-cadence', 'dark-glow', ]; diff --git a/.trae/skills/impeccable/scripts/detector/registry/antipatterns.mjs b/.trae/skills/impeccable/scripts/detector/registry/antipatterns.mjs index b85f7e62f..84c69b0b8 100644 --- a/.trae/skills/impeccable/scripts/detector/registry/antipatterns.mjs +++ b/.trae/skills/impeccable/scripts/detector/registry/antipatterns.mjs @@ -210,17 +210,6 @@ const ANTIPATTERNS = [ skillSection: 'Layout & Space', skillGuideline: 'numbered section markers', }, - { - id: 'numbered-section-markers', - category: 'slop', - scopes: ['layout'], - severity: 'advisory', - name: 'Numbered section markers (01 / 02 / 03)', - description: - 'Numbered display markers as section labels (01, 02, 03) are the AI editorial scaffold one tier deeper than tracked eyebrow chips. If you find yourself reaching for them, choose a different section cadence.', - skillSection: 'Layout & Space', - skillGuideline: 'numbered section markers', - }, { id: 'em-dash-overuse', category: 'slop', diff --git a/.trae/skills/impeccable/scripts/detector/rules/checks.mjs b/.trae/skills/impeccable/scripts/detector/rules/checks.mjs index 3fdd6b2dd..9bace11b3 100644 --- a/.trae/skills/impeccable/scripts/detector/rules/checks.mjs +++ b/.trae/skills/impeccable/scripts/detector/rules/checks.mjs @@ -352,27 +352,57 @@ function isAccentColor(cssColor) { return false; } +function resolveHeroHeadingSizePx(value) { + const input = String(value || '').trim().toLowerCase(); + if (!input) return 0; + + const simpleLengthPx = (token) => { + const match = /^(-?\d*\.?\d+)\s*(px|rem|em|%)?$/.exec(String(token || '').trim()); + if (!match) return null; + const amount = Number(match[1]); + if (!Number.isFinite(amount)) return null; + if (match[2] === 'rem' || match[2] === 'em') return amount * 16; + if (match[2] === '%') return amount * 0.16; + return amount; + }; + + const direct = simpleLengthPx(input); + if (direct !== null) return direct; + + // Static CSS engines cannot resolve viewport units, but clamp's min/max + // bounds still tell us whether the heading can ever reach hero scale. + const clamp = /^clamp\((.*)\)$/.exec(input); + if (clamp) { + const parts = clamp[1].split(','); + if (parts.length === 3) { + const bounds = [simpleLengthPx(parts[0]), simpleLengthPx(parts[2])] + .filter((candidate) => candidate !== null); + if (bounds.length > 0) return Math.max(...bounds); + } + } + + return 0; +} + // Sibling-relationship rule. Anchor on a hero-scale h1, look at the // previousElementSibling, and gate on EITHER the classic tracked- // uppercase eyebrow OR the modern accent-colored bold eyebrow. function checkHeroEyebrow(opts) { const { headingTag, headingText, headingFontSize, + headingInApplicationContext, siblingTag, siblingText, siblingTextTransform, siblingFontSize, siblingLetterSpacing, siblingFontWeight, siblingColor, siblingHasAccentDashPseudo, } = opts; if (headingTag !== 'h1') return []; - // We previously gated on headingFontSize >= 48 to anchor "hero scale". - // But modern hero h1s use clamp() / vw / var(--text-*), none of which - // jsdom can resolve — the computed value comes back as "2em" or - // "var(--text-9xl)" and parseFloat returns 2 or NaN. The gate fails - // on virtually every Tailwind v4 / framework build. The other gates - // (sibling text 2-60 chars, font-size ≤ 14px, accent-bold OR - // tracked-caps) are tight enough to avoid false positives on non- - // hero h1s — a tiny tan label directly above any h1 is the - // antipattern regardless of how big the h1 ends up. + // This is specifically a marketing-hero cliché, not a ban on compact + // context labels in product UI (for example, a station name inside a tab + // panel). Browser-computed sizes are reliable; the static adapter also + // resolves ordinary px/rem/em and clamp() bounds before reaching here. + if (headingInApplicationContext) return []; + if (!(headingFontSize >= 48)) return []; if (!siblingTag) return []; // An h2 above an h1 is a different anti-pattern (heading hierarchy / dual // headings) — never an eyebrow. @@ -1938,6 +1968,7 @@ function checkElementHeroEyebrowDOM(el) { headingTag: tag, headingText: el.textContent || '', headingFontSize: parseFloat(headStyle.fontSize) || 0, + headingInApplicationContext: !!el.closest('[role="tabpanel"], [role="dialog"], [role="application"], dialog'), siblingTag: sibling.tagName.toLowerCase(), siblingText: sibling.textContent || '', siblingTextTransform: sibStyle.textTransform || '', @@ -3367,7 +3398,8 @@ function checkElementHeroEyebrow(el, style, tag, window, customPropMap) { return checkHeroEyebrow({ headingTag: tag, headingText: el.textContent || '', - headingFontSize: parseFloat(headingFontSizeRaw) || 0, + headingFontSize: resolveHeroHeadingSizePx(headingFontSizeRaw), + headingInApplicationContext: !!el.closest?.('[role="tabpanel"], [role="dialog"], [role="application"], dialog'), siblingTag: sibling.tagName.toLowerCase(), siblingText: sibling.textContent || '', siblingTextTransform: sibStyle.textTransform || '', diff --git a/.trae/skills/impeccable/scripts/hook-lib.mjs b/.trae/skills/impeccable/scripts/hook-lib.mjs index e61ea692b..50aa2f8fb 100644 --- a/.trae/skills/impeccable/scripts/hook-lib.mjs +++ b/.trae/skills/impeccable/scripts/hook-lib.mjs @@ -46,6 +46,7 @@ import path from 'node:path'; import { pathToFileURL, fileURLToPath } from 'node:url'; import { extractPlatform, loadContext } from './context.mjs'; import { IMPECCABLE_COMMAND } from './lib/provider.mjs'; +import { renderStopSlopReview } from './lib/slop-review.mjs'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); @@ -1868,22 +1869,42 @@ export const CONTRACT_MAX_CHARS = 1800; // Cap contract sections per Stop emission so many touched artifacts cannot // stack an unbounded message. export const CONTRACT_AUDIT_MAX_FILES = 3; +export const CONTRACT_EXTS = new Set(['.html', '.htm', '.astro', '.svelte', '.vue', '.jsx', '.tsx']); +export const CONTRACT_REQUIRED_FIELDS = ['UNIQUE', 'NOT-TEMPLATE', 'OWN-WORLD', 'STORY', 'FIRST VIEWPORT', 'FORM']; /** - * Extract the artifact's own direction-contract comment: the first HTML - * comment in the head of the file, when its opening chars identify it as a - * contract/concept block. Returns the trimmed, length-capped body, or null - * when the file carries none (no comment, unclosed comment, marker missing, - * or the comment starts past the head window). + * Extract the artifact's own direction-contract comment from the head of an + * HTML or component file. Supports HTML-family comments and JSX block + * comments so the contract works in the Astro/Svelte/Vue/React scaffolds the + * skill actually builds. Returns the trimmed, length-capped body, or null + * when the file carries no valid contract block. */ export function extractDirectionContract(content) { if (typeof content !== 'string' || !content) return null; const head = content.slice(0, CONTRACT_HEAD_CHARS); - const m = //.exec(head); - if (!m) return null; - const body = m[1].trim(); - if (!body || !/contract|concept/i.test(body.slice(0, CONTRACT_MARKER_CHARS))) return null; - return body.slice(0, CONTRACT_MAX_CHARS); + const candidates = []; + for (const pattern of [//g, /\{\/\*([\s\S]*?)\*\/\}/g]) { + for (const match of head.matchAll(pattern)) { + const index = match.index ?? 0; + const linePrefix = head.slice(head.lastIndexOf('\n', index - 1) + 1, index).trim(); + if (linePrefix.startsWith('//')) continue; + candidates.push({ index, body: match[1].trim() }); + } + } + candidates.sort((a, b) => a.index - b.index); + for (const candidate of candidates) { + if (!candidate.body || !/direction\s+contract|concept\s+contract/i.test(candidate.body.slice(0, CONTRACT_MARKER_CHARS))) continue; + return candidate.body.slice(0, CONTRACT_MAX_CHARS); + } + return null; +} + +export function missingDirectionContractFields(contract) { + const body = typeof contract === 'string' ? contract : ''; + return CONTRACT_REQUIRED_FIELDS.filter((field) => { + const label = field.replace(/[.*+?^${}()|[\]\\]/g, '\\$&').replace(/\s+/g, '\\s+'); + return !new RegExp(`\\b${label}\\s*:`, 'i').test(body); + }); } /** @@ -1899,7 +1920,11 @@ export function renderContractAudit(entries, opts = {}) { const shown = entries.slice(0, CONTRACT_AUDIT_MAX_FILES); const blocks = shown.map(({ filePath, contract }) => { const display = relativize(filePath, cwd); - return `${display} opens with this direction contract, written when the direction was decided:\n\n${contract}`; + const missing = missingDirectionContractFields(contract); + const integrity = missing.length > 0 + ? `\n\nContract integrity defect: missing ${missing.join(', ')}. Repair the contract and the implementation together.` + : ''; + return `${display} opens with this direction contract, written when the direction was decided:\n\n${contract}${integrity}`; }); return [ `${ENVELOPE_PREFIX} Direction-contract audit. Before finishing, audit the rendered page against the contract it opens with, promise by promise.`, @@ -1984,9 +2009,22 @@ export async function runStopHook({ stdinJson, env = {}, cwd = process.cwd(), no return result({ skipped: 'native-platform', platform, durationMs: Date.now() - started }); } + const session = ensureSession(cache, sessionId); + const needsSlopReview = session.llmSlopReviewed !== true; + const det = detector || await loadDetector(); if (!det || typeof det.detectText !== 'function') { - return result({ skipped: 'detector-missing', durationMs: Date.now() - started }); + if (!needsSlopReview) return result({ skipped: 'detector-missing', durationMs: Date.now() - started }); + session.llmSlopReviewed = true; + session.updatedAt = Date.now(); + persistCache(projectCwd, cache); + const text = renderStopSlopReview(); + return { + exitCode: 0, + stdout: payload(text, 'Stop', harness), + emission: { kind: 'stop-llm-slop-review', llmSlopReview: true }, + audit: { ...audit, emitted: true, detectorMissing: true, llmSlopReview: true, chars: text.length, durationMs: Date.now() - started }, + }; } const scanOptions = designSystemOptions(config, det, projectCwd); @@ -2013,10 +2051,11 @@ export async function runStopHook({ stdinJson, env = {}, cwd = process.cwd(), no ? configuredExt.engine === 'html' : (ext === '.html' || ext === '.htm'); - // Direction-contract audit: HTML artifacts only, at most once per file + // Direction-contract audit: HTML and component artifacts, at most once per file // per session. The flag lives on the same session cache entry the // finding dedupe uses, so a second Stop fire stays quiet about it. - if (useHtmlEngine) { + const contractCapable = useHtmlEngine || CONTRACT_EXTS.has(ext); + if (contractCapable) { const fileEntry = ensureFile(cache, sessionId, filePath); if (!fileEntry.contractAudited) { const contract = extractDirectionContract(content); @@ -2046,10 +2085,15 @@ export async function runStopHook({ stdinJson, env = {}, cwd = process.cwd(), no } audit.scannedFiles = scanned; - if (freshGroups.length === 0 && contractEntries.length === 0) { + if (freshGroups.length === 0 && contractEntries.length === 0 && !needsSlopReview) { return result({ emitted: false, skipped: 'stop-clean', durationMs: Date.now() - started }); } + if (needsSlopReview) { + session.llmSlopReviewed = true; + session.updatedAt = Date.now(); + } + // Fresh findings and first-time contract audits earn the cache write; // both mark this batch as surfaced so the next Stop fire is silent // unless new issues appear. @@ -2064,6 +2108,9 @@ export async function runStopHook({ stdinJson, env = {}, cwd = process.cwd(), no if (contractEntries.length > 0) { parts.push(renderContractAudit(contractEntries, { cwd: projectCwd })); } + if (needsSlopReview) { + parts.push(renderStopSlopReview()); + } const text = appendDesignSystemNote(parts.join('\n\n'), scanOptions); return { exitCode: 0, @@ -2074,6 +2121,7 @@ export async function runStopHook({ stdinJson, env = {}, cwd = process.cwd(), no ...(contractEntries.length > 0 ? { contractFiles: contractEntries.map((entry) => entry.filePath) } : {}), + ...(needsSlopReview ? { llmSlopReview: true } : {}), }, audit: { ...audit, @@ -2081,6 +2129,7 @@ export async function runStopHook({ stdinJson, env = {}, cwd = process.cwd(), no freshFiles: freshGroups.length, freshFindings: freshGroups.reduce((sum, group) => sum + group.findings.length, 0), ...(contractEntries.length > 0 ? { contractAudits: contractEntries.length } : {}), + ...(needsSlopReview ? { llmSlopReview: true } : {}), chars: text.length, durationMs: Date.now() - started, }, diff --git a/.trae/skills/impeccable/scripts/lib/provider.mjs b/.trae/skills/impeccable/scripts/lib/provider.mjs index 7fa928a9c..d4aecb4e2 100644 --- a/.trae/skills/impeccable/scripts/lib/provider.mjs +++ b/.trae/skills/impeccable/scripts/lib/provider.mjs @@ -1,4 +1,5 @@ // Source scripts default to slash commands. The provider build replaces only // this exact declaration, avoiding heuristic rewrites across executable code. export const IMPECCABLE_COMMAND_PREFIX = "/"; +export const IMPECCABLE_PROVIDER_ID = "trae"; export const IMPECCABLE_COMMAND = `${IMPECCABLE_COMMAND_PREFIX}impeccable`; diff --git a/.trae/skills/impeccable/scripts/lib/slop-review.mjs b/.trae/skills/impeccable/scripts/lib/slop-review.mjs new file mode 100644 index 000000000..30f03e34d --- /dev/null +++ b/.trae/skills/impeccable/scripts/lib/slop-review.mjs @@ -0,0 +1,30 @@ +export const LLM_ONLY_SLOP_ITEMS = Object.freeze([ + 'Monospace used merely to signal “technical” or “developer.”', + 'Light or dark mode chosen by category habit rather than the actual use scene.', + 'Everything wrapped in cards, or identical icon-heading-text cards repeated as the page structure.', + 'Hero-metric scaffolds: one oversized number, a small label, supporting stats, and an accent treatment.', + 'Decorative glassmorphism, meaningless sparklines, or generic rounded rectangles with drop shadows.', + 'A modal chosen by reflex when the task does not require interruption or protected focus.', +]); + +export function renderLlmOnlySlopReview({ manualDetector = false, automaticDetector = false, scriptsPath = null } = {}) { + const lines = [ + automaticDetector + ? 'AI_SLOP_REVIEW_REQUIRED: The automatic detector covers mechanical rules, but this harness has no reliable late review. Before finishing changed UI, inspect the authored result for detector-blind model reflexes:' + : 'AI_SLOP_REVIEW_REQUIRED: The automatic Impeccable design hook is not available for this session. Before finishing changed UI, inspect the authored result for detector-blind model reflexes:', + ...LLM_ONLY_SLOP_ITEMS.map((item) => `- ${item}`), + 'Fix reflexes, not intentional choices required by the brief or established visual authority.', + ]; + if (manualDetector && scriptsPath) { + lines.push(`Then run the mechanical detector once over the changed web UI: \`node ${scriptsPath}/detect.mjs --json \`. Do not run it earlier during concept selection.`); + } + return lines.join('\n'); +} + +export function renderStopSlopReview() { + return [ + '[impeccable@1] Detector-blind AI-slop review. The mechanical pass is complete; now inspect the rendered result for model reflexes it cannot reliably detect:', + ...LLM_ONLY_SLOP_ITEMS.map((item) => `- ${item}`), + 'Fix reflexes, not intentional choices required by the brief or established visual authority. Do not rerun the detector; this is the authored judgment layer.', + ].join('\n'); +} diff --git a/.trae/skills/impeccable/scripts/lib/surface-briefs.mjs b/.trae/skills/impeccable/scripts/lib/surface-briefs.mjs new file mode 100644 index 000000000..f83416f69 --- /dev/null +++ b/.trae/skills/impeccable/scripts/lib/surface-briefs.mjs @@ -0,0 +1,151 @@ +import fs from 'node:fs'; +import path from 'node:path'; +import { slugFromTarget } from './target-slug.mjs'; + +export const SURFACE_BRIEF_VERSION = 1; + +export function getSurfaceBriefDir(projectRoot) { + return path.join(projectRoot, '.impeccable', 'surfaces'); +} + +export function normalizeSurfaceTarget(target, { projectRoot = process.cwd() } = {}) { + if (!target || typeof target !== 'string' || !target.trim()) return null; + const trimmed = target.trim(); + if (/^https?:\/\//i.test(trimmed)) { + try { + const url = new URL(trimmed); + url.hash = ''; + url.search = ''; + return url.toString().replace(/\/$/, '') || url.origin; + } catch { + return null; + } + } + if (/^route:/i.test(trimmed)) { + const route = trimmed.slice(trimmed.indexOf(':') + 1).trim(); + if (!route.startsWith('/') || route.includes('..')) return null; + const normalizedRoute = route.split(/[?#]/, 1)[0].replace(/\/{2,}/g, '/').replace(/\/$/, '') || '/'; + return `route:${normalizedRoute}`; + } + if (trimmed === '/') return 'route:/'; + if (trimmed.startsWith('/')) { + const absolute = path.resolve(trimmed); + const relativeToProject = path.relative(projectRoot, absolute); + const isProjectFile = relativeToProject && !relativeToProject.startsWith('..') && !path.isAbsolute(relativeToProject); + if (!isProjectFile && !fs.existsSync(absolute) && !trimmed.includes('..')) { + const normalizedRoute = trimmed.split(/[?#]/, 1)[0].replace(/\/{2,}/g, '/').replace(/\/$/, '') || '/'; + return `route:${normalizedRoute}`; + } + } + const abs = path.isAbsolute(trimmed) ? trimmed : path.resolve(projectRoot, trimmed); + const rel = path.relative(projectRoot, abs); + if (!rel || rel === '.' || rel.startsWith('..') || path.isAbsolute(rel)) return null; + return rel.split(path.sep).join('/'); +} + +export function surfaceBriefPathForTarget(target, { projectRoot = process.cwd() } = {}) { + const normalized = normalizeSurfaceTarget(target, { projectRoot }); + if (!normalized) return null; + const slugInput = normalized.startsWith('route:') ? `route${normalized.slice('route:'.length)}` : normalized; + const slug = slugFromTarget(slugInput, { cwd: projectRoot }); + return slug ? path.join(getSurfaceBriefDir(projectRoot), `${slug}.md`) : null; +} + +export function parseSurfaceBrief(text, filePath = null) { + const match = String(text || '').match(/^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/); + const meta = {}; + if (match) { + for (const line of match[1].split(/\r?\n/)) { + const colon = line.indexOf(':'); + if (colon < 0) continue; + const key = line.slice(0, colon).trim(); + const raw = line.slice(colon + 1).trim(); + if (!key) continue; + if (/^(?:\[|\{|\")/.test(raw) || /^(?:true|false|null|-?\d+(?:\.\d+)?)$/.test(raw)) { + try { meta[key] = JSON.parse(raw); continue; } catch { /* keep string */ } + } + meta[key] = raw.replace(/^['"]|['"]$/g, ''); + } + } + const primaryTarget = typeof meta.primary_target === 'string' ? meta.primary_target : null; + const relatedTargets = Array.isArray(meta.related_targets) + ? meta.related_targets.filter((value) => typeof value === 'string') + : []; + return { + path: filePath, + text: String(text || ''), + body: match ? String(text || '').slice(match[0].length).trim() : String(text || '').trim(), + meta, + slug: typeof meta.slug === 'string' ? meta.slug : filePath ? path.basename(filePath, '.md') : null, + primaryTarget, + relatedTargets, + targets: [primaryTarget, ...relatedTargets].filter(Boolean), + }; +} + +export function listSurfaceBriefs(projectRoot = process.cwd()) { + const dir = getSurfaceBriefDir(projectRoot); + let names; + try { + names = fs.readdirSync(dir).filter((name) => name.endsWith('.md')).sort(); + } catch { + return []; + } + return names.flatMap((name) => { + const filePath = path.join(dir, name); + try { + return [parseSurfaceBrief(fs.readFileSync(filePath, 'utf-8'), filePath)]; + } catch { + return []; + } + }); +} + +export function resolveSurfaceBrief(projectRoot = process.cwd(), target = null) { + const briefs = listSurfaceBriefs(projectRoot); + if (!target) { + return { + brief: briefs.length === 1 ? briefs[0] : null, + candidates: briefs, + reason: briefs.length === 1 ? 'only-brief' : briefs.length > 1 ? 'ambiguous' : 'none', + }; + } + + const normalized = normalizeSurfaceTarget(target, { projectRoot }); + if (!normalized) return { brief: null, candidates: briefs, reason: 'invalid-target' }; + const exactPath = surfaceBriefPathForTarget(normalized, { projectRoot }); + const exact = briefs.find((brief) => brief.path === exactPath && (!brief.targets.length || brief.targets.includes(normalized))); + if (exact) return { brief: exact, candidates: briefs, reason: 'slug' }; + const mapped = briefs.filter((brief) => brief.targets.includes(normalized)); + return { + brief: mapped.length === 1 ? mapped[0] : null, + candidates: mapped.length > 1 ? mapped : briefs, + reason: mapped.length === 1 ? 'mapping' : mapped.length > 1 ? 'ambiguous-target' : 'not-found', + }; +} + +export function writeSurfaceBrief({ + projectRoot = process.cwd(), + primaryTarget, + relatedTargets = [], + body, +}) { + const normalizedPrimary = normalizeSurfaceTarget(primaryTarget, { projectRoot }); + if (!normalizedPrimary) throw new Error('surface brief requires a concrete project-relative primary target or URL'); + const normalizedRelated = [...new Set(relatedTargets + .map((target) => normalizeSurfaceTarget(target, { projectRoot })) + .filter((target) => target && target !== normalizedPrimary))]; + const slug = slugFromTarget(normalizedPrimary, { cwd: projectRoot }); + const filePath = surfaceBriefPathForTarget(normalizedPrimary, { projectRoot }); + fs.mkdirSync(path.dirname(filePath), { recursive: true }); + const frontmatter = [ + '---', + `version: ${SURFACE_BRIEF_VERSION}`, + `slug: ${JSON.stringify(slug)}`, + `primary_target: ${JSON.stringify(normalizedPrimary)}`, + `related_targets: ${JSON.stringify(normalizedRelated)}`, + '---', + ].join('\n'); + fs.writeFileSync(filePath, `${frontmatter}\n\n${String(body || '').trim()}\n`, 'utf-8'); + return filePath; +} diff --git a/.trae/skills/impeccable/scripts/lib/target-slug.mjs b/.trae/skills/impeccable/scripts/lib/target-slug.mjs new file mode 100644 index 000000000..025915ad5 --- /dev/null +++ b/.trae/skills/impeccable/scripts/lib/target-slug.mjs @@ -0,0 +1,33 @@ +import path from 'node:path'; + +const SLUG_MAX = 50; + +/** Derive one clone-stable slug from a concrete file path or URL. */ +export function slugFromTarget(resolved, { cwd = process.cwd() } = {}) { + if (!resolved || typeof resolved !== 'string') return null; + const trimmed = resolved.trim(); + if (!trimmed) return null; + + if (/^https?:\/\//i.test(trimmed)) { + let url; + try { url = new URL(trimmed); } catch { return null; } + return kebab(`${url.hostname}${url.pathname}`); + } + + const abs = path.isAbsolute(trimmed) ? trimmed : path.resolve(cwd, trimmed); + let rel = path.relative(cwd, abs); + if (rel.startsWith('..') || path.isAbsolute(rel)) rel = path.basename(abs); + if (!rel || rel === '.') return null; + return kebab(rel); +} + +export function kebab(value) { + const slug = String(value || '') + .toLowerCase() + .replace(/[/\\.]+/g, '-') + .replace(/[^a-z0-9-]+/g, '-') + .replace(/-+/g, '-') + .replace(/^-|-$/g, ''); + if (!slug) return null; + return slug.length <= SLUG_MAX ? slug : slug.slice(slug.length - SLUG_MAX).replace(/^-/, ''); +} diff --git a/.trae/skills/impeccable/scripts/live-browser.js b/.trae/skills/impeccable/scripts/live-browser.js index 616f1290a..47bbfefbf 100644 --- a/.trae/skills/impeccable/scripts/live-browser.js +++ b/.trae/skills/impeccable/scripts/live-browser.js @@ -2592,7 +2592,7 @@ minWidth: '16px', height: '16px', padding: '0 4px', borderRadius: '999px', background: tuneOpen ? C.brand : BP.hairline, - color: tuneOpen ? 'oklch(98% 0 0)' : 'inherit', + color: tuneOpen ? C.ink : 'inherit', fontFamily: MONO, fontSize: '9.5px', fontWeight: '600', lineHeight: '1', boxSizing: 'border-box', @@ -3131,7 +3131,7 @@ position: 'absolute', top: '2px', left: initial ? '18px' : '2px', width: '16px', height: '16px', borderRadius: '50%', - background: 'oklch(98% 0 0)', + background: C.ink, transition: 'left 0.18s ' + EASE, boxShadow: '0 1px 2px oklch(0% 0 0 / 0.2)', }); @@ -3165,7 +3165,7 @@ const b = el('button', { padding: '5px 4px', border: 'none', borderRadius: '3px', background: active ? C.brand : 'transparent', - color: active ? 'oklch(98% 0 0)' : P.text, + color: active ? C.ink : P.text, fontFamily: FONT, fontSize: '10.5px', fontWeight: '500', cursor: 'pointer', whiteSpace: 'nowrap', transition: 'background 0.1s ease, color 0.1s ease', @@ -3178,7 +3178,7 @@ segBtns.forEach(({ btn, val }) => { const on = val === o.value; btn.style.background = on ? C.brand : 'transparent'; - btn.style.color = on ? 'oklch(98% 0 0)' : P.text; + btn.style.color = on ? C.ink : P.text; }); applyParamValue(variantEl, p, o.value); queueCheckpoint('param_changed'); diff --git a/.trae/skills/impeccable/scripts/surface-brief.mjs b/.trae/skills/impeccable/scripts/surface-brief.mjs new file mode 100644 index 000000000..723f7c1b4 --- /dev/null +++ b/.trae/skills/impeccable/scripts/surface-brief.mjs @@ -0,0 +1,74 @@ +#!/usr/bin/env node +import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath, pathToFileURL } from 'node:url'; +import { resolveProjectRoot } from './context.mjs'; +import { + listSurfaceBriefs, + resolveSurfaceBrief, + surfaceBriefPathForTarget, + writeSurfaceBrief, +} from './lib/surface-briefs.mjs'; + +function summary(brief, projectRoot) { + return { + slug: brief.slug, + path: path.relative(projectRoot, brief.path).split(path.sep).join('/'), + primaryTarget: brief.primaryTarget, + relatedTargets: brief.relatedTargets, + }; +} + +function main(argv) { + const [command, target, bodyFile, ...relatedTargets] = argv; + const projectRoot = resolveProjectRoot(process.cwd(), target ? { targetPath: target } : {}); + if (command === 'path') { + const filePath = surfaceBriefPathForTarget(target, { projectRoot }); + if (!filePath) throw new Error('surface brief path requires a concrete target'); + process.stdout.write(`${path.relative(process.cwd(), filePath) || filePath}\n`); + return; + } + if (command === 'list') { + process.stdout.write(`${JSON.stringify(listSurfaceBriefs(projectRoot).map((brief) => summary(brief, projectRoot)), null, 2)}\n`); + return; + } + if (command === 'read') { + const result = resolveSurfaceBrief(projectRoot, target || null); + if (result.brief) { + process.stdout.write(result.brief.text); + return; + } + if (result.candidates.length) process.stderr.write(`${JSON.stringify(result.candidates.map((brief) => summary(brief, projectRoot)), null, 2)}\n`); + process.exit(2); + } + if (command === 'write') { + if (!target || !bodyFile) throw new Error('usage: surface-brief.mjs write '); + const filePath = writeSurfaceBrief({ + projectRoot, + primaryTarget: target, + relatedTargets, + body: fs.readFileSync(bodyFile, 'utf-8'), + }); + process.stdout.write(`${path.relative(process.cwd(), filePath) || filePath}\n`); + return; + } + throw new Error('usage: surface-brief.mjs [target] [body-file] [related-target ...]'); +} + +function isMainModule() { + if (!process.argv[1]) return false; + try { + return fs.realpathSync(fileURLToPath(import.meta.url)) === fs.realpathSync(process.argv[1]); + } catch { + return import.meta.url === pathToFileURL(process.argv[1]).href; + } +} + +if (isMainModule()) { + try { + main(process.argv.slice(2)); + } catch (error) { + process.stderr.write(`${error?.message || error}\n`); + process.exit(1); + } +} diff --git a/astro.config.mjs b/astro.config.mjs index 3a45f8976..208c80155 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -1,5 +1,6 @@ import { defineConfig } from 'astro/config'; import { impeccableShikiThemes } from './site/lib/impeccable-shiki-theme.mjs'; +import { worldsReviewPlugin } from './scripts/worlds-review-vite-plugin.mjs'; export default defineConfig({ srcDir: './site', @@ -19,6 +20,7 @@ export default defineConfig({ }, outDir: './build', vite: { + plugins: [worldsReviewPlugin()], build: { assetsInlineLimit: 0, }, diff --git a/plugin/.claude-plugin/plugin.json b/plugin/.claude-plugin/plugin.json index 2604b8470..9c1b5b430 100644 --- a/plugin/.claude-plugin/plugin.json +++ b/plugin/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "impeccable", "description": "Design fluency for frontend development. 1 skill with 23 commands (/impeccable polish, /impeccable audit, /impeccable critique, etc.) and curated anti-pattern detection.", - "version": "4.0.0-alpha.9", + "version": "4.0.0-alpha.10", "author": { "name": "Paul Bakaus", "email": "paul@paulbakaus.com" diff --git a/plugin/skills/impeccable/SKILL.md b/plugin/skills/impeccable/SKILL.md index 80a242b4e..aaff837d5 100644 --- a/plugin/skills/impeccable/SKILL.md +++ b/plugin/skills/impeccable/SKILL.md @@ -1,7 +1,7 @@ --- name: impeccable description: Use when the user wants to design, redesign, shape, critique, audit, polish, clarify, distill, harden, optimize, adapt, animate, colorize, extract, or otherwise improve a frontend interface. Covers websites, landing pages, dashboards, product UI, app shells, components, forms, settings, onboarding, and empty states. Handles UX review, visual hierarchy, information architecture, cognitive load, accessibility, performance, responsive behavior, theming, anti-patterns, typography, fonts, spacing, layout, alignment, color, motion, micro-interactions, UX copy, error states, edge cases, i18n, and reusable design systems or tokens. Also use for bland designs that need to become bolder or more delightful, loud designs that should become quieter, live browser iteration on UI elements, or ambitious visual effects that should feel technically extraordinary. Not for backend-only or non-UI tasks. -version: 4.0.0-alpha.9 +version: 4.0.0-alpha.10 user-invocable: true argument-hint: "[craft|shape · audit|critique · animate|bolder|colorize|delight|layout|overdrive|quieter|typeset · adapt|clarify|distill · harden|onboard|optimize|polish · init|document|extract|live] [target]" license: Apache 2.0 @@ -10,56 +10,55 @@ allowed-tools: - Bash(node .claude/skills/impeccable/scripts/*) --- -Designs and iterates production-grade frontend interfaces. Real working code, committed design choices, exceptional craft. +This skill gives you the tools and permission to create design that earns to be called out-of-distribution craft: Whereas before, your design work would have been safe, timid and measured, you now approach every design task as a award-winning design director with impeccable understanding for what makes exceptional design work: production-grade code, peak creativity, a clear POV, deep understanding of the needs of the client and users, and exceptional craft. -Approach every design task as the design lead at a small studio known for giving every client a visual identity that could not be mistaken for anyone else's. The client has already rejected work that felt templated; they are paying for a point of view. Claude is capable of extraordinary work. Don't hold back. +Core principles: +- Go all out. No hedging, no shortcuts. The deliverable must be complete (except assets the user must provide). +- Dream big and bold. Distinct, beautiful, outstanding and highly inspiring work. +- Iterate with tools available to you (e.g. visual understanding, browser screenshots) until you think this meets the bar. ## Setup -1. Run `node .claude/skills/impeccable/scripts/context.mjs` once per session (if the runtime shows this skill's loaded base directory, run `node /scripts/context.mjs`; keep cwd at the user's project). It prints the project's context and its directives; follow what it prints. Once its output is in the conversation, never rerun it on a later turn. -2. `craft` and `shape` are build-path exceptions: the new-work gate below owns their flow, and on unattended runs its checkpoints resolve without pausing. For any other invoked sub-command (`audit`, `polish`, `live`, ...), immediately read **`reference/.md`** after `context.mjs` (the `.native` variant from the Commands table when the platform is `ios`/`android`/`adaptive`) and follow it. This read is a hard gate: do not inspect the target, run command-specific scripts, or edit files until the reference is loaded. -3. Read at least one project file (CSS / tokens / theme / a representative component) to learn what world you're in. If PRODUCT.md's `## Platform` is `ios` or `android`, also read `reference/.md` (`adaptive` reads both). +1. Run `node .claude/skills/impeccable/scripts/context.mjs` once per session (if the runtime shows this skill's loaded base directory, run `node /scripts/context.mjs`; keep cwd at the user's project). Pass a named source file or route as `--target `. It loads PRODUCT.md, DESIGN.md, the matching surface brief, and native-platform guidance when applicable; follow its directives and do not rerun it. +2. Before acting, load the one playbook that owns the request: the Commands table's reference for an explicit or clearly implied sub-command, or [reference/new-work.md](reference/new-work.md) for a new surface or replacement visual world. Then inspect the target and at least one representative source of incumbent visual truth (tokens, theme, CSS, component, or asset) before editing. ## How to design -**The brief wins.** Where the brief pins down a direction (a named aesthetic, an era, a place, a material, a specific font or palette), follow it exactly, including when it asks for a look this skill warns is saturated. Redirecting a pinned direction toward your own taste is a failure, not a save. - -**Existing worlds are sacred.** Most of impeccable's work happens inside a site or app that already exists. When the surface has a committed design system (real tokens, deliberately chosen faces, a palette the brand owns), work inside that world: extend it, sharpen it, leave it unmistakably the same brand, and never degrade a working page's performance. Inventing parallel colors, fonts, or styles on an existing surface is a defect, not creativity. A scoped refinement keeps the named scope's content and media footprint unless the brief explicitly changes them; build the added emphasis by recomposing what is already there through hierarchy, proportion, rhythm, and the system's own motifs. - -**New identity work reads the playbook first.** When nothing committed exists (greenfield, or a codebase with no real tokens or chosen faces), or the user explicitly asks for a redesign that discards the current look, you MUST read [reference/new-work.md](reference/new-work.md) before making any design decision. Missing DESIGN.md alone does not make work greenfield: code, tokens, chosen type, components, and assets are incumbent design authority. Preserve and extend them unless the user asked to replace them. Producing a genuinely new identity without the playbook yields the generic default this skill exists to prevent. Scoped fixes inside an existing world don't need it; the craft floor below governs them. `context.mjs` prints the appropriate authority directive. +- **The brief wins.** Honor pinned aesthetics, eras, materials, fonts, and palettes even when they conflict with a saturated-pattern warning. Redirecting a clear brief toward your taste is failure. +- **Refinement preserves; redesign replaces.** Refinement keeps the incumbent identity, behavior, copy, and everything outside scope. Ask before replacing factual copy or adding claims. Redesign keeps product truth, content, function, native affordances, and constraints, but treats the old look as evidence and anti-reference; choose a replacement world in new-work and replace DESIGN.md. Never split the difference into polish on the discarded look. +- **Visual authority is evidence, not a filename.** Missing DESIGN.md alone does not make a project greenfield; new-work decides whether to preserve, expand, or replace the incumbent world. ## Modes -Name the visitor's mode before designing; the page's grammar follows from it, and most ruined pages are one mode wearing another mode's grammar. **The mode belongs to the surface, not the subject**: a landing page for a dense technical tool is still Persuade, with Persuade's full permission to be striking; a docs page for a fashion house is still Read. Deciding a page can be plain because its subject is workmanlike is the same category error in reverse. The brief and the surface decide the mode; PRODUCT.md's `register` field survives only as a family hint (`brand` covers Persuade and Experience, `product` covers Operate and Read). Depth beyond the paragraphs below: [reference/new-work.md](reference/new-work.md) when inventing identity, [reference/operate.md](reference/operate.md) for substantial Operate and Read work. +Choose the mode from the requested surface, not the product, and persist it only in that surface brief. A tool's landing page is still Persuade; a fashion house's documentation is still Read. See [new-work.md](reference/new-work.md) for new surfaces and [operate.md](reference/operate.md) for deeper Operate/Read guidance. -**Persuade** (the surface exists to win someone over; design IS the product). The deliverable is an impression that stops the scroll, earns the click, converts. Spans every genre; don't collapse them into one look. On new surfaces, briefs that imply imagery must ship real, verified imagery; a colored rectangle where a photo belongs reads as incomplete. New Persuade surfaces take their typeface procedure and reject list from [reference/new-work.md](reference/new-work.md). - -**Operate** (the surface is a tool someone works in; design SERVES the task). A person getting something done: scanability and consistency outrank expressiveness. These surfaces earn trust by feeling native to their platform: system font stacks and workhorse UI faces are legitimate and often correct here (the Persuade reject list does not apply). The brand lives in the details: focus states, empty states, microcopy, one owned accent. The usage scene is part of the spec: an interface read outdoors, in motion, or at a glance must survive its real ambient light, and the theme follows the scene, not the category's habit. - -**Read** (the surface exists to be understood; long-form, reference, guidance). The deliverable is comprehension, and comprehension is earned twice: a structure the reader can hold in their head with nothing standing between them and the answer, and a reading experience good enough to stay in, through typographic quality and whatever visual or interactive support genuinely helps the reader follow. The brand lives in type, spacing, and small accents. - -**Experience** (the surface presents a body of work; the page IS the work). The artifact leads, the interface recedes, and the visitor meets the work itself in the first viewport at every screen size. Boldness here means trusting the work. +- **Persuade:** win someone over; design is the product. Earn attention and action. Ship real imagery when the brief needs it; follow the committed world, not category habit. +- **Operate:** help someone do work. Scanability, consistency, native expectations, and the real usage scene outrank expression. Brand lives in precise details. +- **Read:** make something understood. Structure for comprehension, then make the reading experience worth staying in. +- **Experience:** present a body of work. Let the artifact lead from the first viewport; the interface recedes. ## Craft floor -Build to this floor without announcing it. The design detector (the project hook, `node .claude/skills/impeccable/scripts/detect.mjs --json `, or `audit`) verifies most of it mechanically; resolve every finding before finalizing. Fix real defects, but use context judgment rather than distorting intentional design to appease a false positive. Classify any intentional exception explicitly and use the hook system's narrowest appropriate waiver when it must persist. +Build to this floor without announcing it. -- Contrast: body text ≥4.5:1 against its background (placeholders too); large text ≥3:1. Gray text on a colored background looks washed out: use a darker shade of the background's own hue, or a transparency of the text color. -- Shadows describe real light: an offset and a soft blur. A zero-offset colored halo is decoration announcing itself. -- Spacing has rhythm: generous separations, tight groupings; cramped padding reads as broken; the space above a heading exceeds the space below it. Verify computed spacing, not intended spacing. -- Type: body line length 65-75ch; display clamp() max ≤6rem; letter-spacing ≥-0.04em; `text-wrap: balance` on headings; modular scale ≥1.25 between steps; light-on-dark adds 0.05-0.1 line-height. Pair faces on a contrast axis, never two similar-but-not-identical ones; one family with committed weight contrast beats a timid pair. Test headings at every breakpoint; overflow means reduce the clamp or rewrite the copy. -- Motion is part of the build: one orchestrated moment beats scattered effects; ease-out exponential curves; reveals enhance an already-visible default (content gated on a class-triggered transition ships blank in hidden tabs and headless renderers). Responsive down to mobile and visible keyboard focus are part of the floor. -- Ship real content (no placeholders, dead links, or fake controls) and cover the interaction states people will actually hit (hover, focus, disabled, loading, error, empty). -- Copy is design material: name things the way the page's own people speak, make every control say what it does, and make every error say what happened and what to do next. -- Before finishing, re-read the brief: every requirement it names must exist on the page, findable in seconds. A beautiful page missing an asked-for feature is unfinished. +- **Contrast:** body and placeholder text ≥4.5:1; large text ≥3:1. On colored surfaces, tint secondary text from that hue or the foreground instead of using gray. +- **Depth:** shadows describe light with offset and soft blur; zero-offset colored halos are decoration. +- **Spacing:** tight groups, generous separation, no cramped containers; space above a heading exceeds space below. Verify computed values. +- **Type:** body measure 65–75ch; display max 6rem and tracking floor -0.04em; balance headings; use clear scale/weight contrast; test overflow at every breakpoint. +- **Motion:** author one coherent moment instead of scattered effects. Use exponential ease-out and an already-visible default. Premium motion is not transform/opacity alone: it may add focus, depth, masks, light, or material change through blur/filter, backdrop-filter, clip-path/masks, or shadow when smooth. Always provide reduced motion. +- **Shipping:** real content, working controls, responsive composition, keyboard focus, and the states users hit: hover, disabled, loading, error, and empty. +- **Copy:** use the product's language; controls name their action, errors name the problem and recovery. +- **Coverage:** every brief requirement must exist and be findable within seconds. + +Before finishing changed UI, follow the quality guidance supplied by `context.mjs` and hooks. Context requests a manual scan only when no automatic detector is active; never add a second detector pass. ## Commands | Command | Category | Description | Reference | |---|---|---|---| -| `craft [feature]` | Build | The standard build flow with attended checkpoints | [reference/new-work.md](reference/new-work.md) | +| `craft [feature]` | Build | Deprecated alias for an ordinary new-work request | [reference/craft.md](reference/craft.md) | | `shape [feature]` | Build | Plan UX/UI before writing code | [reference/shape.md](reference/shape.md) | -| `init` | Build | Set up project context: PRODUCT.md, DESIGN.md, live config, next steps | [reference/init.md](reference/init.md) | +| `init` | Build | Capture durable product context in PRODUCT.md | [reference/init.md](reference/init.md) | | `document` | Build | Generate DESIGN.md from existing project code | [reference/document.md](reference/document.md) | | `extract [target]` | Build | Pull reusable tokens and components into design system | [reference/extract.md](reference/extract.md) | | `critique [target]` | Evaluate | UX design review with heuristic scoring | [reference/critique.md](reference/critique.md) | @@ -81,7 +80,14 @@ Build to this floor without announcing it. The design detector (the project hook | `optimize [target]` | Fix | Diagnose and fix UI performance | [reference/optimize.md](reference/optimize.md) | | `live` | Iterate | Visual variant mode: pick elements in the browser, generate alternatives | [reference/live.md](reference/live.md) | -Routing: **no argument** → read [reference/routing.md](reference/routing.md) and present the context-aware menu (never auto-run a command). **First word matches a command** (or `pin` / `unpin` / `hooks`) → load its reference (native variant on native platforms) and follow it; everything after the command name is the target. **Intent clearly maps to one command** ("fix the spacing" → `layout`, "rewrite this error" → `clarify`) → same; if two fit, ask once. **Otherwise** → general design invocation: apply Setup and this file's guidance; builds flow through the new-work gate above, whose playbook carries the direction checkpoint and the finishing pass. `teach` routes to `init`, and `craft` routes to the standard build flow with attended checkpoints. If setup diverted into `init` for a build request, finish init, refresh context, then resume. +Routing: + +- **No argument:** read [routing.md](reference/routing.md) and present its context-aware menu; never auto-run a command. +- **Explicit or clearly implied command:** load its reference (native variant on native platforms) and follow it. Ask once if two commands fit. +- **Otherwise:** treat the request as general design work. Missing PRODUCT.md routes through init; new surfaces and replacement worlds use new-work. +- `teach` aliases `init`. `craft` is a deprecated alias for ordinary new-work and adds nothing. `shape` owns task discovery, then enters new-work only for visual-world and surface-concept decisions. + +After init writes PRODUCT.md, resume without rerunning `context.mjs`. **Pin / Unpin:** `node .claude/skills/impeccable/scripts/pin.mjs ` creates or removes a standalone `/` shortcut. Report the script's result concisely; relay stderr verbatim on error. diff --git a/plugin/skills/impeccable/reference/android.md b/plugin/skills/impeccable/reference/android.md index 3575c31fb..6337b9018 100644 --- a/plugin/skills/impeccable/reference/android.md +++ b/plugin/skills/impeccable/reference/android.md @@ -2,7 +2,7 @@ For native Android apps: Jetpack Compose, Android Views, React Native, Expo, Flutter shipping to Android hardware. -On native, register narrows. Material Design 3 governs structure, navigation, and interaction whatever the register; brand expresses through Material's theming (color roles, type scale, shape, motion). A Material-everywhere cross-platform app that also ships to iPhone still owes iOS its OS guarantees on that hardware: safe-area insets, Reduce Motion, edge-swipe back. +On native, the visitor mode narrows what expression may override. Material Design 3 governs structure, navigation, and interaction in every mode; brand expresses through Material's theming (color roles, type scale, shape, motion). A Material-everywhere cross-platform app that also ships to iPhone still owes iOS its OS guarantees on that hardware: safe-area insets, Reduce Motion, edge-swipe back. ## The Android slop test diff --git a/plugin/skills/impeccable/reference/animate.md b/plugin/skills/impeccable/reference/animate.md index c57e65d1b..d51bb6d63 100644 --- a/plugin/skills/impeccable/reference/animate.md +++ b/plugin/skills/impeccable/reference/animate.md @@ -1,203 +1,88 @@ > **Additional context needed**: performance constraints. -Add motion that conveys state, gives feedback, and clarifies hierarchy. Cut motion that exists only for decoration. Animation fatigue is a real cost; spend the budget on the moments that need it. +Use motion to explain state, relationship, and hierarchy, or to create one authored moment the surface has earned. Decoration without purpose is animation debt. --- -## Register +## Visitor mode -Persuade + Experience: 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. +- **Persuade + Experience:** motion may carry the voice. Prefer one rehearsed focal sequence to repeated section reveals. +- **Operate + Read:** motion serves feedback, state, and continuity. Keep routine transitions fast and do not make users wait through page-load choreography. +- **Native (`ios` / `android` / `adaptive`):** follow the Motion section of [ios.md](ios.md) or [android.md](android.md), including the platform's Reduce Motion behavior. Do not apply the web tooling below. -Operate + Read: 150–250 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. +## Find the job -Native (`ios` / `android` / `adaptive`): implementation follows the Motion section of [ios.md](ios.md) / [android.md](android.md) (read it first if Setup hasn't already): system transitions and OS Reduce Motion, never the web tooling below. +Inspect the existing motion language, interaction states, target devices, and performance budget. Find only the places where motion would: ---- +- acknowledge an action; +- make a state change or spatial relationship legible; +- preserve continuity through navigation or layout change; +- direct attention at a meaningful moment; +- embody the selected visual world. -## Assess Animation Opportunities +Ask only when a material constraint cannot be inferred. Do not animate a static area merely because it exists. -Analyze where motion would improve the experience: +## Set the motion thesis -1. **Identify static areas**: - - **Missing feedback**: Actions without visual acknowledgment (button clicks, form submission, etc.) - - **Jarring transitions**: Instant state changes that feel abrupt (show/hide, page loads, route changes) - - **Unclear relationships**: Spatial or hierarchical relationships that aren't obvious - - **Lack of delight**: Functional but joyless interactions - - **Missed guidance**: Opportunities to direct attention or explain behavior +Write a short plan before implementation: -2. **Understand the context**: - - What's the personality? (Playful vs serious, energetic vs calm) - - What's the performance budget? (Mobile-first? Complex page?) - - Who's the audience? (Motion-sensitive users? Power users who want speed?) - - What matters most? (One hero animation vs many micro-interactions?) +- **Focal moment:** the one sequence or interaction that deserves authorship, if any. +- **Continuity:** the state, layout, or navigation changes that need explanation. +- **Feedback:** the controls and outcomes that need acknowledgment. +- **Budget:** which effects may be expensive, how often they run, and the reduced-motion equivalent. -If any of these are unclear from the codebase, STOP and call the AskUserQuestion tool to clarify. +The focal moment must come from this product and surface concept. A generic fade-and-rise, hover lift, parallax layer, or scroll reveal is not a thesis. -**CRITICAL**: Respect `prefers-reduced-motion`. Always provide non-animated alternatives for users who need them. +## Choose material by meaning -## Plan Animation Strategy +Transform and opacity are reliable foundations, not the entire palette. Choose properties for what the transition communicates: -Create a purposeful animation plan: +- **Continuity and relationship:** shared-element motion, FLIP-style transforms, view transitions, or deliberate spatial movement. +- **Focus and depth:** bounded blur, filter, backdrop, light, or shadow changes. +- **Reveal and composition:** masks, clip paths, cropping, or controlled occlusion. +- **Material and energy:** color, gradient position, texture, distortion, or shader effects when the world and runtime support them. +- **State and feedback:** the smallest change that makes cause and result unmistakable. -- **Feedback layer**: Which interactions need acknowledgment? -- **Transition layer**: Which state changes need smoothing? -- **Entrance moment**: The ONE entrance worth rehearsing, where the mode invites it. Not every surface wants one. -- **Delight layer**: Where can we surprise and delight? +Do not stack techniques for spectacle. One strong material idea, carried through the focal sequence and quiet supporting states, is usually enough. -**IMPORTANT**: One well-orchestrated experience beats scattered animations everywhere. Focus on high-impact moments. +Sibling stagger is appropriate when a list appears as a list. Cap the total delay, and never reinterpret every scrolled section as a staggered list. -## Implement Animations +## Timing and easing -Add motion systematically across these categories: +Timing should express distance and consequence: -### Micro-interactions -- **Button feedback**: - - Hover: Subtle scale (1.02-1.05), color shift, shadow increase - - Click: Quick scale down then up (0.95 → 1), ripple effect - - Loading: Spinner or pulse state -- **Form interactions**: - - Input focus: Border color transition, slight scale or glow - - Validation: Shake on error, check mark on success, smooth color transitions -- **Toggle switches**: Smooth slide + color transition (200-300ms) -- **Checkboxes/radio**: Check mark animation, ripple effect -- **Like/favorite**: Scale + rotation, particle effects, color transition +| Duration | Typical use | +|---|---| +| 100–150 ms | immediate feedback | +| 150–300 ms | routine state change | +| 300–500 ms | layout, overlay, or view transition | +| 500–800 ms | a deliberately authored focal entrance | -### State Transitions -- **Show/hide**: Fade + slide (not instant), appropriate timing (200-300ms) -- **Expand/collapse**: Height transition with overflow handling, icon rotation -- **Loading states**: Skeleton screen fades, spinner animations, progress bars -- **Success/error**: Color transitions, icon animations, gentle scale pulse -- **Enable/disable**: Opacity transitions, cursor changes +Exit faster than entrance. Use natural deceleration such as `cubic-bezier(0.16, 1, 0.3, 1)` for confident arrivals; do not use bounce or elastic curves by reflex. Long feedback feels like latency. -### Entrance Animations -- **The one entrance moment** (where the mode invites it): a committed entrance for primary content (scale, parallax, or a creative effect) -- **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. +## Implement to the runtime - 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. +- Use CSS transitions and keyframes for declarative state and bounded sequences. +- Use Web Animations API or the project's existing motion library for interruption, sequencing, and dynamic values. +- Use View Transitions or shared-element techniques when continuity across states is the point. +- Use scroll-driven motion only when the scroll relationship itself carries meaning, with a robust fallback. +- Do not add a dependency for an effect the existing stack can express cleanly. -### Navigation & Flow -- **Page transitions**: Crossfade between routes, shared element transitions -- **Tab switching**: Slide indicator, content fade/slide -- **Carousel/slider**: Smooth transforms, snap points, momentum -- **Scroll effects**: Parallax layers, sticky headers with state changes, scroll progress indicators +Keep content visible in the default state so failed scripts do not hide the page. Avoid casually animating layout-driving properties such as `width`, `height`, `top`, `left`, and margins; use FLIP, transforms, or grid techniques when appropriate. Bound blur, filter, shadow, canvas, and shader work to isolated regions. Apply `will-change` only during known animation. Measure on target viewports and devices rather than assuming transform means fast. -### Feedback & Guidance -- **Hover hints**: Tooltip fade-ins, cursor changes, element highlights -- **Drag & drop**: Lift effect (shadow + scale), drop zone highlights, smooth repositioning -- **Copy/paste**: Brief highlight flash on paste, "copied" confirmation -- **Focus flow**: Highlight path through form or workflow +## Accessibility and control -### Delight Moments -- **Empty states**: Subtle floating animations on illustrations -- **Completed actions**: Confetti, check mark flourish, success celebrations -- **Easter eggs**: Hidden interactions for discovery -- **Contextual animation**: Weather effects, time-of-day themes, seasonal touches +Honor `prefers-reduced-motion` with an intentional alternative. Preserve state change and hierarchy while removing travel, parallax, flashing, or prolonged sequences. Do not replace every animation globally with `0.01ms` if that destroys useful feedback. Motion must never block focus, interaction, reading, or task completion. -## Technical Implementation +Respect autoplay and sound preferences. Any nonessential loop must stop when offscreen or hidden. -Use appropriate techniques for each animation: +## Verify -### Timing & Easing +- The focal motion is specific to the selected world and surface. +- Every supporting animation explains feedback, state, or relationship. +- Interruption and repeated use behave correctly. +- Desktop, mobile, keyboard, and reduced-motion paths remain usable. +- Expensive effects stay smooth on the target device. +- Removing an animation would lose meaning or authored character, not merely decoration. -**Duration: the 100/300/500 rule.** Timing matters more than easing for "feels right": - -| Duration | Use Case | Examples | -|----------|----------|----------| -| **100–150ms** | Instant feedback | Button press, toggle, color change | -| **200–300ms** | State changes | Menu open, tooltip, hover state | -| **300–500ms** | Layout changes | Accordion, modal, drawer | -| **500–800ms** | Entrance animations | Page load, hero reveal | - -**Easing curves (use these, not CSS defaults):** -```css -/* Recommended: natural deceleration */ ---ease-out-quart: cubic-bezier(0.25, 1, 0.5, 1); /* Smooth */ ---ease-out-quint: cubic-bezier(0.22, 1, 0.36, 1); /* Slightly snappier */ ---ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1); /* Confident, decisive */ - -/* AVOID: feel dated and tacky */ -/* bounce: cubic-bezier(0.34, 1.56, 0.64, 1); */ -/* elastic: cubic-bezier(0.68, -0.6, 0.32, 1.6); */ -``` - -**Exit animations are faster than entrances.** Use ~75% of enter duration. - -### CSS Animations -```css -/* Prefer for simple, declarative animations */ -- transitions for state changes -- @keyframes for complex sequences -- transform and opacity for reliable movement -- blur, filters, masks, clip paths, shadows, and color shifts for premium atmospheric effects when verified smooth -``` - -### JavaScript Animation -```javascript -/* Use for complex, interactive animations */ -- Web Animations API for programmatic control -- Framer Motion for React -- 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 -- **Layout safety**: Avoid casual animation of layout-driving properties (`width`, `height`, `top`, `left`, margins) -- **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) { - * { - animation-duration: 0.01ms !important; - animation-iteration-count: 1 !important; - transition-duration: 0.01ms !important; - } -} -``` - -**NEVER**: -- Use bounce or elastic easing curves; they feel dated and draw attention to the animation itself -- Animate layout properties casually (`width`, `height`, `top`, `left`, margins) when transform, FLIP, or grid-based techniques would work -- Use durations over 500ms for feedback (it feels laggy) -- Animate without purpose (every animation needs a reason) -- Ignore `prefers-reduced-motion` (this is an accessibility violation) -- Animate everything (animation fatigue makes interfaces feel exhausting) -- Block interaction during animations unless intentional - -## Verify Quality - -Test animations thoroughly: - -- **Smooth at 60fps**: No jank on target devices -- **Feels natural**: Easing curves feel organic, not robotic -- **Appropriate timing**: Not too fast (jarring) or too slow (laggy) -- **Reduced motion works**: Animations disabled or simplified appropriately -- **Doesn't block**: Users can interact during/after animations -- **Adds value**: Makes interface clearer or more delightful - -When the motion clarifies state instead of decorating it, hand off to `/impeccable polish` for the final pass. +When motion earns its place, hand off to `/impeccable polish` for the final pass. diff --git a/plugin/skills/impeccable/reference/audit.md b/plugin/skills/impeccable/reference/audit.md index 72815af69..70d80771a 100644 --- a/plugin/skills/impeccable/reference/audit.md +++ b/plugin/skills/impeccable/reference/audit.md @@ -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 - diff --git a/plugin/skills/impeccable/reference/audit.native.md b/plugin/skills/impeccable/reference/audit.native.md index 737d50e4f..0126fa157 100644 --- a/plugin/skills/impeccable/reference/audit.native.md +++ b/plugin/skills/impeccable/reference/audit.native.md @@ -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) diff --git a/plugin/skills/impeccable/reference/bolder.md b/plugin/skills/impeccable/reference/bolder.md index 044c31931..fced49456 100644 --- a/plugin/skills/impeccable/reference/bolder.md +++ b/plugin/skills/impeccable/reference/bolder.md @@ -8,12 +8,12 @@ ## Why it reads flat -A section usually reads flat for reasons its neighbors have already solved. Look at what the rest of the page does that this section does not: the display type at full strength, the structural devices that carry meaning, the signature motif, the density and pacing, the specificity of the copy. A flat section is typically one that quietly opts out of the system's own strongest moves. The most reliable bolder pass brings the target up to the expressive level its neighbors already reach, in the system's own vocabulary rather than a new one. +A section usually reads flat for reasons its neighbors have already solved. Look at what the rest of the page does that this section does not: the display type at full strength, the structural devices that carry meaning, the signature motif, the density and pacing. A flat section is typically one that quietly opts out of the system's own strongest moves. The most reliable bolder pass brings the target up to the expressive level its neighbors already reach, in the system's own vocabulary rather than a new one. ## The amplification - **Amplify what the system already owns.** Reuse its motif and its type scale at full strength, turned up for this section rather than invented for it. The bolder version should look more like the same brand, not less. -- **Let the content carry the weight.** Flat copy makes a flat design, and no amount of size fixes a generic claim. Make every word specific enough to earn its place, and let the section's real evidence do the work that a decorative addition would otherwise fake. +- **Keep content true.** Existing claims are part of the scope: preserve them unless the user supplies replacements. If real evidence is essential to the direction but absent, ask for it. - **Commit, then clarify.** Half-measures read as noise. Make the one decisive move completely, then quiet everything around it so the move is legible. If every element got louder, the section got flatter. - **Give it its own rhythm.** The target should read as a peak in the scroll, a shift in density or pace from what surrounds it, not simply more of the same. diff --git a/plugin/skills/impeccable/reference/clarify.md b/plugin/skills/impeccable/reference/clarify.md index 00ee978c3..3047a9d82 100644 --- a/plugin/skills/impeccable/reference/clarify.md +++ b/plugin/skills/impeccable/reference/clarify.md @@ -1,288 +1,94 @@ -> **Additional context needed**: audience technical level and users' mental state in context. +> **Additional context needed**: audience knowledge and emotional state. -Find the unclear, confusing, or poorly written interface text and rewrite it. Vague copy creates support tickets and abandonment; specific copy gets users through the task. +Rewrite unclear interface text so users understand what happened, what matters, and what to do next. Preserve factual meaning, product terminology, and brand voice. +## Audit the language ---- +Read the entire interaction path, not isolated strings. Identify: -## Assess Current Copy +- ambiguous nouns, verbs, and actions; +- internal jargon or assumed knowledge; +- vague labels, outcomes, and system states; +- missing consequences, recovery, or timing; +- inconsistent terminology and capitalization; +- redundant headings, intros, helper text, and confirmations; +- text that breaks at realistic widths or in translation; +- tone that ignores stress, risk, success, or urgency. -Identify what makes the text unclear or ineffective: +Infer audience and task from product context and surrounding UI. Ask before changing factual claims, legal meaning, or a term that may be domain-specific. -1. **Find clarity problems**: - - **Jargon**: Technical terms users won't understand - - **Ambiguity**: Multiple interpretations possible - - **Passive voice**: "Your file has been uploaded" vs "We uploaded your file" - - **Length**: Too wordy or too terse - - **Assumptions**: Assuming user knowledge they don't have - - **Missing context**: Users don't know what to do or why - - **Tone mismatch**: Too formal, too casual, or inappropriate for situation +## Set the message hierarchy -2. **Understand the context**: - - Who's the audience? (Technical? General? First-time users?) - - What's the user's mental state? (Stressed during error? Confident during success?) - - What's the action? (What do we want users to do?) - - What's the constraint? (Character limits? Space limitations?) +For each state, decide: -**CRITICAL**: Clear copy helps users succeed. Unclear copy creates frustration, errors, and support tickets. +1. the one fact the user needs now; +2. the action available next; +3. supporting context that changes the decision; +4. the appropriate tone for this moment. -## Plan Copy Improvements +Say each idea once. If the heading already explains the state, the introduction should add new information or disappear. -Create a strategy for clearer communication: +## Rewrite by function -- **Primary message**: What's the ONE thing users need to know? -- **Action needed**: What should users do next (if anything)? -- **Tone**: How should this feel? (Helpful? Apologetic? Encouraging?) -- **Constraints**: Length limits, brand voice, localization considerations +### Actions and navigation -**IMPORTANT**: Good UX writing is invisible. Users should understand immediately without noticing the words. +Use a specific verb and object when the outcome is not already obvious. Labels should describe what will happen, not the gesture used to trigger it. Keep the same noun and verb for the same concept throughout the product. -## Improve Copy Systematically +For destructive actions, name the object and consequence. Prefer undo over confirmation when recovery is safe. When confirmation is necessary, name the action on both the message and button instead of using `Yes`, `No`, `OK`, or `Submit`. -Refine text across these common areas: +### Forms -### Error Messages -**Bad**: "Error 403: Forbidden" -**Good**: "You don't have permission to view this page. Contact your admin for access." +Use persistent labels; placeholders are examples, not labels. Put format and eligibility requirements before submission. Explain why information is requested only when it is not obvious. Required and optional treatment should be consistent. -**Bad**: "Invalid input" -**Good**: "Email addresses need an @ symbol. Try: name@example.com" +Validation says what needs attention and how to correct it without blaming the user. Keep related instructions near the field and announce errors accessibly. -**Principles**: -- Explain what went wrong in plain language -- Suggest how to fix it -- Don't blame the user -- Include examples when helpful -- Link to help/support if applicable - -### Form Labels & Instructions -**Bad**: "DOB (MM/DD/YYYY)" -**Good**: "Date of birth" (with placeholder showing format) - -**Bad**: "Enter value here" -**Good**: "Your email address" or "Company name" - -**Principles**: -- Use clear, specific labels (not generic placeholders) -- Show format expectations with examples -- Explain why you're asking (when not obvious) -- Put instructions before the field, not after -- Keep required field indicators clear - -### Button & CTA Text -**Bad**: "Click here" | "Submit" | "OK" -**Good**: "Create account" | "Save changes" | "Got it, thanks" +### Errors and permissions -**Principles**: -- Describe the action specifically -- Use active voice (verb + noun) -- Match user's mental model -- Be specific ("Save" is better than "OK") +An actionable error answers: -### Help Text & Tooltips -**Bad**: "This is the username field" -**Good**: "Choose a username. You can change this later in Settings." +1. what failed; +2. why, when known and useful; +3. how to recover or what alternative remains. -**Principles**: -- Add value (don't just repeat the label) -- Answer the implicit question ("What is this?" or "Why do you need this?") -- Keep it brief but complete -- Link to detailed docs if needed +Do not expose internal codes as the primary message. Do not promise a cause or resolution the system cannot know. Treat privacy, payment, deletion, access loss, and blocked work seriously; warmth is welcome, jokes are not. -### Empty States -**Bad**: "No items" -**Good**: "No projects yet. Create your first project to get started." +### Loading, empty, and success states -**Principles**: -- Explain why it's empty (if not obvious) -- Show next action clearly -- Make it welcoming, not dead-end +Loading text names the real operation and sets an honest expectation when the wait is meaningful. Show determinate progress when available; never invent progress. -### Success Messages -**Bad**: "Success" -**Good**: "Settings saved! Your changes will take effect immediately." +An empty state distinguishes first use, no results, filters, permissions, and failure. Explain the state and provide the next useful action. -**Principles**: -- Confirm what happened -- Explain what happens next (if relevant) -- Be brief but complete -- Match the user's emotional moment (celebrate big wins) - -### Loading States -**Bad**: "Loading..." (for 30+ seconds) -**Good**: "Analyzing your data... this usually takes 30-60 seconds" - -**Principles**: -- Set expectations (how long?) -- Explain what's happening (when it's not obvious) -- Show progress when possible -- Offer escape hatch if appropriate ("Cancel") +Success confirms the completed outcome and mentions the next consequence only when it changes what the user should do. Routine success should be brief. -### Confirmation Dialogs -**Bad**: "Are you sure?" -**Good**: "Delete 'Project Alpha'? This can't be undone." +### Help and instructional text -**Principles**: -- State the specific action -- Explain consequences (especially for destructive actions) -- Use clear button labels ("Delete project" not "Yes") -- Don't overuse confirmations (only for risky actions) +Helper text answers an implicit question instead of restating the control. Use progressive disclosure for uncommon detail. Link text must make sense out of context; icon-only controls need accessible names. -### Navigation & Wayfinding -**Bad**: Generic labels like "Items" | "Things" | "Stuff" -**Good**: Specific labels like "Your projects" | "Team members" | "Settings" +## Voice, accessibility, and localization -**Principles**: -- Be specific and descriptive -- Use language users understand (not internal jargon) -- Make hierarchy clear -- Consider information scent (breadcrumbs, current location) +Voice stays consistent; tone adapts to the moment. Use plain language without flattening terminology the audience genuinely knows. -## Apply Clarity Principles +- Write complete translatable messages rather than concatenated fragments. +- Keep variables and numbers structured so translators can reorder them. +- Allow expansion instead of abbreviating prematurely. +- Make alt text convey the image's information; use empty alt for decoration. +- Keep screen-reader names aligned with visible labels and outcomes. +- Do not rely on punctuation, color, or iconography to carry the message alone. -Every piece of copy should follow these rules: +Maintain a short terminology glossary when inconsistency spans the product. Do not vary words for literary effect in an interface. -1. **Be specific**: "Enter email" not "Enter value" -2. **Be concise**: Cut unnecessary words (but don't sacrifice clarity) -3. **Be active**: "Save changes" not "Changes will be saved" -4. **Be human**: "Oops, something went wrong" not "System error encountered" -5. **Tell users what to do**, not just what happened -6. **Be consistent**: Use same terms throughout (don't vary for variety) +## Verify -**NEVER**: -- Use jargon without explanation -- Blame users ("You made an error" → "This field is required") -- Be vague ("Something went wrong" without explanation) -- Use passive voice unnecessarily -- Write overly long explanations (be concise) -- Use humor for errors (be empathetic instead) -- Assume technical knowledge -- Vary terminology (pick one term and stick with it) -- Repeat information (headers restating intros, redundant explanations) -- Use placeholders as the only labels (they disappear when users type) +Read the flow in context and test: -## Verify Improvements +- comprehension without hidden product knowledge; +- actionability at errors, empty states, and decision points; +- factual accuracy and consistent terminology; +- scanability at target widths and 200% zoom; +- long names, localization expansion, pluralization, and dynamic values; +- accessible names and announced state changes; +- tone appropriate to consequence and emotional context. -Test that copy improvements work: +The final copy is as short as it can be without removing meaning or recovery. -- **Comprehension**: Can users understand without context? -- **Actionability**: Do users know what to do next? -- **Brevity**: Is it as short as possible while remaining clear? -- **Consistency**: Does it match terminology elsewhere? -- **Tone**: Is it appropriate for the situation? - -When the copy reads cleanly, hand off to `/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. +When the language reads cleanly, hand off to `/impeccable polish` for the final pass. diff --git a/plugin/skills/impeccable/reference/codex.md b/plugin/skills/impeccable/reference/codex.md index 18a924a91..6c550f1dc 100644 --- a/plugin/skills/impeccable/reference/codex.md +++ b/plugin/skills/impeccable/reference/codex.md @@ -1,105 +1,38 @@ -# Codex: Visual Direction & Asset Production +# Codex: Surface Probes & Asset Production -This file is loaded by `/impeccable craft` when the harness has native image generation (currently Codex via `image_gen`). Other harnesses skip it. It covers the two craft steps that depend on real image generation: landing the visual direction, and producing the raster assets the implementation will compose. +Load this from [new-work.md](new-work.md) only when the harness has native image generation and a substantial, high-fidelity surface would benefit from seeing the shortlisted concept before code. PRODUCT.md and DESIGN.md are preconditions. New-work has already resolved the visual world; this file must not reopen it. -Read this *before* generating any images. The order matters, and the per-step user pauses are what keep generated imagery from drifting away from the brief. +The purpose of a probe is to test composition, narrative, hierarchy, density, focal moment, signature use, and image requirements. It is not a second identity workshop. Keep DESIGN.md's palette, typography direction, material language, component character, imagery stance, and motion grammar fixed. -### Four stop points before code +## Generate the smallest useful probe set -Steps A through D each end with the user. Do not advance past any of them on your own read of the situation. +Generate one to three high-fidelity north-star comps using the native image-generation capability. Base them on the real content and the surface concepts already developed with the user. -1. **STOP after Step A questions.** Wait for answers. -2. **STOP after Step B palette generation.** Wait for "confirm palette." -3. **STOP after Step C mocks.** Wait for direction approval or delegation. -4. **Only after Step D approves a direction** do you return to the build (new-work.md's plan, then code). +- When the user shortlisted multiple concepts, show one clear expression of each. +- When one concept is already selected, vary only the structural uncertainty that the image can resolve: topology, sequence, density, hierarchy, focal composition, or interaction framing. +- Show enough beyond the opening moment to prove the concept can govern the whole requested surface. +- Do not generate a palette artifact, ask new atmosphere questions, introduce a different type voice, or invent a new motif. If the committed world cannot support the concept, return to the concept shortlist rather than changing the world. -Prior shape approval does **not** satisfy any of these. Shape's "confirm or override" advances you into Step A; it is not a substitute for it. +Treat each comp as a direction test, not a screenshot specification. Core UI text, responsive behavior, accessibility, semantics, and interaction states remain implementation responsibilities. -## Step A: Explore Directions with the User +## One approval point -Before generating anything, run a brief direction conversation grounded in the shape brief. +Show the probes together and ask what should carry forward, what feels false to the world, and whether the selected surface concept should be approved, combined, revised, or rejected. Then stop and wait. A structured simulated user counts as attended and receives the same question. -**Step A is required even when shape just produced a confirmed brief.** The shape questions and Step A questions cover different ground: shape pins purpose, content, scope; Step A pins palette, atmosphere, and named visual references for the comps you're about to generate. The only time you can skip Step A is when the user has already answered these exact palette/atmosphere/reference questions in the same session. +Do not begin code until the user approves a direction or explicitly delegates the choice. If they delegate, choose using the task brief, PRODUCT.md, and DESIGN.md, and state the evidence. Approval refines the task concept; it does not modify DESIGN.md. -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: +After approval, summarize the composition and the parts of the comp that must not be literalized. Return to new-work.md and write the direction contract from the approved surface concept before code. -- "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.)" +## Inventory implementation fidelity -**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. +Before building, inventory the approved comp's major visible ingredients and choose an implementation medium for each: semantic HTML/CSS/SVG, existing project asset, generated raster, sourced raster, icon library, canvas/WebGL, or accepted omission. -## Step B: Generate the Brand Palette First +Pay special attention to the dominant composition, signature use, image-native content, second-fold system, and any interaction the still image only implies. If the concept depends on a photograph, architectural scene, product object, portrait, or other raster-native material, do not silently replace it with generic CSS scenery. -Generate **one** palette artifact before any mocks. This is a small, focused image: typography pairing on the chosen background, primary + accent color swatches, one signature ornament or motif. Single image, single pass. +Treat the comp as a north star, not something to trace. Do not rasterize core UI text or controls. Do not substitute a different visual driver after approval without asking. -Why palette first: mocks generated against a vague color sense produce noise that drowns out the structural decisions. A confirmed palette is the first concrete contract for everything downstream. +## Produce only the assets the build needs -Show the palette to the user. Ask one question: "This is the palette I'm locking in for the mocks. Confirm, or call out what to shift?" +When clean raster ingredients are required and a scoped subagent is available and authorized, use `impeccable_asset_producer`. Give it the approved comp, output paths, required dimensions and formats, transparency needs, crop notes, and what must remain semantic code. Otherwise produce the minimum required assets with the native image-generation capability in the current thread. -**STOP and wait for confirmation.** Do not generate mocks against an unconfirmed palette. "Probably good enough" is the wrong call here; the palette is the contract for everything downstream. - -## Step C: Generate 1-3 Visual Mocks Against the Palette - -Once the palette is confirmed, generate **1 to 3** high-fidelity north-star comps. Each mock must use the confirmed palette and typography. Mocks differ in *structural* direction (hierarchy, topology, density, composition), not in color or motif. - -- Brand work: push visual identity, composition, mood, and signature motifs. -- Product work: push hierarchy, topology, density, tone, grounded in realistic product structure. -- Landing pages and long-form brand surfaces: show enough of the second fold to establish the system beyond the hero. - -Use the `image_gen` tool directly (or via the imagegen skill when available). Don't ask the user to install anything. - -## Step D: Approval Loop - -Show the comps. Ask what carries forward. Iterate until **one direction is approved** or the user explicitly delegates. - -**STOP and wait for the approval or the delegation.** Do not begin Step E or return to the build until a single direction is named. If the user delegates, pick the strongest direction and explain it from the brief, not personal taste. - -Before moving to assets, summarize what to carry into code and what *not* to literalize from the mock. This is the handoff between visual exploration and semantic implementation. - -## Step E: Mock Fidelity Inventory - -Inventory the approved mock's major visible ingredients. For each, decide implementation: semantic HTML/CSS/SVG, generated raster, sourced raster, icon library, canvas/WebGL, or accepted omission. - -Common ingredients to inventory: - -- Hero silhouette and dominant composition -- Signature motifs (planets, devices, portraits, charts, route lines, insets, badges, etc.) -- Nav and primary-action treatment (when the surface has one) -- Section sequence, especially the second fold -- Image-native content the concept depends on -- Typography, density, color/material treatment, motion cues - -Treat the mock as a north star, not a screenshot to trace. Don't rasterize core UI text. But if the live result lacks the mock's major ingredients, the implementation is wrong. - -If a photographic, architectural, product, or place-led mock becomes generic CSS scenery, decorative diagrams, bullets, or copy, stop and fix it. That's a broken implementation, not a harmless interpretation. - -Don't substitute a different hero composition or visual driver post-approval without user sign-off. - -## Step F: Asset Slicing via the Asset Producer - -Raster ingredients identified in Step E need clean production assets. Use the bundled `impeccable_asset_producer` subagent rather than producing inline. - -Spawn it as a scoped subagent. If you do not have explicit permission to use agents, stop and ask: - -```text -Asset production will work better as a scoped subagent job. Should I spawn the Impeccable asset producer subagent for this step? -``` - -Pass to the agent: - -- Approved mock path or screenshot reference -- Crop paths or a contact sheet with crop ids -- Output directory -- Required dimensions, format, transparency needs -- Avoid list -- Notes on what should remain semantic HTML/CSS/SVG instead of raster - -Attach image generation capability to the spawned agent when the harness supports it. Do **not** load image-generation reference material into the parent thread. - -Inline asset production is allowed only if the user declines subagents, the harness cannot spawn the authorized agent, or the user explicitly asks for single-thread mode. - -Prefer HTML/CSS/SVG/canvas when they can credibly reproduce an ingredient; reach for real, generated, or stock imagery when the mock or subject matter calls for actual visual content. - -## After This File - -Once Steps A through F are complete, return to the build: implement per new-work.md and SKILL.md's craft floor. The implementation builds against the confirmed palette, approved mock, and the assets the producer wrote. +Return to [new-work.md](new-work.md) for the direction contract, implementation, and finishing pass. diff --git a/plugin/skills/impeccable/reference/colorize.md b/plugin/skills/impeccable/reference/colorize.md index b0a61dade..dc45f88c5 100644 --- a/plugin/skills/impeccable/reference/colorize.md +++ b/plugin/skills/impeccable/reference/colorize.md @@ -1,257 +1,86 @@ > **Additional context needed**: existing brand colors. -Replace timid grayscale or single-accent designs with a strategic palette: pick a color strategy, choose a hue family that fits the brand, then apply color with intent. More color ≠ better. Strategic color beats rainbow vomit. +Introduce color as hierarchy, meaning, and atmosphere. Preserve confirmed brand and semantic conventions; do not replace a visual world under the guise of colorizing it. --- -## Register +## Visitor mode -Persuade + Experience: palette IS voice. Pick a color strategy first per SKILL.md (Restrained / Committed / Full palette / Drenched) and follow its dosage. Committed, Full palette, and Drenched deliberately exceed the ≤10% rule; that rule is Restrained only. Unexpected combinations are allowed; a dominant color can own the page when the chosen strategy calls for it. +- **Persuade + Experience:** color may carry the voice and own large regions when the selected world calls for it. +- **Operate + Read:** color primarily encodes action, selection, status, wayfinding, and reading hierarchy. Rarity gives an accent force. -Operate + Read: semantic-first and almost always Restrained. Accent color is reserved for primary action, current selection, and state indicators. Not decoration. Every color has a consistent meaning across every screen. +## Audit before choosing ---- +Read DESIGN.md, tokens, assets, current themes, and representative states. Identify: -## Assess Color Opportunity +- which colors are confirmed brand commitments; +- current surface, text, action, and semantic roles; +- places where grayscale obscures hierarchy or state; +- contrast failures and color-only communication; +- light/dark or data-visualization requirements; +- whether the task asks for more color or a new identity. -Analyze the current state and identify opportunities: +If a new identity is required, use [new-work.md](new-work.md). Ask only when a binding brand decision cannot be inferred. -1. **Understand current state**: - - **Color absence**: Pure grayscale? Limited neutrals? One timid accent? - - **Missed opportunities**: Where could color add meaning, hierarchy, or delight? - - **Context**: What's appropriate for this domain and audience? - - **Brand**: Are there existing brand colors we should use? +## Choose a strategy -2. **Identify where color adds value**: - - **Semantic meaning**: Success (green), error (red), warning (yellow/orange), info (blue) - - **Hierarchy**: Drawing attention to important elements - - **Categorization**: Different sections, types, or states - - **Emotional tone**: Warmth, energy, trust, creativity - - **Wayfinding**: Helping users navigate and understand structure - - **Delight**: Moments of visual interest and personality +Name the intended emotional temperature, dominant relationship, contrast range, and color dosage before editing. The strategy may be restrained or immersive; it must follow the brief and selected world rather than a fixed percentage rule. -If any of these are unclear from the codebase, STOP and call the AskUserQuestion tool to clarify. +Build roles, not a bag of swatches: -**CRITICAL**: More color ≠ better. Strategic color beats rainbow vomit every time. Every color should have a purpose. +- canvas and elevated surfaces; +- primary and secondary text; +- action, focus, and selection; +- borders and separators; +- success, warning, error, and information; +- data categories or scales when needed. -## Plan Color Strategy +Use the project's existing color space. For a new web palette, prefer OKLCH because lightness and chroma can be adjusted predictably. Choose hue from product meaning and visual direction, never from a default category association. -Create a purposeful color introduction plan: +## Apply at system scale -- **Color palette**: What colors match the brand/context? (Choose 2-4 colors max beyond neutrals) -- **Dominant color**: Which color owns 60% of colored elements? -- **Accent colors**: Which colors provide contrast and highlights? (30% and 10%) -- **Application strategy**: Where does each color appear and why? +- Let the strongest color own a deliberate region or role instead of scattering tiny accents. +- Keep the primary action easy to find; do not spend its color on decoration. +- Tint neutrals only when the brand hue genuinely creates cohesion. Neutral gray is valid when it serves the world. +- On colored surfaces, derive secondary text from the foreground or surface hue rather than using washed-out generic gray. +- Keep semantic meanings consistent, but respect platform and domain conventions instead of assuming fixed hues. +- For data, use distinct lightness, chroma, shape, label, or pattern so color is not the only code. +- 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. -**IMPORTANT**: Color should enhance hierarchy and meaning, not create chaos. Less is more when it matters more. +Decoration without a relationship to hierarchy, state, content, or the visual world is not a color strategy. -## Introduce Color Strategically +## Contrast and perception -Add color systematically across these dimensions: +Verify computed foreground/background pairs: -### Semantic Color -- **State indicators**: - - Success: Green tones (emerald, forest, mint) - - Error: Red/pink tones (rose, crimson, coral) - - Warning: Orange/amber tones - - Info: Blue tones (sky, ocean, indigo) - - Neutral: Gray/slate for inactive states +| Content | WCAG AA minimum | +|---|---| +| body text | 4.5:1 | +| large text | 3:1 | +| controls, icons, focus indicators | 3:1 | -- **Status badges**: Colored backgrounds or borders for states (active, pending, completed, etc.) -- **Progress indicators**: Colored bars, rings, or charts showing completion or health +Do not rely on eyesight alone. Check interactive states, overlays, text on images, disabled content, and both themes. Simulate common vision deficiencies. Information conveyed by color also needs text, shape, iconography, or position. -### Accent Color Application -- **Primary actions**: Color the most important buttons/CTAs -- **Links**: Add color to clickable text (maintain accessibility) -- **Icons**: Colorize key icons for recognition and personality -- **Headers/titles**: Add color to section headers or key labels -- **Hover states**: Introduce color on interaction +When deriving OKLCH ramps, vary lightness and reduce chroma near white and black. Do not keep high chroma at extreme lightness merely to make the math uniform. Prefer explicit colors over chains of translucent overlays when alpha would make contrast context-dependent. -### Background & Surfaces -- **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 toward the brand, not "for warmth" by reflex +## Verify -**Use OKLCH for color**: It's perceptually uniform, meaning equal steps in lightness *look* equal. Great for generating harmonious scales. - -### Data Visualization -- **Charts & graphs**: Use color to encode categories or values -- **Heatmaps**: Color intensity shows density or importance -- **Comparison**: Color coding for different datasets or timeframes - -### Borders & Accents -- **Hairline borders**: 1px colored borders on full perimeter (not side-stripes; see the absolute ban on `border-left/right > 1px`) -- **Underlines**: Color underlines for emphasis or active states -- **Dividers**: Subtle colored dividers instead of gray lines -- **Focus rings**: Colored focus indicators matching brand -- **Surface tints**: A 4-8% background wash of the accent color instead of a stripe - -**NEVER**: `border-left` or `border-right` greater than 1px as a colored accent stripe. This is one of the three absolute bans in the parent skill. If you want to mark a card as "active" or "warning", use a full hairline border, a background tint, a leading glyph, or a numbered prefix. Not a side stripe. - -### Typography Color -- **Colored headings**: Use brand colors for section headings (maintain contrast) -- **Highlight text**: Color for emphasis or categories -- **Labels & tags**: Small colored labels for metadata or categories - -### Decorative Elements -- **Illustrations**: Add colored illustrations or icons -- **Shapes**: Geometric shapes in brand colors as background elements -- **Gradients**: Colorful gradient overlays or mesh backgrounds -- **Blobs/organic shapes**: Soft colored shapes for visual interest - -## Balance & Refinement - -Ensure color addition improves rather than overwhelms: - -### Maintain Hierarchy -- **Dominant color** (60%): Primary brand color or most used accent -- **Secondary color** (30%): Supporting color for variety -- **Accent color** (10%): High contrast for key moments -- **Neutrals** (remaining): Gray/black/white for structure - -### Accessibility -- **Contrast ratios**: Ensure WCAG compliance (4.5:1 for text, 3:1 for UI components) -- **Don't rely on color alone**: Use icons, labels, or patterns alongside color -- **Test for color blindness**: Verify red/green combinations work for all users - -### Cohesion -- **Consistent palette**: Use colors from defined palette, not arbitrary choices -- **Systematic application**: Same color meanings throughout (green always = success) -- **Temperature consistency**: Warm palette stays warm, cool stays cool - -**NEVER**: -- 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 -- Violate WCAG contrast requirements -- Use color as the only indicator (accessibility issue) -- Make everything colorful (defeats the purpose) -- Default to purple-blue gradients (AI slop aesthetic) - -## Verify Color Addition - -Test that colorization improves the experience: - -- **Better hierarchy**: Does color guide attention appropriately? -- **Clearer meaning**: Does color help users understand states/categories? -- **More engaging**: Does the interface feel warmer and more inviting? -- **Still accessible**: Do all color combinations meet WCAG standards? -- **Not overwhelming**: Is color balanced and purposeful? +- Every color has a stable role or a world-specific atmospheric purpose. +- Attention lands on the intended action, content, or state. +- The palette works across quiet, dense, interactive, error, and empty states. +- Light and dark themes are each composed, not mechanically inverted. +- Contrast and non-color cues pass in all relevant states. +- The result is recognizably this product, not a generic “colorful” treatment. When the palette earns its place, hand off to `/impeccable polish` for the final pass. ## Live-mode signature params -When invoked from live mode, each variant MUST declare a `color-amount` param so the user can dial between a restrained accent and a drenched surface without regeneration. Author the variant's CSS against `var(--p-color-amount, 0.5)`, typically as the alpha multiplier on backgrounds, or as a scaling factor on the chroma axis in an OKLCH expression. 0 = neutral/monochrome, 1 = full saturation / dominant coverage. +When invoked from live mode, every variant declares a `color-amount` parameter. Author CSS against `var(--p-color-amount, 0.5)` so the user can move from neutral to the variant's full color strategy without regeneration. ```json {"id":"color-amount","kind":"range","min":0,"max":1,"step":0.05,"default":0.5,"label":"Color amount"} ``` -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). +Add at most two variant-specific parameters, such as palette, temperature, or tint behavior. Follow [live.md](live.md)'s parameter contract. diff --git a/plugin/skills/impeccable/reference/craft.md b/plugin/skills/impeccable/reference/craft.md index cc7f98d37..dbbc9402c 100644 --- a/plugin/skills/impeccable/reference/craft.md +++ b/plugin/skills/impeccable/reference/craft.md @@ -1,3 +1,5 @@ # Craft (deprecated alias) -`craft` is a deprecated alias for the standard build flow. Builds route through SKILL.md's new-work gate; [new-work.md](new-work.md) carries the direction checkpoint ("Decide, then build"), the identity playbook, and the finishing pass. Invoking `craft` explicitly forces the attended checkpoints: pause at the stated direction for confirmation, and follow [codex.md](codex.md)'s mock flow when the harness generates images. Nothing else differs from a plain build request. +`craft` is a deprecated alias for an ordinary request to make new visual work. It adds no setup, interview, checkpoint, tool, or quality behavior. Apply SKILL.md's normal routing: create missing PRODUCT.md through [init.md](init.md), then follow [new-work.md](new-work.md) for visual authority, world and surface decisions, implementation, and finish. + +Do not tell users they need to invoke `craft`. Natural requests such as “build this feature,” “make a landing page,” or “redesign this screen” use the same flow. diff --git a/plugin/skills/impeccable/reference/critique.md b/plugin/skills/impeccable/reference/critique.md index 429b6c4fc..9306f4420 100644 --- a/plugin/skills/impeccable/reference/critique.md +++ b/plugin/skills/impeccable/reference/critique.md @@ -43,13 +43,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 @@ -115,11 +115,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. @@ -176,7 +176,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. 2. **Pass the structured metadata** through `IMPECCABLE_CRITIQUE_META` (JSON), then run the write command: ```bash diff --git a/plugin/skills/impeccable/reference/delight.md b/plugin/skills/impeccable/reference/delight.md index 3fa4b3205..40aaddc34 100644 --- a/plugin/skills/impeccable/reference/delight.md +++ b/plugin/skills/impeccable/reference/delight.md @@ -1,302 +1,70 @@ -> **Additional context needed**: what's appropriate for the domain (playful vs professional vs quirky vs elegant). +> **Additional context needed**: the brand's emotional range. -Find the moments where personality and unexpected polish would turn a functional interface into one users remember and tell other people about. Add only where the moment earns it; delight everywhere reads as noise. +Make the experience memorable at moments that earn it. Delight is not a layer of generic whimsy; it is product character revealed through a useful interaction, a humane response, or an unexpectedly considered detail. --- -## Register +## Visitor mode -Persuade + Experience: delight can be distributed across copy voice, section transitions, discovery rewards, seasonal touches, personality across the whole surface. +- **Persuade + Experience:** personality may run through voice, composition, motion, and discovery, provided the artifact remains the focus. +- **Operate + Read:** concentrate delight at meaningful moments such as first use, completion, recovery, or mastery. Reliability carries everything else. -Operate + Read: delight at specific moments, not pages. Completion, first-time actions, error recovery, milestone crossings. Reliability and consistency carry the rest of the experience; delight pushed everywhere reads as noise. +## Find the opportunity ---- +Inspect the target, DESIGN.md, product voice, repeated-use frequency, and emotional context. Look for: -## Assess Delight Opportunities +- effort worth acknowledging; +- waiting that can become informative; +- an empty or first-use state that can orient; +- an error or recovery moment that needs empathy; +- an interaction whose physical or verbal response could express the brand; +- a useful capability people might enjoy discovering. -Identify where delight would enhance (not distract from) the experience: +Do not manufacture a celebration for an ordinary click. Ask only when the brand's emotional range or the stakes cannot be inferred. -1. **Find natural delight moments**: - - **Success states**: Completed actions (save, send, publish) - - **Empty states**: First-time experiences, onboarding - - **Loading states**: Waiting periods that could be entertaining - - **Achievements**: Milestones, streaks, completions - - **Interactions**: Hover states, clicks, drags - - **Errors**: Softening frustrating moments - - **Easter eggs**: Hidden discoveries for curious users +## Define one delight thesis -2. **Understand the context**: - - What's the brand personality? (Playful? Professional? Quirky? Elegant?) - - Who's the audience? (Tech-savvy? Creative? Corporate?) - - What's the emotional context? (Accomplishment? Exploration? Frustration?) - - What's appropriate? (Banking app ≠ gaming app) +State in one sentence what the user should feel and why that feeling belongs to this product. Then choose the smallest system that can deliver it: -3. **Define delight strategy**: - - **Subtle sophistication**: Refined micro-interactions (luxury brands) - - **Playful personality**: Whimsical illustrations and copy (consumer apps) - - **Helpful surprises**: Anticipating needs before users ask (productivity tools) - - **Sensory richness**: Satisfying sounds, smooth animations (creative tools) +- a distinctive response to a meaningful action; +- product-specific language that clarifies while carrying voice; +- an interaction or transition with a recognizable material behavior; +- an illustration, sound, haptic, or environmental detail grounded in the product world; +- a discovery reward that reveals real utility. -If any of these are unclear from the codebase, STOP and call the AskUserQuestion tool to clarify. +Derive the treatment from product mechanism and visual world, not a stock catalog. -**CRITICAL**: Delight should enhance usability, never obscure it. If users notice the delight more than accomplishing their goal, you've gone too far. +## Build for the emotional moment -## Delight Principles +- **Success:** match the response to the effort and consequence. Major milestones can expand; routine saves should simply feel certain. +- **Waiting:** show truthful progress, useful context, or product-specific activity. Never fake work or delay completion to stage a flourish. +- **Empty and first use:** make the next action clear before adding personality. +- **Error and recovery:** lead with the problem and recovery. Warmth may reduce stress; jokes must not trivialize loss, money, privacy, or blocked work. +- **Repeated interaction:** keep the response satisfying after the hundredth use. Variation is useful only when it remains coherent and predictable enough to trust. +- **Discovery:** reward curiosity without hiding required functionality. -Follow these guidelines: +Copy must use the product's language. Generic whimsy is worse than neutral clarity. -### Delight Amplifies, Never Blocks -- Delight moments should be quick (< 1 second) -- Never delay core functionality for delight -- Make delight skippable or subtle -- Respect user's time and task focus +## Protect the experience -### Surprise and Discovery -- Hide delightful details for users to discover -- Reward exploration and curiosity -- Don't announce every delight moment -- Let users share discoveries with others +Delight must not: -### Appropriate to Context -- Match delight to emotional moment (celebrate success, empathize with errors) -- Respect the user's state (don't be playful during critical errors) -- Match brand personality and audience expectations -- Cultural sensitivity (what's delightful varies by culture) +- delay, block, or obscure the primary task; +- override platform conventions or accessibility; +- add unrequested factual claims; +- play sound without consent or ignore mute settings; +- become mandatory, unskippable, or exhausting on repeat; +- add a dependency or asset cost disproportionate to the moment. -### Compound Over Time -- Delight should remain fresh with repeated use -- Vary responses (not same animation every time) -- Reveal deeper layers with continued use -- Build anticipation through patterns +For authored motion, load [animate.md](animate.md). Respect reduced motion, screen readers, keyboard use, touch, localization, and cultural context. Nonessential loops stop when hidden. Make celebration intensity proportional to frequency and consequence. -## Delight Techniques +## Verify -Add personality and joy through these methods: +- The moment is specific enough that a neighboring product could not use it unchanged. +- It improves comprehension, confidence, motivation, or emotional recovery. +- The interface remains fast and obvious without the flourish. +- Repetition does not turn charm into friction. +- Reduced-motion, muted, keyboard, touch, and localized paths work. +- The result feels like the selected world, not a generic “delight” treatment. -### Micro-interactions & Animation - -**Button delight**: -```css -/* Satisfying button press */ -.button { - transition: transform 0.1s, box-shadow 0.1s; -} -.button:active { - transform: translateY(2px); - box-shadow: 0 2px 4px rgba(0,0,0,0.2); -} - -/* Ripple effect on click */ -/* Smooth lift on hover */ -.button:hover { - transform: translateY(-2px); - transition: transform 0.2s cubic-bezier(0.25, 1, 0.5, 1); /* ease-out-quart */ -} -``` - -**Loading delight**: -- Playful loading animations (not just spinners) -- Personality in loading messages (write product-specific ones, not generic AI filler) -- Progress indication with encouraging messages -- Skeleton screens with subtle animations - -**Success animations**: -- Checkmark draw animation -- Confetti burst for major achievements -- Gentle scale + fade for confirmation -- Satisfying sound effects (subtle) - -**Hover surprises**: -- Icons that animate on hover -- Color shifts or glow effects -- Tooltip reveals with personality -- Cursor changes (custom cursors for branded experiences) - -### Personality in Copy - -**Playful error messages**: -``` -"Error 404" -"This page is playing hide and seek. (And winning)" - -"Connection failed" -"Looks like the internet took a coffee break. Want to retry?" -``` - -**Encouraging empty states**: -``` -"No projects" -"Your canvas awaits. Create something amazing." - -"No saved articles" -"Nothing on the nightstand yet. Save a story for later." -``` - -**Playful labels & tooltips**: -``` -"Delete" -"Send to void" (for playful brand) - -"Help" -"Rescue me" (tooltip) -``` - -**IMPORTANT**: Match copy personality to brand. Banks shouldn't be wacky, but they can be warm. - -### Illustrations & Visual Personality - -**Custom illustrations**: -- Empty state illustrations (not stock icons) -- Error state illustrations (friendly monsters, quirky characters) -- Loading state illustrations (animated characters) -- Success state illustrations (celebrations) - -**Icon personality**: -- Custom icon set matching brand personality -- Animated icons (subtle motion on hover/click) -- Illustrative icons (more detailed than generic) -- Consistent style across all icons - -**Background effects**: -- Subtle particle effects -- Gradient mesh backgrounds -- Geometric patterns -- Parallax depth -- Time-of-day themes (morning vs night) - -### Satisfying Interactions - -**Drag and drop delight**: -- Lift effect on drag (shadow, scale) -- Snap animation when dropped -- Satisfying placement sound -- Undo toast ("Dropped in wrong place? [Undo]") - -**Toggle switches**: -- Smooth slide with spring physics -- Color transition -- Haptic feedback on mobile -- Optional sound effect - -**Progress & achievements** (Operate surfaces with recurring tasks only; gamification on editorial, portfolio, or one-visit surfaces reads as noise): -- Streak counters with celebratory milestones -- Progress bars that "celebrate" at 100% -- Badge unlocks with animation -- Playful stats ("You're on fire! 5 days in a row") - -**Form interactions**: -- Input fields that animate on focus -- Checkboxes with a satisfying scale pulse when checked -- Success state that celebrates valid input -- Auto-grow textareas - -### Sound Design - -**Subtle audio cues** (when appropriate): -- Notification sounds (distinctive but not annoying) -- Success sounds (satisfying "ding") -- Error sounds (empathetic, not harsh) -- Typing sounds for chat/messaging -- Ambient background audio (very subtle) - -**IMPORTANT**: -- Respect system sound settings -- Provide mute option -- Keep volumes quiet (subtle cues, not alarms) -- Don't play on every interaction (sound fatigue is real) - -### Easter Eggs & Hidden Delights - -**Discovery rewards**: -- Konami code unlocks special theme -- Hidden keyboard shortcuts (Cmd+K for special features) -- Hover reveals on logos or illustrations -- Alt text jokes on images (for screen reader users too!) -- A view-source or console colophon (a note on the typefaces, a thank-you, a hiring line) - -**Seasonal touches**: -- Holiday themes (subtle, tasteful) -- Seasonal color shifts -- Weather-based variations -- Time-based changes (dark at night, light during day) - -**Contextual personality**: -- Different messages based on time of day -- Responses to specific user actions -- Randomized variations (not same every time) -- Progressive reveals with continued use - -### Loading & Waiting States - -**Make waiting engaging**: -- Interesting loading messages that rotate -- Progress bars with personality -- Mini-games during long loads -- Fun facts or tips while waiting -- Countdown with encouraging messages - -``` -Loading messages: write ones specific to your product, not generic AI filler: -- "Syncing with your team's changes..." -- "Fetching this week's issue..." -- "Developing your photos..." -- "Checking tomorrow's tide tables..." -``` - -**WARNING**: Avoid cliched loading messages like "Herding pixels", "Teaching robots to dance", "Consulting the magic 8-ball", "Counting backwards from infinity". These are AI-slop copy, instantly recognizable as machine-generated. Write messages that are specific to what your product actually does. - -### Celebration Moments - -**Success celebrations**: -- Confetti for major milestones -- Animated checkmarks for completions -- Progress bar celebrations at 100% -- "Achievement unlocked" style notifications -- Personalized messages ("You published your 10th article!") - -**Milestone recognition** (same scoping as Progress & achievements: Operate surfaces with recurring tasks): -- First-time actions get special treatment -- Streak tracking and celebration -- Progress toward goals -- Anniversary celebrations - -## Implementation Patterns - -**Animation libraries**: -- Framer Motion (React) -- GSAP (universal) -- Lottie (After Effects animations) -- Canvas confetti (party effects) - -**Sound libraries**: -- Howler.js (audio management) -- Use-sound (React hook) - -**Physics libraries**: -- React Spring (spring physics) -- Popmotion (animation primitives) - -**IMPORTANT**: File size matters. Compress images, optimize animations, lazy load delight features. - -**NEVER**: -- Delay core functionality for delight -- Force users through delightful moments (make skippable) -- Use delight to hide poor UX -- Overdo it (less is more) -- Ignore accessibility (animate responsibly, provide alternatives) -- Make every interaction delightful (special moments should be special) -- Sacrifice performance for delight -- Be inappropriate for context (read the room) - -## Verify Delight Quality - -Test that delight actually delights: - -- **User reactions**: Do users smile? Share screenshots? -- **Doesn't annoy**: Still pleasant after 100th time? -- **Doesn't block**: Can users opt out or skip? -- **Performant**: No jank, no slowdown -- **Appropriate**: Matches brand and context -- **Accessible**: Works with reduced motion, screen readers - -When the moments feel earned, hand off to `/impeccable polish` for the final pass. +When the personality feels earned, hand off to `/impeccable polish` for the final pass. diff --git a/plugin/skills/impeccable/reference/document.md b/plugin/skills/impeccable/reference/document.md index 4c52e92c5..6bcf9b824 100644 --- a/plugin/skills/impeccable/reference/document.md +++ b/plugin/skills/impeccable/reference/document.md @@ -1,6 +1,6 @@ Generate a `DESIGN.md` file at the project root that captures the current visual design system, so AI agents generating new screens stay on-brand. -DESIGN.md follows the [official DESIGN.md format spec](https://raw.githubusercontent.com/google-labs-code/design.md/main/docs/spec.md): YAML frontmatter carrying machine-readable design tokens, followed by a markdown body with exactly six sections in a fixed order. **Tokens are normative; prose provides context for how to apply them.** Sections may be omitted when not relevant, but **do not reorder them and do not rename them**. Section headers must match the spec character-for-character so the file stays parseable by other DESIGN.md-aware tools (Stitch itself, awesome-design-md, skill-rest, etc.). +DESIGN.md follows the [official DESIGN.md format spec](https://raw.githubusercontent.com/google-labs-code/design.md/main/docs/spec.md): optional YAML frontmatter carrying machine-readable design tokens, followed by up to eight markdown sections in a fixed order. **Tokens are normative; prose provides context for how to apply them.** Sections may be omitted when not relevant, but those present stay in the specified order. Use the canonical headings below so the file remains portable across DESIGN.md-aware tools. ## The frontmatter: token schema @@ -43,26 +43,28 @@ components: 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. +- **Colors accept any valid CSS color string.** Hex is the recommended default for portability, but preserve an incumbent `rgb()`, `hsl()`, `oklch()`, wide-gamut, or mixed-color value when it is the project's normative source. 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 (`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) +## The markdown body: eight sections (canonical order) 1. `## Overview` 2. `## Colors` 3. `## Typography` -4. `## Elevation` -5. `## Components` -6. `## Do's and Don'ts` +4. `## Layout` +5. `## Elevation & Depth` +6. `## Shapes` +7. `## Components` +8. `## Do's and Don'ts` -Optional evocative subtitles are allowed in the form `## 2. Colors: The [Name] Palette` (Stitch's own outputs do this), but the literal word in each header (Overview, Colors, Typography, Elevation, Components, Do's and Don'ts) must be present. Do NOT add extra top-level sections (Layout Principles, Responsive Behavior, Motion, Agent Prompt Guide). Fold that content into the six spec sections where it naturally belongs. +Omit irrelevant sections rather than filling them with invented rules. Put responsive layout in Layout, depth in Elevation & Depth, radius and form language in Shapes, and per-component behavior in Components. Unknown sections are preserved by the format, but new visual guidance should use the canonical structure whenever it fits. ## When to run -- 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. +- New-work found a coherent incumbent visual system but no `DESIGN.md`. +- The first implementation of a new world is complete and its provisional decisions need to be carbonized. - An existing `DESIGN.md` is stale (the design has drifted). - Before a large redesign, to capture the current state as a reference. @@ -71,9 +73,9 @@ 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 init, nothing built yet). Interview for five high-level answers, write a minimal DESIGN.md marked ``. Re-run in scan mode once there's code. +- **Seed mode**: the project is pre-implementation. Ensure PRODUCT.md exists, then reuse new-work's visual-world workshop and write its directional DESIGN.md 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. +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` requests new-work's world workshop, but it does not authorize replacing coherent code: when an incumbent system exists, offer scan mode or route an explicit identity-replacement request through new-work. ## Scan mode (approach C: auto-extract, then confirm descriptive language) @@ -97,7 +99,8 @@ Build a structured draft from the discovered tokens. For each token class: - **Typography**: Map observed sizes and weights to the Material hierarchy (display / headline / title / body / label). Note font-family stacks and the scale ratio. - **Elevation**: Catalogue the shadow vocabulary. If the project is flat and uses tonal layering instead, that's a valid answer; state it explicitly. - **Components**: For each common component (button, card, input, chip, list item, tooltip, nav), extract shape (radius), color assignment, hover/focus treatment, internal padding. -- **Spacing + layout**: Fold into Overview or relevant Components. The spec does NOT have a Layout section. +- **Layout + spacing**: Extract grid, container, breakpoint, rhythm, and density behavior into Layout. +- **Shapes**: Extract radius, corner, border, clipping, and recurring form behavior into Shapes. ### Step 2b: Stage the frontmatter @@ -112,19 +115,19 @@ Skip anything the project doesn't have. Empty scale keys or fabricated tokens po ### Step 3: Ask the user for qualitative language -The following require creative input that cannot be auto-extracted. Group them into one `AskUserQuestion` interaction: +The following require creative input that cannot be auto-extracted. Ask them in two structured rounds of no more than three questions each (or the harness's lower limit), waiting between rounds: - **Creative North Star**: a single named metaphor for the whole system ("The Editorial Sanctuary", "The Golden State Curator", "The Lab Notebook"). Offer 2-3 options that honor PRODUCT.md's brand personality. -- **Overview voice**: mood adjectives, aesthetic philosophy in 2-3 sentences, anti-references (what the system should not feel like). +- **Overview voice**: mood adjectives, aesthetic philosophy in 2-3 sentences, and any confirmed visual anti-reference. - **Color character** (for auto-extracted colors): descriptive names ("Deep Muted Teal-Navy", not "blue-800"). Suggest 2-3 options per key color based on hue/saturation. - **Elevation philosophy**: flat/layered/lifted. If shadows exist, is their role ambient or structural? - **Component philosophy**: the feel of buttons, cards, inputs in one phrase ("tactile and confident" vs. "refined and restrained"). -Quote a line from PRODUCT.md when possible so the user sees their own strategic language carry forward. +Carry a line from PRODUCT.md only when it is a durable brand commitment that actually constrains the visual system. Page strategy and surface concepts do not belong here. ### Step 4: Write DESIGN.md -The file opens with the YAML frontmatter staged in Step 2b (schema documented at the top of this reference), then the markdown body using the structure below. Headers must match character-for-character. Optional evocative subtitles (e.g. `## 2. Colors: The Coastal Palette`) are allowed. +The file opens with the YAML frontmatter staged in Step 2b (schema documented at the top of this reference), then the markdown body using the canonical structure below. ```markdown --- @@ -136,13 +139,13 @@ colors: # Design System: [Project Title] -## 1. Overview +## Overview **Creative North Star: "[Named metaphor in quotes]"** -[2-3 paragraph holistic description: personality, density, aesthetic philosophy. Start from the North Star and work outward. State what this system explicitly rejects (pulled from PRODUCT.md's anti-references). End with a short **Key Characteristics:** bullet list.] +[2-3 paragraph holistic description: personality, density, and aesthetic philosophy. Start from the North Star and work outward. State only confirmed visual rejections. End with a short **Key Characteristics:** bullet list.] -## 2. Colors +## Colors [Describe the palette character in one sentence.] @@ -162,7 +165,7 @@ colors: ### Named Rules (optional, powerful) **The [Rule Name] Rule.** [Short, forceful prohibition or doctrine, e.g. "The One Voice Rule. The primary accent is used on ≤10% of any given screen. Its rarity is the point."] -## 3. Typography +## Typography **Display Font:** [Family] (with [fallback]) **Body Font:** [Family] (with [fallback]) @@ -180,7 +183,11 @@ colors: ### Named Rules (optional) **The [Rule Name] Rule.** [Short doctrine about type use.] -## 4. Elevation +## Layout + +[Describe the grid or spatial model, container behavior, density, responsive changes, and the spacing rhythm. Include exact values only when observed.] + +## Elevation & Depth [One paragraph: does this system use shadows, tonal layering, or a hybrid? If "no shadows", say so explicitly and describe how depth is conveyed instead.] @@ -191,7 +198,11 @@ colors: ### Named Rules (optional) **The [Rule Name] Rule.** [e.g. "The Flat-By-Default Rule. Surfaces are flat at rest. Shadows appear only as a response to state (hover, elevation, focus)."] -## 5. Components +## Shapes + +[Describe the form language: corner/radius strategy, borders, clipping, and any recurring silhouette or geometry.] + +## Components For each component, lead with a short character line, then specify shape, color assignment, states, and any distinctive behavior. @@ -223,16 +234,16 @@ For each component, lead with a short character line, then specify shape, color ### [Signature Component] (optional; if the project has a distinctive custom component worth documenting) [Description.] -## 6. Do's and Don'ts +## Do's and Don'ts -Concrete, forceful guardrails. Lead each with "Do" or "Don't". Be specific: include exact colors, pixel values, and named anti-patterns the user mentioned in PRODUCT.md. **Every anti-reference in PRODUCT.md should show up here as a "Don't" with the same language**, so the visual spec carries the strategic line through. Quote PRODUCT.md directly where possible: if PRODUCT.md says *"avoid dark mode with purple gradients, neon accents, glassmorphism"*, the Don'ts here should repeat that by name. +Concrete visual guardrails grounded in the incumbent implementation or the user's chosen world. Lead each with "Do" or "Don't" and include exact values only when established. Do not turn a task-specific concept or surface strategy into a system-wide prohibition. ### Do: - **Do** [specific prescription with exact values / named rule]. - **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** [...] ``` @@ -307,7 +318,7 @@ The `html` and `css` fields must be **self-contained, drop-in snippets** that re Aim for a tight set of **5-10 components** that best represent the visual system: - **Canonical primitives (always include if the project has them):** button (each variant as a separate component entry), input/text field, navigation, chip/tag, card. -- **Signature components (include if distinctive):** hero CTA, featured card, filter pill, a distinctive table or list-row treatment, any custom pattern the user mentioned as important in PRODUCT.md. +- **Signature components (include if distinctive):** the recurring custom patterns that actually define the implemented system. - **Skip the rest.** Utility components, form building blocks, wrapper layouts: not worth documenting unless visually distinctive. If the project has **no component library yet** (bare landing page, new project), synthesize canonical primitives from the tokens using best-practice defaults consistent with the DESIGN.md's rules. Every `.impeccable/design.json` has *something* to render, even on day zero. @@ -338,64 +349,40 @@ Your own write is the freshest source; subsequent commands in this session don't ## Seed mode -For projects with no visual system to extract yet. Produces a minimal scaffold, not a full spec. +For projects with no visual system to extract yet. Produces a user-chosen visual-world scaffold, not a fabricated token spec. -### Step 1: Confirm seed mode +### Step 1: Route through new-work's workshop -Before interviewing: "There's no existing visual system to scan. I'll ask five quick questions to seed a starter DESIGN.md. You can re-run `/impeccable document` once there's code, to capture the real tokens and components. OK?" +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 the user prefers to skip, stop. No file. +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. -### Step 2: Five questions +If new-work already completed the workshop in this session, use its chosen direction directly. Do not ask again. -Group into one `AskUserQuestion` interaction. Options must be concrete. +### Step 2: Write seed DESIGN.md -1. **Color strategy.** Pick one: - - Restrained: tinted neutrals + one accent ≤10% - - Committed: one saturated color carries 30–60% of the surface - - Full palette: 3–4 named color roles, each deliberate - - Drenched: the surface IS the color - - Then: one hue family or anchor reference ("deep teal", "mustard", "Klim #ff4500 orange"). - -2. **Typography direction.** Pick one (specific fonts come later): - - Serif display + sans body - - Single sans (warm / technical / geometric / humanist; pick a feel) - - Display + mono - - Mono-forward - - Editorial script + sans - -3. **Motion energy.** Pick one: - - Restrained: state changes only - - Responsive: feedback + transitions, no choreography - - Choreographed: orchestrated entrances, scroll-driven sequences - -4. **Three named references.** Brands, products, printed objects. Not adjectives. - -5. **One anti-reference.** What it should NOT feel like. Also named. - -### Step 3: Write seed DESIGN.md - -Use the six-section spec from Scan mode. Populate what the interview answers; leave the rest as honest placeholders. The seed is a scaffold, not a fabricated spec. +Use the canonical section order from Scan mode. Populate the selected workshop direction and leave unresolved implementation facts as honest placeholders. The seed commits a world and its invariants; it does not pretend implementation tokens already exist. Lead the file with: ```markdown - + ``` Per-section guidance in seed mode: -- **Overview**: Creative North Star and philosophy phrased from the answers (color strategy + motion energy + references). Reference the user's anti-reference directly. -- **Colors**: Color strategy as a Named Rule (e.g. *"The Drenched Rule. The surface IS the color."*). Hue family or anchor reference. No hex values; mark as `[to be resolved during implementation]`. -- **Typography**: the direction the user picked (e.g. "Serif display + sans body"). No font names yet: `[font pairing to be chosen at implementation]`. -- **Elevation**: inferred from motion energy. Restrained/Responsive → flat by default; Choreographed → layered. One sentence. +- **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. +- **Elevation & Depth**: the selected material and depth behavior, stated as an invariant rather than inferred from a generic preset. +- **Shapes**: the selected form and corner language. - **Components**: omit entirely; no components exist yet. -- **Do's and Don'ts**: carry PRODUCT.md's anti-references directly plus the anti-reference named in Q5. +- **Do's and Don'ts**: record the durable guardrails confirmed during the world choice, not task-local refusals. 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 +### Step 3: 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." @@ -405,15 +392,15 @@ Your own write is the freshest source; no reload needed. ## Style guidelines - **Frontmatter first, prose second.** Tokens go in the YAML frontmatter; prose contextualizes them. Don't redefine a token value in two places; the frontmatter is normative. -- **Cite PRODUCT.md anti-references by name** in the Do's and Don'ts section. If PRODUCT.md lists "SaaS landing-page clichés" or "generic AI tool marketing" as anti-references, the DESIGN.md Don'ts should repeat those phrases verbatim so the visual spec enforces the strategic line. -- **Match the spec, don't invent new sections.** The six section names are fixed. If you have Layout/Motion/Responsive content to document, fold it into Overview (philosophy-level rules) or Components (per-component behavior). +- **Carry only durable product constraints.** A binding logo, identity asset, accessibility need, or brand commitment from PRODUCT.md may constrain DESIGN.md. Surface strategy stays in its surface brief. +- **Match the spec.** Use its eight canonical sections in order and omit any that are irrelevant. Put motion guidance with the world or component it affects rather than creating a token group the schema does not support. - **Descriptive > technical**: "Gently curved edges (8px radius)" > "rounded-lg". Include the technical value in parens, lead with the description. - **Functional > decorative**: for each token, explain WHERE and WHY it's used, not just WHAT it is. - **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 forceful**. The voice of a design director. "Prohibited", "forbidden", "never", "always", not "consider", "might", "prefer". Match PRODUCT.md's tone. -- **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. -- **Reference PRODUCT.md**. The anti-references section of PRODUCT.md should directly inform the Do's and Don'ts section here. Quote or paraphrase. +- **Be decisive where evidence is decisive.** Use hard language for actual invariants and softer language for provisional guidance. +- **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. ## Pitfalls @@ -423,7 +410,7 @@ Your own write is the freshest source; no reload needed. - Don't invent components that don't exist. If the project only has buttons and cards, only document those. - Don't overwrite an existing DESIGN.md without asking. - Don't duplicate content from PRODUCT.md. DESIGN.md is strictly visual. -- Don't add a "Layout Principles" or "Motion" or "Responsive Behavior" top-level section. The spec has six, not nine. Fold that content where it belongs. +- Don't replace canonical sections with near-synonyms. Put layout and responsive behavior in `Layout`; put motion with the affected world or component. - Don't rename sections even slightly. "Colors" not "Color Palette & Roles". "Typography" not "Typography Rules". Tooling parsing depends on exact headers. - Don't duplicate token values between frontmatter and prose. If a color is in `colors.primary` as hex, the prose can name it and describe its role but should not reassert a different hex. The frontmatter is normative. - Don't invent frontmatter token groups outside Stitch's schema (no `motion:`, `breakpoints:`, `shadows:` at the top level). Stitch's Zod schema only accepts `colors`, `typography`, `rounded`, `spacing`, `components`. Anything else belongs in the sidecar's `extensions`. diff --git a/plugin/skills/impeccable/reference/hooks.md b/plugin/skills/impeccable/reference/hooks.md index ed277ad6e..8eaae2852 100644 --- a/plugin/skills/impeccable/reference/hooks.md +++ b/plugin/skills/impeccable/reference/hooks.md @@ -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. diff --git a/plugin/skills/impeccable/reference/init.md b/plugin/skills/impeccable/reference/init.md index e2b1df04e..2e6736137 100644 --- a/plugin/skills/impeccable/reference/init.md +++ b/plugin/skills/impeccable/reference/init.md @@ -1,224 +1,114 @@ -# Init Flow +# Init flow -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 [DESIGN.md format spec](https://raw.githubusercontent.com/google-labs-code/design.md/main/docs/spec.md). Answers "how it looks". -- **`.impeccable/live/config.json`** (live mode): pre-configured so `/impeccable live` boots straight into variant mode with no first-time detour. - -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. +`init` captures durable product truth in PRODUCT.md. It does not invent a visual world and does not write DESIGN.md; [new-work.md](new-work.md) creates or expands one, and [document.md](document.md) records an incumbent one. Existing runnable web projects may also receive `.impeccable/live/config.json`. ## Step 1: Load current state -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). +Use the PRODUCT.md path resolved by context.mjs. Update it instead of creating a competing authority. In a child app inheriting root context, confirm shared versus app-specific scope before writing. -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. -- **PRODUCT.md exists, DESIGN.md missing**: skip to Step 5 and offer to run `/impeccable document` for DESIGN.md. -- **PRODUCT.md exists but has no `## Register` section (legacy)**: add it. Infer a hypothesis from the codebase (see Step 2), confirm with the user, write the field. -- **PRODUCT.md exists but has no `## Platform` section (legacy)**: add it the same way, but only when the project is native (`ios` / `android` / `adaptive`) or the user wants it explicit; a missing field already means `web`. -- **Both exist**: STOP and call the AskUserQuestion tool to clarify. Ask which file to refresh. Skip the one the user doesn't want changed. -- **Just DESIGN.md exists (unusual)**: do Steps 2-4 to produce PRODUCT.md. +- **No PRODUCT.md:** explore, interview, and write it. +- **PRODUCT.md exists:** ask what product knowledge is stale or missing; do not reopen confirmed fields without a reason. +- **Legacy PRODUCT.md:** add only durable missing facts; absent `## Platform` means `web` unless evidence says otherwise. +- **Only DESIGN.md exists:** leave it untouched and create PRODUCT.md. +- **Redesign/rebrand request:** preserve confirmed product truth unless the user changes it. Visual replacement happens later in new-work, not here. -Never silently overwrite an existing file. Always confirm first. +Never silently overwrite an existing file or offer DESIGN.md during init. If another request invoked init, finish PRODUCT.md and resume it. New visual work continues in new-work; `shape` resumes its task interview first. -If init was invoked as a setup blocker by another command, such as `/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 project -## Step 2: Explore the codebase +Before asking, scan enough to avoid making the user repeat known facts: product docs and copy; package/config and app boundaries; features, workflows, routes, and roles; names, logos, legal/proof assets, and brand commitments; platform/accessibility signals; and the dev command/entry when live mode applies. -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: +Treat repository evidence as a hypothesis, not user approval. Note visual maturity without documenting, extending, or replacing the world. -- **README and docs**: Project purpose, target audience, any stated goals -- **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 -- **Any style guides or brand documentation** +Form a platform hypothesis: `web`, `ios`, `android`, or `adaptive` (one product that genuinely adapts its design language per OS). Mobile web remains `web`; a native wrapper around a website does not make its design language native. -Also form a **register hypothesis** from what you find: +## Step 3: Interview for product truth -- Brand signals: `/`, `/about`, `/pricing`, `/blog/*`, `/docs/*`, hero sections, big typography, scroll-driven sections, landing-page-shaped content. -- Product signals: `/app/*`, `/dashboard`, `/settings`, `/(auth)`, forms, data tables, side/top nav, app-shell components. +STOP and call the AskUserQuestion tool to clarify. Ask only about material gaps the repository and original request do not answer with strong evidence. -Register is a hypothesis at this point, not a decision; Step 3 confirms it. +Use the structured question tool when available; otherwise ask and wait. Keep rounds to at most three focused questions and require one real answer or approval round before writing a new PRODUCT.md. Confirm inferences. -Also form a **platform hypothesis**: +Start with the unknowns that most change future product decisions: -- Native signals: React Native / Expo (`react-native`, `expo`), Flutter (`pubspec.yaml`, `flutter`), SwiftUI / UIKit (`.swift`, `.xcodeproj`, an `ios/` app target), Jetpack Compose / Android (`build.gradle`, an `android/` app module, `AndroidManifest.xml`). An `ios/` and/or `android/` directory that is a real app target, not just a Capacitor/Cordova wrapper around a website. -- Web signals (the default): a web framework (Vite, Next, Nuxt, SvelteKit, Astro), an HTML entry, a CSS/Tailwind setup, no native app target. +1. Who is the primary user, in what situation, and what job are they doing? +2. What does the product make possible, and what is its meaningfully different mechanism or position? +3. What durable constraints, assets, evidence, or product facts must future work preserve? -Values: `web` / `ios` / `android` / `adaptive` (one codebase, ships both, adapts per OS). Mobile web is still `web`. Like register, this is a hypothesis; Step 3 confirms it. +Confirm ambiguous platform separately. Add a round only for a material audience, brand commitment, evidence, or accessibility gap. Record undecided facts instead of inventing them. -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. +Do not ask for an aesthetic direction, emotional feel, visual references, colors, typography, or style during init. If the user volunteers a binding visual constraint, record it without expanding it. -## Step 3: Ask strategic questions (for PRODUCT.md) +### What belongs here -STOP and call the AskUserQuestion tool to clarify. Ask about anything the codebase doesn't answer with strong, explicit evidence. +- users, jobs, workflows, purpose, success, positioning, and operating context; +- capabilities, constraints, terminology, evidence, platform, and accessibility; +- confirmed voice, assets, and brand commitments. -### Interview mode, not confirmation mode +### What does not belong here -If the repo is empty or the user's brief is sparse, run a short interview before proposing PRODUCT.md. Do **not** turn a one-sentence request into a complete inferred PRODUCT.md and ask for blanket confirmation. - -- Use the harness's structured question tool when one exists. Otherwise, ask directly in chat and stop: one question at a time, with lettered options where the crawl suggests likely answers, waiting for each answer before the next. -- Keep skill vocabulary (register, belief ladder, anti-references) out of question text; ask for the thing in words the user would use. For the brand register, ask like a magazine editor profiling the brand: curious and narrative, drawing out the story, the feel, and what a visitor should come to believe. -- Ask in focused rounds and wait for answers between them. Keep **one topic per question**; add rounds rather than fold several topics into one either-or choice. Options obey the same rule: an option answers only the question asked; never write a compound option that bundles a feeling with a business outcome or names an additional audience. -- Use inferred answers as hypotheses or options, not as finished facts. -- Complete at least one real user-answer round before drafting PRODUCT.md, unless every required answer is directly discoverable from repo docs. -- Round 1 should establish register, platform, users, purpose, positioning, and desired outcome. -- Round 2 should establish brand personality or references, anti-references, and accessibility needs, plus conversion & proof for Persuade surfaces. - -### Minimum viable interview - -Ask enough to complete PRODUCT.md. At minimum, cover register confirmation, **platform confirmation** (`web` / `ios` / `android` / `adaptive`), users, purpose, positioning, brand personality, anti-references, and accessibility needs (plus conversion & proof for Persuade surfaces) unless each answer is directly discoverable from repo context. Never let the template's default `web` stand unconfirmed for a native or cross-platform repo. After at least one interview round, you may propose inferred answers, but the user must confirm them before you write PRODUCT.md. Never synthesize PRODUCT.md from the original task prompt alone. - -### Register (ask first; it shapes everything below) - -Every design task serves one of four visitor modes (see SKILL.md's Registers): **Persuade** (marketing, landing, campaigns), **Experience** (portfolios, albums, bodies of work), **Operate** (app UI, admin, dashboards, tools), and **Read** (documentation, editorial, long-form). PRODUCT.md stores the two-value family that covers them: `brand` covers Persuade and Experience, `product` covers Operate and Read. The brief decides the exact mode per task; the stored register only sets the default family. - -If Step 2 produced a clear hypothesis, lead with it: *"From the codebase, this looks like a [brand / product] surface. Does that match your intent, or should we treat it differently?"* - -If the signal is genuinely split (e.g. a product with a big marketing landing), STOP and call the AskUserQuestion tool to clarify. Ask which register describes the **primary** surface. The register can be overridden per task later, but PRODUCT.md carries one default. Settle the default before drafting any register-dependent questions; never batch Persuade-only questions (Conversion & proof) into the same round as the question that decides the register. - -### Platform (ask right after register) - -Every project targets **web** (includes responsive mobile web), **ios**, **android**, or **adaptive** (one codebase, ships both, adapts per OS: Flutter, React Native, KMP). Platform picks the native rulebook: HIG for `ios`, Material 3 for `android`, both for `adaptive`, none for `web`. - -If Step 2 produced a clear hypothesis, lead with it: *"From the codebase, this looks like a [web / ios / android / adaptive] project. Does that match?"* For cross-platform apps, decide by the **design language the app renders**, not the toolchain: one look on both platforms (Flutter's Material-everywhere default) takes that platform's value; genuine per-OS adaptation (Cupertino on iOS, Material on Android) is `adaptive`. When in doubt, `web`. - -A monorepo shipping both a website and a native app gets a PRODUCT.md per app, each with its own `## Platform`; the root PRODUCT.md carries the primary surface's platform. - -### Users & Purpose -- Who uses this? What's their context when using it? -- What job are they trying to get done? -- What is this for? A purpose stated in README or docs is a hypothesis, not strong evidence; confirm it, don't transcribe it. -- What does success look like? -- If more than one kind of user is plausible, confirm a primary and secondary audience; don't manufacture a split that isn't there. An audience implied by another answer (a success metric, a CTA) is still unconfirmed; ask before writing it as secondary. -- If the surface speaks to a different audience than the people who use the product, ask the user to name both. -- For brand: what emotions should the interface evoke? (confidence, delight, calm, urgency) Ask this standalone; don't fold emotions into the success question. -- For product: what workflow are they in? What's the primary task on any given screen? - -### Positioning -- In one line, what does this do that nothing else does? The single strategic claim every screen reinforces. - -### 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? - - 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? - -### Conversion & proof (Persuade surfaces only) - -Ask these only when the primary surface is a Persuade one (marketing, landing, campaigns). Experience and Read surfaces (portfolios, albums, long-form writing, documentation) get no CTA, belief-ladder, or proof questions even when the stored register is `brand`; skip this section and its PRODUCT.md counterpart. - -- What's the primary CTA? -- What's the secondary fallback, for visitors not ready for the primary? -- The one line a visitor should remember after 10 seconds. -- What must the visitor believe, in order, before taking the primary CTA? (The template's belief ladder.) -- What proof is on hand? Ask the user to hand over any testimonials, case studies, press, or client/partner logos they already have. If you can receive files directly, collect them; otherwise create `.impeccable/assets/proof/` and ask the user to add files there. Reference supplied files by path; record text proof inline. - -### Accessibility & Inclusion -- Specific accessibility requirements? (WCAG level, known user needs) -- Considerations for reduced motion, color blindness, or other accommodations? - -Skip questions where the answer is already clear. **Do NOT ask about colors, fonts, radii, or visual styling here.** Those belong in DESIGN.md, not PRODUCT.md. +- visual worlds, palettes, typography, components, or page concepts; +- visitor mode, narrative, CTA/proof sequence, or other surface strategy; +- invented testimonials, customers, benchmarks, pricing, licensing, or deployment claims; +- a requirement to decide every optional field. ## Step 4: Write PRODUCT.md -Write PRODUCT.md only after the user has confirmed the strategic answers from Step 3. If an inferred answer is uncertain or unconfirmed, ask before writing. Confirmed means what the user actually said yes to; do not pad a confirmed answer with extras they never picked (additional anti-references, audiences, roadmap claims, a WCAG level), whether drawn from the crawl, another answer, or your own option text. If an extra belongs in the doc, ask about it first. - -Synthesize into a strategic document: +Write only confirmed facts and explicitly marked open decisions. Omit irrelevant sections rather than filling them with generic prose. ```markdown # Product -## Register - -product - ## Platform web ## Users -[Who they are, their context, the job to be done. Primary audience; a secondary audience or a surface-vs-user split only when they apply.] +[Primary users, their situation, and job. Add other audiences only when confirmed.] ## Product Purpose -[What this product does, why it exists, what success looks like] +[What the product does, why it exists, and what success means.] ## Positioning -[The single strategic claim every screen reinforces. Not a visual rule, not an anti-reference.] +[The product mechanism or claim a neighboring product could not truthfully copy.] -## Conversion & proof -[Persuade surfaces only (marketing, landing, campaigns). Omit this section entirely, heading included, for the product register and for Experience or Read surfaces such as portfolios, albums, or long-form writing.] -- Primary and secondary CTA: [...] -- The line a visitor remembers after 10 seconds: [...] -- Belief ladder: [...] -- Proof on hand: [testimonials, case studies, press, or logos, referenced by path] +## Operating Context +[Workflows, environments, tools, documents, materials, and rituals that are factual parts of using or evaluating the product.] -## Brand Personality -[Voice, tone, 3-word personality, emotional goals] +## Capabilities and Constraints +[Confirmed functionality, technical constraints, terminology, and explicitly undecided product facts.] -## Anti-references -[What this should NOT look like. Specific bad-example sites or patterns to avoid.] +## Brand Commitments +[Existing name, voice, assets, personality, identity constraints, and references the user explicitly made binding. Omit when none exist.] -## Design Principles -[3-5 strategic principles derived from the conversation. Principles like "practice what you preach", "show, don't tell", "expert confidence". NOT visual rules like "use OKLCH" or "magenta accent".] +## Evidence on Hand +[Real content, data, demonstrations, testimonials, case studies, press, or assets, with paths where applicable. State absences that future work must not fabricate.] + +## Product Principles +[Three to five durable strategic principles derived from confirmed answers; no visual recipes.] ## Accessibility & Inclusion -[WCAG level, known user needs, considerations] +[Known user needs or required standard. Omit when no product-specific requirement was established.] ``` -Register is either `brand` or `product` as a bare value. No prose, no commentary. Platform is `web`, `ios`, `android`, or `adaptive`, also a bare value; omit the section only on legacy files you're leaving untouched, otherwise write `web` explicitly. +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. -Write fields as prose, and use bold sparingly: only where a word carries a decision, never as a label lead-in on every line. +### Completion gate -Write to `PROJECT_ROOT/PRODUCT.md`. If `.impeccable.md` existed, the loader already renamed it; merge into that content rather than starting from scratch. +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: Decide on DESIGN.md +## Step 5: Configure live mode when useful -Offer `/impeccable document` either way. Two paths: +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. -- **Code exists** (CSS tokens, components, a running site): "I can generate a DESIGN.md that captures your visual system (colors, typography, components) so variants stay on-brand. Want to do that now?" -- **Pre-implementation** (empty project): "I can seed a starter DESIGN.md from five quick questions about color strategy, type direction, motion energy, and references. You can re-run once there's code, to capture the real tokens. Want to do that now?" +## Step 6: Wrap up or resume -If the user agrees, delegate to `/impeccable document` (it auto-detects scan vs seed). Load its reference and follow that flow. +Summarize captured and deliberately undecided facts. Do not offer DESIGN.md merely because it is missing. -If the user prefers to skip, mention they can run `/impeccable document` any time later. +Recommend the next action from the actual project state: -## Step 6: Configure live mode (when code exists) +- Empty or early project: ask naturally for the surface to be built, or use `/impeccable shape ` when the user wants a confirmed brief without implementation. New-work will establish a visual world only when the requested work needs one. +- Existing coherent interface without DESIGN.md: `/impeccable document` if the user wants the incumbent system recorded independently of a new build. +- Existing surface needing work: name the most relevant scoped command. +- Web project ready for visual iteration: `/impeccable live` when configured. -**Skip this step when the platform is native** (`ios` / `android` / `adaptive`): live mode drives a browser overlay. A hybrid wrapper or Expo web target serving HTML doesn't change that. - -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 .claude/skills/impeccable/scripts/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) and platform captured (web / ios / android / adaptive) -- 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 or live config is pending, one line on how to set it up later - -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 platform**, offer the 2-4 most relevant (not a menu dump), and give the exact command to type. Group by intent: - -- **Build something new**: `/impeccable craft ` (shape, then build end-to-end) or `/impeccable shape ` (plan first). Lead with this for empty or early-stage projects. -- **Improve what's there**: name the specific surface. `/impeccable critique ` for a scored UX review; `/impeccable audit ` for a11y / perf / responsive checks; `/impeccable polish ` 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** (web only): `/impeccable live` (configured in Step 6) to pick elements in the browser and generate variants in place. **Skip this group for native platforms.** - -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 STOP and call the AskUserQuestion tool to clarify. Ask whether they'd like a brief summary of PRODUCT.md appended to CLAUDE.md for easier agent reference. If yes, append a short **Design Context** pointer section there. +If init was invoked by another request, resume without rerunning context.mjs; new-work owns later visual decisions. diff --git a/plugin/skills/impeccable/reference/ios.md b/plugin/skills/impeccable/reference/ios.md index ef10ee0d2..ccef5d2c4 100644 --- a/plugin/skills/impeccable/reference/ios.md +++ b/plugin/skills/impeccable/reference/ios.md @@ -2,7 +2,7 @@ For native iOS / iPadOS apps: SwiftUI, UIKit, React Native, Expo, Flutter shipping to Apple hardware. -On native, register narrows. HIG conformance governs structure, navigation, and interaction whatever the register; brand expresses through the expressive layer the platform provides (tint, type, motion, content). Calm, Duolingo, and Spotify carry strong identity entirely inside HIG conventions. +On native, the visitor mode narrows what expression may override. HIG conformance governs structure, navigation, and interaction in every mode; brand expresses through the layer the platform leaves open (tint, type, motion, content). ## The iOS slop test diff --git a/plugin/skills/impeccable/reference/layout.md b/plugin/skills/impeccable/reference/layout.md index 23e9f4b74..c8404091d 100644 --- a/plugin/skills/impeccable/reference/layout.md +++ b/plugin/skills/impeccable/reference/layout.md @@ -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. --- -## Register +## 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, register, 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 .claude/skills/impeccable/scripts/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 `/impeccable polish` for the final pass. +When the structure holds, hand off to `/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) * )`: 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. diff --git a/plugin/skills/impeccable/reference/live.md b/plugin/skills/impeccable/reference/live.md index 8c2cccd0a..d47a556c0 100644 --- a/plugin/skills/impeccable/reference/live.md +++ b/plugin/skills/impeccable/reference/live.md @@ -33,7 +33,7 @@ Chat is overhead. No recap, no tutorial output, no pasting PRODUCT / DESIGN bodi node .claude/skills/impeccable/scripts/live.mjs ``` -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. +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, DESIGN.md, and any surface brief already loaded by Setup in mind for variant generation: **DESIGN.md wins on visual decisions; PRODUCT.md wins on durable product and voice decisions; the surface brief wins on this surface's strategy.** 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 requires the user's explicit redesign/replacement intent. `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). @@ -187,7 +187,7 @@ All three carry `fallback: "agent-driven"`. Follow **Handle fallback** below. ### 3. Load the action's reference -If `event.action` is `impeccable` (the default freeform action), use SKILL.md's shared laws plus the loaded register reference (`brand.md` or `product.md`). Do not load a sub-command reference. **Freeform is not a pass to skip parameters:** you still follow the composition budget and the freeform bias in **§7 Parameters** below. Sub-command files list MUST-have signature knobs; freeform has no such file, so sizing knobs from surface weight and primary axes is entirely on you. +If `event.action` is `impeccable` (the default freeform action), use SKILL.md's shared laws and decide the visitor mode from the selected surface. Do not load a sub-command reference. **Freeform is not a pass to skip parameters:** you still follow the composition budget and the freeform bias in **§7 Parameters** below. Sub-command files list MUST-have signature knobs; freeform has no such file, so sizing knobs from surface weight and primary axes is entirely on you. Any other `event.action` (`bolder`, `quieter`, `distill`, `polish`, `typeset`, `colorize`, `layout`, `adapt`, `animate`, `delight`, `overdrive`): Read `reference/.md` before planning. Each sub-command encodes a specific discipline; skipping its reference produces generic output. Those files may require specific params; layer them on top of the §7 budget, not instead of it. @@ -216,7 +216,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. @@ -224,10 +224,7 @@ This sentence is the **identity lock**. Every variant must be readable as the sa **Default mode**: the existing identity is preserved. Variants vary expression axes within it. *This is the right mode for ~90% of live sessions.* The user picked an element on a real product they're shipping; they expect variants of *their* hero, not three different brands' heroes. -**Departure mode**: the existing identity is rejected. Variants propose alternatives consistent with PRODUCT.md voice. Trigger only when at least one is true: - -- PRODUCT.md anti-references explicitly call out the current surface ("the current `index.html` is itself an example"; "diffuse away from this"; "the page on screen is the failure"). Generic anti-references that describe what to avoid in general do **not** trigger departure mode; only ones that point at *this* surface specifically. -- The user's freeform prompt explicitly asks for departure ("rebuild this from scratch", "what if it weren't editorial at all", "show me something completely different"). +**Departure mode**: the existing identity is rejected. Variants propose alternatives consistent with durable product and brand truth. Trigger only when the user explicitly asks for departure in the current request or freeform prompt ("redesign this", "rebuild this from scratch", "what if it weren't editorial at all", "show me something completely different"). A stale page critique or an old task note is not replacement authorization. If you're unsure, you're in default mode. The cost of being wrong about default is "three on-brand variants with similar feel": recoverable, the user picks none. The cost of being wrong about departure is "three off-brand variants": unrecoverable, the user is annoyed. @@ -246,13 +243,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 the brand's stated voice and register in PRODUCT.md. Do NOT pick from a fixed catalog of lane categories. The right three directions for this brand are not the same as the right three for another brand, 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. @@ -262,7 +259,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. @@ -289,7 +286,7 @@ In **default mode**, the prompt narrows the axes you choose, not the identity. * In **departure mode**, the prompt narrows the lanes you draw from, not the families. *"Make it feel like a newspaper front page"* would itself be a departure-mode prompt; honor it but pick three meaningfully different newspaper-adjacent lanes (broadsheet vs. tabloid vs. trade journal), and run the family pass to confirm they don't collapse into one. -When the prompt and PRODUCT.md anti-references conflict (the prompt asks for X, the anti-references ban X), the anti-references win; they describe the brand's standing position, the prompt is one moment. +When the prompt conflicts with a confirmed binding brand commitment or DESIGN.md invariant, preserve the invariant unless the user explicitly revokes or replaces it. Task-local strategy from the matching surface brief may change when the user changes that surface's goal. ### 6. Write all variants in a single edit diff --git a/plugin/skills/impeccable/reference/new-work.md b/plugin/skills/impeccable/reference/new-work.md index 00c4dfdc5..3cf5547bf 100644 --- a/plugin/skills/impeccable/reference/new-work.md +++ b/plugin/skills/impeccable/reference/new-work.md @@ -1,47 +1,178 @@ -# New identity work +# New visual work -Read this only when the project has no committed visual identity, or the user explicitly asked to replace the current one. A missing DESIGN.md is not enough: code, tokens, chosen type, components, and assets are incumbent design authority. If they exist, preserve and extend them unless the brief says to discard them. +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. -This playbook is task-scoped. PRODUCT.md owns durable product facts and DESIGN.md owns durable visual invariants; the direction for one page or feature belongs in the current task, not in either document. +## 1. Name the intent -## Choose the authority +- **Greenfield:** no coherent visual implementation. +- **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. -Classify the work before inventing anything: +A plain “redesign this page/site” authorizes replacement. “Redesign this within the current brand/system” means extension or refinement. Ask once only when the wording is genuinely ambiguous. -- **Extend**: an incumbent system exists and the task adds or improves a surface. Preserve its lineage, semantics, and affordances. -- **Surface redesign**: the current identity remains, but this surface may change its topology, hierarchy, and expression. -- **New identity**: nothing committed exists, or the user explicitly authorized a rebrand. +## 2. Resolve visual authority -When the evidence is mixed, preserve. Novelty is not permission to erase authored decisions. +Read DESIGN.md and representative code, tokens, components, and assets. Choose one path: -## Find the concept +### A. Explicit redesign -Name the subject, audience, page job, and visitor mode from SKILL.md. Then identify the product's unique mechanism: what it does, proves, or enables that a neighboring product cannot truthfully claim. +The old DESIGN.md and implementation are not authority. Keep only unrevoked product facts, content, function, native expectations, constraints, and brand commitments. Establish a replacement world. -Derive the concept from that mechanism through one primary transformation: information topology, evidence, temporal behavior, or spatial mapping. The transformation must change how the surface is organized or behaves, not merely how it is decorated. This produces stranger and more defensible work than imitating a culturally specific document or object. Borrowing a literal cultural form is allowed only when it is genuinely native to the product and audience, and never as costume. +### B. DESIGN.md covers this kind of surface -For **Operate** and **Read**, start with the task and information structure. Native controls remain native controls; expression comes from hierarchy, density, rhythm, state, and the system around them. For **Persuade** and **Experience** creating a new identity, explore more freely, but keep the product legible and the primary action obvious. +Use its invariants and normative tokens. Skip world-building and discover the surface. -Run `node .claude/skills/impeccable/scripts/concept-seed.mjs` only for Persuade or Experience new-identity work when two or more directions remain equally credible. It is an entropy source, not an authority: never let the draw overrule the strongest fit. When color is genuinely unconstrained, `node .claude/skills/impeccable/scripts/palette.mjs` may break a reflex palette; subject evidence and incumbent color still win. +### C. A coherent implementation exists but DESIGN.md does not -## State the direction +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. -Before code, write a task-scoped direction contract of at most 120 words in your reasoning: +### D. The brand exists, but a whole-surface family is unresolved -- the thesis this surface owns; -- the structural or behavioral transformation; -- what authority is preserved; -- the first viewport's composition and primary action; -- the signature element the surface will be remembered by. +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. -Do not embed this contract as a comment in the production artifact or turn it into a second design system. In an attended substantial build, confirm it once. In an unattended run, record it and continue. +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. -## Build once, commit fully +### E. No confirmed visual authority exists -Plan one compact token system and one layout source: the concept or the content's real structure. Build the strongest coherent direction once. Commitment means the transformation governs the whole surface; it does not mean every control must be rebuilt as a metaphor. +Establish a world. Scaffolds, framework defaults, and stray utilities are not identity. -Make the first viewport demonstrate the product's mechanism. Pace long surfaces through contrast in density and scale, and cut sections that only repeat claims. Briefs that depend on imagery ship real, verified imagery. Preserve semantic HTML, familiar interaction behavior, accessibility, and the project's technical conventions. +## 3. Discover the requested surface -## Finish proportionally +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. -Inspect desktop and mobile, write one honest critique against the brief and direction, patch material defects, then run the detector once. Repeat only when a real defect remains. A separate reviewer is optional when the harness already provides one and the extra cost is earned by the risk; it is not a default step and its presence is not evidence of quality. +Ask one attended round of at most three material questions without repeating durable facts. CTA hierarchy, proof sequence, content gaps, and interaction outcomes belong here, not PRODUCT.md. For a fully specified narrow request, state the interpretation and invite correction. + +When `shape` has already completed its discovery interview, reuse those confirmed answers and do not ask this round again. + +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. Each candidate joins a durable visual system (identity thesis, information/layout grammar, material and type behavior, color strategy, imagery, motion, reusable signature) to a concrete expression for this surface (topology, sequence, focal moment, and implementation consequence). Do not rank yet. +3. **Break the ranking rut once.** Run `node .claude/skills/impeccable/scripts/concept-seed.mjs --scope direction`. Promote the assigned grounded pair and weigh its challengers only when they can become both a coherent system and a strong solution to this task. +4. **Test coupling and breadth.** Reject a pair if its surface could swap into another candidate unchanged, or if its world is merely the surface motif repeated. Test each survivor on the requested surface plus navigation, quiet and dense content, interaction/state, and one unlike future surface. Compare it with both the category habit and predictable contrarian response. +5. **Offer coupled choices.** Present two or three materially different pairs without recommendation cues. For each, show the world rules, this surface's 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. 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 .claude/skills/impeccable/scripts/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 promoted candidate when a roll ran; otherwise use the strongest grounded structure. It must survive both tests. + +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). + +### Write or update DESIGN.md + +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: + +`` + +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 .claude/skills/impeccable/scripts/surface-brief.mjs read ` + +Exit 0: preserve still-valid decisions and change only what the user changed. For redesign, retain valid product strategy, content, function, and open decisions; replace visual direction and contract. Exit 2: no brief. After the contract, write with `node .claude/skills/impeccable/scripts/surface-brief.mjs write [related-target ...]`. + +The body is concise and contains: + +```markdown +# Surface brief: [name] + +## Scope +[Primary/related route or artifact, visitor mode, and what this surface owns.] + +## Product strategy +[Surface-specific audience and job, desired outcome, primary/secondary action or task, content and proof sequence, factual constraints, and explicitly open decisions.] + +## Selected direction +[Reference to the applicable DESIGN.md world or expression range, selected surface concept, focal moment, narrative/interaction sequence, and implementation consequence.] + +## Direction contract +[The six contract blocks below.] + +## Open decisions +[Only unresolved items that later work must not silently invent. Omit when empty.] +``` + +Commit `.impeccable/surfaces/.md` as stable later-work context. Exclude global truth, exact tokens, transient notes, and work logs. + +## 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. + +Before code, write a direction contract of at most 150 words in an opening HTML or framework comment. The first 200 characters must name `DIRECTION CONTRACT`. + +- `UNIQUE`: the task thesis tied to the product mechanism; +- `NOT-TEMPLATE`: the category-default arrangement this structure refuses; +- `OWN-WORLD`: the confirmed DESIGN.md invariants, tokens, and materials it uses; +- `STORY`: what the visitor understands, believes, and does; +- `FIRST VIEWPORT`: exact composition, hierarchy, action, and where the concept exceeds competent convention; +- `FORM`: chosen structure or behavior, signature, implementation consequence, and seed key. + +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. + +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. + +## 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. + +Build the strongest coherent direction once. Its grammar governs navigation, actions, controls, content, and transitions without disguising affordances. Give the focal form the scale that gives it force; do not trap it inside a standard hero panel. + +**Make the opening a thesis.** Demonstrate the mechanism immediately; leave an idea, interaction, or evidence, not merely mood. + +**Commit before correcting.** Land the hard move at full strength before refining it. In unattended work, safety is the known risk. + +**Commit at page scale.** Let color, material, image, or type own a region when the world calls for it. Scattered signature decoration is not commitment. + +**Pace the whole surface.** Vary density, scale, image, motion, and quiet inside one grammar. Cut repeated claims; prove the mechanism with real artifact, interaction, data, transformation, or content. + +**Author motion as material.** Motion expresses the world and task. Premium moments go beyond repeated transform/opacity through earned focus, depth, masks, continuity, light, or material change. Bound expensive effects, test in-browser, keep content visible by default, and design reduced motion. + +Preserve semantics, affordances, accessibility, performance, responsiveness, and project conventions. Operate/Read express through topology, hierarchy, density, rhythm, and state; Persuade/Experience may earn drama. + +## 8. Solidify the visual record + +After first implementation of a new/replacement world or approved expansion, refresh DESIGN.md from the build: + +- replace provisional direction with the exact type, color roles, tokens, spacing/radii, components, states, and motion that survived; +- add normative YAML tokens only for values the implementation actually uses; +- remove the directional-seed status once the record and implementation agree; +- preserve broader world invariants and expression ranges; +- do not promote the task's story, hero composition, or one-off motif into a global rule unless it is intentionally reusable. + +Ordinary extension does not rewrite DESIGN.md; only approved durable changes do. + +## 9. Finish like a 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. diff --git a/plugin/skills/impeccable/reference/operate.md b/plugin/skills/impeccable/reference/operate.md index 04ee9d7d2..f420cbaa2 100644 --- a/plugin/skills/impeccable/reference/operate.md +++ b/plugin/skills/impeccable/reference/operate.md @@ -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. - No orchestrated page-load sequences. Product loads into a task; users don't want to watch it load. -## Product bans (on top of the shared absolute bans) +## Product constraints - Decorative motion that doesn't convey state. - Inconsistent component vocabulary across screens. If the "save" button looks different in two places, one is wrong. @@ -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. diff --git a/plugin/skills/impeccable/reference/polish.md b/plugin/skills/impeccable/reference/polish.md index 22c18c157..5d38db9be 100644 --- a/plugin/skills/impeccable/reference/polish.md +++ b/plugin/skills/impeccable/reference/polish.md @@ -1,241 +1,98 @@ -> **Additional context needed**: quality bar (MVP vs flagship). +> **Additional context needed**: quality bar and shipping constraints. -Perform a meticulous final pass to catch all the small details that separate good work from great work. The difference between shipped and polished. +Polish is refinement, never concealed redesign. Preserve the incumbent visual world, content, behavior, and everything outside scope. If the concept itself is wrong, say so and recommend redesign or `bolder` instead of smuggling in a replacement. -Detector and automated QA output are defect evidence only. A clean script result is never proof that the design is strong; gather browser evidence and inspect the real interaction path. +A detector result is defect evidence, not proof of quality. Inspect the rendered experience and real interaction path. -## Design System Discovery +## 1. Establish the system -Aligning the feature to the design system is **not optional**. Polish without alignment is decoration on top of drift, and it makes the next person's job harder. Discovery comes before any other polish work. +Read DESIGN.md and representative tokens, shared components, patterns, and neighboring flows. If no formal system exists, use coherent project conventions. -1. **Find the design system**: Search for design system documentation, component libraries, style guides, or token definitions. Study the core patterns: design principles, target audience, color tokens, spacing scale, typography styles, component API, motion conventions. -2. **Note the conventions**: How are shared components imported? What spacing scale is used? Which colors come from tokens vs hard-coded values? What motion and interaction patterns are established? What flow shapes are used for comparable actions (modal vs full-page, inline vs route, save-on-blur vs explicit submit)? -3. **Identify drift, then name the root cause**: For every deviation, classify it as a **missing token** (the value should exist in the system but doesn't), a **one-off implementation** (a shared component already exists but wasn't used), or a **conceptual misalignment** (the feature's flow, IA, or hierarchy doesn't match neighboring features). The fix differs by category: patch the value, swap to the shared component, or rework the flow. Fixing the symptom without naming the cause is how drift compounds. +Classify each drift before fixing it: -If a design system exists, polish **must** align the feature with it. If none exists, polish against the conventions visible in the codebase. **If anything about the system is ambiguous, ask. Never guess at design system principles.** +- **missing token:** the system needs a reusable value; +- **one-off implementation:** an existing shared component or pattern should replace it; +- **conceptual mismatch:** the flow, information architecture, or hierarchy differs from comparable product areas; +- **local defect:** the implementation is simply incomplete or inconsistent. -## Pre-Polish Assessment +Fix the cause at the narrowest correct level. Ask when a binding system principle cannot be inferred. -Understand the current state and goals before touching anything: +## 2. Gather the evidence -1. **Review completeness**: - - Is it functionally complete? - - Are there known issues to preserve (mark with TODOs)? - - What's the quality bar? (MVP vs flagship feature?) - - When does it ship? (How much time for polish?) +Use the feature yourself at representative desktop and mobile sizes. Determine: -2. **Think experience-first**: Who actually uses this, and what's the best possible experience for them? Effective design beats decorative polish; a feature that looks beautiful but fights the user's flow is not polished. Walk the path from their perspective before opening DevTools. +- whether the path is functionally complete; +- the intended quality bar and time available; +- known constraints or deliberately unfinished work; +- the states, content lengths, roles, and input methods users will actually encounter. -3. **Identify polish areas**: - - Visual inconsistencies - - Spacing and alignment issues - - Interaction state gaps - - Copy inconsistencies - - Edge cases and error states - - Loading and transition smoothness - - Information architecture and flow drift (does this feature reveal complexity the way neighboring features do?) +If a prior critique exists, use it as one input: -4. **Pull in any prior critique** (optional signal): If `/impeccable critique` has been run on the same target, its priority issues are a useful prior for what to address first. Resolve the target to a file path or URL, then: - ```bash - slug=$(node .claude/skills/impeccable/scripts/critique-storage.mjs slug "") - node .claude/skills/impeccable/scripts/critique-storage.mjs latest "$slug" - ``` - Exit 0 with body = found; fold the P0/P1 items into your polish list and mention the snapshot path so the user sees what you read. Exit 2 = no snapshot, continue without it. The critique is one input among many. Do your own pass either way. +```bash +slug=$(node .claude/skills/impeccable/scripts/critique-storage.mjs slug "") +node .claude/skills/impeccable/scripts/critique-storage.mjs latest "$slug" +``` -5. **Triage cosmetic vs functional**: Classify each issue as **cosmetic** (looks off, doesn't impede the user) or **functional** (breaks, blocks, or confuses the experience). When polish time is tight, functional issues ship first; cosmetic ones can land in a follow-up. Quality should be consistent; never perfect one corner while leaving another rough. +Exit 0 returns the latest snapshot; incorporate relevant P0/P1 findings and name the snapshot read. Exit 2 means none exists. Perform an independent pass either way. -**CRITICAL**: Polish is the last step, not the first. Don't polish work that's not functionally complete. +## 3. Triage -## Polish Systematically +Separate functional defects from cosmetic ones and fix in this order: -Work through these dimensions methodically: +1. broken or blocked tasks, data loss, misleading state, and inaccessible paths; +2. missing loading, empty, error, success, disabled, and permission states; +3. flow, hierarchy, responsive, and design-system drift; +4. visual and motion inconsistencies; +5. code and asset cleanup. -### Visual Alignment & Spacing +Do not perfect one corner while leaving the rest below the same quality bar. -- **Pixel-perfect alignment**: Everything lines up to grid -- **Consistent spacing**: All gaps use spacing scale (no random 13px gaps) -- **Optical alignment**: Adjust for visual weight (icons may need offset for optical centering) -- **Responsive consistency**: Spacing and alignment work at all breakpoints -- **Grid adherence**: Elements snap to baseline grid +## 4. Polish the whole path -**Check**: -- Enable grid overlay and verify alignment -- Check spacing with browser inspector -- Test at multiple viewport sizes -- Look for elements that "feel" off +### Flow and hierarchy -### Information Architecture & Flow +- Match neighboring mental models, terminology, disclosure, routing, save behavior, and optimistic or pessimistic patterns. +- Make the primary task and current state obvious without flattening every element to equal weight. +- Ensure arrival, transition, empty, and recovery paths connect instead of behaving as isolated screens. -Visual polish on a misshapen flow is wasted work. Match the *shape* of the experience to the system, not just the surface. +### Layout and type -- **Progressive disclosure**: Match how much is revealed when, compared to neighboring features. A settings page exposing 40 fields when the rest of the app reveals 5 at a time is drift, even if every field is perfectly styled. -- **Established user flows**: Multi-step actions follow the same shape as comparable flows elsewhere: modal vs full-page, inline edit vs separate route, save-on-blur vs explicit submit, optimistic vs pessimistic updates. -- **Hierarchy & complexity**: The same conceptual weight gets the same visual weight throughout. Primary actions don't become tertiary in one corner of the product, and tertiary actions don't shout. -- **Empty, loading, and arrival transitions**: How content arrives, updates, and leaves matches how it does in adjacent features. -- **Naming and mental model**: The feature uses the same nouns and verbs as the rest of the system. A "Workspace" here shouldn't be a "Project" three screens away. +- Align to the project's grid and spacing scale; fix optical as well as mathematical alignment. +- Group related content tightly and separate distinct groups generously. +- Keep same-role typography consistent; test measure, wrapping, localization expansion, zoom, and font loading. +- Verify every supported viewport rather than correcting only the current screenshot. -### Typography Refinement +### Color, imagery, and icons -- **Hierarchy consistency**: Same elements use same sizes/weights throughout -- **Line length**: 45-75 characters for body text -- **Line height**: Appropriate for font size and context -- **Widows & orphans**: No single words on last line -- **Hyphenation**: Appropriate for language and column width -- **Kerning**: Adjust letter spacing where needed (especially headlines) -- **Font loading**: No FOUT/FOIT flashes +- Use semantic tokens and stable color meanings across themes. +- Verify text, control, and focus contrast in every state. +- Keep icon families, stroke/weight, sizing, and optical alignment coherent. +- Prevent image layout shift; use correct aspect ratios, responsive sources, and useful alt text. -### Color & Contrast +### Interaction and state -- **Contrast ratios**: All text meets WCAG standards -- **Consistent token usage**: No hard-coded colors, all use design tokens -- **Theme consistency**: Works in all theme variants -- **Color meaning**: Same colors mean same things throughout -- **Accessible focus**: Focus indicators visible with sufficient contrast -- **Gray on color**: Never put gray text on colored backgrounds; use a shade of that color or transparency +- Every control needs appropriate default, hover, focus, active, disabled, loading, error, and success behavior. +- Preserve visible keyboard focus, logical tab order, labels, and platform-appropriate touch targets. +- Keep motion coherent, interruptible, performant, and reduced-motion aware. Do not add animation merely to make polish visible. +- Validate long, missing, localized, offline, slow, and permission-limited content where the product can encounter it. -### Interaction States +### Content and code -Every interactive element needs all states: +- Keep terminology, capitalization, punctuation, and factual copy consistent. Ask before changing claims. +- Remove debug output, dead code, unused imports, obsolete styles, and polish-created duplication. +- Replace custom implementations with shared components where the system owns the pattern. +- Promote genuinely reusable values to tokens; do not create a system abstraction for one local exception. -- **Default**: Resting state -- **Hover**: Subtle feedback (color, scale, shadow) -- **Focus**: Keyboard focus indicator (never remove without replacement) -- **Active**: Click/tap feedback -- **Disabled**: Clearly non-interactive -- **Loading**: Async action feedback -- **Error**: Validation or error state -- **Success**: Successful completion +## 5. Verify and finish -**Missing states create confusion and broken experiences**. +Walk the complete path again with mouse, keyboard, and touch where applicable. Check: -### Micro-interactions & Transitions +- mobile, intermediate, and wide layouts; +- loading, empty, error, success, disabled, long-content, and missing-content states; +- zoom, contrast, focus, semantics, screen-reader names, and reduced motion; +- console errors, layout shift, interaction latency, image loading, and supported browsers; +- agreement with DESIGN.md, neighboring features, and the user's scope. -- **Smooth transitions**: All state changes animated appropriately (150-300ms) -- **Consistent easing**: Use ease-out-quart/quint/expo for natural deceleration. Never bounce or elastic; they feel dated. -- **No jank**: Smooth animations; use atmospheric blur/filter/mask/shadow effects when they add polish, but bound expensive paint areas and avoid casual layout-property animation -- **Appropriate motion**: Motion serves purpose, not decoration -- **Reduced motion**: Respects `prefers-reduced-motion` +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. -### Content & Copy - -- **Consistent terminology**: Same things called same names throughout -- **Consistent capitalization**: Title Case vs Sentence case applied consistently -- **Grammar & spelling**: No typos -- **Appropriate length**: Not too wordy, not too terse -- **Punctuation consistency**: Periods on sentences, not on labels (unless all labels have them) - -### Icons & Images - -- **Consistent style**: All icons from same family or matching style -- **Appropriate sizing**: Icons sized consistently for context -- **Proper alignment**: Icons align with adjacent text optically -- **Alt text**: All images have descriptive alt text -- **Loading states**: Images don't cause layout shift, proper aspect ratios -- **Retina support**: 2x assets for high-DPI screens - -### Forms & Inputs - -- **Label consistency**: All inputs properly labeled -- **Required indicators**: Clear and consistent -- **Error messages**: Helpful and consistent -- **Tab order**: Logical keyboard navigation -- **Auto-focus**: Appropriate (don't overuse) -- **Validation timing**: Consistent (on blur vs on submit) - -### Edge Cases & Error States - -- **Loading states**: All async actions have loading feedback -- **Empty states**: Helpful empty states, not just blank space -- **Error states**: Clear error messages with recovery paths -- **Success states**: Confirmation of successful actions -- **Long content**: Handles very long names, descriptions, etc. -- **No content**: Handles missing data gracefully -- **Offline**: Appropriate offline handling (if applicable) - -### Responsiveness - -- **All breakpoints**: Test mobile, tablet, desktop -- **Touch targets**: 44x44px minimum on touch devices -- **Readable text**: No text smaller than 14px on mobile -- **No horizontal scroll**: Content fits viewport -- **Appropriate reflow**: Content adapts logically - -### Performance - -- **Fast initial load**: Optimize critical path -- **No layout shift**: Elements don't jump after load (CLS) -- **Smooth interactions**: No lag or jank -- **Optimized images**: Appropriate formats and sizes -- **Lazy loading**: Off-screen content loads lazily - -### Code Quality - -- **Remove console logs**: No debug logging in production -- **Remove commented code**: Clean up dead code -- **Remove unused imports**: Clean up unused dependencies -- **Consistent naming**: Variables and functions follow conventions -- **Type safety**: No TypeScript `any` or ignored errors -- **Accessibility**: Proper ARIA labels and semantic HTML - -## Polish Checklist - -Go through systematically: - -- [ ] Aligned to the design system (drift named and resolved by root cause) -- [ ] Information architecture and flow shape match neighboring features -- [ ] Visual alignment perfect at all breakpoints -- [ ] Spacing uses design tokens consistently -- [ ] Typography hierarchy consistent -- [ ] All interactive states implemented -- [ ] All transitions smooth (60fps) -- [ ] Copy is consistent and polished -- [ ] Icons are consistent and properly sized -- [ ] All forms properly labeled and validated -- [ ] Error states are helpful -- [ ] Loading states are clear -- [ ] Empty states are welcoming -- [ ] Touch targets are 44x44px minimum -- [ ] Contrast ratios meet WCAG AA -- [ ] Keyboard navigation works -- [ ] Focus indicators visible -- [ ] No console errors or warnings -- [ ] No layout shift on load -- [ ] Works in all supported browsers -- [ ] Respects reduced motion preference -- [ ] Code is clean (no TODOs, console.logs, commented code) - -**IMPORTANT**: Polish is about details. Zoom in. Squint at it. Use it yourself. The little things add up. - -Sweat the details. Zoom in until the alignment is right and the spacing reads as deliberate. Then ship. - -**NEVER**: -- Polish before it's functionally complete -- Polish without aligning to the design system; that's decoration on drift -- Guess at design system principles instead of asking when something is ambiguous -- Spend hours on polish if it ships in 30 minutes (triage) -- Introduce bugs while polishing (test thoroughly) -- Ignore systematic issues (if spacing is off everywhere, fix the system, not just one screen) -- Perfect one thing while leaving others rough (consistent quality level) -- Create new one-off components when design system equivalents exist -- Hard-code values that should use design tokens -- Introduce new patterns or flows that diverge from established ones - -## Final Verification - -Before marking as done: - -- **Use it yourself**: Actually interact with the feature. -- **Test on real devices**: Not just browser DevTools. -- **Ask someone else to review**: Fresh eyes catch things. -- **Compare to design**: Match intended design. -- **Check all states**: Don't just test happy path. -- **Treat automation carefully**: Run detector or QA commands when they are available and relevant, fix their defects, but never cite a clean result as proof that the work is polished. - -## Clean Up - -After polishing, ensure code quality: - -- **Replace custom implementations**: If the design system provides a component you reimplemented, switch to the shared version. -- **Remove orphaned code**: Delete unused styles, components, or files made obsolete by polish. -- **Consolidate tokens**: If you introduced new values, check whether they should be tokens. -- **Verify DRYness**: Look for duplication introduced during polishing and consolidate. +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. diff --git a/plugin/skills/impeccable/reference/quieter.md b/plugin/skills/impeccable/reference/quieter.md index ea5b54925..c20b38fb3 100644 --- a/plugin/skills/impeccable/reference/quieter.md +++ b/plugin/skills/impeccable/reference/quieter.md @@ -2,7 +2,7 @@ Quiet design is harder than bold design. Subtlety needs precision. Reduce visual --- -## Register +## Visitor mode Persuade + Experience: "quieter" means more restrained palette, more whitespace, more typographic air. Drama is reduced, not eliminated; the POV stays intact. diff --git a/plugin/skills/impeccable/reference/routing.md b/plugin/skills/impeccable/reference/routing.md index 0b107da93..da49eb993 100644 --- a/plugin/skills/impeccable/reference/routing.md +++ b/plugin/skills/impeccable/reference/routing.md @@ -11,7 +11,7 @@ Reason over the signals; there is no score to obey: - `critique.latest` with a low `score` or non-zero `p0` / `p1` → `polish` (it reads that snapshot as its backlog), or re-run `critique` if the snapshot looks stale. - `git.changedFiles` pointing at one surface → scope `audit` or `polish` to those files specifically, naming them. - `devServer.running` true → `live` is available for in-browser iteration; if false, don't lead with `live`. **`live` and the bundled `detect.mjs` are web-only.** If `setup.platform` is `ios`, `android`, or `adaptive`, don't lead with either; the browser overlay and the HTML rule engine don't apply to native app code. -- Otherwise group by intent exactly as init's "Recommend starting points" step does (build new / improve what's there / iterate visually), tailored to `setup.register`. +- Otherwise group by intent (build new / improve what's there / iterate visually), tailored to the current surface and `setup.platform`. **If `scan.targets` is non-empty and `setup.platform` is not `ios`/`android`/`adaptive`, run `node .claude/skills/impeccable/scripts/detect.mjs --json ` once** (the bundled detector over local files: no network, no npx; it reads HTML/CSS, so skip it for native projects). `scan.via` tells you what they are: `git-changes` (the markup/style files in your dirty tree, the most relevant set), `source-dir` (e.g. `src`, `app`), `html`, or `root`. Fold the hits into your picks: many quality / contrast hits → `audit` or `polish`; a specific slop family → the matching command (gradient text or eyebrows → `quieter` / `typeset`, flat or gray palette → `colorize`, and so on). It's a real, current signal that beats guessing. If detect errors or the tree is large and slow, skip it and recommend the user run `audit` themselves; never block the suggestion on it. diff --git a/plugin/skills/impeccable/reference/shape.md b/plugin/skills/impeccable/reference/shape.md index 23e286df5..acbb26e5b 100644 --- a/plugin/skills/impeccable/reference/shape.md +++ b/plugin/skills/impeccable/reference/shape.md @@ -1,167 +1,61 @@ -Shape the UX and UI for a feature before any code is written. This command produces a **design brief**: a structured artifact that guides implementation through discovery, not guesswork. +# Shape -**Scope**: Design planning only. This command does NOT write code. It produces the thinking that makes code good. +Discover what should be made and how it should work, then return a confirmed design brief without code. -**Output**: A design brief that can be handed off to /impeccable craft, or directly to /impeccable for freeform implementation. When visual direction probes are used, the images are supporting artifacts, not the primary output. +**Product gate:** when context reports that PRODUCT.md is missing, load and complete [init.md](init.md) before Phase 1. Do not substitute shape questions for the product interview. Once PRODUCT.md exists, return here; product context does not replace task discovery. -## Philosophy +## Phase 1: Discovery interview -Most AI-generated UIs fail not because of bad code, but because of skipped thinking. They jump to "here's a card grid" without asking "what is the user trying to accomplish?" This command inverts that: understand deeply first, so implementation is precise. +Do not write code or choose visual direction yet. -## Phase 1: Discovery Interview +### Cadence -**Do NOT write any code or make any design decisions during this phase.** Your only job is to understand the feature deeply enough to make excellent design decisions later. +- Use the structured question tool when available; otherwise ask and stop. +- Ask two or three related questions per round, then wait. One round is the default; add a second only when the answers expose a material gap. +- Do not dump a questionnaire, repeat settled facts, or turn obvious facts into menus. Assert the likely reading and invite correction. +- A sparse prompt requires at least one answer round. A precise prompt may need only a compact confirmation. -**Unattended runs:** when no user can respond (a one-shot task, an automated run, or an explicit instruction not to ask questions), answer the interview questions yourself from the brief, record the answers as the design brief, and continue without pausing at any confirmation gate in this file. The thinking is still required; only the waiting is waived. +### Round 1: purpose, people, and outcome -This is a required interaction, not optional guidance. Ask these questions in conversation, adapting based on answers. Don't dump them all at once; have a natural dialogue. STOP and call the AskUserQuestion tool to clarify. +Choose the two or three questions that most change the result: -### Interview cadence +- What is this surface or feature for, and what problem must it solve? +- Who specifically reaches it, in what situation and state of mind? +- What is the primary thing they must understand or do? What would success look like? +- What is uniquely true here that a neighboring product or generic template could not claim? -Discovery includes at least one user-answer round unless PRODUCT.md, DESIGN.md, or an already-confirmed brief directly answers the needed inputs. With a sparse prompt, do **not** synthesize a complete brief for confirmation on the first response. +### Round 2: material, behavior, and boundaries -- Use the harness's structured question tool when one exists. Otherwise, ask directly in chat and stop. -- Ask **2-3 questions per round**, then wait for answers. -- Treat PRODUCT.md and DESIGN.md as anchors; they reduce repeated questions but do **not** replace shape for craft. Shape is task-specific. -- One round is the default. Add a second only if the first answers leave material gaps. Don't run a second round just to feel thorough. -- Round 1 should clarify purpose, audience/context, content/scope, and (for brand) visual direction. -- Round 2, when needed, fills in whatever's still genuinely missing. +Run only for material unresolved decisions: -**Assert-then-confirm, not menu-with-escape.** When PRODUCT.md and the user's prompt make one option obvious, name it and ask the user to confirm or override. Don't enumerate "Restrained / Committed / Or something else?" as a real choice; "This reads as Restrained, confirm?" beats a four-option menu when the answer is already clear. +- What real content, evidence, data, and assets must the experience carry? What are realistic minimum, typical, and maximum ranges? +- Which states and transitions matter: first-run, empty, loading, error, success, permissions, overflow, or expert use? +- What is the intended fidelity, breadth, and interactivity: exploration, production-ready screen, full flow, or broader surface? +- What must remain untouched? What would make the result feel wrong even if it looked polished? +- Which platform, framework, performance, accessibility, localization, or delivery constraints are binding? -### Purpose & Context -- What is this feature for? What problem does it solve? -- Who specifically will use it? (Not "users"; be specific: role, context, frequency) -- What does success look like? How will you know this feature is working? -- What's the user's state of mind when they reach this feature? (Rushed? Exploring? Anxious? Focused?) +Never ask for CSS values or canned aesthetic lanes. New-work owns visual-world and concept choices. -### Content & Data -- What content or data does this feature display or collect? -- What are the realistic ranges? (Minimum, typical, maximum, e.g., 0 items, 5 items, 500 items) -- What are the edge cases? (Empty state, error state, first-time use, power user) -- Is any content dynamic? What changes and how often? -- What visual assets are real content here? Note required images, product shots, illustrations, maps, textures, diagrams, generated objects, or existing project assets. +## Phase 2: Resolve the design direction -### Design Direction +For new surfaces, brand expansion, or replacement, follow [new-work.md](new-work.md) through visual authority, any world workshop, and concept choice. Reuse discovery, then return before its contract, persistence, or implementation. Inside an established world, use its concept process only when composition or interaction remains materially open. -Force a visual decision on three fronts. Skip anything PRODUCT.md or DESIGN.md already answers; ask only what's missing. +## Phase 3: Write the brief -- **Color strategy for this surface.** Pick one: Restrained / Committed / Full palette / Drenched. Can override the project default if the surface earns it (e.g. a drenched hero inside an otherwise Restrained product). -- **Theme via scene sentence.** Write one sentence of physical context for this surface: who uses it, where, under what ambient light, in what mood. The sentence forces dark vs light. If it doesn't, add detail until it does. -- **Two or three named anchor references.** Specific products, brands, objects. Not adjectives like "modern" or "clean." +Write the smallest useful brief: -### Scope +1. **Job and audience:** who arrives, their context, need, and visitor mode. +2. **Outcome and proof:** primary task/action, success, real evidence, and product-specific truth. +3. **Selected direction:** visual authority, structural/interaction thesis, sequence, focal moment, and implementation consequence. +4. **Scope and boundaries:** fidelity, breadth, interactivity, named target, what remains untouched, and explicit anti-goals. +5. **States and ranges:** realistic content/data ranges and material states. +6. **Interaction and layout:** hierarchy, topology, responsiveness, affordances, feedback, and transitions; intent, not CSS. +7. **Constraints and open decisions:** platform, delivery, accessibility, localization, reusable components, and choices a builder must not invent. -Always ask. Sketch quality and shipped quality are different outputs; don't guess between them. +Use three to five bullets when the task is settled; use the full structure only for ambiguous, multi-screen, or standalone planning. Do not restate the conversation. -- **Fidelity.** Sketch / mid-fi / high-fi / production-ready? -- **Breadth.** One screen / a flow / a whole surface? -- **Interactivity.** Static visual / interactive prototype / shipped-quality component? -- **Time intent.** Quick exploration, or polish until it ships? +## Confirm and stop -Scope answers are task-scoped. Don't write them to PRODUCT.md or DESIGN.md; carry them through the design brief only. +Present the brief for explicit confirmation or one correction round, then stop: shape never writes code or a direction contract. -### Constraints -- Are there technical constraints? (Framework, performance budget, browser support) -- Are there content constraints? (Localization, dynamic text length, user-generated content) -- Mobile/responsive requirements? -- Accessibility requirements beyond WCAG AA? - -### Anti-Goals -- What should this NOT be? What would be a wrong direction? -- What's the biggest risk of getting this wrong? - -## Phase 1.5: Visual Direction Probe (Capability-Gated) - -After the discovery interview, generate a small set of visual direction probes **before** writing the final brief when all of these are true: - -- The work is **net-new** or directionally ambiguous enough that visual exploration will clarify the brief. -- The requested fidelity is **mid-fi, high-fi, or production-ready**. Skip for sketch-only planning. -- The current harness gives you native image generation (Codex's `image_gen`, an equivalent MCP tool, or similar). Don't ask the user to install APIs or tooling. - -When those conditions are met, this step is mandatory. If image generation isn't natively available, do not ask the user to install APIs or tooling. State in one line that the image step is skipped because the harness lacks native image generation, then proceed. The one-line announcement is required, not optional; it forces a conscious decision instead of letting the step quietly evaporate. - -Use probes to explore visual lanes, not to replace the brief. - -Do not skip probes because the final UI will be semantic, editable, code-native, responsive, or accessible. Those are implementation requirements, not reasons to avoid visual exploration. - -### What to generate - -Generate **2 to 4** distinct direction probes based on the discovery answers, especially: - -- Color strategy -- Theme scene sentence -- Named anchor references -- Scope and fidelity - -The probes should differ in primary visual direction (hierarchy, topology, density, typographic voice, or color strategy), not just palette tweaks. - -### How to use the probes - -- Treat them as **direction tests**, not final designs. -- Use them to pressure-test whether the brief is pointing at the right lane. -- Ask the user which direction feels closest, what feels off, and what should carry forward. -- If the probes reveal a mismatch, revise the brief inputs before finalizing the brief. - -### Important limits - -- Do **not** skip discovery because image generation is available. -- Do **not** treat generated imagery as final UX specification, final copy, or final accessibility behavior. -- Do **not** use this step for minor refinements of existing work. It's for shaping a new surface or clarifying a big directional choice. - -If image generation isn't natively available, announce the skip in one line and proceed to the design brief. - -## Phase 2: Design Brief - -After the interview and any required probes, present a brief and **end your response**. The user must confirm before any implementation runs. Do not present a brief and then continue to code in the same response, even if the brief feels obvious to you. The user's confirmation is the gate. - -**Choose the brief shape based on how clear the answers are:** - -- **Compact form (3-5 bullets)** when discovery was crisp and the original prompt + PRODUCT.md already pinned scope, content, and direction. State what you're building, the visual lane, and end with one or two specific questions or a clear "confirm or override?" prompt. This is the default for typical craft requests with a clear prompt. -- **Full structured form (sections below)** when the task is genuinely ambiguous, multi-screen, or when the user asked for shape as a standalone step. Use this when the discipline of structure earns its weight. - -Don't pad a clear brief into a long one to look thorough. A 70-line brief restating answers the user just gave is noise, not rigor. Equally, don't skip the confirmation pause to look efficient: the pause is the point. - -Present the brief, then **stop and wait for explicit confirmation**. You are not the judge of whether the user already approved. Even when the brief feels obviously right, ask once and wait. The pause is what separates shape from premature implementation. - -### Brief Structure - -**1. Feature Summary** (2-3 sentences) -What this is, who it's for, what it needs to accomplish. - -**2. Primary User Action** -The single most important thing a user should do or understand here. - -**3. Design Direction** -Color strategy (Restrained / Committed / Full palette / Drenched) + the theme scene sentence + 2–3 named anchor references. Reference PRODUCT.md and DESIGN.md where they already answer, and note any per-surface overrides. - -If you ran the Visual Direction Probe step, name which probe direction won and what changed in the brief because of it. - -**4. Scope** -Fidelity, breadth, interactivity, and time intent from the Scope section of the interview. Task-scoped; these don't persist beyond the brief. - -**5. Layout Strategy** -High-level spatial approach: what gets emphasis, what's secondary, how information flows. Describe the visual hierarchy and rhythm, not specific CSS. - -**6. Key States** -List every state the feature needs: default, empty, loading, error, success, edge cases. For each, note what the user needs to see and feel. - -**7. Interaction Model** -How users interact with this feature. What happens on click, hover, scroll? What feedback do they get? What's the flow from entry to completion? - -**8. Content Requirements** -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., 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. - ---- - -STOP and call the AskUserQuestion tool to clarify. Ask for explicit confirmation of the brief before finishing. - -If the user disagrees with any part, revisit the relevant discovery questions. A shape run is incomplete until the user confirms direction. - -Once confirmed, the brief is complete. The user can now hand it to /impeccable, or use it to guide any other implementation approach. (If the user wants the full discovery-then-build flow in one step, they should use /impeccable craft instead, which runs this command internally.) +When no human or structured answer mechanism exists, mark assumptions plainly, return the brief, and stop. diff --git a/plugin/skills/impeccable/reference/typeset.md b/plugin/skills/impeccable/reference/typeset.md index fefa95b0d..7b54de2d5 100644 --- a/plugin/skills/impeccable/reference/typeset.md +++ b/plugin/skills/impeccable/reference/typeset.md @@ -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. --- -## Register +## Visitor mode -New identity work: run the font selection procedure in [new-work.md](new-work.md). Fluid `clamp()` scale, ≥1.25 ratio between steps. +- **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, register, **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 .claude/skills/impeccable/scripts/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 `/impeccable polish` for the final pass. +When the hierarchy holds, hand off to `/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) * )` 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 - -The tactical selection procedure and the reflex-reject list live in [new-work.md](new-work.md) (loaded for new identity work). 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 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. diff --git a/plugin/skills/impeccable/scripts/command-metadata.json b/plugin/skills/impeccable/scripts/command-metadata.json index 83796d54b..dad8ef2e0 100644 --- a/plugin/skills/impeccable/scripts/command-metadata.json +++ b/plugin/skills/impeccable/scripts/command-metadata.json @@ -1,6 +1,6 @@ { "craft": { - "description": "Full confirmed-brief-then-build flow. Runs multi-round shape discovery first, resolves visual probe and north-star mock gates when available, then builds and visually iterates. Use when building a new feature end-to-end.", + "description": "Deprecated compatibility alias for an ordinary Impeccable new-work request. It adds no behavior; natural build and redesign requests use the same flow.", "argumentHint": "[feature description]" }, "init": { diff --git a/plugin/skills/impeccable/scripts/concept-seed.mjs b/plugin/skills/impeccable/scripts/concept-seed.mjs index 1f141a80f..25f20d9ab 100644 --- a/plugin/skills/impeccable/scripts/concept-seed.mjs +++ b/plugin/skills/impeccable/scripts/concept-seed.mjs @@ -1,6 +1,7 @@ #!/usr/bin/env node /** - * Concept-seed picker: the dice half of the new-work concept procedure. + * External concept seed: the dice half of new-work's coupled-direction and + * established-world surface procedures. * * The model derives a grounded shortlist of candidate FORMS from the * audience's world and the subject's cultural home (see @@ -12,10 +13,9 @@ * * This script rolls them from outside, the same trick that made the * palette seed work: - * - BUILD INDEX (2-5): which entry of the model's own resonance-ordered - * shortlist to build. The dice never choose an ungrounded ingredient; - * they only refuse the argmax rut. (Index 1 is excluded: that's the - * concept every run would ship anyway.) + * - PROMOTED INDEX: which entry of the model's own resonance-ordered + * shortlist must be taken seriously beside its favorites. The dice never + * choose an ungrounded ingredient; they only refuse the argmax rut. * - CHALLENGERS (3): outside forms from concept-ingredients.json, weighed * against the derived candidates on exactly two axes — audience * identification and product clarity. They win only when they beat the @@ -23,8 +23,8 @@ * material and win over thin categories, which is the intended shape. * * Usage: - * node scripts/concept-seed.mjs # roll at random - * node scripts/concept-seed.mjs --from # deterministic (hash key) + * node scripts/concept-seed.mjs --scope direction + * node scripts/concept-seed.mjs --scope surface --from * * Env vars: * IMPECCABLE_CONCEPT_SEED — same as --from; for reproducible eval runs. @@ -40,6 +40,12 @@ const pool = JSON.parse(readFileSync(join(here, 'concept-ingredients.json'), 'ut const args = process.argv.slice(2); const fromIdx = args.indexOf('--from'); +const scopeIdx = args.indexOf('--scope'); +const scope = scopeIdx !== -1 ? args[scopeIdx + 1] : 'surface'; +if (scope !== 'surface' && scope !== 'direction') { + process.stderr.write('concept-seed: --scope must be direction or surface\n'); + process.exit(1); +} // When no key is supplied, generate one and print it: a user reporting a // bad outcome can hand us the key and we replay the exact roll. const key = fromIdx !== -1 @@ -47,7 +53,7 @@ const key = fromIdx !== -1 : (process.env.IMPECCABLE_CONCEPT_SEED || crypto.randomBytes(4).toString('hex')); function hashUnit(k, salt) { - const h = crypto.createHash('sha256').update(`${salt}:${k}`).digest(); + const h = crypto.createHash('sha256').update(`${scope}:${salt}:${k}`).digest(); return h.readUInt32BE(0) / 0xffffffff; } const unit = (salt) => hashUnit(key, salt); @@ -67,22 +73,48 @@ for (let i = 0; picks.length < 3 && i < 60; i++) { } } -process.stdout.write(`CONCEPT SEED (key: ${key}; rerun with --from ${key} to reproduce this roll) -BUILD INDEX: ${buildIndex} - After ordering your derived candidates by resonance, build the page whose - form comes from candidate number ${buildIndex}, exactly as if it had ranked - first: full commitment. Your top-ranked candidate is what every run in this - category would ship; the assignment exists to refuse that rut, not to - punish it. -CHALLENGERS (weigh against your derived candidates on the same two axes, -audience identification and product clarity; a challenger wins only when -it beats the grounded list on both): +const promotedInstruction = scope === 'direction' + ? `After ordering the grounded coupled directions by product fit, promote + candidate ${buildIndex} into the serious shortlist. Each candidate must join + a durable visual system to a concrete expression for the requested first + surface; select or revise that pair as one decision. It must survive the + current task plus navigation, quiet and dense content, interaction and state, + and a substantially different future surface.` + : `After ordering the task's grounded structural candidates by resonance, + promote candidate ${buildIndex} into the serious shortlist. In an attended + run, present it beside the strongest materially different candidates and + let the user select or revise the surface concept. In a truly unattended + run, use it when it survives audience identification and product clarity.`; + +const challengerInstruction = scope === 'direction' + ? `A challenger enters only when it can supply both reusable identity grammar + and a strong first-surface structure, not a one-page costume or a style pasted + onto an unrelated layout. Weigh audience identification, product clarity, + current-surface force, and cross-surface system breadth.` + : `A challenger wins only when it beats the grounded list on both audience + identification and product clarity. It may change task topology or + interaction, but never the committed visual identity.`; + +const authorityInstruction = scope === 'direction' + ? `PRODUCT.md and explicit incumbent brand commitments constrain every coupled +direction. The seed never chooses exact colors, fonts, tokens, or a user +preference, and it never permits the world and first surface to be selected +independently.` + : `PRODUCT.md and DESIGN.md constrain every surface candidate's identity +vocabulary; they do not cancel task-level composition. The seed never +authorizes a new palette, type system, material world, or unfamiliar control +behavior.`; + +process.stdout.write(`${scope.toUpperCase()} CONCEPT SEED (key: ${key}; rerun with --scope ${scope} --from ${key} to reproduce this roll) +PROMOTED INDEX: ${buildIndex} + ${promotedInstruction} + The promotion exists to refuse the model's ranking rut, not to outrank the + user or the brief. +CHALLENGERS: 1. ${picks[0]} 2. ${picks[1]} 3. ${picks[2]} -If a challenger wins, it replaces the assigned candidate. If the surface is -an existing world whose incumbent carries a deliberate, ownable idea, the -incumbent IS the chosen candidate: intensify its lineage and ignore the -roll entirely. The same override applies when the user, PRODUCT.md, or -DESIGN.md pins a direction: pinned direction beats the roll, always. +${challengerInstruction} +${authorityInstruction} +A user- or brief-pinned decision beats the roll, always. `); diff --git a/plugin/skills/impeccable/scripts/context-signals.mjs b/plugin/skills/impeccable/scripts/context-signals.mjs index 2fc27bea7..d296320d7 100644 --- a/plugin/skills/impeccable/scripts/context-signals.mjs +++ b/plugin/skills/impeccable/scripts/context-signals.mjs @@ -11,7 +11,7 @@ * output is always valid JSON. * * Signals: - * - setup: PRODUCT.md / DESIGN.md presence, register, whether code exists + * - setup: PRODUCT.md / DESIGN.md presence and whether code exists * - critique: the latest cached critique score (.impeccable/critique) * - git: branch + files changed vs the default branch (a scope hint) * - devServer: whether a local dev server answers on a common port (gates live) @@ -21,7 +21,7 @@ import net from 'node:net'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { execFileSync } from 'node:child_process'; -import { loadContext, extractRegister, extractPlatform } from './context.mjs'; +import { loadContext, extractPlatform } from './context.mjs'; import { getCritiqueDir } from './lib/impeccable-paths.mjs'; /** Is there code here at all, or just context files / an empty repo? */ @@ -196,7 +196,6 @@ export async function gatherSignals(cwd = process.cwd()) { hasDesign: ctx.hasDesign, designPath: ctx.designPath, hasCode: hasCode(cwd), - register: extractRegister(ctx.product), platform: extractPlatform(ctx.product), }, critique: { latest: latestCritique(cwd) }, diff --git a/plugin/skills/impeccable/scripts/context.mjs b/plugin/skills/impeccable/scripts/context.mjs index 2ef2d4ce8..080e7ee3a 100644 --- a/plugin/skills/impeccable/scripts/context.mjs +++ b/plugin/skills/impeccable/scripts/context.mjs @@ -1,8 +1,10 @@ /** - * Context loader: prints PRODUCT.md (and DESIGN.md if present) as one - * markdown block on stdout, or prints a `NO_PRODUCT_MD:` message when no + * Context loader: prints PRODUCT.md, DESIGN.md when present, the matching + * persisted surface brief when one can be resolved, and native-platform + * guidance selected from PRODUCT.md. It prints a + * `NO_PRODUCT_MD:` message when no * PRODUCT.md is found anywhere. The skill keys off that message to branch: - * from-scratch build commands (init / teach / craft / shape) and clear + * from-scratch build requests (plus init / teach / shape) and clear * build/shape intent divert into the init flow, while scoped commands proceed * using the existing code as context. * @@ -23,10 +25,13 @@ import os from 'node:os'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { parseTargetOptions } from './lib/target-args.mjs'; -import { IMPECCABLE_COMMAND } from './lib/provider.mjs'; +import { IMPECCABLE_COMMAND, IMPECCABLE_PROVIDER_ID } from './lib/provider.mjs'; +import { renderLlmOnlySlopReview } from './lib/slop-review.mjs'; +import { resolveSurfaceBrief } from './lib/surface-briefs.mjs'; const PRODUCT_NAMES = ['PRODUCT.md', 'Product.md', 'product.md']; const DESIGN_NAMES = ['DESIGN.md', 'Design.md', 'design.md']; +const SKILL_REFERENCE_DIR = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', 'reference'); const FALLBACK_DIRS = ['.agents/context', 'docs']; const MONOREPO_MARKER_FILES = ['pnpm-workspace.yaml', 'turbo.json', 'nx.json', 'lerna.json']; const MONOREPO_FALLBACK_PROJECT_DIRS = ['apps', 'packages']; @@ -41,6 +46,8 @@ const WORKSPACE_DISCOVERY_IGNORED_DIRS = new Set([ '.turbo', '.cache', 'coverage', + 'vendor', + 'vendors', ]); const VISUAL_SOURCE_DIRS = ['src', 'app', 'pages', 'components', 'site', 'public', 'styles']; const STYLE_EXTENSIONS = new Set(['.css', '.scss', '.sass', '.less', '.styl']); @@ -73,6 +80,12 @@ export function loadContext(cwd = process.cwd(), options = {}) { const designPath = resolved.designPath; const product = productPath ? safeRead(productPath) : null; const design = designPath ? safeRead(designPath) : null; + const platform = extractPlatform(product); + const surfaceResolution = resolveSurfaceBrief( + resolved.projectRoot, + hasTargetOption(options) ? options.targetPath : null, + ); + const surfaceBrief = surfaceResolution.brief; return { hasProduct: !!product, product, @@ -83,7 +96,18 @@ export function loadContext(cwd = process.cwd(), options = {}) { contextDir: resolved.contextDir, productContextDir: productPath ? path.dirname(productPath) : null, designContextDir: designPath ? path.dirname(designPath) : null, + hasSurfaceBrief: !!surfaceBrief, + surfaceBrief: surfaceBrief?.text ?? null, + surfaceBriefPath: surfaceBrief?.path ? path.relative(absCwd, surfaceBrief.path) : null, + surfaceBriefReason: surfaceResolution.reason, + surfaceBriefCandidates: surfaceResolution.candidates.map((brief) => ({ + slug: brief.slug, + path: path.relative(absCwd, brief.path), + primaryTarget: brief.primaryTarget, + relatedTargets: brief.relatedTargets, + })), hasVisualImplementation: hasVisualImplementation(resolved.projectRoot), + platform, projectRoot: resolved.projectRoot, repoRoot: resolved.repoRoot, isMonorepo: resolved.isMonorepo, @@ -696,6 +720,19 @@ function safeRead(p) { } } +function loadNativePlatformReferences(platform) { + const names = platform === 'adaptive' + ? ['ios', 'android'] + : platform === 'ios' || platform === 'android' + ? [platform] + : []; + return names.flatMap((name) => { + const filePath = path.join(SKILL_REFERENCE_DIR, `${name}.md`); + const content = safeRead(filePath); + return content ? [{ name, filePath, content }] : []; + }); +} + /** * Best-effort evidence that the project already has an incumbent visual * implementation. DESIGN.md is documentation, not the only source of design @@ -719,9 +756,11 @@ export function hasVisualImplementation(projectRoot) { let styledComponents = 0; const inspectFile = (filePath) => { - if (scannedFiles++ >= VISUAL_SCAN_FILE_LIMIT) return false; const ext = path.extname(filePath).toLowerCase(); if (!STYLE_EXTENSIONS.has(ext) && !UI_EXTENSIONS.has(ext)) return false; + const base = path.basename(filePath).toLowerCase(); + if (/\.min\.[a-z]+$/.test(base)) return false; + if (scannedFiles++ >= VISUAL_SCAN_FILE_LIMIT) return false; let body; try { body = fs.readFileSync(filePath, 'utf-8').slice(0, 64 * 1024); @@ -729,18 +768,28 @@ export function hasVisualImplementation(projectRoot) { return false; } - const base = path.basename(filePath).toLowerCase(); + const evidence = body + .replace(/\/\*[\s\S]*?\*\//g, '') + .replace(//g, '') + .replace(/^\s*\/\/.*$/gm, ''); if (STYLE_EXTENSIONS.has(ext)) { - const customProperties = body.match(/--[a-z0-9_-]+\s*:/gi)?.length ?? 0; - const visualDeclarations = body.match(/\b(?:color|background(?:-color)?|border(?:-color)?|font-family)\s*:/gi)?.length ?? 0; - if (/\b(?:tokens?|theme|design-system)\b/.test(base) && body.trim().length > 80) return true; + const customProperties = evidence.match(/--[a-z0-9_-]+\s*:/gi)?.length ?? 0; + const visualDeclarations = evidence.match(/\b(?:color|background(?:-color)?|border(?:-color)?|font-family)\s*:/gi)?.length ?? 0; + if (/\b(?:tokens?|theme|design-system)\b/.test(base) && evidence.trim().length > 80) return true; if (customProperties >= 3 || visualDeclarations >= 5) return true; } - if ((ext === '.html' || ext === '.htm') && body.length > 600 && /]+stylesheet/i.test(body)) { + if ((ext === '.html' || ext === '.htm') && evidence.length > 600 && /]+stylesheet/i.test(evidence)) { return true; } - if (!['.html', '.htm'].includes(ext) && body.length > 300 && /class(?:Name)?\s*=|style\s*=|styled\(|css`/i.test(body)) { + if (!['.html', '.htm'].includes(ext) && evidence.length > 300) { + const embeddedCustomProperties = evidence.match(/--[a-z0-9_-]+\s*:/gi)?.length ?? 0; + const embeddedVisualDeclarations = evidence.match(/\b(?:color|background(?:-color)?|border(?:-color)?|font-family)\s*:/gi)?.length ?? 0; + const classTokens = [...evidence.matchAll(/class(?:Name)?\s*=\s*["'`]([^"'`]+)["'`]/gi)] + .reduce((count, match) => count + match[1].trim().split(/\s+/).length, 0); + if ((embeddedCustomProperties >= 3 && embeddedVisualDeclarations >= 3) || embeddedVisualDeclarations >= 5 || classTokens >= 12) return true; + } + if (!['.html', '.htm'].includes(ext) && evidence.length > 300 && /class(?:Name)?\s*=|style\s*=|styled\(|css`/i.test(evidence)) { styledComponents += 1; if (styledComponents >= 3) return true; } @@ -781,9 +830,9 @@ function escapeRegExp(value) { /** * Read the first non-empty line under a bare `## ` section of - * PRODUCT.md (e.g. `## Register`, `## Platform`). Returns null when the + * PRODUCT.md (for example `## Platform`). Returns null when the * section is absent. The heading match is exact (`\s*$`) so near-miss - * headings like `## Register guidelines` don't shadow the real field. + * near-miss headings don't shadow the real field. */ export function extractSectionValue(product, heading) { if (!product) return null; @@ -802,16 +851,6 @@ export function extractSectionValue(product, heading) { return null; } -/** - * Pull the register (`brand` or `product`) out of PRODUCT.md by looking - * for a `## Register` section and reading the first non-empty line that - * follows it. Returns null when the file is legacy / register-less. - */ -export function extractRegister(product) { - const word = (extractSectionValue(product, 'Register') || '').toLowerCase(); - return word === 'brand' || word === 'product' ? word : null; -} - /** * Pull the platform (`web`, `ios`, `android`, or `adaptive`) out of PRODUCT.md * by looking for a `## Platform` section and reading the first non-empty line @@ -985,30 +1024,35 @@ async function cli() { const parts = ctx.hasVisualImplementation ? [ 'NO_PRODUCT_MD: This project has no PRODUCT.md yet, but it does have an incumbent visual implementation. ' + - 'For `init` or `teach`, load reference/init.md and create PRODUCT.md. For `craft`, `shape`, or other work ' + - 'against the existing product, read its CSS, tokens, components, and assets first and proceed without blocking; ' + - `offer \`${IMPECCABLE_COMMAND} init\` as a follow-up. Only treat the work as greenfield when the user explicitly ` + - 'asks to discard or replace the current identity.', - 'EXISTING_VISUAL_SYSTEM: Code and assets are the incumbent design authority. Missing DESIGN.md is a documentation ' + - 'gap, not permission to invent a replacement identity. Preserve and extend the implementation unless the user ' + - 'explicitly requests a rebrand, or inspection proves the detected files contain no coherent visual decisions.', + 'For `init`, `teach`, `shape`, or any request to create a new surface or replacement visual world, load reference/init.md and create PRODUCT.md with the user first. ' + + 'After init writes PRODUCT.md, reference/new-work.md preserves and documents the incumbent system for an ' + + 'extension or replaces it with the user for a redesign/rebrand. Other ' + + 'narrow refinement commands may read the CSS, tokens, components, and assets and proceed without blocking, then ' + + `offer \`${IMPECCABLE_COMMAND} init\` as a follow-up.`, + 'BUILD_INIT_REQUIRED: Before shape or any new-surface/redesign flow, init must capture PRODUCT.md with the human or structured ' + + 'simulated user. Init writes product truth only; reference/new-work.md owns every visual decision.', + 'SCOPED_EXISTING_ALLOWED: Narrow refinement commands may use the incumbent implementation as authority without ' + + 'blocking on context setup; they must preserve it and offer init afterward.', + 'EXISTING_VISUAL_SYSTEM: For refinement or extension, code and assets are incumbent design authority and missing ' + + 'DESIGN.md is a documentation gap. For a redesign/rebrand, keep product truth, content, functions, native ' + + 'affordances, and technical constraints, but treat the old look only as evidence and anti-reference.', ] : [ 'NO_PRODUCT_MD: This project has no PRODUCT.md yet. ' + - 'For `init`, `teach`, `craft`, `shape`, ' + + 'For `init`, `teach`, `shape`, ' + 'or wording that clearly maps to a from-scratch build/shape flow, load ' + - 'reference/init.md and write PRODUCT.md first, unless no user can ' + - 'respond (a one-shot or automated run, or the user said not to ask): ' + - 'then write a one-paragraph understanding of the product, audience, ' + - 'and the page\'s job from the brief, and continue. For any other ' + + 'reference/init.md, complete its human or structured simulated-user interview, and write PRODUCT.md before ' + + 'designing. If no answer mechanism truly exists, init may infer only from the explicit brief and must label its ' + + 'assumptions. It never writes DESIGN.md. For any other ' + '(scoped) command against existing code, proceed using the code as ' + `context and offer \`${IMPECCABLE_COMMAND} init\` as a suggestion (do not block).`, - 'NEW_WORK: No committed design context was found. If this task produces ' + - 'new design (a build from scratch, or a redesign that discards the ' + - 'current look), you MUST read reference/new-work.md before making any ' + - 'design decision. Scoped fixes to existing code do not need it.', + 'PRODUCT_INIT_REQUIRED: No product context or visual authority was found. New builds and redesigns ' + + 'must finish reference/init.md for PRODUCT.md, then reference/new-work.md establishes the world and surface. Scoped ' + + 'fixes to existing code do not need the new-surface flow.', ]; + appendSurfaceBriefContext(parts, ctx); parts.push(buildResolvedContextDirective(ctx, cliOptions, { targetExists })); + appendHookFallback(parts, ctx); if (shouldWarnMissingTarget(ctx, targetProvided, targetExists)) { parts.push(buildMissingTargetDirective()); } @@ -1020,37 +1064,29 @@ async function cli() { if (ctx.hasDesign) { parts.push(`# DESIGN.md\n\n${ctx.design.trim()}`); } + appendSurfaceBriefContext(parts, ctx); parts.push(buildResolvedContextDirective(ctx, cliOptions, { targetExists })); + appendHookFallback(parts, ctx); if (shouldWarnMissingTarget(ctx, targetProvided, targetExists)) { parts.push(buildMissingTargetDirective()); } - const register = extractRegister(ctx.product); - // The register field survives as a family hint (brand = Persuade/Experience, - // product = Operate/Read); SKILL.md's mode section carries the essentials - // inline, so no register-file read is mandated here. What IS mandated: - // the new-work playbook when no committed design system exists yet. if (!ctx.hasDesign) { parts.push(ctx.hasVisualImplementation - ? 'EXISTING_VISUAL_SYSTEM: PRODUCT.md exists and DESIGN.md is missing, but code contains incumbent visual decisions. ' + - 'Treat CSS, tokens, components, and assets as design authority. Do not route to new identity work merely because the ' + - 'document is absent; preserve and extend the implementation unless the user explicitly asks to discard it.' - : 'NEW_WORK: PRODUCT.md exists but no DESIGN.md or incumbent visual implementation was found. If this task produces ' + - 'new design or a redesign that discards the current look, you MUST read reference/new-work.md before making any ' + - 'design decision. Scoped fixes to existing code do not need it.'); + ? 'INCUMBENT_WORLD_UNDOCUMENTED: PRODUCT.md exists and DESIGN.md is missing, but code contains incumbent visual decisions. ' + + 'For shape or a new-surface/redesign request, load reference/new-work.md: an extension documents and preserves the code-defined world; ' + + 'a redesign replaces it with the user and uses the old look only as evidence and anti-reference. Narrow refinement ' + + 'commands may proceed using the implementation directly.' + : 'WORLD_DISCOVERY_REQUIRED: PRODUCT.md exists but no DESIGN.md or incumbent visual implementation was found. ' + + 'For a new build or redesign, load reference/new-work.md and establish the visual world with the human or structured ' + + 'simulated user before developing the task concept. Scoped fixes to existing code do not need this flow.'); } - if (register) { - parts.push(`REGISTER: \`${register}\` (family hint: brand = Persuade/Experience surfaces, product = Operate/Read). Derive the visitor's mode per SKILL.md.`); - } - const platform = extractPlatform(ctx.product); - const nativeRefs = - platform === 'adaptive' ? ['ios', 'android'] : platform === 'ios' || platform === 'android' ? [platform] : []; - if (nativeRefs.length) { - const refList = nativeRefs.map(p => `\`reference/${p}.md\``).join(' and '); - const label = platform === 'adaptive' ? '`adaptive` (both iOS and Android)' : `\`${platform}\``; + const platformReferences = loadNativePlatformReferences(ctx.platform); + for (const reference of platformReferences) { parts.push( - `NEXT STEP: This project targets ${label}. Also read ${refList} for native conventions, in addition to SKILL.md's mode guidance.`, + `# NATIVE PLATFORM REFERENCE: ${reference.name.toUpperCase()} (reference/${reference.name}.md)\n\n${reference.content.trim()}`, ); - } else if (!platform) { + } + if (!ctx.platform) { // A `## Platform` section that names something we don't recognize (a // toolchain like `flutter`, a typo) would otherwise silently fall back to // web — the wrong default exactly when the user tried to say "native". @@ -1078,6 +1114,72 @@ function pathExistsForTarget(cwd, targetPath) { return fs.existsSync(abs); } +const HOOK_MANIFESTS_BY_PROVIDER = Object.freeze({ + 'claude-code': ['.claude/settings.local.json', '.claude/settings.json'], + codex: ['.codex/hooks.json'], + agents: ['.codex/hooks.json'], + cursor: ['.cursor/hooks.json'], + github: ['.github/hooks/impeccable.json'], +}); + +function truthyEnv(value) { + return typeof value === 'string' && /^(1|true|yes|on)$/i.test(value.trim()); +} + +function valueHasHookMarker(value) { + if (typeof value === 'string') { + return value.includes('skills/impeccable/scripts/hook.mjs') + || value.includes('skills/impeccable/scripts/hook-before-edit.mjs'); + } + if (Array.isArray(value)) return value.some(valueHasHookMarker); + if (value && typeof value === 'object') return Object.values(value).some(valueHasHookMarker); + return false; +} + +function hookEnabledAt(root) { + if (truthyEnv(process.env.IMPECCABLE_HOOK_DISABLED)) return false; + let enabled = true; + for (const name of ['.impeccable/config.json', '.impeccable/config.local.json']) { + const raw = readJson(path.join(root, name)); + if (raw?.hook && Object.prototype.hasOwnProperty.call(raw.hook, 'enabled')) { + enabled = raw.hook.enabled !== false; + } + } + return enabled; +} + +const STOP_REVIEW_PROVIDERS = new Set(['claude-code', 'codex', 'agents']); + +function automaticHookMode(ctx) { + if (ctx.platform === 'ios' || ctx.platform === 'android' || ctx.platform === 'adaptive') { + return 'none'; + } + const activeRoot = path.resolve(ctx.projectRoot || process.cwd()); + if (!hookEnabledAt(activeRoot)) return 'none'; + const manifests = HOOK_MANIFESTS_BY_PROVIDER[IMPECCABLE_PROVIDER_ID] || []; + const roots = [...new Set([process.cwd(), ctx.projectRoot, ctx.repoRoot].filter(Boolean).map((root) => path.resolve(root)))]; + for (const root of roots) { + for (const rel of manifests) { + const raw = readJson(path.join(root, rel)); + if (raw?.hooks && valueHasHookMarker(raw.hooks)) { + return STOP_REVIEW_PROVIDERS.has(IMPECCABLE_PROVIDER_ID) ? 'stop' : 'per-edit'; + } + } + } + return 'none'; +} + +function appendHookFallback(parts, ctx) { + const hookMode = automaticHookMode(ctx); + if (hookMode === 'stop') return; + const native = ctx.platform === 'ios' || ctx.platform === 'android' || ctx.platform === 'adaptive'; + parts.push(renderLlmOnlySlopReview({ + automaticDetector: hookMode === 'per-edit', + manualDetector: hookMode === 'none' && !native, + scriptsPath: path.dirname(fileURLToPath(import.meta.url)), + })); +} + function buildResolvedContextDirective(ctx, options, { targetExists = null } = {}) { const targetPath = hasTargetOption(options) ? options.targetPath : null; return `RESOLVED_CONTEXT:\n${JSON.stringify({ @@ -1087,10 +1189,29 @@ function buildResolvedContextDirective(ctx, options, { targetExists = null } = { repoRoot: ctx.repoRoot, productPath: ctx.productPath, designPath: ctx.designPath, + surfaceBriefPath: ctx.surfaceBriefPath, + surfaceBriefReason: ctx.surfaceBriefReason, + surfaceBriefCandidates: ctx.surfaceBriefCandidates, hasVisualImplementation: ctx.hasVisualImplementation, + platform: ctx.platform, }, null, 2)}`; } +function appendSurfaceBriefContext(parts, ctx) { + if (ctx.hasSurfaceBrief && ctx.surfaceBrief) { + parts.push(`# SURFACE BRIEF (${ctx.surfaceBriefPath})\n\n${ctx.surfaceBrief.trim()}`); + return; + } + if (!ctx.surfaceBriefCandidates?.length) return; + const helper = path.join(path.dirname(fileURLToPath(import.meta.url)), 'surface-brief.mjs'); + parts.push( + 'SURFACE_CONTEXT_AVAILABLE: Persisted surface briefs exist, but none was selected unambiguously for this invocation. ' + + 'Resolve the requested surface to its concrete primary or related source path, then run ' + + `\`node ${helper} read \` once before changing that surface. Candidates:\n` + + JSON.stringify(ctx.surfaceBriefCandidates, null, 2), + ); +} + function shouldWarnMissingTarget(ctx, targetProvided, targetExists = null) { if (ctx.isMonorepo && targetProvided && targetExists === false) return true; return !!( diff --git a/plugin/skills/impeccable/scripts/critique-storage.mjs b/plugin/skills/impeccable/scripts/critique-storage.mjs index 6b4d225cf..38674285a 100644 --- a/plugin/skills/impeccable/scripts/critique-storage.mjs +++ b/plugin/skills/impeccable/scripts/critique-storage.mjs @@ -29,8 +29,9 @@ import fs from 'node:fs'; import path from 'node:path'; import { fileURLToPath, pathToFileURL } from 'node:url'; import { getCritiqueDir } from './lib/impeccable-paths.mjs'; +import { slugFromTarget } from './lib/target-slug.mjs'; -const SLUG_MAX = 50; +export { slugFromTarget } from './lib/target-slug.mjs'; /** * Mechanically derive a slug from a resolved target. Returns null if the @@ -40,46 +41,6 @@ const SLUG_MAX = 50; * concrete artifact before calling this — we never slug a natural-language * phrase. */ -export function slugFromTarget(resolved, { cwd = process.cwd() } = {}) { - if (!resolved || typeof resolved !== 'string') return null; - const trimmed = resolved.trim(); - if (!trimmed) return null; - - // URL - if (/^https?:\/\//i.test(trimmed)) { - let url; - try { url = new URL(trimmed); } catch { return null; } - const hostPath = `${url.hostname}${url.pathname}`; - return kebab(hostPath); - } - - // File path. Make it project-relative so two devs critiquing the same - // checkout get the same slug regardless of where their repo is cloned. - const abs = path.isAbsolute(trimmed) ? trimmed : path.resolve(cwd, trimmed); - let rel = path.relative(cwd, abs); - // If the target is outside cwd, fall back to the basename so we still - // produce a stable slug (vs the absolute path, which would include - // home dirs / usernames). - if (rel.startsWith('..') || path.isAbsolute(rel)) { - rel = path.basename(abs); - } - if (!rel || rel === '.' || rel === '') return null; - return kebab(rel); -} - -function kebab(s) { - const slug = s - .toLowerCase() - .replace(/[/\\.]+/g, '-') - .replace(/[^a-z0-9-]+/g, '-') - .replace(/-+/g, '-') - .replace(/^-|-$/g, ''); - if (!slug) return null; - // Cap from the tail — the tail (filename) is more identifying than the - // top-level directory. - return slug.length <= SLUG_MAX ? slug : slug.slice(slug.length - SLUG_MAX).replace(/^-/, ''); -} - /** * Filename-safe UTC ISO timestamp: hyphens for separators, trailing Z. * Plain colons aren't allowed on Windows filesystems. diff --git a/plugin/skills/impeccable/scripts/detector/detect-antipatterns-browser.js b/plugin/skills/impeccable/scripts/detector/detect-antipatterns-browser.js index 033573e36..76bf4ddeb 100644 --- a/plugin/skills/impeccable/scripts/detector/detect-antipatterns-browser.js +++ b/plugin/skills/impeccable/scripts/detector/detect-antipatterns-browser.js @@ -312,17 +312,6 @@ const ANTIPATTERNS = [ skillSection: 'Layout & Space', skillGuideline: 'numbered section markers', }, - { - id: 'numbered-section-markers', - category: 'slop', - scopes: ['layout'], - severity: 'advisory', - name: 'Numbered section markers (01 / 02 / 03)', - description: - 'Numbered display markers as section labels (01, 02, 03) are the AI editorial scaffold one tier deeper than tracked eyebrow chips. If you find yourself reaching for them, choose a different section cadence.', - skillSection: 'Layout & Space', - skillGuideline: 'numbered section markers', - }, { id: 'em-dash-overuse', category: 'slop', diff --git a/plugin/skills/impeccable/scripts/detector/engines/regex/detect-text.mjs b/plugin/skills/impeccable/scripts/detector/engines/regex/detect-text.mjs index 81c30ab99..f641d5917 100644 --- a/plugin/skills/impeccable/scripts/detector/engines/regex/detect-text.mjs +++ b/plugin/skills/impeccable/scripts/detector/engines/regex/detect-text.mjs @@ -280,22 +280,6 @@ const REGEX_ANALYZERS = [ if (count === 0) return []; return [finding('marketing-buzzword', filePath, `${count} buzzword phrase${count === 1 ? '' : 's'}: "${firstSample}"`)]; }, - // Numbered section markers (01 / 02 / 03 ...) - (content, filePath) => { - const text = stripHtmlToText(content); - const re = /\b(0[1-9]|1[0-2])\b/g; - const seen = new Set(); - let m; - while ((m = re.exec(text)) !== null) seen.add(m[1]); - if (seen.size < 3) return []; - const sorted = [...seen].sort(); - let sequential = 0; - for (let i = 1; i < sorted.length; i++) { - if (parseInt(sorted[i], 10) === parseInt(sorted[i - 1], 10) + 1) sequential++; - } - if (sequential < 2) return []; - return [finding('numbered-section-markers', filePath, `Sequence: ${sorted.slice(0, 6).join(', ')}`)]; - }, // Aphoristic cadence: manufactured-contrast + short-rebuttal (content, filePath) => { const text = stripHtmlToText(content); @@ -430,21 +414,20 @@ function runRegexMatchers(lines, filePath, lineOffset = 0, blockContext = null, } /** Page-level analyzers that scan rendered text content (em-dash use, - * buzzword phrases, numbered section markers, aphoristic cadence). + * buzzword phrases, aphoristic cadence). * These are detector-agnostic — they work on any HTML/text source * and don't need a parsed DOM. Exported so detectHtml can call them * for `.html` files (which otherwise skip the regex engine). */ const TEXT_CONTENT_ANALYZER_IDS = [ 'em-dash-overuse', 'marketing-buzzword', - 'numbered-section-markers', 'aphoristic-cadence', ]; function runTextContentAnalyzers(content, filePath, options = {}) { const profile = options?.profile; if (!shouldRunPageAnalyzers(content, filePath)) return []; - // The 4 text-content analyzers are at indices 3-6 in REGEX_ANALYZERS. + // The 3 text-content analyzers are at indices 3-5 in REGEX_ANALYZERS. const findings = []; for (let i = 0; i < TEXT_CONTENT_ANALYZER_IDS.length; i++) { const analyzer = REGEX_ANALYZERS[3 + i]; @@ -535,7 +518,6 @@ function detectText(content, filePath, options = {}) { 'monotonous-spacing', 'em-dash-overuse', 'marketing-buzzword', - 'numbered-section-markers', 'aphoristic-cadence', 'dark-glow', ]; diff --git a/plugin/skills/impeccable/scripts/detector/registry/antipatterns.mjs b/plugin/skills/impeccable/scripts/detector/registry/antipatterns.mjs index b85f7e62f..84c69b0b8 100644 --- a/plugin/skills/impeccable/scripts/detector/registry/antipatterns.mjs +++ b/plugin/skills/impeccable/scripts/detector/registry/antipatterns.mjs @@ -210,17 +210,6 @@ const ANTIPATTERNS = [ skillSection: 'Layout & Space', skillGuideline: 'numbered section markers', }, - { - id: 'numbered-section-markers', - category: 'slop', - scopes: ['layout'], - severity: 'advisory', - name: 'Numbered section markers (01 / 02 / 03)', - description: - 'Numbered display markers as section labels (01, 02, 03) are the AI editorial scaffold one tier deeper than tracked eyebrow chips. If you find yourself reaching for them, choose a different section cadence.', - skillSection: 'Layout & Space', - skillGuideline: 'numbered section markers', - }, { id: 'em-dash-overuse', category: 'slop', diff --git a/plugin/skills/impeccable/scripts/detector/rules/checks.mjs b/plugin/skills/impeccable/scripts/detector/rules/checks.mjs index 3fdd6b2dd..9bace11b3 100644 --- a/plugin/skills/impeccable/scripts/detector/rules/checks.mjs +++ b/plugin/skills/impeccable/scripts/detector/rules/checks.mjs @@ -352,27 +352,57 @@ function isAccentColor(cssColor) { return false; } +function resolveHeroHeadingSizePx(value) { + const input = String(value || '').trim().toLowerCase(); + if (!input) return 0; + + const simpleLengthPx = (token) => { + const match = /^(-?\d*\.?\d+)\s*(px|rem|em|%)?$/.exec(String(token || '').trim()); + if (!match) return null; + const amount = Number(match[1]); + if (!Number.isFinite(amount)) return null; + if (match[2] === 'rem' || match[2] === 'em') return amount * 16; + if (match[2] === '%') return amount * 0.16; + return amount; + }; + + const direct = simpleLengthPx(input); + if (direct !== null) return direct; + + // Static CSS engines cannot resolve viewport units, but clamp's min/max + // bounds still tell us whether the heading can ever reach hero scale. + const clamp = /^clamp\((.*)\)$/.exec(input); + if (clamp) { + const parts = clamp[1].split(','); + if (parts.length === 3) { + const bounds = [simpleLengthPx(parts[0]), simpleLengthPx(parts[2])] + .filter((candidate) => candidate !== null); + if (bounds.length > 0) return Math.max(...bounds); + } + } + + return 0; +} + // Sibling-relationship rule. Anchor on a hero-scale h1, look at the // previousElementSibling, and gate on EITHER the classic tracked- // uppercase eyebrow OR the modern accent-colored bold eyebrow. function checkHeroEyebrow(opts) { const { headingTag, headingText, headingFontSize, + headingInApplicationContext, siblingTag, siblingText, siblingTextTransform, siblingFontSize, siblingLetterSpacing, siblingFontWeight, siblingColor, siblingHasAccentDashPseudo, } = opts; if (headingTag !== 'h1') return []; - // We previously gated on headingFontSize >= 48 to anchor "hero scale". - // But modern hero h1s use clamp() / vw / var(--text-*), none of which - // jsdom can resolve — the computed value comes back as "2em" or - // "var(--text-9xl)" and parseFloat returns 2 or NaN. The gate fails - // on virtually every Tailwind v4 / framework build. The other gates - // (sibling text 2-60 chars, font-size ≤ 14px, accent-bold OR - // tracked-caps) are tight enough to avoid false positives on non- - // hero h1s — a tiny tan label directly above any h1 is the - // antipattern regardless of how big the h1 ends up. + // This is specifically a marketing-hero cliché, not a ban on compact + // context labels in product UI (for example, a station name inside a tab + // panel). Browser-computed sizes are reliable; the static adapter also + // resolves ordinary px/rem/em and clamp() bounds before reaching here. + if (headingInApplicationContext) return []; + if (!(headingFontSize >= 48)) return []; if (!siblingTag) return []; // An h2 above an h1 is a different anti-pattern (heading hierarchy / dual // headings) — never an eyebrow. @@ -1938,6 +1968,7 @@ function checkElementHeroEyebrowDOM(el) { headingTag: tag, headingText: el.textContent || '', headingFontSize: parseFloat(headStyle.fontSize) || 0, + headingInApplicationContext: !!el.closest('[role="tabpanel"], [role="dialog"], [role="application"], dialog'), siblingTag: sibling.tagName.toLowerCase(), siblingText: sibling.textContent || '', siblingTextTransform: sibStyle.textTransform || '', @@ -3367,7 +3398,8 @@ function checkElementHeroEyebrow(el, style, tag, window, customPropMap) { return checkHeroEyebrow({ headingTag: tag, headingText: el.textContent || '', - headingFontSize: parseFloat(headingFontSizeRaw) || 0, + headingFontSize: resolveHeroHeadingSizePx(headingFontSizeRaw), + headingInApplicationContext: !!el.closest?.('[role="tabpanel"], [role="dialog"], [role="application"], dialog'), siblingTag: sibling.tagName.toLowerCase(), siblingText: sibling.textContent || '', siblingTextTransform: sibStyle.textTransform || '', diff --git a/plugin/skills/impeccable/scripts/hook-lib.mjs b/plugin/skills/impeccable/scripts/hook-lib.mjs index e61ea692b..50aa2f8fb 100644 --- a/plugin/skills/impeccable/scripts/hook-lib.mjs +++ b/plugin/skills/impeccable/scripts/hook-lib.mjs @@ -46,6 +46,7 @@ import path from 'node:path'; import { pathToFileURL, fileURLToPath } from 'node:url'; import { extractPlatform, loadContext } from './context.mjs'; import { IMPECCABLE_COMMAND } from './lib/provider.mjs'; +import { renderStopSlopReview } from './lib/slop-review.mjs'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); @@ -1868,22 +1869,42 @@ export const CONTRACT_MAX_CHARS = 1800; // Cap contract sections per Stop emission so many touched artifacts cannot // stack an unbounded message. export const CONTRACT_AUDIT_MAX_FILES = 3; +export const CONTRACT_EXTS = new Set(['.html', '.htm', '.astro', '.svelte', '.vue', '.jsx', '.tsx']); +export const CONTRACT_REQUIRED_FIELDS = ['UNIQUE', 'NOT-TEMPLATE', 'OWN-WORLD', 'STORY', 'FIRST VIEWPORT', 'FORM']; /** - * Extract the artifact's own direction-contract comment: the first HTML - * comment in the head of the file, when its opening chars identify it as a - * contract/concept block. Returns the trimmed, length-capped body, or null - * when the file carries none (no comment, unclosed comment, marker missing, - * or the comment starts past the head window). + * Extract the artifact's own direction-contract comment from the head of an + * HTML or component file. Supports HTML-family comments and JSX block + * comments so the contract works in the Astro/Svelte/Vue/React scaffolds the + * skill actually builds. Returns the trimmed, length-capped body, or null + * when the file carries no valid contract block. */ export function extractDirectionContract(content) { if (typeof content !== 'string' || !content) return null; const head = content.slice(0, CONTRACT_HEAD_CHARS); - const m = //.exec(head); - if (!m) return null; - const body = m[1].trim(); - if (!body || !/contract|concept/i.test(body.slice(0, CONTRACT_MARKER_CHARS))) return null; - return body.slice(0, CONTRACT_MAX_CHARS); + const candidates = []; + for (const pattern of [//g, /\{\/\*([\s\S]*?)\*\/\}/g]) { + for (const match of head.matchAll(pattern)) { + const index = match.index ?? 0; + const linePrefix = head.slice(head.lastIndexOf('\n', index - 1) + 1, index).trim(); + if (linePrefix.startsWith('//')) continue; + candidates.push({ index, body: match[1].trim() }); + } + } + candidates.sort((a, b) => a.index - b.index); + for (const candidate of candidates) { + if (!candidate.body || !/direction\s+contract|concept\s+contract/i.test(candidate.body.slice(0, CONTRACT_MARKER_CHARS))) continue; + return candidate.body.slice(0, CONTRACT_MAX_CHARS); + } + return null; +} + +export function missingDirectionContractFields(contract) { + const body = typeof contract === 'string' ? contract : ''; + return CONTRACT_REQUIRED_FIELDS.filter((field) => { + const label = field.replace(/[.*+?^${}()|[\]\\]/g, '\\$&').replace(/\s+/g, '\\s+'); + return !new RegExp(`\\b${label}\\s*:`, 'i').test(body); + }); } /** @@ -1899,7 +1920,11 @@ export function renderContractAudit(entries, opts = {}) { const shown = entries.slice(0, CONTRACT_AUDIT_MAX_FILES); const blocks = shown.map(({ filePath, contract }) => { const display = relativize(filePath, cwd); - return `${display} opens with this direction contract, written when the direction was decided:\n\n${contract}`; + const missing = missingDirectionContractFields(contract); + const integrity = missing.length > 0 + ? `\n\nContract integrity defect: missing ${missing.join(', ')}. Repair the contract and the implementation together.` + : ''; + return `${display} opens with this direction contract, written when the direction was decided:\n\n${contract}${integrity}`; }); return [ `${ENVELOPE_PREFIX} Direction-contract audit. Before finishing, audit the rendered page against the contract it opens with, promise by promise.`, @@ -1984,9 +2009,22 @@ export async function runStopHook({ stdinJson, env = {}, cwd = process.cwd(), no return result({ skipped: 'native-platform', platform, durationMs: Date.now() - started }); } + const session = ensureSession(cache, sessionId); + const needsSlopReview = session.llmSlopReviewed !== true; + const det = detector || await loadDetector(); if (!det || typeof det.detectText !== 'function') { - return result({ skipped: 'detector-missing', durationMs: Date.now() - started }); + if (!needsSlopReview) return result({ skipped: 'detector-missing', durationMs: Date.now() - started }); + session.llmSlopReviewed = true; + session.updatedAt = Date.now(); + persistCache(projectCwd, cache); + const text = renderStopSlopReview(); + return { + exitCode: 0, + stdout: payload(text, 'Stop', harness), + emission: { kind: 'stop-llm-slop-review', llmSlopReview: true }, + audit: { ...audit, emitted: true, detectorMissing: true, llmSlopReview: true, chars: text.length, durationMs: Date.now() - started }, + }; } const scanOptions = designSystemOptions(config, det, projectCwd); @@ -2013,10 +2051,11 @@ export async function runStopHook({ stdinJson, env = {}, cwd = process.cwd(), no ? configuredExt.engine === 'html' : (ext === '.html' || ext === '.htm'); - // Direction-contract audit: HTML artifacts only, at most once per file + // Direction-contract audit: HTML and component artifacts, at most once per file // per session. The flag lives on the same session cache entry the // finding dedupe uses, so a second Stop fire stays quiet about it. - if (useHtmlEngine) { + const contractCapable = useHtmlEngine || CONTRACT_EXTS.has(ext); + if (contractCapable) { const fileEntry = ensureFile(cache, sessionId, filePath); if (!fileEntry.contractAudited) { const contract = extractDirectionContract(content); @@ -2046,10 +2085,15 @@ export async function runStopHook({ stdinJson, env = {}, cwd = process.cwd(), no } audit.scannedFiles = scanned; - if (freshGroups.length === 0 && contractEntries.length === 0) { + if (freshGroups.length === 0 && contractEntries.length === 0 && !needsSlopReview) { return result({ emitted: false, skipped: 'stop-clean', durationMs: Date.now() - started }); } + if (needsSlopReview) { + session.llmSlopReviewed = true; + session.updatedAt = Date.now(); + } + // Fresh findings and first-time contract audits earn the cache write; // both mark this batch as surfaced so the next Stop fire is silent // unless new issues appear. @@ -2064,6 +2108,9 @@ export async function runStopHook({ stdinJson, env = {}, cwd = process.cwd(), no if (contractEntries.length > 0) { parts.push(renderContractAudit(contractEntries, { cwd: projectCwd })); } + if (needsSlopReview) { + parts.push(renderStopSlopReview()); + } const text = appendDesignSystemNote(parts.join('\n\n'), scanOptions); return { exitCode: 0, @@ -2074,6 +2121,7 @@ export async function runStopHook({ stdinJson, env = {}, cwd = process.cwd(), no ...(contractEntries.length > 0 ? { contractFiles: contractEntries.map((entry) => entry.filePath) } : {}), + ...(needsSlopReview ? { llmSlopReview: true } : {}), }, audit: { ...audit, @@ -2081,6 +2129,7 @@ export async function runStopHook({ stdinJson, env = {}, cwd = process.cwd(), no freshFiles: freshGroups.length, freshFindings: freshGroups.reduce((sum, group) => sum + group.findings.length, 0), ...(contractEntries.length > 0 ? { contractAudits: contractEntries.length } : {}), + ...(needsSlopReview ? { llmSlopReview: true } : {}), chars: text.length, durationMs: Date.now() - started, }, diff --git a/plugin/skills/impeccable/scripts/lib/provider.mjs b/plugin/skills/impeccable/scripts/lib/provider.mjs index 7fa928a9c..5f24d569b 100644 --- a/plugin/skills/impeccable/scripts/lib/provider.mjs +++ b/plugin/skills/impeccable/scripts/lib/provider.mjs @@ -1,4 +1,5 @@ // Source scripts default to slash commands. The provider build replaces only // this exact declaration, avoiding heuristic rewrites across executable code. export const IMPECCABLE_COMMAND_PREFIX = "/"; +export const IMPECCABLE_PROVIDER_ID = "claude-code"; export const IMPECCABLE_COMMAND = `${IMPECCABLE_COMMAND_PREFIX}impeccable`; diff --git a/plugin/skills/impeccable/scripts/lib/slop-review.mjs b/plugin/skills/impeccable/scripts/lib/slop-review.mjs new file mode 100644 index 000000000..30f03e34d --- /dev/null +++ b/plugin/skills/impeccable/scripts/lib/slop-review.mjs @@ -0,0 +1,30 @@ +export const LLM_ONLY_SLOP_ITEMS = Object.freeze([ + 'Monospace used merely to signal “technical” or “developer.”', + 'Light or dark mode chosen by category habit rather than the actual use scene.', + 'Everything wrapped in cards, or identical icon-heading-text cards repeated as the page structure.', + 'Hero-metric scaffolds: one oversized number, a small label, supporting stats, and an accent treatment.', + 'Decorative glassmorphism, meaningless sparklines, or generic rounded rectangles with drop shadows.', + 'A modal chosen by reflex when the task does not require interruption or protected focus.', +]); + +export function renderLlmOnlySlopReview({ manualDetector = false, automaticDetector = false, scriptsPath = null } = {}) { + const lines = [ + automaticDetector + ? 'AI_SLOP_REVIEW_REQUIRED: The automatic detector covers mechanical rules, but this harness has no reliable late review. Before finishing changed UI, inspect the authored result for detector-blind model reflexes:' + : 'AI_SLOP_REVIEW_REQUIRED: The automatic Impeccable design hook is not available for this session. Before finishing changed UI, inspect the authored result for detector-blind model reflexes:', + ...LLM_ONLY_SLOP_ITEMS.map((item) => `- ${item}`), + 'Fix reflexes, not intentional choices required by the brief or established visual authority.', + ]; + if (manualDetector && scriptsPath) { + lines.push(`Then run the mechanical detector once over the changed web UI: \`node ${scriptsPath}/detect.mjs --json \`. Do not run it earlier during concept selection.`); + } + return lines.join('\n'); +} + +export function renderStopSlopReview() { + return [ + '[impeccable@1] Detector-blind AI-slop review. The mechanical pass is complete; now inspect the rendered result for model reflexes it cannot reliably detect:', + ...LLM_ONLY_SLOP_ITEMS.map((item) => `- ${item}`), + 'Fix reflexes, not intentional choices required by the brief or established visual authority. Do not rerun the detector; this is the authored judgment layer.', + ].join('\n'); +} diff --git a/plugin/skills/impeccable/scripts/lib/surface-briefs.mjs b/plugin/skills/impeccable/scripts/lib/surface-briefs.mjs new file mode 100644 index 000000000..f83416f69 --- /dev/null +++ b/plugin/skills/impeccable/scripts/lib/surface-briefs.mjs @@ -0,0 +1,151 @@ +import fs from 'node:fs'; +import path from 'node:path'; +import { slugFromTarget } from './target-slug.mjs'; + +export const SURFACE_BRIEF_VERSION = 1; + +export function getSurfaceBriefDir(projectRoot) { + return path.join(projectRoot, '.impeccable', 'surfaces'); +} + +export function normalizeSurfaceTarget(target, { projectRoot = process.cwd() } = {}) { + if (!target || typeof target !== 'string' || !target.trim()) return null; + const trimmed = target.trim(); + if (/^https?:\/\//i.test(trimmed)) { + try { + const url = new URL(trimmed); + url.hash = ''; + url.search = ''; + return url.toString().replace(/\/$/, '') || url.origin; + } catch { + return null; + } + } + if (/^route:/i.test(trimmed)) { + const route = trimmed.slice(trimmed.indexOf(':') + 1).trim(); + if (!route.startsWith('/') || route.includes('..')) return null; + const normalizedRoute = route.split(/[?#]/, 1)[0].replace(/\/{2,}/g, '/').replace(/\/$/, '') || '/'; + return `route:${normalizedRoute}`; + } + if (trimmed === '/') return 'route:/'; + if (trimmed.startsWith('/')) { + const absolute = path.resolve(trimmed); + const relativeToProject = path.relative(projectRoot, absolute); + const isProjectFile = relativeToProject && !relativeToProject.startsWith('..') && !path.isAbsolute(relativeToProject); + if (!isProjectFile && !fs.existsSync(absolute) && !trimmed.includes('..')) { + const normalizedRoute = trimmed.split(/[?#]/, 1)[0].replace(/\/{2,}/g, '/').replace(/\/$/, '') || '/'; + return `route:${normalizedRoute}`; + } + } + const abs = path.isAbsolute(trimmed) ? trimmed : path.resolve(projectRoot, trimmed); + const rel = path.relative(projectRoot, abs); + if (!rel || rel === '.' || rel.startsWith('..') || path.isAbsolute(rel)) return null; + return rel.split(path.sep).join('/'); +} + +export function surfaceBriefPathForTarget(target, { projectRoot = process.cwd() } = {}) { + const normalized = normalizeSurfaceTarget(target, { projectRoot }); + if (!normalized) return null; + const slugInput = normalized.startsWith('route:') ? `route${normalized.slice('route:'.length)}` : normalized; + const slug = slugFromTarget(slugInput, { cwd: projectRoot }); + return slug ? path.join(getSurfaceBriefDir(projectRoot), `${slug}.md`) : null; +} + +export function parseSurfaceBrief(text, filePath = null) { + const match = String(text || '').match(/^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/); + const meta = {}; + if (match) { + for (const line of match[1].split(/\r?\n/)) { + const colon = line.indexOf(':'); + if (colon < 0) continue; + const key = line.slice(0, colon).trim(); + const raw = line.slice(colon + 1).trim(); + if (!key) continue; + if (/^(?:\[|\{|\")/.test(raw) || /^(?:true|false|null|-?\d+(?:\.\d+)?)$/.test(raw)) { + try { meta[key] = JSON.parse(raw); continue; } catch { /* keep string */ } + } + meta[key] = raw.replace(/^['"]|['"]$/g, ''); + } + } + const primaryTarget = typeof meta.primary_target === 'string' ? meta.primary_target : null; + const relatedTargets = Array.isArray(meta.related_targets) + ? meta.related_targets.filter((value) => typeof value === 'string') + : []; + return { + path: filePath, + text: String(text || ''), + body: match ? String(text || '').slice(match[0].length).trim() : String(text || '').trim(), + meta, + slug: typeof meta.slug === 'string' ? meta.slug : filePath ? path.basename(filePath, '.md') : null, + primaryTarget, + relatedTargets, + targets: [primaryTarget, ...relatedTargets].filter(Boolean), + }; +} + +export function listSurfaceBriefs(projectRoot = process.cwd()) { + const dir = getSurfaceBriefDir(projectRoot); + let names; + try { + names = fs.readdirSync(dir).filter((name) => name.endsWith('.md')).sort(); + } catch { + return []; + } + return names.flatMap((name) => { + const filePath = path.join(dir, name); + try { + return [parseSurfaceBrief(fs.readFileSync(filePath, 'utf-8'), filePath)]; + } catch { + return []; + } + }); +} + +export function resolveSurfaceBrief(projectRoot = process.cwd(), target = null) { + const briefs = listSurfaceBriefs(projectRoot); + if (!target) { + return { + brief: briefs.length === 1 ? briefs[0] : null, + candidates: briefs, + reason: briefs.length === 1 ? 'only-brief' : briefs.length > 1 ? 'ambiguous' : 'none', + }; + } + + const normalized = normalizeSurfaceTarget(target, { projectRoot }); + if (!normalized) return { brief: null, candidates: briefs, reason: 'invalid-target' }; + const exactPath = surfaceBriefPathForTarget(normalized, { projectRoot }); + const exact = briefs.find((brief) => brief.path === exactPath && (!brief.targets.length || brief.targets.includes(normalized))); + if (exact) return { brief: exact, candidates: briefs, reason: 'slug' }; + const mapped = briefs.filter((brief) => brief.targets.includes(normalized)); + return { + brief: mapped.length === 1 ? mapped[0] : null, + candidates: mapped.length > 1 ? mapped : briefs, + reason: mapped.length === 1 ? 'mapping' : mapped.length > 1 ? 'ambiguous-target' : 'not-found', + }; +} + +export function writeSurfaceBrief({ + projectRoot = process.cwd(), + primaryTarget, + relatedTargets = [], + body, +}) { + const normalizedPrimary = normalizeSurfaceTarget(primaryTarget, { projectRoot }); + if (!normalizedPrimary) throw new Error('surface brief requires a concrete project-relative primary target or URL'); + const normalizedRelated = [...new Set(relatedTargets + .map((target) => normalizeSurfaceTarget(target, { projectRoot })) + .filter((target) => target && target !== normalizedPrimary))]; + const slug = slugFromTarget(normalizedPrimary, { cwd: projectRoot }); + const filePath = surfaceBriefPathForTarget(normalizedPrimary, { projectRoot }); + fs.mkdirSync(path.dirname(filePath), { recursive: true }); + const frontmatter = [ + '---', + `version: ${SURFACE_BRIEF_VERSION}`, + `slug: ${JSON.stringify(slug)}`, + `primary_target: ${JSON.stringify(normalizedPrimary)}`, + `related_targets: ${JSON.stringify(normalizedRelated)}`, + '---', + ].join('\n'); + fs.writeFileSync(filePath, `${frontmatter}\n\n${String(body || '').trim()}\n`, 'utf-8'); + return filePath; +} diff --git a/plugin/skills/impeccable/scripts/lib/target-slug.mjs b/plugin/skills/impeccable/scripts/lib/target-slug.mjs new file mode 100644 index 000000000..025915ad5 --- /dev/null +++ b/plugin/skills/impeccable/scripts/lib/target-slug.mjs @@ -0,0 +1,33 @@ +import path from 'node:path'; + +const SLUG_MAX = 50; + +/** Derive one clone-stable slug from a concrete file path or URL. */ +export function slugFromTarget(resolved, { cwd = process.cwd() } = {}) { + if (!resolved || typeof resolved !== 'string') return null; + const trimmed = resolved.trim(); + if (!trimmed) return null; + + if (/^https?:\/\//i.test(trimmed)) { + let url; + try { url = new URL(trimmed); } catch { return null; } + return kebab(`${url.hostname}${url.pathname}`); + } + + const abs = path.isAbsolute(trimmed) ? trimmed : path.resolve(cwd, trimmed); + let rel = path.relative(cwd, abs); + if (rel.startsWith('..') || path.isAbsolute(rel)) rel = path.basename(abs); + if (!rel || rel === '.') return null; + return kebab(rel); +} + +export function kebab(value) { + const slug = String(value || '') + .toLowerCase() + .replace(/[/\\.]+/g, '-') + .replace(/[^a-z0-9-]+/g, '-') + .replace(/-+/g, '-') + .replace(/^-|-$/g, ''); + if (!slug) return null; + return slug.length <= SLUG_MAX ? slug : slug.slice(slug.length - SLUG_MAX).replace(/^-/, ''); +} diff --git a/plugin/skills/impeccable/scripts/live-browser.js b/plugin/skills/impeccable/scripts/live-browser.js index 616f1290a..47bbfefbf 100644 --- a/plugin/skills/impeccable/scripts/live-browser.js +++ b/plugin/skills/impeccable/scripts/live-browser.js @@ -2592,7 +2592,7 @@ minWidth: '16px', height: '16px', padding: '0 4px', borderRadius: '999px', background: tuneOpen ? C.brand : BP.hairline, - color: tuneOpen ? 'oklch(98% 0 0)' : 'inherit', + color: tuneOpen ? C.ink : 'inherit', fontFamily: MONO, fontSize: '9.5px', fontWeight: '600', lineHeight: '1', boxSizing: 'border-box', @@ -3131,7 +3131,7 @@ position: 'absolute', top: '2px', left: initial ? '18px' : '2px', width: '16px', height: '16px', borderRadius: '50%', - background: 'oklch(98% 0 0)', + background: C.ink, transition: 'left 0.18s ' + EASE, boxShadow: '0 1px 2px oklch(0% 0 0 / 0.2)', }); @@ -3165,7 +3165,7 @@ const b = el('button', { padding: '5px 4px', border: 'none', borderRadius: '3px', background: active ? C.brand : 'transparent', - color: active ? 'oklch(98% 0 0)' : P.text, + color: active ? C.ink : P.text, fontFamily: FONT, fontSize: '10.5px', fontWeight: '500', cursor: 'pointer', whiteSpace: 'nowrap', transition: 'background 0.1s ease, color 0.1s ease', @@ -3178,7 +3178,7 @@ segBtns.forEach(({ btn, val }) => { const on = val === o.value; btn.style.background = on ? C.brand : 'transparent'; - btn.style.color = on ? 'oklch(98% 0 0)' : P.text; + btn.style.color = on ? C.ink : P.text; }); applyParamValue(variantEl, p, o.value); queueCheckpoint('param_changed'); diff --git a/plugin/skills/impeccable/scripts/surface-brief.mjs b/plugin/skills/impeccable/scripts/surface-brief.mjs new file mode 100644 index 000000000..723f7c1b4 --- /dev/null +++ b/plugin/skills/impeccable/scripts/surface-brief.mjs @@ -0,0 +1,74 @@ +#!/usr/bin/env node +import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath, pathToFileURL } from 'node:url'; +import { resolveProjectRoot } from './context.mjs'; +import { + listSurfaceBriefs, + resolveSurfaceBrief, + surfaceBriefPathForTarget, + writeSurfaceBrief, +} from './lib/surface-briefs.mjs'; + +function summary(brief, projectRoot) { + return { + slug: brief.slug, + path: path.relative(projectRoot, brief.path).split(path.sep).join('/'), + primaryTarget: brief.primaryTarget, + relatedTargets: brief.relatedTargets, + }; +} + +function main(argv) { + const [command, target, bodyFile, ...relatedTargets] = argv; + const projectRoot = resolveProjectRoot(process.cwd(), target ? { targetPath: target } : {}); + if (command === 'path') { + const filePath = surfaceBriefPathForTarget(target, { projectRoot }); + if (!filePath) throw new Error('surface brief path requires a concrete target'); + process.stdout.write(`${path.relative(process.cwd(), filePath) || filePath}\n`); + return; + } + if (command === 'list') { + process.stdout.write(`${JSON.stringify(listSurfaceBriefs(projectRoot).map((brief) => summary(brief, projectRoot)), null, 2)}\n`); + return; + } + if (command === 'read') { + const result = resolveSurfaceBrief(projectRoot, target || null); + if (result.brief) { + process.stdout.write(result.brief.text); + return; + } + if (result.candidates.length) process.stderr.write(`${JSON.stringify(result.candidates.map((brief) => summary(brief, projectRoot)), null, 2)}\n`); + process.exit(2); + } + if (command === 'write') { + if (!target || !bodyFile) throw new Error('usage: surface-brief.mjs write '); + const filePath = writeSurfaceBrief({ + projectRoot, + primaryTarget: target, + relatedTargets, + body: fs.readFileSync(bodyFile, 'utf-8'), + }); + process.stdout.write(`${path.relative(process.cwd(), filePath) || filePath}\n`); + return; + } + throw new Error('usage: surface-brief.mjs [target] [body-file] [related-target ...]'); +} + +function isMainModule() { + if (!process.argv[1]) return false; + try { + return fs.realpathSync(fileURLToPath(import.meta.url)) === fs.realpathSync(process.argv[1]); + } catch { + return import.meta.url === pathToFileURL(process.argv[1]).href; + } +} + +if (isMainModule()) { + try { + main(process.argv.slice(2)); + } catch (error) { + process.stderr.write(`${error?.message || error}\n`); + process.exit(1); + } +} diff --git a/scripts/build.js b/scripts/build.js index 8098ca03c..a85ba4c28 100644 --- a/scripts/build.js +++ b/scripts/build.js @@ -440,6 +440,38 @@ function copyDirSync(src, dest) { } } +/** + * Make an existing directory match a generated source without removing the + * destination root. Provider skill roots can be watched by the running agent; + * replacing that root fails on some platforms and can invalidate the live + * skill path mid-session. + */ +function mirrorDirContentsSync(src, dest) { + fs.mkdirSync(dest, { recursive: true }); + const sourceEntries = new Map( + fs.readdirSync(src, { withFileTypes: true }).map(entry => [entry.name, entry]), + ); + + for (const destEntry of fs.readdirSync(dest, { withFileTypes: true })) { + if (!sourceEntries.has(destEntry.name)) { + fs.rmSync(path.join(dest, destEntry.name), { recursive: true, force: true }); + } + } + + for (const entry of sourceEntries.values()) { + const srcPath = path.join(src, entry.name); + const destPath = path.join(dest, entry.name); + const destStat = fs.existsSync(destPath) ? fs.lstatSync(destPath) : null; + if (entry.isDirectory()) { + if (destStat && !destStat.isDirectory()) fs.rmSync(destPath, { recursive: true, force: true }); + mirrorDirContentsSync(srcPath, destPath); + } else { + if (destStat?.isDirectory()) fs.rmSync(destPath, { recursive: true, force: true }); + fs.copyFileSync(srcPath, destPath); + } + } +} + function syncRootHookManifests(rootDir) { const synced = []; for (const config of Object.values(PROVIDERS)) { @@ -688,11 +720,17 @@ async function build() { if (fs.existsSync(skillsSrc)) { // Preserve legacy per-project script artifacts (e.g. live-mode config.json) - // across the rm + recopy. The build intentionally doesn't ship them, - // so without this the sync destroys local state on every rebuild. + // while replacing only skills generated by this build. Removing the + // whole provider skills directory can erase unrelated repo-local skills, + // and watched directories such as `.agents/skills` may reject the parent + // removal while Codex is using them. const stashed = stashPerProjectArtifacts(skillsDest); - if (fs.existsSync(skillsDest)) fs.rmSync(skillsDest, { recursive: true }); - copyDirSync(skillsSrc, skillsDest); + fs.mkdirSync(skillsDest, { recursive: true }); + for (const entry of fs.readdirSync(skillsSrc, { withFileTypes: true })) { + const generatedDest = path.join(skillsDest, entry.name); + if (entry.isDirectory()) mirrorDirContentsSync(path.join(skillsSrc, entry.name), generatedDest); + else fs.copyFileSync(path.join(skillsSrc, entry.name), generatedDest); + } restorePerProjectArtifacts(skillsDest, stashed); } } diff --git a/scripts/lib/transformers/factory.js b/scripts/lib/transformers/factory.js index fafc4b06b..78f89eda2 100644 --- a/scripts/lib/transformers/factory.js +++ b/scripts/lib/transformers/factory.js @@ -263,7 +263,7 @@ export function createTransformer(config) { const scriptsOutDir = path.join(skillDir, 'scripts'); ensureDir(scriptsOutDir); for (const script of skill.scripts) { - const scriptContent = replaceScriptProviderMarker(script.content, placeholderKey); + const scriptContent = replaceScriptProviderMarker(script.content, placeholderKey, provider); writeFile(path.join(scriptsOutDir, script.name), scriptContent); scriptCount++; } diff --git a/scripts/lib/utils.js b/scripts/lib/utils.js index 48a4c2c27..2ff493ae5 100644 --- a/scripts/lib/utils.js +++ b/scripts/lib/utils.js @@ -763,12 +763,14 @@ export function replacePlaceholders(content, provider, commandNames = [], allSki * their command prefix from lib/provider.mjs, whose declaration is replaced * here by an exact string match. */ -export function replaceScriptProviderMarker(content, provider) { +export function replaceScriptProviderMarker(content, provider, buildProvider = provider) { const placeholders = PROVIDER_PLACEHOLDERS[provider] || PROVIDER_PLACEHOLDERS.cursor; const commandPrefix = placeholders.command_prefix || '/'; - const marker = "export const IMPECCABLE_COMMAND_PREFIX = '/'; // @impeccable-provider-command-prefix"; - const rendered = `export const IMPECCABLE_COMMAND_PREFIX = ${JSON.stringify(commandPrefix)};`; - return content.replace(marker, rendered); + const prefixMarker = "export const IMPECCABLE_COMMAND_PREFIX = '/'; // @impeccable-provider-command-prefix"; + const providerMarker = "export const IMPECCABLE_PROVIDER_ID = 'source'; // @impeccable-provider-id"; + return content + .replace(prefixMarker, `export const IMPECCABLE_COMMAND_PREFIX = ${JSON.stringify(commandPrefix)};`) + .replace(providerMarker, `export const IMPECCABLE_PROVIDER_ID = ${JSON.stringify(buildProvider)};`); } /** diff --git a/scripts/test-suites.mjs b/scripts/test-suites.mjs index dc0d4f415..f7d295e37 100644 --- a/scripts/test-suites.mjs +++ b/scripts/test-suites.mjs @@ -25,11 +25,11 @@ export const SUITES = { triggers: [ ...COMMON_INFRA_PATTERNS, /^scripts\/(?!benchmark-detector|build-browser-detector|build-extension)/, - /^skill\/(SKILL\.src\.md|agents\/|reference\/|scripts\/(cleanup-deprecated|concept-seed|context|context-signals|critique-storage|design-parser|hook|impeccable-paths|is-generated|lib\/(provider|surface-briefs|target-slug)|pin|surface-brief))/, + /^skill\/(SKILL\.src\.md|agents\/|reference\/|scripts\/(cleanup-deprecated|concept-ingredients|concept-reviews|concept-seed|context|context-signals|critique-storage|design-parser|hook|impeccable-paths|is-generated|lib\/(concept-catalog|provider|surface-briefs|target-slug)|pin|surface-brief|validate-concept-catalog))/, /^site\/(pages|content|components|layouts)\//, /^README(\.npm)?\.md$/, /^cli\/bin\//, - /^tests\/(build|cleanup-deprecated|cli-ignores|concept-seed|context|context-signals|critique-storage|design-parser|docs-integrity|github-sheriff|hook|hook-build|impeccable-paths|openai-plugin|pin|shiki-theme|skills-cli|slop-catalog|surface-brief|target-args|test-suites|theme|windows-path-fix|zip)\.test\.(js|mjs)$/, + /^tests\/(build|cleanup-deprecated|cli-ignores|concept-seed|context|context-signals|critique-storage|design-parser|docs-integrity|github-sheriff|hook|hook-build|impeccable-paths|openai-plugin|pin|shiki-theme|skills-cli|slop-catalog|surface-brief|target-args|test-suites|theme|windows-path-fix|worlds-review-vite-plugin|zip)\.test\.(js|mjs)$/, /^tests\/lib\//, ], commands: [ @@ -71,6 +71,7 @@ export const SUITES = { 'tests/surface-brief.test.mjs', 'tests/test-suites.test.mjs', 'tests/theme.test.mjs', + 'tests/worlds-review-vite-plugin.test.mjs', 'tests/zip.test.mjs', ], }, diff --git a/scripts/worlds-review-vite-plugin.mjs b/scripts/worlds-review-vite-plugin.mjs new file mode 100644 index 000000000..6c7ecdfd2 --- /dev/null +++ b/scripts/worlds-review-vite-plugin.mjs @@ -0,0 +1,148 @@ +import { readFile, rename, writeFile } from 'node:fs/promises'; +import path from 'node:path'; + +const API_PATH = '/__impeccable/worlds'; +const MAX_BODY_BYTES = 64 * 1024; +const REVIEW_STATUSES = new Set(['pending', 'approved', 'rejected']); + +function jsonResponse(res, status, payload) { + res.statusCode = status; + res.setHeader('Content-Type', 'application/json; charset=utf-8'); + res.setHeader('Cache-Control', 'no-store'); + res.end(`${JSON.stringify(payload)}\n`); +} + +function sameOrigin(req) { + const origin = req.headers.origin; + if (!origin) return true; + try { + return new URL(origin).host === req.headers.host; + } catch { + return false; + } +} + +async function readJsonBody(req) { + const chunks = []; + let size = 0; + for await (const chunk of req) { + size += chunk.length; + if (size > MAX_BODY_BYTES) throw new Error('Request body is too large'); + chunks.push(chunk); + } + return JSON.parse(Buffer.concat(chunks).toString('utf8')); +} + +async function readJson(filePath) { + return JSON.parse(await readFile(filePath, 'utf8')); +} + +async function writeJsonAtomic(filePath, value) { + const temporaryPath = `${filePath}.${process.pid}.${Date.now()}.tmp`; + await writeFile(temporaryPath, `${JSON.stringify(value, null, 2)}\n`, 'utf8'); + await rename(temporaryPath, filePath); +} + +function findConcept(catalog, id) { + for (const family of catalog.families || []) { + const index = family.concepts?.findIndex(concept => concept.id === id) ?? -1; + if (index !== -1) return { family, index, concept: family.concepts[index] }; + } + return null; +} + +function validateTags(tags) { + return Array.isArray(tags) + && tags.length === 3 + && tags.every(tag => typeof tag === 'string' && tag.trim().length >= 2 && tag.trim().length <= 40); +} + +export function worldsReviewPlugin({ root = process.cwd() } = {}) { + const catalogPath = path.join(root, 'skill', 'scripts', 'concept-ingredients.json'); + const reviewsPath = path.join(root, 'skill', 'scripts', 'concept-reviews.json'); + let mutationQueue = Promise.resolve(); + + async function mutate(body) { + const catalog = await readJson(catalogPath); + const reviewData = await readJson(reviewsPath); + const match = findConcept(catalog, body.id); + if (!match) throw new Error('Concept was not found'); + + if (body.action === 'review') { + if (!REVIEW_STATUSES.has(body.status)) throw new Error('Review status is invalid'); + if (body.status === 'pending') { + delete reviewData.reviews[body.id]; + } else { + reviewData.reviews[body.id] = { + status: body.status, + reviewedBy: 'pbakaus', + reviewedAt: new Date().toISOString(), + }; + } + reviewData.reviews = Object.fromEntries(Object.entries(reviewData.reviews).sort(([a], [b]) => a.localeCompare(b))); + await writeJsonAtomic(reviewsPath, reviewData); + return { id: body.id, status: body.status, review: reviewData.reviews[body.id] || null }; + } + + if (body.action === 'update') { + const form = typeof body.form === 'string' ? body.form.trim() : ''; + const lineage = typeof body.lineage === 'string' ? body.lineage.trim() : ''; + const targetFamily = catalog.families.find(family => family.id === body.familyId); + if (form.length < 12 || form.length > 600 || !form.includes(',')) { + throw new Error('Form must be 12–600 characters and include inherited structure after a comma'); + } + if (lineage.length < 2 || lineage.length > 160) throw new Error('Lineage must be 2–160 characters'); + if (!validateTags(body.tags)) throw new Error('Exactly three structural tags are required'); + if (!targetFamily) throw new Error('Family was not found'); + + const updated = { + ...match.concept, + form, + lineage, + tags: body.tags.map(tag => tag.trim()), + }; + if (targetFamily.id === match.family.id) { + match.family.concepts[match.index] = updated; + } else { + match.family.concepts.splice(match.index, 1); + targetFamily.concepts.push(updated); + targetFamily.concepts.sort((a, b) => a.id.localeCompare(b.id)); + } + catalog.catalogVersion = new Date().toISOString(); + await writeJsonAtomic(catalogPath, catalog); + return { id: body.id, concept: updated, familyId: targetFamily.id }; + } + + throw new Error('Action is invalid'); + } + + return { + name: 'impeccable-worlds-review', + apply: 'serve', + configureServer(server) { + server.middlewares.use(API_PATH, async (req, res) => { + if (req.method !== 'POST') { + jsonResponse(res, 405, { error: 'Method not allowed' }); + return; + } + if (!sameOrigin(req)) { + jsonResponse(res, 403, { error: 'Cross-origin writes are not allowed' }); + return; + } + if (!String(req.headers['content-type'] || '').startsWith('application/json')) { + jsonResponse(res, 415, { error: 'Expected application/json' }); + return; + } + + try { + const body = await readJsonBody(req); + const operation = mutationQueue.then(() => mutate(body)); + mutationQueue = operation.catch(() => {}); + jsonResponse(res, 200, { ok: true, result: await operation }); + } catch (error) { + jsonResponse(res, 400, { error: error instanceof Error ? error.message : String(error) }); + } + }); + }, + }; +} diff --git a/site/pages/worlds/index.astro b/site/pages/worlds/index.astro new file mode 100644 index 000000000..51f4de690 --- /dev/null +++ b/site/pages/worlds/index.astro @@ -0,0 +1,756 @@ +--- +import fs from 'node:fs'; +import path from 'node:path'; +import Base from '../../layouts/Base.astro'; +import '../../styles/sub-pages.css'; +import '../../styles/worlds-lab.css'; + +const ROOT = process.cwd(); +const catalog = JSON.parse(fs.readFileSync(path.join(ROOT, 'skill', 'scripts', 'concept-ingredients.json'), 'utf8')); +const reviewData = JSON.parse(fs.readFileSync(path.join(ROOT, 'skill', 'scripts', 'concept-reviews.json'), 'utf8')); +const reviews = reviewData.reviews || {}; +const families = catalog.families.map(family => ({ + id: family.id, + label: family.label, + description: family.description, + concepts: family.concepts.map(concept => ({ + ...concept, + familyId: family.id, + familyLabel: family.label, + status: reviews[concept.id]?.status || 'pending', + review: reviews[concept.id] || null, + })), +})); +const concepts = families.flatMap(family => family.concepts); +const initialConcept = concepts.find(concept => concept.status === 'pending') || concepts[0]; +const counts = { + total: concepts.length, + approved: concepts.filter(concept => concept.status === 'approved').length, + pending: concepts.filter(concept => concept.status === 'pending').length, + rejected: concepts.filter(concept => concept.status === 'rejected').length, +}; +counts.reviewed = counts.approved + counts.rejected; +const pageData = JSON.stringify({ + catalogVersion: catalog.catalogVersion, + canWrite: import.meta.env.DEV, + families, +}).replace(/ + +
+ + + +
+
+ +
+ +
+ {import.meta.env.DEV ? 'Source writing active' : 'Read-only catalog'} + {import.meta.env.DEV ? 'Changes persist to skill/scripts' : 'Run bun run dev to review'} +
+
+
+ +
+ + +
+
+
+ {initialConcept.familyLabel} + + {initialConcept.id} +
+ {initialConcept.status} +
+ +
+ +
+

Structural challenger

+

{initialConcept.form}

+
+ {initialConcept.tags.map(tag => {tag})} +
+
+
+ + + + + +
+
+
+ +

{import.meta.env.DEV ? 'Decisions write to concept-reviews.json. Review shortcuts require Option.' : 'Review actions are available on the local development server.'}

+
+ +
+
+ + + +
+
+
+
+
+
+ + + + + + diff --git a/site/styles/worlds-lab.css b/site/styles/worlds-lab.css new file mode 100644 index 000000000..868b190fe --- /dev/null +++ b/site/styles/worlds-lab.css @@ -0,0 +1,1200 @@ +/* + * World Catalog — public browser + local source-writing review workbench. + * The fixed rail follows Detector Lab; the review stage is purpose-built for + * one-at-a-time human judgment over a very large challenger corpus. + */ + +.worlds-lab-page { + background: var(--ks-lacquer); + color: var(--ks-text); + font-family: var(--ks-font); + overflow: hidden; +} + +.worlds-lab-page [hidden] { + display: none !important; +} + +.worlds-main-shell, +.worlds-workbench { + min-height: 0; + height: 100vh; +} + +.worlds-workbench { + --worlds-rail-width: 292px; + display: grid; + grid-template-columns: var(--worlds-rail-width) minmax(0, 1fr); + background: var(--ks-lacquer); +} + +.worlds-skip-links { + position: fixed; + top: var(--spacing-xs); + left: var(--spacing-xs); + z-index: 100; + display: flex; + gap: 4px; + transform: translateY(calc(-100% - var(--spacing-md))); +} + +.worlds-skip-links:focus-within { + transform: translateY(0); +} + +.worlds-skip-links a { + min-height: 44px; + padding: 12px var(--spacing-sm); + border: 1px solid var(--ks-kinpaku); + border-radius: 3px; + background: var(--ks-lacquer-deep); + color: var(--ks-kinpaku); + font: 600 var(--ks-type-mono-size)/1.2 var(--ks-font); + text-decoration: none; +} + +.worlds-family-rail { + position: fixed; + inset: 0 auto 0 0; + z-index: 10; + display: flex; + flex-direction: column; + width: var(--worlds-rail-width); + border-right: 1px solid var(--ks-rule); + background: var(--ks-lacquer-deep); +} + +.worlds-rail-head { + flex: 0 0 auto; + padding: var(--spacing-md); + border-bottom: 1px solid var(--ks-rule); +} + +.worlds-home-link { + color: var(--ks-kinpaku); + text-decoration: none; +} + +.worlds-home-link .ks-mark { + width: 30px; + height: 30px; +} + +.worlds-home-link .ks-mark svg { + width: 26px; + height: 26px; +} + +.worlds-home-link .ks-wordmark { + font-size: var(--ks-type-wordmark-size); + letter-spacing: 0.16em; +} + +.worlds-title-lockup { + margin-top: var(--spacing-md); +} + +.worlds-title-lockup p, +.worlds-concept-kicker, +.worlds-queue-head p, +.worlds-context-panel p, +.worlds-edit-form label > span, +.worlds-edit-form legend { + color: var(--ks-text-muted); + font-family: var(--ks-mono); + font-size: var(--ks-type-eyebrow-size); + font-weight: 500; + letter-spacing: var(--ks-type-eyebrow-track); + text-transform: uppercase; +} + +.worlds-title-lockup h1 { + margin-top: 4px; + color: var(--ks-champagne); + font-size: var(--ks-type-title-size); + font-weight: 600; + line-height: var(--ks-type-title-line); +} + +.worlds-progress-block { + margin-top: var(--spacing-md); +} + +.worlds-progress-copy { + display: flex; + align-items: baseline; + justify-content: space-between; + gap: var(--spacing-sm); + color: var(--ks-text-muted); + font-size: var(--ks-type-mono-size); +} + +.worlds-progress-copy strong { + color: var(--ks-patina); + font-family: var(--ks-mono); +} + +.worlds-progress-track { + height: 3px; + margin-top: var(--spacing-xs); + overflow: hidden; + background: var(--ks-graphite-2); +} + +.worlds-progress-track span { + display: block; + width: var(--worlds-progress); + height: 100%; + background: var(--ks-patina); +} + +.worlds-count-grid { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + margin-top: var(--spacing-sm); + border-top: 1px solid var(--ks-rule); + border-bottom: 1px solid var(--ks-rule); +} + +.worlds-count-grid div { + min-width: 0; + padding: 10px 5px; + border-right: 1px solid var(--ks-rule); +} + +.worlds-count-grid div:nth-child(2n) { + border-right: 0; +} + +.worlds-count-grid div:nth-child(-n + 2) { + border-bottom: 1px solid var(--ks-rule); +} + +.worlds-count-grid dt { + overflow: hidden; + color: var(--ks-text-faint); + font-family: var(--ks-mono); + font-size: var(--ks-type-eyebrow-size); + text-overflow: ellipsis; + white-space: nowrap; +} + +.worlds-count-grid dd { + margin-top: 3px; + color: var(--ks-champagne); + font-family: var(--ks-mono); + font-size: var(--ks-type-mono-size); +} + +.worlds-status-tabs { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 4px; + margin-top: var(--spacing-sm); +} + +.worlds-status-tabs button, +.worlds-family-button { + min-height: 44px; + border: 1px solid transparent; + border-radius: 3px; + background: transparent; + color: var(--ks-text-muted); + font: 500 var(--ks-type-mono-size)/1.25 var(--ks-font); + cursor: pointer; +} + +.worlds-status-tabs button:hover, +.worlds-family-button:hover { + background: var(--ks-graphite); + color: var(--ks-champagne); +} + +.worlds-status-tabs button:focus-visible, +.worlds-family-button:focus-visible, +.worlds-queue-item:focus-visible, +.worlds-review-actions button:focus-visible, +.worlds-context-panel button:focus-visible, +.worlds-queue-foot button:focus-visible, +.worlds-edit-form button:focus-visible, +.worlds-edit-form input:focus-visible, +.worlds-edit-form textarea:focus-visible, +.worlds-edit-form select:focus-visible, +.worlds-search input:focus-visible, +.worlds-family-search input:focus-visible, +.worlds-family-expand:focus-visible, +.worlds-undo:focus-visible, +.worlds-skip-links a:focus-visible { + outline: 2px solid var(--ks-patina); + outline-offset: 2px; +} + +.worlds-status-tabs button[aria-pressed="true"] { + border-color: var(--ks-kinpaku); + background: var(--ks-kinpaku); + color: var(--ks-dark-ink); +} + +.worlds-family-scroll { + min-height: 0; + overflow: auto; + padding: var(--spacing-sm); + scrollbar-color: var(--ks-graphite-2) transparent; +} + +.worlds-family-search { + display: block; + flex: 0 0 auto; + padding: var(--spacing-sm); + border-bottom: 1px solid var(--ks-rule); +} + +.worlds-family-search span { + display: block; + margin-bottom: 6px; + color: var(--ks-text-faint); + font: 500 var(--ks-type-eyebrow-size)/1.2 var(--ks-mono); + letter-spacing: var(--ks-type-eyebrow-track); + text-transform: uppercase; +} + +.worlds-family-search input { + width: 100%; + min-height: 44px; + padding: 0 11px; + border: 1px solid var(--ks-rule); + border-radius: 3px; + background: var(--ks-lacquer-raised); + color: var(--ks-champagne); + font: 500 var(--ks-type-mono-size)/1.2 var(--ks-font); +} + +.worlds-family-search input::placeholder { + color: var(--ks-text-faint); +} + +.worlds-family-button { + display: grid; + grid-template-columns: minmax(0, 1fr) auto; + gap: var(--spacing-sm); + align-items: center; + width: 100%; + padding: 8px 10px; + text-align: left; +} + +.worlds-family-button > span:first-child { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.worlds-family-button > span:last-child { + color: var(--ks-text-faint); + font-family: var(--ks-mono); +} + +.worlds-family-button[aria-pressed="true"] { + border-color: var(--ks-rule); + background: var(--ks-graphite-2); + color: var(--ks-champagne); +} + +.worlds-family-button[aria-pressed="true"] > span:last-child { + color: var(--ks-patina); +} + +.worlds-family-expand { + width: 100%; + min-height: 44px; + margin-top: var(--spacing-xs); + padding: 8px 10px; + border: 1px solid var(--ks-rule); + border-radius: 3px; + background: transparent; + color: var(--ks-kinpaku); + font: 500 var(--ks-type-mono-size)/1.25 var(--ks-font); + text-align: left; + cursor: pointer; +} + +.worlds-family-expand:hover { + background: var(--ks-graphite); +} + +.worlds-family-empty { + padding: 10px; + color: var(--ks-text-faint); + font-size: var(--ks-type-mono-size); +} + +.worlds-workspace { + grid-column: 2; + display: flex; + min-width: 0; + height: 100vh; + flex-direction: column; +} + +.worlds-toolbar { + position: relative; + z-index: 8; + display: flex; + flex: 0 0 auto; + min-height: 72px; + align-items: center; + justify-content: space-between; + gap: var(--spacing-md); + padding: 12px var(--spacing-md); + border-bottom: 1px solid var(--ks-rule); + background: var(--ks-lacquer-deep); +} + +.worlds-search { + position: relative; + display: flex; + width: min(680px, 100%); + align-items: center; +} + +.worlds-search svg { + position: absolute; + left: var(--spacing-sm); + width: 18px; + height: 18px; + color: var(--ks-text-faint); + pointer-events: none; +} + +.worlds-search input { + width: 100%; + min-height: 44px; + padding: 0 48px; + border: 1px solid var(--ks-rule); + border-radius: 4px; + background: var(--ks-lacquer-raised); + color: var(--ks-champagne); + font: 400 var(--ks-type-body-size)/1.2 var(--ks-font); +} + +.worlds-search input::placeholder { + color: var(--ks-text-faint); +} + +.worlds-search kbd { + position: absolute; + right: 10px; +} + +.worlds-runtime-state { + display: flex; + flex: 0 0 auto; + align-items: center; + gap: 10px; +} + +.worlds-runtime-state > span, +.worlds-action-note > span { + width: 7px; + height: 7px; + border-radius: 50%; + background: var(--ks-text-faint); +} + +.worlds-runtime-state > span.is-live, +.worlds-action-note > span.is-live { + background: var(--ks-patina); + box-shadow: 0 0 16px color-mix(in oklch, var(--ks-patina), transparent 62%); +} + +.worlds-runtime-state strong, +.worlds-runtime-state small { + display: block; +} + +.worlds-runtime-state strong { + color: var(--ks-champagne); + font-size: var(--ks-type-mono-size); + font-weight: 600; +} + +.worlds-runtime-state small { + margin-top: 2px; + color: var(--ks-text-faint); + font-size: var(--ks-type-eyebrow-size); +} + +.worlds-workspace-grid { + display: grid; + grid-template-columns: 332px minmax(0, 1fr); + min-height: 0; + flex: 1 1 auto; +} + +.worlds-queue { + display: flex; + min-width: 0; + min-height: 0; + flex-direction: column; + border-right: 1px solid var(--ks-rule); + background: var(--ks-lacquer-raised); +} + +.worlds-queue-head, +.worlds-queue-foot { + display: flex; + flex: 0 0 auto; + align-items: center; + justify-content: space-between; + gap: var(--spacing-sm); + min-height: 64px; + padding: 10px var(--spacing-sm); + border-bottom: 1px solid var(--ks-rule); +} + +.worlds-queue-head strong { + display: block; + margin-top: 3px; + color: var(--ks-champagne); + font-size: var(--ks-type-title-size); + font-weight: 600; +} + +.worlds-queue-head > span { + color: var(--ks-patina); + font-family: var(--ks-mono); + font-size: var(--ks-type-mono-size); +} + +.worlds-queue-list { + min-height: 0; + overflow: auto; + flex: 1 1 auto; + scrollbar-color: var(--ks-graphite-2) transparent; +} + +.worlds-queue-item { + display: grid; + grid-template-columns: 30px minmax(0, 1fr) 8px; + gap: 10px; + align-items: center; + width: 100%; + min-height: 58px; + padding: 8px var(--spacing-sm); + border: 0; + border-bottom: 1px solid var(--ks-rule); + background: transparent; + color: var(--ks-text-muted); + font-family: var(--ks-font); + text-align: left; + cursor: pointer; +} + +.worlds-queue-item:hover { + background: var(--ks-graphite); +} + +.worlds-queue-item[aria-pressed="true"] { + background: var(--ks-graphite-2); + box-shadow: inset 3px 0 0 var(--ks-kinpaku); +} + +.worlds-queue-item > span:first-child { + color: var(--ks-text-faint); + font-family: var(--ks-mono); + font-size: var(--ks-type-eyebrow-size); +} + +.worlds-queue-item small, +.worlds-queue-item strong { + display: block; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.worlds-queue-item small { + color: var(--ks-text-faint); + font-size: var(--ks-type-eyebrow-size); +} + +.worlds-queue-item strong { + margin-top: 3px; + color: var(--ks-text); + font-size: var(--ks-type-mono-size); + font-weight: 500; +} + +.worlds-queue-item i { + width: 7px; + height: 7px; + border-radius: 50%; + background: var(--ks-text-faint); +} + +.worlds-queue-item i.is-approved { background: var(--ks-patina); } +.worlds-queue-item i.is-rejected { background: var(--ks-vermilion); } +.worlds-queue-item i.is-pending { background: var(--ks-kinpaku-deep); } + +.worlds-queue-empty { + padding: var(--spacing-lg) var(--spacing-sm); + color: var(--ks-text-muted); +} + +.worlds-queue-empty strong { + color: var(--ks-champagne); + font-size: var(--ks-type-title-size); +} + +.worlds-queue-empty p { + margin-top: var(--spacing-xs); + line-height: var(--ks-type-body-line); +} + +.worlds-queue-foot { + min-height: 50px; + border-top: 1px solid var(--ks-rule); + border-bottom: 0; + color: var(--ks-text-faint); + font-family: var(--ks-mono); + font-size: var(--ks-type-eyebrow-size); +} + +.worlds-queue-foot div { + display: flex; + gap: 4px; +} + +.worlds-queue-foot button { + width: 44px; + height: 44px; + border: 1px solid var(--ks-rule); + border-radius: 3px; + background: var(--ks-graphite); + color: var(--ks-text); + cursor: pointer; +} + +.worlds-queue-foot button:disabled { + color: var(--ks-text-mute-deep); + cursor: not-allowed; +} + +kbd { + display: inline-flex; + min-width: 20px; + min-height: 20px; + align-items: center; + justify-content: center; + border: 1px solid var(--ks-rule); + border-radius: 3px; + background: var(--ks-graphite-2); + color: currentColor; + font: 500 var(--ks-type-eyebrow-size)/1 var(--ks-mono); +} + +.worlds-review-stage { + position: relative; + display: grid; + grid-template-columns: minmax(0, 1fr); + grid-template-rows: auto minmax(0, 1fr) auto; + min-width: 0; + min-height: 0; + overflow: hidden; + background: var(--ks-lacquer); +} + +.worlds-review-stage[aria-busy="true"] .worlds-review-actions { + background: var(--ks-graphite); +} + +.worlds-review-head { + position: relative; + z-index: 2; + display: flex; + min-height: 58px; + align-items: center; + justify-content: space-between; + gap: var(--spacing-md); + padding: 10px var(--spacing-md); + border-bottom: 1px solid var(--ks-rule); + background: color-mix(in oklch, var(--ks-lacquer), transparent 4%); +} + +.worlds-review-path { + display: flex; + min-width: 0; + align-items: center; + gap: var(--spacing-xs); + color: var(--ks-text-faint); + font-size: var(--ks-type-mono-size); +} + +.worlds-review-path span:first-child { + color: var(--ks-text-muted); +} + +.worlds-review-path code { + overflow: hidden; + color: var(--ks-text-faint); + font-family: var(--ks-mono); + text-overflow: ellipsis; + white-space: nowrap; +} + +.worlds-status-badge { + flex: 0 0 auto; + padding: 5px 9px; + border: 1px solid var(--ks-rule); + border-radius: 999px; + color: var(--ks-text-muted); + font: 500 var(--ks-type-eyebrow-size)/1 var(--ks-mono); + letter-spacing: 0.08em; + text-transform: uppercase; +} + +.worlds-status-badge.is-approved { + border-color: color-mix(in oklch, var(--ks-patina), transparent 54%); + color: var(--ks-patina); +} + +.worlds-status-badge.is-rejected { + border-color: color-mix(in oklch, var(--ks-vermilion), transparent 44%); + color: var(--ks-vermilion); +} + +.worlds-status-badge.is-pending { + border-color: color-mix(in oklch, var(--ks-kinpaku), transparent 58%); + color: var(--ks-kinpaku); +} + +.worlds-concept-body { + position: relative; + display: grid; + grid-template-columns: 64px minmax(0, 1fr) 260px; + gap: var(--spacing-lg); + min-height: 0; + overflow: auto; + align-items: center; + padding: clamp(40px, 8vh, 104px) clamp(32px, 6vw, 96px); +} + +.worlds-concept-index { + align-self: stretch; + display: flex; + flex-direction: column; + align-items: center; + gap: 10px; + color: var(--ks-text-faint); + font: 500 var(--ks-type-eyebrow-size)/1 var(--ks-mono); + writing-mode: vertical-rl; +} + +.worlds-concept-index i { + width: 1px; + flex: 1 1 auto; + background: linear-gradient(var(--ks-kinpaku), transparent); +} + +.worlds-concept-index strong { + color: var(--ks-kinpaku); + font: inherit; +} + +.worlds-concept-copy { + min-width: 0; + max-width: 900px; +} + +.worlds-concept-kicker { + color: var(--ks-kinpaku); +} + +.worlds-concept-copy h2 { + max-width: 22ch; + margin-top: var(--spacing-sm); + color: var(--ks-champagne); + font-family: var(--ks-font); + font-size: var(--ks-type-headline-size); + font-weight: 400; + line-height: 1.12; + letter-spacing: -0.02em; + text-wrap: balance; +} + +.worlds-tag-row { + display: flex; + flex-wrap: wrap; + gap: 6px; + margin-top: var(--spacing-lg); +} + +.worlds-tag-row span { + padding: 6px 9px; + border: 1px solid var(--ks-rule); + border-radius: 3px; + background: var(--ks-lacquer-raised); + color: var(--ks-text-muted); + font: 500 var(--ks-type-eyebrow-size)/1 var(--ks-mono); +} + +.worlds-context-panel { + align-self: center; + border-left: 1px solid var(--ks-rule); + background: color-mix(in oklch, var(--ks-lacquer-raised), transparent 8%); +} + +.worlds-context-panel > div { + padding: var(--spacing-sm); + border-bottom: 1px solid var(--ks-rule); +} + +.worlds-context-panel strong { + display: block; + margin-top: var(--spacing-xs); + color: var(--ks-text); + font-size: var(--ks-type-mono-size); + font-weight: 500; + line-height: 1.55; +} + +.worlds-context-panel button { + width: 100%; + min-height: 42px; + border: 0; + background: transparent; + color: var(--ks-kinpaku); + font: 500 var(--ks-type-mono-size)/1 var(--ks-font); + cursor: pointer; +} + +.worlds-context-panel button:hover:not(:disabled) { + background: var(--ks-graphite); +} + +.worlds-context-panel button:disabled { + color: var(--ks-text-mute-deep); + cursor: not-allowed; +} + +.worlds-edit-form { + position: absolute; + inset: 58px 0 78px auto; + z-index: 6; + width: min(640px, calc(100% - 24px)); + overflow: auto; + padding: var(--spacing-md); + border-left: 1px solid var(--ks-kinpaku); + background: var(--ks-lacquer-deep); + box-shadow: -24px 0 70px color-mix(in oklch, var(--ks-lacquer-deep), transparent 18%); +} + +.worlds-edit-form[hidden] { + display: none; +} + +.worlds-edit-form header, +.worlds-edit-form footer, +.worlds-edit-row { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: var(--spacing-sm); +} + +.worlds-edit-form header { + padding-bottom: var(--spacing-md); + border-bottom: 1px solid var(--ks-rule); +} + +.worlds-edit-form header p { + color: var(--ks-champagne); + font-size: var(--ks-type-title-size); + font-weight: 600; +} + +.worlds-edit-form header strong { + display: block; + margin-top: 4px; + color: var(--ks-text-faint); + font-size: var(--ks-type-mono-size); + font-weight: 400; +} + +.worlds-edit-form header button { + width: 34px; + height: 34px; + border: 1px solid var(--ks-rule); + border-radius: 3px; + background: transparent; + color: var(--ks-text); + font-size: var(--ks-type-title-size); + cursor: pointer; +} + +.worlds-edit-form > label, +.worlds-edit-form fieldset, +.worlds-edit-row { + margin-top: var(--spacing-md); +} + +.worlds-edit-form label { + display: block; + min-width: 0; + flex: 1 1 0; +} + +.worlds-edit-form input, +.worlds-edit-form textarea, +.worlds-edit-form select { + width: 100%; + margin-top: var(--spacing-xs); + border: 1px solid var(--ks-rule); + border-radius: 4px; + background: var(--ks-lacquer-raised); + color: var(--ks-text); + font: 400 var(--ks-type-body-size)/1.55 var(--ks-font); +} + +.worlds-edit-form input, +.worlds-edit-form select { + min-height: 42px; + padding: 0 12px; +} + +.worlds-edit-form textarea { + min-height: 142px; + resize: vertical; + padding: 12px; +} + +.worlds-edit-form fieldset { + padding: 0; + border: 0; +} + +.worlds-edit-row.is-tags input { + margin-top: 0; +} + +.worlds-edit-form footer { + justify-content: flex-end; + margin-top: var(--spacing-lg); + padding-top: var(--spacing-md); + border-top: 1px solid var(--ks-rule); +} + +.worlds-button-secondary, +.worlds-button-primary { + min-height: 42px; + padding: 0 18px; + border-radius: 3px; + font: 600 var(--ks-type-mono-size)/1 var(--ks-font); + cursor: pointer; +} + +.worlds-button-secondary { + border: 1px solid var(--ks-rule); + background: transparent; + color: var(--ks-text); +} + +.worlds-button-primary { + border: 1px solid var(--ks-kinpaku); + background: var(--ks-kinpaku); + color: var(--ks-dark-ink); +} + +.worlds-review-actions { + position: relative; + z-index: 3; + display: flex; + min-height: 78px; + align-items: center; + justify-content: space-between; + gap: var(--spacing-md); + padding: 12px var(--spacing-md); + border-top: 1px solid var(--ks-rule); + background: var(--ks-lacquer-deep); +} + +.worlds-action-note { + display: flex; + align-items: center; + gap: 10px; + color: var(--ks-text-faint); + font-size: var(--ks-type-mono-size); +} + +.worlds-action-status { + display: flex; + min-width: 0; + align-items: center; + gap: var(--spacing-sm); +} + +.worlds-undo { + flex: 0 1 auto; + max-width: 360px; +} + +.worlds-undo span { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.worlds-action-buttons { + display: flex; + gap: var(--spacing-xs); +} + +.worlds-review-actions button { + display: inline-flex; + min-height: 44px; + align-items: center; + justify-content: space-between; + gap: var(--spacing-md); + padding: 0 12px 0 16px; + border: 1px solid var(--ks-rule); + border-radius: 3px; + background: var(--ks-lacquer-raised); + color: var(--ks-text); + font: 600 var(--ks-type-mono-size)/1 var(--ks-font); + cursor: pointer; +} + +.worlds-review-actions button:hover:not(:disabled) { + background: var(--ks-graphite-2); +} + +.worlds-review-actions .worlds-action-reject { + color: var(--ks-vermilion); +} + +.worlds-review-actions .worlds-action-approve { + border-color: var(--ks-kinpaku); + background: var(--ks-kinpaku); + color: var(--ks-dark-ink); +} + +.worlds-review-actions .worlds-action-approve:hover:not(:disabled) { + background: var(--ks-kinpaku-pale); +} + +.worlds-review-actions .worlds-action-approve kbd { + border-color: var(--ks-dark-ink); + background: var(--ks-dark-ink); + color: var(--ks-kinpaku); +} + +.worlds-review-actions button.is-current { + box-shadow: inset 0 -3px 0 currentColor; +} + +.worlds-review-actions button:disabled { + border-color: var(--ks-rule); + background: var(--ks-graphite); + color: var(--ks-text-mute-deep); + cursor: not-allowed; +} + +.worlds-toast { + position: fixed; + right: var(--spacing-md); + bottom: var(--spacing-md); + z-index: 40; + max-width: min(420px, calc(100vw - 32px)); + padding: 12px var(--spacing-sm); + border-left: 3px solid var(--ks-patina); + background: var(--ks-graphite-2); + color: var(--ks-champagne); + box-shadow: 0 18px 42px color-mix(in oklch, var(--ks-lacquer-deep), transparent 20%); + font-size: var(--ks-type-mono-size); +} + +.worlds-toast[data-kind="error"] { + border-left-color: var(--ks-vermilion); +} + +@media (max-width: 1500px) { + .worlds-workbench { + --worlds-rail-width: 264px; + } + + .worlds-workspace-grid { + grid-template-columns: 292px minmax(0, 1fr); + } + + .worlds-concept-body { + grid-template-columns: 48px minmax(0, 1fr); + gap: var(--spacing-md); + padding: var(--spacing-lg); + } + + .worlds-concept-copy h2 { + max-width: 26ch; + font-size: calc(var(--ks-type-title-size) * 1.8); + } + + .worlds-context-panel { + grid-column: 1 / -1; + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)) auto; + align-self: start; + border-top: 1px solid var(--ks-rule); + border-left: 0; + } + + .worlds-context-panel > div { + border-right: 1px solid var(--ks-rule); + border-bottom: 0; + } +} + +@media (max-width: 940px) { + .worlds-lab-page { + overflow: auto; + } + + .worlds-main-shell, + .worlds-workbench, + .worlds-workspace { + height: auto; + min-height: 100vh; + } + + .worlds-workbench { + display: block; + } + + .worlds-family-rail { + position: static; + width: auto; + max-height: none; + border-right: 0; + border-bottom: 1px solid var(--ks-rule); + } + + .worlds-family-scroll { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + max-height: 308px; + overflow: auto; + } + + .worlds-family-button { + width: 100%; + } + + .worlds-family-expand, + .worlds-family-empty { + grid-column: 1 / -1; + } + + .worlds-workspace { + display: block; + } + + .worlds-workspace-grid { + grid-template-columns: 280px minmax(0, 1fr); + min-height: 720px; + } +} + +@media (max-width: 700px) { + .worlds-rail-head { + padding: var(--spacing-sm); + } + + .worlds-title-lockup { + margin-top: var(--spacing-sm); + } + + .worlds-progress-block { + display: none; + } + + .worlds-status-tabs { + grid-template-columns: repeat(4, minmax(0, 1fr)); + } + + .worlds-family-scroll { + padding: var(--spacing-xs) var(--spacing-sm); + grid-template-columns: 1fr; + max-height: 264px; + } + + .worlds-family-button { + min-width: 0; + } + + .worlds-toolbar, + .worlds-review-actions { + align-items: stretch; + flex-direction: column; + } + + .worlds-action-status { + align-items: stretch; + flex-direction: column; + } + + .worlds-undo { + width: 100%; + max-width: none; + } + + .worlds-runtime-state { + align-self: flex-start; + } + + .worlds-workspace-grid { + display: block; + min-height: 0; + } + + .worlds-queue { + height: 220px; + border-right: 0; + border-bottom: 1px solid var(--ks-rule); + } + + .worlds-review-stage { + min-height: 720px; + } + + .worlds-concept-body { + display: block; + padding: var(--spacing-lg) var(--spacing-md); + } + + .worlds-concept-index { + display: none; + } + + .worlds-context-panel { + display: block; + margin-top: var(--spacing-lg); + border-top: 1px solid var(--ks-rule); + } + + .worlds-context-panel > div { + border-right: 0; + border-bottom: 1px solid var(--ks-rule); + } + + .worlds-action-buttons, + .worlds-review-actions button { + width: 100%; + } + + .worlds-action-buttons { + display: grid; + grid-template-columns: 1fr; + } + + .worlds-edit-row { + flex-direction: column; + } +} diff --git a/skill/SKILL.src.md b/skill/SKILL.src.md index a22a15152..b77ac9335 100644 --- a/skill/SKILL.src.md +++ b/skill/SKILL.src.md @@ -38,7 +38,7 @@ Choose the mode from the requested surface, not the product, and persist it only ## Craft floor -Build to this floor without announcing it. Run the detector before finishing; fix real defects and narrowly waive intentional exceptions rather than designing for the scanner. +Build to this floor without announcing it. - **Contrast:** body and placeholder text ≥4.5:1; large text ≥3:1. On colored surfaces, tint secondary text from that hue or the foreground instead of using gray. - **Depth:** shadows describe light with offset and soft blur; zero-offset colored halos are decoration. @@ -49,12 +49,14 @@ Build to this floor without announcing it. Run the detector before finishing; fi - **Copy:** use the product's language; controls name their action, errors name the problem and recovery. - **Coverage:** every brief requirement must exist and be findable within seconds. +Before finishing changed UI, follow the quality guidance supplied by `context.mjs` and hooks. Context requests a manual scan only when no automatic detector is active; never add a second detector pass. + Calibration for this provider: - Display tracking stops at -0.04em; -0.02 to -0.03em is usually enough. - Declare elevation once: border or shadow, not both as decoration. Keep container radii modest; reserve pills for small controls. -- Use real illustration or none. Treat backgrounds as surfaces, add texture only from the subject's world, and make specific claims without meta-commentary. +- Use real illustration or none. Treat backgrounds as surfaces and add texture only from the subject's world. Claims, evidence, and configuration come from supplied truth; label illustrative behavior and unresolved values honestly. diff --git a/skill/reference/audit.md b/skill/reference/audit.md index 28bafdf17..f993530da 100644 --- a/skill/reference/audit.md +++ b/skill/reference/audit.md @@ -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 - diff --git a/skill/reference/audit.native.md b/skill/reference/audit.native.md index b79684e86..a880ccfb3 100644 --- a/skill/reference/audit.native.md +++ b/skill/reference/audit.native.md @@ -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) diff --git a/skill/reference/colorize.md b/skill/reference/colorize.md index 14f2b5317..ba536574a 100644 --- a/skill/reference/colorize.md +++ b/skill/reference/colorize.md @@ -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 diff --git a/skill/reference/critique.md b/skill/reference/critique.md index e5ccf472b..968ad9943 100644 --- a/skill/reference/critique.md +++ b/skill/reference/critique.md @@ -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: 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. diff --git a/skill/reference/delight.md b/skill/reference/delight.md index 58c17eb5f..17c154ebe 100644 --- a/skill/reference/delight.md +++ b/skill/reference/delight.md @@ -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 diff --git a/skill/reference/document.md b/skill/reference/document.md index eb39f53e5..751baa3ec 100644 --- a/skill/reference/document.md +++ b/skill/reference/document.md @@ -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. diff --git a/skill/reference/hooks.md b/skill/reference/hooks.md index 50713e77a..81c4324a1 100644 --- a/skill/reference/hooks.md +++ b/skill/reference/hooks.md @@ -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. diff --git a/skill/reference/init.md b/skill/reference/init.md index 853648f97..445e13d58 100644 --- a/skill/reference/init.md +++ b/skill/reference/init.md @@ -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. diff --git a/skill/reference/layout.md b/skill/reference/layout.md index da8725a14..fa5db921d 100644 --- a/skill/reference/layout.md +++ b/skill/reference/layout.md @@ -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) * )`: 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. diff --git a/skill/reference/live.md b/skill/reference/live.md index fb3ecf6a4..b8d715dbc 100644 --- a/skill/reference/live.md +++ b/skill/reference/live.md @@ -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. diff --git a/skill/reference/new-work.md b/skill/reference/new-work.md index 6adf4083a..ff4284225 100644 --- a/skill/reference/new-work.md +++ b/skill/reference/new-work.md @@ -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: - -`` - -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: + +`` + +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 ` @@ -112,7 +124,7 @@ The body is concise and contains: Commit `.impeccable/surfaces/.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. -## 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. +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. diff --git a/skill/reference/operate.md b/skill/reference/operate.md index c4a87272d..82c6a98f1 100644 --- a/skill/reference/operate.md +++ b/skill/reference/operate.md @@ -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. - No orchestrated page-load sequences. Product loads into a task; users don't want to watch it load. -## Product bans (on top of the shared absolute bans) +## Product constraints - Decorative motion that doesn't convey state. - Inconsistent component vocabulary across screens. If the "save" button looks different in two places, one is wrong. @@ -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. diff --git a/skill/reference/polish.md b/skill/reference/polish.md index b81f7c94b..8d950db56 100644 --- a/skill/reference/polish.md +++ b/skill/reference/polish.md @@ -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. diff --git a/skill/reference/typeset.md b/skill/reference/typeset.md index 269ea5548..9dcefc436 100644 --- a/skill/reference/typeset.md +++ b/skill/reference/typeset.md @@ -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) * )` 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. diff --git a/skill/scripts/concept-ingredients.json b/skill/scripts/concept-ingredients.json index bd610673f..729c3cafc 100644 --- a/skill/scripts/concept-ingredients.json +++ b/skill/scripts/concept-ingredients.json @@ -1,132 +1,23309 @@ { - "_comment": "Challenger pool for concept-seed.mjs. Each entry is a culturally legible FORM (a thing people already know how to read) that can supply a page's structure, reading order, and component conventions. Sources: original curation + gpt-5.6-sol + gemini-3.5-pro expansion passes (2026-07-14), deduped. FOR PAUL'S CURATION: cut freely; the script samples 3 per run. Keep entries brand-free and subject-agnostic; the structural elements after the comma are what the page inherits.", - "printed_editorial": [ - "a naturalist's field guide, with specimen plates and identification keys", - "a broadsheet newspaper's sports section, with match reports and standings tables", - "a farmer's almanac, with seasonal charts and terse forecasts", - "a classified ads section, with dense typographic categories and bold lead-in keywords", - "a dictionary spread, with alphabetic guide words, nested definitions, and cross-references", - "an encyclopedia double-spread, with a central subject summary, marginal cross-references, and index tags", - "a comic-book page, with panel rhythm, reading gutters, and caption tiers", - "a storyboard sheet, with shot frames, sequence numbers, and action notes", - "a literary journal's table of contents, with author names, abstract snippets, and leader dots", - "a fashion lookbook, with full-bleed imagery, asymmetric text columns, and tiny credits", - "a zine, hand-assembled, with cut-paper collage and typewritten columns", - "a paperback's back cover and front matter, with blurbs and a table of contents", - "a mail-order catalog, with numbered item grids, specification tables, and an order form", - "a theater playbill, with cast hierarchies, act-and-scene structure, and advertising margins", - "a pharmacopoeia monograph, with indications, dosage tables, and contraindications", - "an atlas plate, with indexed regions, layered legends, and coordinate grids" + "schemaVersion": 2, + "catalogVersion": "2026-07-18.2", + "comment": "Human-reviewed challenger forms for concept-seed.mjs. Runtime prompts use only form; metadata supports review, balance, provenance, and validation. Entries remain brand- and subject-agnostic and inherit concrete structure, reading order, or interaction grammar.", + "researchAnchors": [ + "https://awards.ixda.org/about.html", + "https://segd.org/awards/the-segd-global-design-awards/", + "https://goldenpin.org.tw/goldenpin/en/participate/golden-pin-design-award", + "https://www.cooperhewitt.org/national-design-awards/award-categories-2/", + "https://www.moma.org/magazine/articles/813", + "https://www.computerhistory.org/tdih/January/7/", + "https://www.g-mark.org/en/apply/gda/guide/categories", + "https://www.wipo.int/en/web/traditional-knowledge/traditional-cultural-expressions/index" ], - "instruments_panels": [ - "a mission-control status wall, with countdown clocks and go/no-go polls", - "a ship's bridge instrument panel, with engine-order telegraph and compass repeaters", - "a recording studio mixing console, with repeated channel strips, shared buses, and master controls", - "a patch-bay diagram, with source-destination matrices and signal groupings", - "an aircraft preflight checklist, with phased sections, binary confirmations, and hold points", - "an oscilloscope faceplate, with a calibrated grid, overlaid traces, and channel controls", - "a sonar sweep display, with concentric distance rings, sweep lines, and blip markers", - "a weather station's synoptic chart, with fronts, isobars, and station models", - "a seismograph station's drum recorders and event logs", - "a darkroom contact sheet, with thumbnail grids, frame numbers, and grease-pencil selection marks", - "a camera viewfinder overlay, with framing grid lines, exposure bars, and status indicators", - "a medical triage chart, with color-coded priority tiers, vital-sign thresholds, and symptom checklists", - "a hospital patient chart, with a summary header, time-series observations, and alert annotations", - "a periodic table, with categorical families, ordered coordinates, and encoded properties" - ], - "places_signage": [ - "a metro system's map and station signage, with colored line routes, transfer nodes, and zone boundaries", - "a rail-platform sign system, with directional bands, stop sequences, and numbered exits", - "an airport departures board, with flip-dot rows, gate status alerts, and scheduled timelines", - "a motorway sign gantry, with lane-aligned choices, route shields, and distance cues", - "an emergency evacuation plan, with a fixed you-are-here point, branching routes, and priority exits", - "a theater's lobby cards and marquee, with cast lists and act summaries", - "a race circuit's pit wall timing screens, with sector splits and tyre stints", - "a national park trailhead kiosk, with route markers, difficulty grades, and safety notices", - "a botanical garden trail map, with legend symbols, path grades, and specimen stations", - "a museum gallery directory, with floor-plan shapes, wing names, and room-by-room listings", - "a harbor's tide table board and small-craft advisories", - "a pilgrimage route map, with staged waypoints, distance intervals, and destination seals", - "a parking garage level guide, with colored floor zones, bay numbering, and exit arrows", - "a supermarket planogram, with shelf bands, repeated facings, and priority zones" - ], - "records_documents": [ - "a court transcript, with examination, exhibits, and a verdict", - "a legal case file, with a cover index, chronologically tabbed evidence, and disposition stamps", - "a ship's log, with watch entries, positions, and weather remarks", - "an expedition's field notebook, with sketches, measurements, and daily entries", - "a laboratory notebook, with dated experiments, observations, and sign-offs", - "a botanist's herbarium sheet, with mounted specimens, label cards, and collection metadata", - "a passport booklet, with identity front matter, repeated entry fields, and chronological stamps", - "a customs declaration form, with gated sections, coded responses, and signature checkpoints", - "a census questionnaire, with fill-in boxes, branching yes/no paths, and instruction margins", - "a ballot paper, with constrained choices, section-by-section progression, and verification marks", - "a title deed and property survey, with plot boundaries and easements", - "a cadastral survey map, with parcel boundaries, plot numbers, and keyed ownership records", - "a patent folio, with numbered figures, claim hierarchies, and reference labels", - "an industrial standards sheet, with numbered clauses, dimensioned diagrams, and tolerance tables", - "a repair manual's exploded-parts diagrams, with numbered callouts and lookup tables", - "a double-entry ledger, with mirrored debit-credit columns, running balances, and period totals", - "a bank statement, with opening and closing summaries, chronological transactions, and reconciled totals", - "a bank passbook, with ruled entries and teller stamps", - "a utility bill, with historical usage charts, payment-due boxes, and itemized fees", - "a shipping manifest, with grouped consignments, tracking codes, and exception flags", - "a telephone directory, with alphabetic columns, index tabs, and compact locator codes", - "a library card catalog, with standardized records, alphabetical dividers, and linked subject codes" - ], - "broadcast_ephemera": [ - "a teletext service, with page numbers, block graphics, and channel colors", - "a printed TV programme guide, with time grids and circled listings", - "a radio station's program log and request-line cards", - "a vintage radio receiver plate, with slide tuning bands, frequency markings, and signal meters", - "a cinema's projection booth reel-change cue sheets", - "a cinema ticket stub, with seat coordinates, screen numbers, and entry barcodes", - "a vinyl double-album gatefold, with liner notes, track listing, and credits panel", - "a video-rental shop, with hand-labeled cassettes and membership cards", - "a shortwave listener's QSL card collection and frequency schedules", - "a photographic slide carousel and its typed index card" - ], - "commerce_packaging": [ - "a seed packet's front-and-back panels, with sowing instructions and zone tables", - "a mail-order seed catalog, with variety grids and growing-zone tables", - "a hardware store's parts drawers, with bin labels and spec cards", - "a pharmacy prescription label and patient-information leaflet", - "a matchbook and cigar-band graphics, with foil stamping and tiny type", - "a produce market's chalkboard price signs and crate-side stencils", - "a market price board, with commodity rows, live rate changes, and unit legends", - "a restaurant order rail, with time-ordered tickets, station assignments, and completion marks", - "a restaurant order ticket, with seat numbers, modifier checkmarks, and timestamps", - "an auction house catalog, with lot numbers, estimate bands, and condition reports", - "a coupon sheet, with perforated modules, denomination hierarchy, and redemption conditions", - "a recipe card, with an ingredient inventory, ordered steps, and timing checkpoints" - ], - "games_rituals": [ - "a chess annotation sheet, with move pairs and evaluation symbols", - "a tournament bracket, with converging match paths, round-by-round hierarchy, and a single outcome", - "a scorekeeper's baseball scorecard, with position numbers and inning grids", - "a bingo hall's number board and dabbed cards", - "a lotería board, with a numbered image grid, compact labels, and call-and-response progression", - "a tarot spread, with card positions and a reading order", - "a board game's rulebook, with setup diagrams and turn order", - "a crossword page, with grid, clues across and down, and a setter's note", - "a kanban board, with staged columns, movable work cards, and explicit capacity limits", - "a perpetual calendar, with nested time scales, repeating cycles, and movable indicators" - ], - "world_manuscript_forms": [ - "an East Asian handscroll, with continuous lateral progression, scene breaks, and a terminal colophon", - "a Chinese accordion-fold book, with panel-by-panel progression and paired image-text registers", - "a Japanese bento box partition, with distinct compartments and a central main focus", - "a Korean folding screen, with modular vertical panels, panoramic continuity, and a center axis", - "an Indian palm-leaf manuscript, with long horizontal folios, line bands around a binding axis, and leaf numbers", - "a Persian manuscript page, with a central narrative panel, nested marginal commentary, and illuminated thresholds", - "an annotated manuscript folio, with a central text block, marginal glosses, and tiered section markers", - "an Ethiopian codex spread, with facing text columns, color-coded voices, and ornamental dividers", - "a Brazilian cordel chapbook, with a declarative cover, short sequential sections, and illustrated breaks", - "an Aztec tribute codex, with pictorial quantity symbols, item icons, and origin-town signs", - "an Islamic geometric tile system, with interlocking symmetry lines, border courses, and calligraphic bands", - "an African block-print textile grid, with repeating symbolic patterns, border frames, and color blocks", - "a vintage postcard back, with a split-half line, message field, and stamp-and-address boxes", - "a stamp collector's album, with mounted specimens and perforation notes", - "sheet music, with synchronized staves, movement markers, and recurring motifs" + "families": [ + { + "id": "editorial-news-serial", + "label": "Editorial, news & serial", + "description": "Recurring publication systems that establish hierarchy, cadence, comparison, and continuation.", + "concepts": [ + { + "id": "editorial-news-serial-naturalist-s-field-guide", + "form": "a naturalist's field guide, with specimen plates and identification keys", + "lineage": "Editorial, news & serial", + "tags": [ + "reading-order", + "editorial-hierarchy", + "cross-reference" + ] + }, + { + "id": "editorial-news-serial-broadsheet-newspaper-s-sports-section", + "form": "a broadsheet newspaper's sports section, with match reports and standings tables", + "lineage": "Editorial, news & serial", + "tags": [ + "reading-order", + "editorial-hierarchy", + "cross-reference" + ] + }, + { + "id": "editorial-news-serial-farmer-s-almanac", + "form": "a farmer's almanac, with seasonal charts and terse forecasts", + "lineage": "Editorial, news & serial", + "tags": [ + "reading-order", + "editorial-hierarchy", + "cross-reference" + ] + }, + { + "id": "editorial-news-serial-classified-ads-section", + "form": "a classified ads section, with dense typographic categories and bold lead-in keywords", + "lineage": "Editorial, news & serial", + "tags": [ + "reading-order", + "editorial-hierarchy", + "cross-reference" + ] + }, + { + "id": "editorial-news-serial-dictionary-spread", + "form": "a dictionary spread, with alphabetic guide words, nested definitions, and cross-references", + "lineage": "Editorial, news & serial", + "tags": [ + "reading-order", + "editorial-hierarchy", + "cross-reference" + ] + }, + { + "id": "editorial-news-serial-encyclopedia-double-spread", + "form": "an encyclopedia double-spread, with a central subject summary, marginal cross-references, and index tags", + "lineage": "Editorial, news & serial", + "tags": [ + "reading-order", + "editorial-hierarchy", + "cross-reference" + ] + }, + { + "id": "editorial-news-serial-comic-book-page", + "form": "a comic-book page, with panel rhythm, reading gutters, and caption tiers", + "lineage": "Editorial, news & serial", + "tags": [ + "reading-order", + "editorial-hierarchy", + "cross-reference" + ] + }, + { + "id": "editorial-news-serial-storyboard-sheet", + "form": "a storyboard sheet, with shot frames, sequence numbers, and action notes", + "lineage": "Editorial, news & serial", + "tags": [ + "reading-order", + "editorial-hierarchy", + "cross-reference" + ] + }, + { + "id": "editorial-news-serial-literary-journal-s-table-of-contents", + "form": "a literary journal's table of contents, with author names, abstract snippets, and leader dots", + "lineage": "Editorial, news & serial", + "tags": [ + "reading-order", + "editorial-hierarchy", + "cross-reference" + ] + }, + { + "id": "editorial-news-serial-fashion-lookbook", + "form": "a fashion lookbook, with full-bleed imagery, asymmetric text columns, and tiny credits", + "lineage": "Editorial, news & serial", + "tags": [ + "reading-order", + "editorial-hierarchy", + "cross-reference" + ] + }, + { + "id": "editorial-news-serial-zine", + "form": "a zine, hand-assembled, with cut-paper collage and typewritten columns", + "lineage": "Editorial, news & serial", + "tags": [ + "reading-order", + "editorial-hierarchy", + "cross-reference" + ] + }, + { + "id": "editorial-news-serial-paperback-s-back-cover-and-front-matter", + "form": "a paperback's back cover and front matter, with blurbs and a table of contents", + "lineage": "Editorial, news & serial", + "tags": [ + "reading-order", + "editorial-hierarchy", + "cross-reference" + ] + }, + { + "id": "editorial-news-serial-mail-order-catalog", + "form": "a mail-order catalog, with numbered item grids, specification tables, and an order form", + "lineage": "Editorial, news & serial", + "tags": [ + "reading-order", + "editorial-hierarchy", + "cross-reference" + ] + }, + { + "id": "editorial-news-serial-theater-playbill", + "form": "a theater playbill, with cast hierarchies, act-and-scene structure, and advertising margins", + "lineage": "Editorial, news & serial", + "tags": [ + "reading-order", + "editorial-hierarchy", + "cross-reference" + ] + }, + { + "id": "editorial-news-serial-pharmacopoeia-monograph", + "form": "a pharmacopoeia monograph, with indications, dosage tables, and contraindications", + "lineage": "Editorial, news & serial", + "tags": [ + "reading-order", + "editorial-hierarchy", + "cross-reference" + ] + }, + { + "id": "editorial-news-serial-atlas-plate", + "form": "an atlas plate, with indexed regions, layered legends, and coordinate grids", + "lineage": "Editorial, news & serial", + "tags": [ + "reading-order", + "editorial-hierarchy", + "cross-reference" + ] + }, + { + "id": "editorial-news-serial-broadsheet-newspaper-front-page", + "form": "broadsheet newspaper front page, a fixed masthead zone; descending headline hierarchy; modular reading columns", + "lineage": "Industrial-era newspaper publishing", + "tags": [ + "masthead", + "headline-scale", + "columns" + ] + }, + { + "id": "editorial-news-serial-tabloid-newspaper-cover", + "form": "tabloid newspaper cover, a fixed masthead zone; descending headline hierarchy; modular reading columns", + "lineage": "Industrial-era newspaper publishing", + "tags": [ + "masthead", + "headline-scale", + "columns" + ] + }, + { + "id": "editorial-news-serial-metropolitan-daily-front-page", + "form": "metropolitan daily front page, a fixed masthead zone; descending headline hierarchy; modular reading columns", + "lineage": "Industrial-era newspaper publishing", + "tags": [ + "masthead", + "headline-scale", + "columns" + ] + }, + { + "id": "editorial-news-serial-evening-news-bulletin", + "form": "evening news bulletin, a fixed masthead zone; descending headline hierarchy; modular reading columns", + "lineage": "Industrial-era newspaper publishing", + "tags": [ + "masthead", + "headline-scale", + "columns" + ] + }, + { + "id": "editorial-news-serial-weekend-newspaper-supplement", + "form": "weekend newspaper supplement, a fixed masthead zone; descending headline hierarchy; modular reading columns", + "lineage": "Industrial-era newspaper publishing", + "tags": [ + "masthead", + "headline-scale", + "columns" + ] + }, + { + "id": "editorial-news-serial-financial-daily-market-page", + "form": "financial daily market page, a fixed masthead zone; descending headline hierarchy; modular reading columns", + "lineage": "Industrial-era newspaper publishing", + "tags": [ + "masthead", + "headline-scale", + "columns" + ] + }, + { + "id": "editorial-news-serial-local-weekly-newspaper", + "form": "local weekly newspaper, a fixed masthead zone; descending headline hierarchy; modular reading columns", + "lineage": "Industrial-era newspaper publishing", + "tags": [ + "masthead", + "headline-scale", + "columns" + ] + }, + { + "id": "editorial-news-serial-neighborhood-gazette", + "form": "neighborhood gazette, a fixed masthead zone; descending headline hierarchy; modular reading columns", + "lineage": "Industrial-era newspaper publishing", + "tags": [ + "masthead", + "headline-scale", + "columns" + ] + }, + { + "id": "editorial-news-serial-trade-newspaper-front-page", + "form": "trade newspaper front page, a fixed masthead zone; descending headline hierarchy; modular reading columns", + "lineage": "Industrial-era newspaper publishing", + "tags": [ + "masthead", + "headline-scale", + "columns" + ] + }, + { + "id": "editorial-news-serial-student-newspaper-issue", + "form": "student newspaper issue, a fixed masthead zone; descending headline hierarchy; modular reading columns", + "lineage": "Industrial-era newspaper publishing", + "tags": [ + "masthead", + "headline-scale", + "columns" + ] + }, + { + "id": "editorial-news-serial-multilingual-daily-edition", + "form": "multilingual daily edition, a fixed masthead zone; descending headline hierarchy; modular reading columns", + "lineage": "Industrial-era newspaper publishing", + "tags": [ + "masthead", + "headline-scale", + "columns" + ] + }, + { + "id": "editorial-news-serial-newspaper-special-edition", + "form": "newspaper special edition, a fixed masthead zone; descending headline hierarchy; modular reading columns", + "lineage": "Industrial-era newspaper publishing", + "tags": [ + "masthead", + "headline-scale", + "columns" + ] + }, + { + "id": "editorial-news-serial-newsweekly-magazine-cover", + "form": "newsweekly magazine cover, issue and volume metadata; recurring department architecture; paced spread-to-spread reveals", + "lineage": "Twentieth-century periodical publishing", + "tags": [ + "issue-meta", + "departments", + "paced-spreads" + ] + }, + { + "id": "editorial-news-serial-cultural-review-contents-page", + "form": "cultural review contents page, issue and volume metadata; recurring department architecture; paced spread-to-spread reveals", + "lineage": "Twentieth-century periodical publishing", + "tags": [ + "issue-meta", + "departments", + "paced-spreads" + ] + }, + { + "id": "editorial-news-serial-fashion-editorial-opener", + "form": "fashion editorial opener, issue and volume metadata; recurring department architecture; paced spread-to-spread reveals", + "lineage": "Twentieth-century periodical publishing", + "tags": [ + "issue-meta", + "departments", + "paced-spreads" + ] + }, + { + "id": "editorial-news-serial-science-periodical-spread", + "form": "science periodical spread, issue and volume metadata; recurring department architecture; paced spread-to-spread reveals", + "lineage": "Twentieth-century periodical publishing", + "tags": [ + "issue-meta", + "departments", + "paced-spreads" + ] + }, + { + "id": "editorial-news-serial-literary-quarterly-folio", + "form": "literary quarterly folio, issue and volume metadata; recurring department architecture; paced spread-to-spread reveals", + "lineage": "Twentieth-century periodical publishing", + "tags": [ + "issue-meta", + "departments", + "paced-spreads" + ] + }, + { + "id": "editorial-news-serial-music-magazine-feature", + "form": "music magazine feature, issue and volume metadata; recurring department architecture; paced spread-to-spread reveals", + "lineage": "Twentieth-century periodical publishing", + "tags": [ + "issue-meta", + "departments", + "paced-spreads" + ] + }, + { + "id": "editorial-news-serial-sports-magazine-scorecard", + "form": "sports magazine scorecard, issue and volume metadata; recurring department architecture; paced spread-to-spread reveals", + "lineage": "Twentieth-century periodical publishing", + "tags": [ + "issue-meta", + "departments", + "paced-spreads" + ] + }, + { + "id": "editorial-news-serial-travel-magazine-department-page", + "form": "travel magazine department page, issue and volume metadata; recurring department architecture; paced spread-to-spread reveals", + "lineage": "Twentieth-century periodical publishing", + "tags": [ + "issue-meta", + "departments", + "paced-spreads" + ] + }, + { + "id": "editorial-news-serial-architecture-journal-case-study", + "form": "architecture journal case study, issue and volume metadata; recurring department architecture; paced spread-to-spread reveals", + "lineage": "Twentieth-century periodical publishing", + "tags": [ + "issue-meta", + "departments", + "paced-spreads" + ] + }, + { + "id": "editorial-news-serial-children-s-magazine-activity-spread", + "form": "children's magazine activity spread, issue and volume metadata; recurring department architecture; paced spread-to-spread reveals", + "lineage": "Twentieth-century periodical publishing", + "tags": [ + "issue-meta", + "departments", + "paced-spreads" + ] + }, + { + "id": "editorial-news-serial-photojournalism-picture-essay", + "form": "photojournalism picture essay, issue and volume metadata; recurring department architecture; paced spread-to-spread reveals", + "lineage": "Twentieth-century periodical publishing", + "tags": [ + "issue-meta", + "departments", + "paced-spreads" + ] + }, + { + "id": "editorial-news-serial-independent-zine-centerfold", + "form": "independent zine centerfold, issue and volume metadata; recurring department architecture; paced spread-to-spread reveals", + "lineage": "Twentieth-century periodical publishing", + "tags": [ + "issue-meta", + "departments", + "paced-spreads" + ] + }, + { + "id": "editorial-news-serial-serialized-novel-installment", + "form": "serialized novel installment, continuation cues between installments; recurring content slots; issue-to-issue memory markers", + "lineage": "Serial print and broadcast listings", + "tags": [ + "continuation", + "recurring-slots", + "serial-memory" + ] + }, + { + "id": "editorial-news-serial-newspaper-comic-strip-page", + "form": "newspaper comic strip page, continuation cues between installments; recurring content slots; issue-to-issue memory markers", + "lineage": "Serial print and broadcast listings", + "tags": [ + "continuation", + "recurring-slots", + "serial-memory" + ] + }, + { + "id": "editorial-news-serial-feuilleton-column", + "form": "feuilleton column, continuation cues between installments; recurring content slots; issue-to-issue memory markers", + "lineage": "Serial print and broadcast listings", + "tags": [ + "continuation", + "recurring-slots", + "serial-memory" + ] + }, + { + "id": "editorial-news-serial-sunday-comics-section", + "form": "Sunday comics section, continuation cues between installments; recurring content slots; issue-to-issue memory markers", + "lineage": "Serial print and broadcast listings", + "tags": [ + "continuation", + "recurring-slots", + "serial-memory" + ] + }, + { + "id": "editorial-news-serial-radio-listings-magazine", + "form": "radio listings magazine, continuation cues between installments; recurring content slots; issue-to-issue memory markers", + "lineage": "Serial print and broadcast listings", + "tags": [ + "continuation", + "recurring-slots", + "serial-memory" + ] + }, + { + "id": "editorial-news-serial-television-guide-grid", + "form": "television guide grid, continuation cues between installments; recurring content slots; issue-to-issue memory markers", + "lineage": "Serial print and broadcast listings", + "tags": [ + "continuation", + "recurring-slots", + "serial-memory" + ] + }, + { + "id": "editorial-news-serial-horse-racing-form", + "form": "horse-racing form, continuation cues between installments; recurring content slots; issue-to-issue memory markers", + "lineage": "Serial print and broadcast listings", + "tags": [ + "continuation", + "recurring-slots", + "serial-memory" + ] + }, + { + "id": "editorial-news-serial-annual-almanac-calendar-leaf", + "form": "annual almanac calendar leaf, continuation cues between installments; recurring content slots; issue-to-issue memory markers", + "lineage": "Serial print and broadcast listings", + "tags": [ + "continuation", + "recurring-slots", + "serial-memory" + ] + }, + { + "id": "editorial-news-serial-partwork-encyclopedia-issue", + "form": "partwork encyclopedia issue, continuation cues between installments; recurring content slots; issue-to-issue memory markers", + "lineage": "Serial print and broadcast listings", + "tags": [ + "continuation", + "recurring-slots", + "serial-memory" + ] + }, + { + "id": "editorial-news-serial-subscription-newsletter-issue", + "form": "subscription newsletter issue, continuation cues between installments; recurring content slots; issue-to-issue memory markers", + "lineage": "Serial print and broadcast listings", + "tags": [ + "continuation", + "recurring-slots", + "serial-memory" + ] + }, + { + "id": "editorial-news-serial-correspondence-course-lesson", + "form": "correspondence course lesson, continuation cues between installments; recurring content slots; issue-to-issue memory markers", + "lineage": "Serial print and broadcast listings", + "tags": [ + "continuation", + "recurring-slots", + "serial-memory" + ] + }, + { + "id": "editorial-news-serial-serial-photo-essay-installment", + "form": "serial photo essay installment, continuation cues between installments; recurring content slots; issue-to-issue memory markers", + "lineage": "Serial print and broadcast listings", + "tags": [ + "continuation", + "recurring-slots", + "serial-memory" + ] + }, + { + "id": "editorial-news-serial-teleprinter-news-sheet", + "form": "teleprinter news sheet, timestamped update blocks; priority-coded story ordering; rapidly replaceable information zones", + "lineage": "Newsroom and broadcast operations", + "tags": [ + "timestamps", + "priority-code", + "replaceable-zones" + ] + }, + { + "id": "editorial-news-serial-newswire-terminal-feed", + "form": "newswire terminal feed, timestamped update blocks; priority-coded story ordering; rapidly replaceable information zones", + "lineage": "Newsroom and broadcast operations", + "tags": [ + "timestamps", + "priority-code", + "replaceable-zones" + ] + }, + { + "id": "editorial-news-serial-radio-news-rundown", + "form": "radio news rundown, timestamped update blocks; priority-coded story ordering; rapidly replaceable information zones", + "lineage": "Newsroom and broadcast operations", + "tags": [ + "timestamps", + "priority-code", + "replaceable-zones" + ] + }, + { + "id": "editorial-news-serial-television-lower-third-package", + "form": "television lower-third package, timestamped update blocks; priority-coded story ordering; rapidly replaceable information zones", + "lineage": "Newsroom and broadcast operations", + "tags": [ + "timestamps", + "priority-code", + "replaceable-zones" + ] + }, + { + "id": "editorial-news-serial-election-night-results-board", + "form": "election-night results board, timestamped update blocks; priority-coded story ordering; rapidly replaceable information zones", + "lineage": "Newsroom and broadcast operations", + "tags": [ + "timestamps", + "priority-code", + "replaceable-zones" + ] + }, + { + "id": "editorial-news-serial-weather-bulletin-map", + "form": "weather bulletin map, timestamped update blocks; priority-coded story ordering; rapidly replaceable information zones", + "lineage": "Newsroom and broadcast operations", + "tags": [ + "timestamps", + "priority-code", + "replaceable-zones" + ] + }, + { + "id": "editorial-news-serial-emergency-broadcast-card", + "form": "emergency broadcast card, timestamped update blocks; priority-coded story ordering; rapidly replaceable information zones", + "lineage": "Newsroom and broadcast operations", + "tags": [ + "timestamps", + "priority-code", + "replaceable-zones" + ] + }, + { + "id": "editorial-news-serial-newsroom-assignment-board", + "form": "newsroom assignment board, timestamped update blocks; priority-coded story ordering; rapidly replaceable information zones", + "lineage": "Newsroom and broadcast operations", + "tags": [ + "timestamps", + "priority-code", + "replaceable-zones" + ] + }, + { + "id": "editorial-news-serial-press-briefing-packet", + "form": "press briefing packet, timestamped update blocks; priority-coded story ordering; rapidly replaceable information zones", + "lineage": "Newsroom and broadcast operations", + "tags": [ + "timestamps", + "priority-code", + "replaceable-zones" + ] + }, + { + "id": "editorial-news-serial-court-reporter-docket-sheet", + "form": "court reporter docket sheet, timestamped update blocks; priority-coded story ordering; rapidly replaceable information zones", + "lineage": "Newsroom and broadcast operations", + "tags": [ + "timestamps", + "priority-code", + "replaceable-zones" + ] + }, + { + "id": "editorial-news-serial-parliamentary-press-gallery-note", + "form": "parliamentary press gallery note, timestamped update blocks; priority-coded story ordering; rapidly replaceable information zones", + "lineage": "Newsroom and broadcast operations", + "tags": [ + "timestamps", + "priority-code", + "replaceable-zones" + ] + }, + { + "id": "editorial-news-serial-community-noticeboard-circular", + "form": "community noticeboard circular, timestamped update blocks; priority-coded story ordering; rapidly replaceable information zones", + "lineage": "Newsroom and broadcast operations", + "tags": [ + "timestamps", + "priority-code", + "replaceable-zones" + ] + } + ] + }, + { + "id": "reference-taxonomy", + "label": "Reference & taxonomy", + "description": "Classification, lookup, indexing, and cross-reference systems built for durable understanding.", + "concepts": [ + { + "id": "reference-taxonomy-library-card-catalog-drawer", + "form": "library card-catalog drawer, hierarchical call identifiers; see-and-see-also pointers; stable retrieval order", + "lineage": "Library cataloguing traditions", + "tags": [ + "identifiers", + "cross-references", + "retrieval-order" + ] + }, + { + "id": "reference-taxonomy-author-catalogue-card", + "form": "author catalogue card, hierarchical call identifiers; see-and-see-also pointers; stable retrieval order", + "lineage": "Library cataloguing traditions", + "tags": [ + "identifiers", + "cross-references", + "retrieval-order" + ] + }, + { + "id": "reference-taxonomy-subject-catalogue-card", + "form": "subject catalogue card, hierarchical call identifiers; see-and-see-also pointers; stable retrieval order", + "lineage": "Library cataloguing traditions", + "tags": [ + "identifiers", + "cross-references", + "retrieval-order" + ] + }, + { + "id": "reference-taxonomy-library-shelf-list-card", + "form": "library shelf-list card, hierarchical call identifiers; see-and-see-also pointers; stable retrieval order", + "lineage": "Library cataloguing traditions", + "tags": [ + "identifiers", + "cross-references", + "retrieval-order" + ] + }, + { + "id": "reference-taxonomy-accession-register", + "form": "accession register, hierarchical call identifiers; see-and-see-also pointers; stable retrieval order", + "lineage": "Library cataloguing traditions", + "tags": [ + "identifiers", + "cross-references", + "retrieval-order" + ] + }, + { + "id": "reference-taxonomy-union-catalogue-volume", + "form": "union catalogue volume, hierarchical call identifiers; see-and-see-also pointers; stable retrieval order", + "lineage": "Library cataloguing traditions", + "tags": [ + "identifiers", + "cross-references", + "retrieval-order" + ] + }, + { + "id": "reference-taxonomy-classified-library-catalogue", + "form": "classified library catalogue, hierarchical call identifiers; see-and-see-also pointers; stable retrieval order", + "lineage": "Library cataloguing traditions", + "tags": [ + "identifiers", + "cross-references", + "retrieval-order" + ] + }, + { + "id": "reference-taxonomy-dictionary-catalogue", + "form": "dictionary catalogue, hierarchical call identifiers; see-and-see-also pointers; stable retrieval order", + "lineage": "Library cataloguing traditions", + "tags": [ + "identifiers", + "cross-references", + "retrieval-order" + ] + }, + { + "id": "reference-taxonomy-map-library-index", + "form": "map library index, hierarchical call identifiers; see-and-see-also pointers; stable retrieval order", + "lineage": "Library cataloguing traditions", + "tags": [ + "identifiers", + "cross-references", + "retrieval-order" + ] + }, + { + "id": "reference-taxonomy-music-library-thematic-catalogue", + "form": "music library thematic catalogue, hierarchical call identifiers; see-and-see-also pointers; stable retrieval order", + "lineage": "Library cataloguing traditions", + "tags": [ + "identifiers", + "cross-references", + "retrieval-order" + ] + }, + { + "id": "reference-taxonomy-archival-finding-aid", + "form": "archival finding aid, hierarchical call identifiers; see-and-see-also pointers; stable retrieval order", + "lineage": "Library cataloguing traditions", + "tags": [ + "identifiers", + "cross-references", + "retrieval-order" + ] + }, + { + "id": "reference-taxonomy-periodical-index-volume", + "form": "periodical index volume, hierarchical call identifiers; see-and-see-also pointers; stable retrieval order", + "lineage": "Library cataloguing traditions", + "tags": [ + "identifiers", + "cross-references", + "retrieval-order" + ] + }, + { + "id": "reference-taxonomy-dictionary-entry-page", + "form": "dictionary entry page, prominent lookup headwords; compact definition and annotation layers; alphabetic or semantic adjacency", + "lineage": "Lexicographic and encyclopedic reference", + "tags": [ + "headwords", + "annotations", + "adjacency" + ] + }, + { + "id": "reference-taxonomy-bilingual-lexicon-page", + "form": "bilingual lexicon page, prominent lookup headwords; compact definition and annotation layers; alphabetic or semantic adjacency", + "lineage": "Lexicographic and encyclopedic reference", + "tags": [ + "headwords", + "annotations", + "adjacency" + ] + }, + { + "id": "reference-taxonomy-thesaurus-category-page", + "form": "thesaurus category page, prominent lookup headwords; compact definition and annotation layers; alphabetic or semantic adjacency", + "lineage": "Lexicographic and encyclopedic reference", + "tags": [ + "headwords", + "annotations", + "adjacency" + ] + }, + { + "id": "reference-taxonomy-encyclopedia-illustration-plate", + "form": "encyclopedia illustration plate, prominent lookup headwords; compact definition and annotation layers; alphabetic or semantic adjacency", + "lineage": "Lexicographic and encyclopedic reference", + "tags": [ + "headwords", + "annotations", + "adjacency" + ] + }, + { + "id": "reference-taxonomy-field-guide-entry", + "form": "field guide entry, prominent lookup headwords; compact definition and annotation layers; alphabetic or semantic adjacency", + "lineage": "Lexicographic and encyclopedic reference", + "tags": [ + "headwords", + "annotations", + "adjacency" + ] + }, + { + "id": "reference-taxonomy-gazetteer-entry", + "form": "gazetteer entry, prominent lookup headwords; compact definition and annotation layers; alphabetic or semantic adjacency", + "lineage": "Lexicographic and encyclopedic reference", + "tags": [ + "headwords", + "annotations", + "adjacency" + ] + }, + { + "id": "reference-taxonomy-biographical-dictionary-entry", + "form": "biographical dictionary entry, prominent lookup headwords; compact definition and annotation layers; alphabetic or semantic adjacency", + "lineage": "Lexicographic and encyclopedic reference", + "tags": [ + "headwords", + "annotations", + "adjacency" + ] + }, + { + "id": "reference-taxonomy-technical-glossary-page", + "form": "technical glossary page, prominent lookup headwords; compact definition and annotation layers; alphabetic or semantic adjacency", + "lineage": "Lexicographic and encyclopedic reference", + "tags": [ + "headwords", + "annotations", + "adjacency" + ] + }, + { + "id": "reference-taxonomy-pronunciation-guide", + "form": "pronunciation guide, prominent lookup headwords; compact definition and annotation layers; alphabetic or semantic adjacency", + "lineage": "Lexicographic and encyclopedic reference", + "tags": [ + "headwords", + "annotations", + "adjacency" + ] + }, + { + "id": "reference-taxonomy-etymology-tree", + "form": "etymology tree, prominent lookup headwords; compact definition and annotation layers; alphabetic or semantic adjacency", + "lineage": "Lexicographic and encyclopedic reference", + "tags": [ + "headwords", + "annotations", + "adjacency" + ] + }, + { + "id": "reference-taxonomy-textual-concordance-index", + "form": "textual concordance index, prominent lookup headwords; compact definition and annotation layers; alphabetic or semantic adjacency", + "lineage": "Lexicographic and encyclopedic reference", + "tags": [ + "headwords", + "annotations", + "adjacency" + ] + }, + { + "id": "reference-taxonomy-handbook-quick-reference-page", + "form": "handbook quick-reference page, prominent lookup headwords; compact definition and annotation layers; alphabetic or semantic adjacency", + "lineage": "Lexicographic and encyclopedic reference", + "tags": [ + "headwords", + "annotations", + "adjacency" + ] + }, + { + "id": "reference-taxonomy-museum-object-label", + "form": "museum object label, persistent accession codes; provenance chains; controlled vocabulary fields", + "lineage": "Museum and archive documentation", + "tags": [ + "accession-code", + "provenance-chain", + "controlled-vocab" + ] + }, + { + "id": "reference-taxonomy-collections-register", + "form": "collections register, persistent accession codes; provenance chains; controlled vocabulary fields", + "lineage": "Museum and archive documentation", + "tags": [ + "accession-code", + "provenance-chain", + "controlled-vocab" + ] + }, + { + "id": "reference-taxonomy-archive-box-list", + "form": "archive box list, persistent accession codes; provenance chains; controlled vocabulary fields", + "lineage": "Museum and archive documentation", + "tags": [ + "accession-code", + "provenance-chain", + "controlled-vocab" + ] + }, + { + "id": "reference-taxonomy-provenance-record-card", + "form": "provenance record card, persistent accession codes; provenance chains; controlled vocabulary fields", + "lineage": "Museum and archive documentation", + "tags": [ + "accession-code", + "provenance-chain", + "controlled-vocab" + ] + }, + { + "id": "reference-taxonomy-conservation-treatment-record", + "form": "conservation treatment record, persistent accession codes; provenance chains; controlled vocabulary fields", + "lineage": "Museum and archive documentation", + "tags": [ + "accession-code", + "provenance-chain", + "controlled-vocab" + ] + }, + { + "id": "reference-taxonomy-specimen-tray-label", + "form": "specimen tray label, persistent accession codes; provenance chains; controlled vocabulary fields", + "lineage": "Museum and archive documentation", + "tags": [ + "accession-code", + "provenance-chain", + "controlled-vocab" + ] + }, + { + "id": "reference-taxonomy-accession-tag", + "form": "accession tag, persistent accession codes; provenance chains; controlled vocabulary fields", + "lineage": "Museum and archive documentation", + "tags": [ + "accession-code", + "provenance-chain", + "controlled-vocab" + ] + }, + { + "id": "reference-taxonomy-exhibition-checklist", + "form": "exhibition checklist, persistent accession codes; provenance chains; controlled vocabulary fields", + "lineage": "Museum and archive documentation", + "tags": [ + "accession-code", + "provenance-chain", + "controlled-vocab" + ] + }, + { + "id": "reference-taxonomy-slide-library-index", + "form": "slide library index, persistent accession codes; provenance chains; controlled vocabulary fields", + "lineage": "Museum and archive documentation", + "tags": [ + "accession-code", + "provenance-chain", + "controlled-vocab" + ] + }, + { + "id": "reference-taxonomy-film-archive-can-label", + "form": "film archive can label, persistent accession codes; provenance chains; controlled vocabulary fields", + "lineage": "Museum and archive documentation", + "tags": [ + "accession-code", + "provenance-chain", + "controlled-vocab" + ] + }, + { + "id": "reference-taxonomy-oral-history-catalogue-record", + "form": "oral-history catalogue record, persistent accession codes; provenance chains; controlled vocabulary fields", + "lineage": "Museum and archive documentation", + "tags": [ + "accession-code", + "provenance-chain", + "controlled-vocab" + ] + }, + { + "id": "reference-taxonomy-digital-asset-metadata-sheet", + "form": "digital asset metadata sheet, persistent accession codes; provenance chains; controlled vocabulary fields", + "lineage": "Museum and archive documentation", + "tags": [ + "accession-code", + "provenance-chain", + "controlled-vocab" + ] + }, + { + "id": "reference-taxonomy-decimal-classification-schedule", + "form": "decimal classification schedule, nested class codes; scope and inclusion notes; explicit exclusion and cross-reference rules", + "lineage": "Administrative and scientific classification", + "tags": [ + "class-codes", + "scope-notes", + "exclusions" + ] + }, + { + "id": "reference-taxonomy-faceted-classification-matrix", + "form": "faceted classification matrix, nested class codes; scope and inclusion notes; explicit exclusion and cross-reference rules", + "lineage": "Administrative and scientific classification", + "tags": [ + "class-codes", + "scope-notes", + "exclusions" + ] + }, + { + "id": "reference-taxonomy-patent-classification-tree", + "form": "patent classification tree, nested class codes; scope and inclusion notes; explicit exclusion and cross-reference rules", + "lineage": "Administrative and scientific classification", + "tags": [ + "class-codes", + "scope-notes", + "exclusions" + ] + }, + { + "id": "reference-taxonomy-customs-tariff-schedule", + "form": "customs tariff schedule, nested class codes; scope and inclusion notes; explicit exclusion and cross-reference rules", + "lineage": "Administrative and scientific classification", + "tags": [ + "class-codes", + "scope-notes", + "exclusions" + ] + }, + { + "id": "reference-taxonomy-occupation-classification-list", + "form": "occupation classification list, nested class codes; scope and inclusion notes; explicit exclusion and cross-reference rules", + "lineage": "Administrative and scientific classification", + "tags": [ + "class-codes", + "scope-notes", + "exclusions" + ] + }, + { + "id": "reference-taxonomy-disease-coding-manual", + "form": "disease coding manual, nested class codes; scope and inclusion notes; explicit exclusion and cross-reference rules", + "lineage": "Administrative and scientific classification", + "tags": [ + "class-codes", + "scope-notes", + "exclusions" + ] + }, + { + "id": "reference-taxonomy-botanical-identification-key", + "form": "botanical identification key, nested class codes; scope and inclusion notes; explicit exclusion and cross-reference rules", + "lineage": "Administrative and scientific classification", + "tags": [ + "class-codes", + "scope-notes", + "exclusions" + ] + }, + { + "id": "reference-taxonomy-geological-legend", + "form": "geological legend, nested class codes; scope and inclusion notes; explicit exclusion and cross-reference rules", + "lineage": "Administrative and scientific classification", + "tags": [ + "class-codes", + "scope-notes", + "exclusions" + ] + }, + { + "id": "reference-taxonomy-color-index-fan", + "form": "color index fan, nested class codes; scope and inclusion notes; explicit exclusion and cross-reference rules", + "lineage": "Administrative and scientific classification", + "tags": [ + "class-codes", + "scope-notes", + "exclusions" + ] + }, + { + "id": "reference-taxonomy-industrial-parts-taxonomy", + "form": "industrial parts taxonomy, nested class codes; scope and inclusion notes; explicit exclusion and cross-reference rules", + "lineage": "Administrative and scientific classification", + "tags": [ + "class-codes", + "scope-notes", + "exclusions" + ] + }, + { + "id": "reference-taxonomy-postal-address-standard", + "form": "postal address standard, nested class codes; scope and inclusion notes; explicit exclusion and cross-reference rules", + "lineage": "Administrative and scientific classification", + "tags": [ + "class-codes", + "scope-notes", + "exclusions" + ] + }, + { + "id": "reference-taxonomy-legal-subject-digest", + "form": "legal subject digest, nested class codes; scope and inclusion notes; explicit exclusion and cross-reference rules", + "lineage": "Administrative and scientific classification", + "tags": [ + "class-codes", + "scope-notes", + "exclusions" + ] + }, + { + "id": "reference-taxonomy-atlas-place-name-index", + "form": "atlas place-name index, row-and-column coordinates; dense standardized abbreviations; repeated lookup grids", + "lineage": "Tabular lookup and directory systems", + "tags": [ + "coordinates", + "abbreviations", + "lookup-grid" + ] + }, + { + "id": "reference-taxonomy-street-directory", + "form": "street directory, row-and-column coordinates; dense standardized abbreviations; repeated lookup grids", + "lineage": "Tabular lookup and directory systems", + "tags": [ + "coordinates", + "abbreviations", + "lookup-grid" + ] + }, + { + "id": "reference-taxonomy-telephone-directory", + "form": "telephone directory, row-and-column coordinates; dense standardized abbreviations; repeated lookup grids", + "lineage": "Tabular lookup and directory systems", + "tags": [ + "coordinates", + "abbreviations", + "lookup-grid" + ] + }, + { + "id": "reference-taxonomy-railway-gazetteer", + "form": "railway gazetteer, row-and-column coordinates; dense standardized abbreviations; repeated lookup grids", + "lineage": "Tabular lookup and directory systems", + "tags": [ + "coordinates", + "abbreviations", + "lookup-grid" + ] + }, + { + "id": "reference-taxonomy-airport-code-list", + "form": "airport code list, row-and-column coordinates; dense standardized abbreviations; repeated lookup grids", + "lineage": "Tabular lookup and directory systems", + "tags": [ + "coordinates", + "abbreviations", + "lookup-grid" + ] + }, + { + "id": "reference-taxonomy-shipping-register", + "form": "shipping register, row-and-column coordinates; dense standardized abbreviations; repeated lookup grids", + "lineage": "Tabular lookup and directory systems", + "tags": [ + "coordinates", + "abbreviations", + "lookup-grid" + ] + }, + { + "id": "reference-taxonomy-astronomical-ephemeris", + "form": "astronomical ephemeris, row-and-column coordinates; dense standardized abbreviations; repeated lookup grids", + "lineage": "Tabular lookup and directory systems", + "tags": [ + "coordinates", + "abbreviations", + "lookup-grid" + ] + }, + { + "id": "reference-taxonomy-tide-table", + "form": "tide table, row-and-column coordinates; dense standardized abbreviations; repeated lookup grids", + "lineage": "Tabular lookup and directory systems", + "tags": [ + "coordinates", + "abbreviations", + "lookup-grid" + ] + }, + { + "id": "reference-taxonomy-logarithm-table", + "form": "logarithm table, row-and-column coordinates; dense standardized abbreviations; repeated lookup grids", + "lineage": "Tabular lookup and directory systems", + "tags": [ + "coordinates", + "abbreviations", + "lookup-grid" + ] + }, + { + "id": "reference-taxonomy-measurement-conversion-chart", + "form": "measurement conversion chart, row-and-column coordinates; dense standardized abbreviations; repeated lookup grids", + "lineage": "Tabular lookup and directory systems", + "tags": [ + "coordinates", + "abbreviations", + "lookup-grid" + ] + }, + { + "id": "reference-taxonomy-calendar-concordance", + "form": "calendar concordance, row-and-column coordinates; dense standardized abbreviations; repeated lookup grids", + "lineage": "Tabular lookup and directory systems", + "tags": [ + "coordinates", + "abbreviations", + "lookup-grid" + ] + }, + { + "id": "reference-taxonomy-time-zone-directory", + "form": "time-zone directory, row-and-column coordinates; dense standardized abbreviations; repeated lookup grids", + "lineage": "Tabular lookup and directory systems", + "tags": [ + "coordinates", + "abbreviations", + "lookup-grid" + ] + }, + { + "id": "reference-taxonomy-collaborative-wiki-article", + "form": "collaborative wiki article, persistent deep-link anchors; nested navigation trees; version and state labels", + "lineage": "Digital knowledge and software documentation", + "tags": [ + "deep-links", + "nested-nav", + "version-labels" + ] + }, + { + "id": "reference-taxonomy-application-programming-interface-reference", + "form": "application programming interface reference, persistent deep-link anchors; nested navigation trees; version and state labels", + "lineage": "Digital knowledge and software documentation", + "tags": [ + "deep-links", + "nested-nav", + "version-labels" + ] + }, + { + "id": "reference-taxonomy-command-line-manual-page", + "form": "command-line manual page, persistent deep-link anchors; nested navigation trees; version and state labels", + "lineage": "Digital knowledge and software documentation", + "tags": [ + "deep-links", + "nested-nav", + "version-labels" + ] + }, + { + "id": "reference-taxonomy-design-token-catalogue", + "form": "design token catalogue, persistent deep-link anchors; nested navigation trees; version and state labels", + "lineage": "Digital knowledge and software documentation", + "tags": [ + "deep-links", + "nested-nav", + "version-labels" + ] + } + ] + }, + { + "id": "scientific-notation", + "label": "Scientific notation", + "description": "Observation and notation forms that encode uncertainty, measurement, method, and repeatability.", + "concepts": [ + { + "id": "scientific-notation-bound-laboratory-notebook-spread", + "form": "bound laboratory notebook spread, sequentially numbered entries; units placed beside measured values; margin annotations for exceptions", + "lineage": "Laboratory recordkeeping", + "tags": [ + "sequence", + "units", + "exceptions" + ] + }, + { + "id": "scientific-notation-carbon-copy-laboratory-worksheet", + "form": "carbon-copy laboratory worksheet, sequentially numbered entries; units placed beside measured values; margin annotations for exceptions", + "lineage": "Laboratory recordkeeping", + "tags": [ + "sequence", + "units", + "exceptions" + ] + }, + { + "id": "scientific-notation-field-observation-notebook", + "form": "field observation notebook, sequentially numbered entries; units placed beside measured values; margin annotations for exceptions", + "lineage": "Laboratory recordkeeping", + "tags": [ + "sequence", + "units", + "exceptions" + ] + }, + { + "id": "scientific-notation-instrument-run-log", + "form": "instrument run log, sequentially numbered entries; units placed beside measured values; margin annotations for exceptions", + "lineage": "Laboratory recordkeeping", + "tags": [ + "sequence", + "units", + "exceptions" + ] + }, + { + "id": "scientific-notation-sample-register", + "form": "sample register, sequentially numbered entries; units placed beside measured values; margin annotations for exceptions", + "lineage": "Laboratory recordkeeping", + "tags": [ + "sequence", + "units", + "exceptions" + ] + }, + { + "id": "scientific-notation-experiment-protocol-card", + "form": "experiment protocol card, sequentially numbered entries; units placed beside measured values; margin annotations for exceptions", + "lineage": "Laboratory recordkeeping", + "tags": [ + "sequence", + "units", + "exceptions" + ] + }, + { + "id": "scientific-notation-reagent-preparation-sheet", + "form": "reagent preparation sheet, sequentially numbered entries; units placed beside measured values; margin annotations for exceptions", + "lineage": "Laboratory recordkeeping", + "tags": [ + "sequence", + "units", + "exceptions" + ] + }, + { + "id": "scientific-notation-calibration-record", + "form": "calibration record, sequentially numbered entries; units placed beside measured values; margin annotations for exceptions", + "lineage": "Laboratory recordkeeping", + "tags": [ + "sequence", + "units", + "exceptions" + ] + }, + { + "id": "scientific-notation-controlled-environment-chart", + "form": "controlled-environment chart, sequentially numbered entries; units placed beside measured values; margin annotations for exceptions", + "lineage": "Laboratory recordkeeping", + "tags": [ + "sequence", + "units", + "exceptions" + ] + }, + { + "id": "scientific-notation-cleanroom-production-traveler", + "form": "cleanroom production traveler, sequentially numbered entries; units placed beside measured values; margin annotations for exceptions", + "lineage": "Laboratory recordkeeping", + "tags": [ + "sequence", + "units", + "exceptions" + ] + }, + { + "id": "scientific-notation-microscopy-image-log", + "form": "microscopy image log, sequentially numbered entries; units placed beside measured values; margin annotations for exceptions", + "lineage": "Laboratory recordkeeping", + "tags": [ + "sequence", + "units", + "exceptions" + ] + }, + { + "id": "scientific-notation-laboratory-shift-handover-sheet", + "form": "laboratory shift handover sheet, sequentially numbered entries; units placed beside measured values; margin annotations for exceptions", + "lineage": "Laboratory recordkeeping", + "tags": [ + "sequence", + "units", + "exceptions" + ] + }, + { + "id": "scientific-notation-ruled-calculation-notebook", + "form": "ruled calculation notebook, aligned equations and numeric columns; a variable and symbol legend; stepwise transformations with carried results", + "lineage": "Mathematical calculation and engineering tables", + "tags": [ + "equation-align", + "symbol-legend", + "stepwise" + ] + }, + { + "id": "scientific-notation-logarithm-table-page", + "form": "logarithm table page, aligned equations and numeric columns; a variable and symbol legend; stepwise transformations with carried results", + "lineage": "Mathematical calculation and engineering tables", + "tags": [ + "equation-align", + "symbol-legend", + "stepwise" + ] + }, + { + "id": "scientific-notation-slide-rule-scale", + "form": "slide-rule scale, aligned equations and numeric columns; a variable and symbol legend; stepwise transformations with carried results", + "lineage": "Mathematical calculation and engineering tables", + "tags": [ + "equation-align", + "symbol-legend", + "stepwise" + ] + }, + { + "id": "scientific-notation-graph-paper-derivation", + "form": "graph-paper derivation, aligned equations and numeric columns; a variable and symbol legend; stepwise transformations with carried results", + "lineage": "Mathematical calculation and engineering tables", + "tags": [ + "equation-align", + "symbol-legend", + "stepwise" + ] + }, + { + "id": "scientific-notation-engineering-nomogram", + "form": "engineering nomogram, aligned equations and numeric columns; a variable and symbol legend; stepwise transformations with carried results", + "lineage": "Mathematical calculation and engineering tables", + "tags": [ + "equation-align", + "symbol-legend", + "stepwise" + ] + }, + { + "id": "scientific-notation-formula-handbook", + "form": "formula handbook, aligned equations and numeric columns; a variable and symbol legend; stepwise transformations with carried results", + "lineage": "Mathematical calculation and engineering tables", + "tags": [ + "equation-align", + "symbol-legend", + "stepwise" + ] + }, + { + "id": "scientific-notation-engineering-calculation-sheet", + "form": "engineering calculation sheet, aligned equations and numeric columns; a variable and symbol legend; stepwise transformations with carried results", + "lineage": "Mathematical calculation and engineering tables", + "tags": [ + "equation-align", + "symbol-legend", + "stepwise" + ] + }, + { + "id": "scientific-notation-astronomical-calculation-ledger", + "form": "astronomical calculation ledger, aligned equations and numeric columns; a variable and symbol legend; stepwise transformations with carried results", + "lineage": "Mathematical calculation and engineering tables", + "tags": [ + "equation-align", + "symbol-legend", + "stepwise" + ] + }, + { + "id": "scientific-notation-actuarial-life-table", + "form": "actuarial life table, aligned equations and numeric columns; a variable and symbol legend; stepwise transformations with carried results", + "lineage": "Mathematical calculation and engineering tables", + "tags": [ + "equation-align", + "symbol-legend", + "stepwise" + ] + }, + { + "id": "scientific-notation-trigonometric-table", + "form": "trigonometric table, aligned equations and numeric columns; a variable and symbol legend; stepwise transformations with carried results", + "lineage": "Mathematical calculation and engineering tables", + "tags": [ + "equation-align", + "symbol-legend", + "stepwise" + ] + }, + { + "id": "scientific-notation-mathematical-proof-folio", + "form": "mathematical proof folio, aligned equations and numeric columns; a variable and symbol legend; stepwise transformations with carried results", + "lineage": "Mathematical calculation and engineering tables", + "tags": [ + "equation-align", + "symbol-legend", + "stepwise" + ] + }, + { + "id": "scientific-notation-numerical-methods-worksheet", + "form": "numerical methods worksheet, aligned equations and numeric columns; a variable and symbol legend; stepwise transformations with carried results", + "lineage": "Mathematical calculation and engineering tables", + "tags": [ + "equation-align", + "symbol-legend", + "stepwise" + ] + }, + { + "id": "scientific-notation-cartesian-graph-plate", + "form": "Cartesian graph plate, explicit axes and units; a legend for encoded marks; comparison baselines or reference bands", + "lineage": "Statistical and analytic graphics", + "tags": [ + "axes", + "mark-legend", + "baseline" + ] + }, + { + "id": "scientific-notation-polar-plot-sheet", + "form": "polar plot sheet, explicit axes and units; a legend for encoded marks; comparison baselines or reference bands", + "lineage": "Statistical and analytic graphics", + "tags": [ + "axes", + "mark-legend", + "baseline" + ] + }, + { + "id": "scientific-notation-ternary-diagram", + "form": "ternary diagram, explicit axes and units; a legend for encoded marks; comparison baselines or reference bands", + "lineage": "Statistical and analytic graphics", + "tags": [ + "axes", + "mark-legend", + "baseline" + ] + }, + { + "id": "scientific-notation-scatterplot-matrix", + "form": "scatterplot matrix, explicit axes and units; a legend for encoded marks; comparison baselines or reference bands", + "lineage": "Statistical and analytic graphics", + "tags": [ + "axes", + "mark-legend", + "baseline" + ] + }, + { + "id": "scientific-notation-error-bar-chart", + "form": "error-bar chart, explicit axes and units; a legend for encoded marks; comparison baselines or reference bands", + "lineage": "Statistical and analytic graphics", + "tags": [ + "axes", + "mark-legend", + "baseline" + ] + }, + { + "id": "scientific-notation-box-and-whisker-study", + "form": "box-and-whisker study, explicit axes and units; a legend for encoded marks; comparison baselines or reference bands", + "lineage": "Statistical and analytic graphics", + "tags": [ + "axes", + "mark-legend", + "baseline" + ] + }, + { + "id": "scientific-notation-process-control-chart", + "form": "process control chart, explicit axes and units; a legend for encoded marks; comparison baselines or reference bands", + "lineage": "Statistical and analytic graphics", + "tags": [ + "axes", + "mark-legend", + "baseline" + ] + }, + { + "id": "scientific-notation-time-series-strip-chart", + "form": "time-series strip chart, explicit axes and units; a legend for encoded marks; comparison baselines or reference bands", + "lineage": "Statistical and analytic graphics", + "tags": [ + "axes", + "mark-legend", + "baseline" + ] + }, + { + "id": "scientific-notation-histogram-panel", + "form": "histogram panel, explicit axes and units; a legend for encoded marks; comparison baselines or reference bands", + "lineage": "Statistical and analytic graphics", + "tags": [ + "axes", + "mark-legend", + "baseline" + ] + }, + { + "id": "scientific-notation-contour-plot", + "form": "contour plot, explicit axes and units; a legend for encoded marks; comparison baselines or reference bands", + "lineage": "Statistical and analytic graphics", + "tags": [ + "axes", + "mark-legend", + "baseline" + ] + }, + { + "id": "scientific-notation-vector-field-diagram", + "form": "vector field diagram, explicit axes and units; a legend for encoded marks; comparison baselines or reference bands", + "lineage": "Statistical and analytic graphics", + "tags": [ + "axes", + "mark-legend", + "baseline" + ] + }, + { + "id": "scientific-notation-phase-diagram", + "form": "phase diagram, explicit axes and units; a legend for encoded marks; comparison baselines or reference bands", + "lineage": "Statistical and analytic graphics", + "tags": [ + "axes", + "mark-legend", + "baseline" + ] + }, + { + "id": "scientific-notation-botanical-illustration-plate", + "form": "botanical illustration plate, leader-line callout labels; scale or orientation references; numbered figure captions", + "lineage": "Scientific illustration and field plates", + "tags": [ + "callouts", + "scale-reference", + "captions" + ] + }, + { + "id": "scientific-notation-zoological-anatomy-plate", + "form": "zoological anatomy plate, leader-line callout labels; scale or orientation references; numbered figure captions", + "lineage": "Scientific illustration and field plates", + "tags": [ + "callouts", + "scale-reference", + "captions" + ] + }, + { + "id": "scientific-notation-geological-stratigraphy-column", + "form": "geological stratigraphy column, leader-line callout labels; scale or orientation references; numbered figure captions", + "lineage": "Scientific illustration and field plates", + "tags": [ + "callouts", + "scale-reference", + "captions" + ] + }, + { + "id": "scientific-notation-astronomical-star-chart", + "form": "astronomical star chart, leader-line callout labels; scale or orientation references; numbered figure captions", + "lineage": "Scientific illustration and field plates", + "tags": [ + "callouts", + "scale-reference", + "captions" + ] + }, + { + "id": "scientific-notation-microscopy-atlas-plate", + "form": "microscopy atlas plate, leader-line callout labels; scale or orientation references; numbered figure captions", + "lineage": "Scientific illustration and field plates", + "tags": [ + "callouts", + "scale-reference", + "captions" + ] + }, + { + "id": "scientific-notation-crystallography-projection", + "form": "crystallography projection, leader-line callout labels; scale or orientation references; numbered figure captions", + "lineage": "Scientific illustration and field plates", + "tags": [ + "callouts", + "scale-reference", + "captions" + ] + }, + { + "id": "scientific-notation-weather-synoptic-chart", + "form": "weather synoptic chart, leader-line callout labels; scale or orientation references; numbered figure captions", + "lineage": "Scientific illustration and field plates", + "tags": [ + "callouts", + "scale-reference", + "captions" + ] + }, + { + "id": "scientific-notation-oceanographic-depth-profile", + "form": "oceanographic depth profile, leader-line callout labels; scale or orientation references; numbered figure captions", + "lineage": "Scientific illustration and field plates", + "tags": [ + "callouts", + "scale-reference", + "captions" + ] + }, + { + "id": "scientific-notation-archaeological-section-drawing", + "form": "archaeological section drawing, leader-line callout labels; scale or orientation references; numbered figure captions", + "lineage": "Scientific illustration and field plates", + "tags": [ + "callouts", + "scale-reference", + "captions" + ] + }, + { + "id": "scientific-notation-medical-anatomy-plate", + "form": "medical anatomy plate, leader-line callout labels; scale or orientation references; numbered figure captions", + "lineage": "Scientific illustration and field plates", + "tags": [ + "callouts", + "scale-reference", + "captions" + ] + }, + { + "id": "scientific-notation-mechanical-cutaway-plate", + "form": "mechanical cutaway plate, leader-line callout labels; scale or orientation references; numbered figure captions", + "lineage": "Scientific illustration and field plates", + "tags": [ + "callouts", + "scale-reference", + "captions" + ] + }, + { + "id": "scientific-notation-chemical-apparatus-diagram", + "form": "chemical apparatus diagram, leader-line callout labels; scale or orientation references; numbered figure captions", + "lineage": "Scientific illustration and field plates", + "tags": [ + "callouts", + "scale-reference", + "captions" + ] + }, + { + "id": "scientific-notation-chemical-reaction-scheme", + "form": "chemical reaction scheme, standardized symbolic primitives; directional relationship connectors; a key that decodes compact notation", + "lineage": "Formal technical notation systems", + "tags": [ + "symbols", + "connectors", + "decode-key" + ] + }, + { + "id": "scientific-notation-electrical-circuit-schematic", + "form": "electrical circuit schematic, standardized symbolic primitives; directional relationship connectors; a key that decodes compact notation", + "lineage": "Formal technical notation systems", + "tags": [ + "symbols", + "connectors", + "decode-key" + ] + }, + { + "id": "scientific-notation-genetic-pedigree-chart", + "form": "genetic pedigree chart, standardized symbolic primitives; directional relationship connectors; a key that decodes compact notation", + "lineage": "Formal technical notation systems", + "tags": [ + "symbols", + "connectors", + "decode-key" + ] + }, + { + "id": "scientific-notation-taxonomic-cladogram", + "form": "taxonomic cladogram, standardized symbolic primitives; directional relationship connectors; a key that decodes compact notation", + "lineage": "Formal technical notation systems", + "tags": [ + "symbols", + "connectors", + "decode-key" + ] + }, + { + "id": "scientific-notation-geological-map-legend", + "form": "geological map legend, standardized symbolic primitives; directional relationship connectors; a key that decodes compact notation", + "lineage": "Formal technical notation systems", + "tags": [ + "symbols", + "connectors", + "decode-key" + ] + }, + { + "id": "scientific-notation-meteorological-station-plot", + "form": "meteorological station plot, standardized symbolic primitives; directional relationship connectors; a key that decodes compact notation", + "lineage": "Formal technical notation systems", + "tags": [ + "symbols", + "connectors", + "decode-key" + ] + }, + { + "id": "scientific-notation-acoustics-spectrogram", + "form": "acoustics spectrogram, standardized symbolic primitives; directional relationship connectors; a key that decodes compact notation", + "lineage": "Formal technical notation systems", + "tags": [ + "symbols", + "connectors", + "decode-key" + ] + }, + { + "id": "scientific-notation-linguistic-syntax-tree", + "form": "linguistic syntax tree, standardized symbolic primitives; directional relationship connectors; a key that decodes compact notation", + "lineage": "Formal technical notation systems", + "tags": [ + "symbols", + "connectors", + "decode-key" + ] + }, + { + "id": "scientific-notation-statistical-model-diagram", + "form": "statistical model diagram, standardized symbolic primitives; directional relationship connectors; a key that decodes compact notation", + "lineage": "Formal technical notation systems", + "tags": [ + "symbols", + "connectors", + "decode-key" + ] + }, + { + "id": "scientific-notation-systems-dynamics-stock-and-flow-diagram", + "form": "systems-dynamics stock-and-flow diagram, standardized symbolic primitives; directional relationship connectors; a key that decodes compact notation", + "lineage": "Formal technical notation systems", + "tags": [ + "symbols", + "connectors", + "decode-key" + ] + }, + { + "id": "scientific-notation-process-flow-diagram", + "form": "process flow diagram, standardized symbolic primitives; directional relationship connectors; a key that decodes compact notation", + "lineage": "Formal technical notation systems", + "tags": [ + "symbols", + "connectors", + "decode-key" + ] + }, + { + "id": "scientific-notation-computational-state-diagram", + "form": "computational state diagram, standardized symbolic primitives; directional relationship connectors; a key that decodes compact notation", + "lineage": "Formal technical notation systems", + "tags": [ + "symbols", + "connectors", + "decode-key" + ] + }, + { + "id": "scientific-notation-research-journal-results-page", + "form": "research journal results page, numbered evidence sections; links between claims and sources; reproducibility and method metadata", + "lineage": "Modern research communication", + "tags": [ + "numbered-sections", + "claim-source", + "repro-metadata" + ] + }, + { + "id": "scientific-notation-scientific-conference-poster", + "form": "scientific conference poster, numbered evidence sections; links between claims and sources; reproducibility and method metadata", + "lineage": "Modern research communication", + "tags": [ + "numbered-sections", + "claim-source", + "repro-metadata" + ] + }, + { + "id": "scientific-notation-preprint-manuscript", + "form": "preprint manuscript, numbered evidence sections; links between claims and sources; reproducibility and method metadata", + "lineage": "Modern research communication", + "tags": [ + "numbered-sections", + "claim-source", + "repro-metadata" + ] + }, + { + "id": "scientific-notation-technical-report-appendix", + "form": "technical report appendix, numbered evidence sections; links between claims and sources; reproducibility and method metadata", + "lineage": "Modern research communication", + "tags": [ + "numbered-sections", + "claim-source", + "repro-metadata" + ] + } + ] + }, + { + "id": "data-evidence", + "label": "Data & evidence", + "description": "Evidence-bearing structures that make quantities, comparisons, distributions, and confidence inspectable.", + "concepts": [ + { + "id": "data-evidence-census-table", + "form": "census table, consistent comparison periods; visible definitions and denominators; totals that reconcile detail rows", + "lineage": "Public statistics and administrative reporting", + "tags": [ + "period-compare", + "definitions", + "reconciled-totals" + ] + }, + { + "id": "data-evidence-demographic-population-pyramid", + "form": "demographic population pyramid, consistent comparison periods; visible definitions and denominators; totals that reconcile detail rows", + "lineage": "Public statistics and administrative reporting", + "tags": [ + "period-compare", + "definitions", + "reconciled-totals" + ] + }, + { + "id": "data-evidence-public-health-surveillance-dashboard", + "form": "public-health surveillance dashboard, consistent comparison periods; visible definitions and denominators; totals that reconcile detail rows", + "lineage": "Public statistics and administrative reporting", + "tags": [ + "period-compare", + "definitions", + "reconciled-totals" + ] + }, + { + "id": "data-evidence-labor-statistics-bulletin", + "form": "labor statistics bulletin, consistent comparison periods; visible definitions and denominators; totals that reconcile detail rows", + "lineage": "Public statistics and administrative reporting", + "tags": [ + "period-compare", + "definitions", + "reconciled-totals" + ] + }, + { + "id": "data-evidence-education-outcomes-report", + "form": "education outcomes report, consistent comparison periods; visible definitions and denominators; totals that reconcile detail rows", + "lineage": "Public statistics and administrative reporting", + "tags": [ + "period-compare", + "definitions", + "reconciled-totals" + ] + }, + { + "id": "data-evidence-crime-statistics-yearbook", + "form": "crime statistics yearbook, consistent comparison periods; visible definitions and denominators; totals that reconcile detail rows", + "lineage": "Public statistics and administrative reporting", + "tags": [ + "period-compare", + "definitions", + "reconciled-totals" + ] + }, + { + "id": "data-evidence-housing-survey-summary", + "form": "housing survey summary, consistent comparison periods; visible definitions and denominators; totals that reconcile detail rows", + "lineage": "Public statistics and administrative reporting", + "tags": [ + "period-compare", + "definitions", + "reconciled-totals" + ] + }, + { + "id": "data-evidence-migration-flow-table", + "form": "migration flow table, consistent comparison periods; visible definitions and denominators; totals that reconcile detail rows", + "lineage": "Public statistics and administrative reporting", + "tags": [ + "period-compare", + "definitions", + "reconciled-totals" + ] + }, + { + "id": "data-evidence-environmental-monitoring-report", + "form": "environmental monitoring report, consistent comparison periods; visible definitions and denominators; totals that reconcile detail rows", + "lineage": "Public statistics and administrative reporting", + "tags": [ + "period-compare", + "definitions", + "reconciled-totals" + ] + }, + { + "id": "data-evidence-election-turnout-table", + "form": "election turnout table, consistent comparison periods; visible definitions and denominators; totals that reconcile detail rows", + "lineage": "Public statistics and administrative reporting", + "tags": [ + "period-compare", + "definitions", + "reconciled-totals" + ] + }, + { + "id": "data-evidence-public-transport-ridership-report", + "form": "public transport ridership report, consistent comparison periods; visible definitions and denominators; totals that reconcile detail rows", + "lineage": "Public statistics and administrative reporting", + "tags": [ + "period-compare", + "definitions", + "reconciled-totals" + ] + }, + { + "id": "data-evidence-municipal-performance-scorecard", + "form": "municipal performance scorecard, consistent comparison periods; visible definitions and denominators; totals that reconcile detail rows", + "lineage": "Public statistics and administrative reporting", + "tags": [ + "period-compare", + "definitions", + "reconciled-totals" + ] + }, + { + "id": "data-evidence-operations-metric-wallboard", + "form": "operations metric wallboard, a primary-to-secondary metric hierarchy; target-versus-actual comparisons; exception highlighting for outliers", + "lineage": "Operations and management reporting", + "tags": [ + "metric-hierarchy", + "target-actual", + "exceptions" + ] + }, + { + "id": "data-evidence-sales-ledger-summary", + "form": "sales ledger summary, a primary-to-secondary metric hierarchy; target-versus-actual comparisons; exception highlighting for outliers", + "lineage": "Operations and management reporting", + "tags": [ + "metric-hierarchy", + "target-actual", + "exceptions" + ] + }, + { + "id": "data-evidence-inventory-variance-report", + "form": "inventory variance report, a primary-to-secondary metric hierarchy; target-versus-actual comparisons; exception highlighting for outliers", + "lineage": "Operations and management reporting", + "tags": [ + "metric-hierarchy", + "target-actual", + "exceptions" + ] + }, + { + "id": "data-evidence-customer-support-queue-dashboard", + "form": "customer-support queue dashboard, a primary-to-secondary metric hierarchy; target-versus-actual comparisons; exception highlighting for outliers", + "lineage": "Operations and management reporting", + "tags": [ + "metric-hierarchy", + "target-actual", + "exceptions" + ] + }, + { + "id": "data-evidence-production-yield-report", + "form": "production yield report, a primary-to-secondary metric hierarchy; target-versus-actual comparisons; exception highlighting for outliers", + "lineage": "Operations and management reporting", + "tags": [ + "metric-hierarchy", + "target-actual", + "exceptions" + ] + }, + { + "id": "data-evidence-service-level-report", + "form": "service-level report, a primary-to-secondary metric hierarchy; target-versus-actual comparisons; exception highlighting for outliers", + "lineage": "Operations and management reporting", + "tags": [ + "metric-hierarchy", + "target-actual", + "exceptions" + ] + }, + { + "id": "data-evidence-quarterly-management-pack", + "form": "quarterly management pack, a primary-to-secondary metric hierarchy; target-versus-actual comparisons; exception highlighting for outliers", + "lineage": "Operations and management reporting", + "tags": [ + "metric-hierarchy", + "target-actual", + "exceptions" + ] + }, + { + "id": "data-evidence-branch-performance-table", + "form": "branch performance table, a primary-to-secondary metric hierarchy; target-versus-actual comparisons; exception highlighting for outliers", + "lineage": "Operations and management reporting", + "tags": [ + "metric-hierarchy", + "target-actual", + "exceptions" + ] + }, + { + "id": "data-evidence-quality-defect-pareto-chart", + "form": "quality-defect Pareto chart, a primary-to-secondary metric hierarchy; target-versus-actual comparisons; exception highlighting for outliers", + "lineage": "Operations and management reporting", + "tags": [ + "metric-hierarchy", + "target-actual", + "exceptions" + ] + }, + { + "id": "data-evidence-workforce-roster-analytics", + "form": "workforce roster analytics, a primary-to-secondary metric hierarchy; target-versus-actual comparisons; exception highlighting for outliers", + "lineage": "Operations and management reporting", + "tags": [ + "metric-hierarchy", + "target-actual", + "exceptions" + ] + }, + { + "id": "data-evidence-subscription-cohort-table", + "form": "subscription cohort table, a primary-to-secondary metric hierarchy; target-versus-actual comparisons; exception highlighting for outliers", + "lineage": "Operations and management reporting", + "tags": [ + "metric-hierarchy", + "target-actual", + "exceptions" + ] + }, + { + "id": "data-evidence-procurement-spend-cube", + "form": "procurement spend cube, a primary-to-secondary metric hierarchy; target-versus-actual comparisons; exception highlighting for outliers", + "lineage": "Operations and management reporting", + "tags": [ + "metric-hierarchy", + "target-actual", + "exceptions" + ] + }, + { + "id": "data-evidence-chain-of-custody-log", + "form": "chain-of-custody log, immutable evidence identifiers; source and attachment references; owner and disposition status", + "lineage": "Audit and evidentiary documentation", + "tags": [ + "evidence-id", + "source-ref", + "disposition" + ] + }, + { + "id": "data-evidence-audit-working-paper", + "form": "audit working paper, immutable evidence identifiers; source and attachment references; owner and disposition status", + "lineage": "Audit and evidentiary documentation", + "tags": [ + "evidence-id", + "source-ref", + "disposition" + ] + }, + { + "id": "data-evidence-regulatory-inspection-checklist", + "form": "regulatory inspection checklist, immutable evidence identifiers; source and attachment references; owner and disposition status", + "lineage": "Audit and evidentiary documentation", + "tags": [ + "evidence-id", + "source-ref", + "disposition" + ] + }, + { + "id": "data-evidence-incident-timeline", + "form": "incident timeline, immutable evidence identifiers; source and attachment references; owner and disposition status", + "lineage": "Audit and evidentiary documentation", + "tags": [ + "evidence-id", + "source-ref", + "disposition" + ] + }, + { + "id": "data-evidence-compliance-evidence-matrix", + "form": "compliance evidence matrix, immutable evidence identifiers; source and attachment references; owner and disposition status", + "lineage": "Audit and evidentiary documentation", + "tags": [ + "evidence-id", + "source-ref", + "disposition" + ] + }, + { + "id": "data-evidence-test-result-certificate", + "form": "test result certificate, immutable evidence identifiers; source and attachment references; owner and disposition status", + "lineage": "Audit and evidentiary documentation", + "tags": [ + "evidence-id", + "source-ref", + "disposition" + ] + }, + { + "id": "data-evidence-forensic-exhibit-index", + "form": "forensic exhibit index, immutable evidence identifiers; source and attachment references; owner and disposition status", + "lineage": "Audit and evidentiary documentation", + "tags": [ + "evidence-id", + "source-ref", + "disposition" + ] + }, + { + "id": "data-evidence-due-diligence-data-room-index", + "form": "due-diligence data-room index, immutable evidence identifiers; source and attachment references; owner and disposition status", + "lineage": "Audit and evidentiary documentation", + "tags": [ + "evidence-id", + "source-ref", + "disposition" + ] + }, + { + "id": "data-evidence-operational-risk-register", + "form": "operational risk register, immutable evidence identifiers; source and attachment references; owner and disposition status", + "lineage": "Audit and evidentiary documentation", + "tags": [ + "evidence-id", + "source-ref", + "disposition" + ] + }, + { + "id": "data-evidence-corrective-action-log", + "form": "corrective action log, immutable evidence identifiers; source and attachment references; owner and disposition status", + "lineage": "Audit and evidentiary documentation", + "tags": [ + "evidence-id", + "source-ref", + "disposition" + ] + }, + { + "id": "data-evidence-regulatory-filing-table", + "form": "regulatory filing table, immutable evidence identifiers; source and attachment references; owner and disposition status", + "lineage": "Audit and evidentiary documentation", + "tags": [ + "evidence-id", + "source-ref", + "disposition" + ] + }, + { + "id": "data-evidence-public-inquiry-evidence-bundle", + "form": "public-inquiry evidence bundle, immutable evidence identifiers; source and attachment references; owner and disposition status", + "lineage": "Audit and evidentiary documentation", + "tags": [ + "evidence-id", + "source-ref", + "disposition" + ] + }, + { + "id": "data-evidence-source-comparison-grid", + "form": "source comparison grid, visible source provenance; confidence or verification markers; timestamped correction and update history", + "lineage": "Evidence-led journalism and public explanation", + "tags": [ + "source-provenance", + "confidence", + "update-history" + ] + }, + { + "id": "data-evidence-fact-check-claim-card", + "form": "fact-check claim card, visible source provenance; confidence or verification markers; timestamped correction and update history", + "lineage": "Evidence-led journalism and public explanation", + "tags": [ + "source-provenance", + "confidence", + "update-history" + ] + }, + { + "id": "data-evidence-polling-average-tracker", + "form": "polling average tracker, visible source provenance; confidence or verification markers; timestamped correction and update history", + "lineage": "Evidence-led journalism and public explanation", + "tags": [ + "source-provenance", + "confidence", + "update-history" + ] + }, + { + "id": "data-evidence-election-results-map-legend", + "form": "election results map legend, visible source provenance; confidence or verification markers; timestamped correction and update history", + "lineage": "Evidence-led journalism and public explanation", + "tags": [ + "source-provenance", + "confidence", + "update-history" + ] + }, + { + "id": "data-evidence-live-results-ticker", + "form": "live results ticker, visible source provenance; confidence or verification markers; timestamped correction and update history", + "lineage": "Evidence-led journalism and public explanation", + "tags": [ + "source-provenance", + "confidence", + "update-history" + ] + }, + { + "id": "data-evidence-investigative-document-index", + "form": "investigative document index, visible source provenance; confidence or verification markers; timestamped correction and update history", + "lineage": "Evidence-led journalism and public explanation", + "tags": [ + "source-provenance", + "confidence", + "update-history" + ] + }, + { + "id": "data-evidence-interview-transcript-annotation", + "form": "interview transcript annotation, visible source provenance; confidence or verification markers; timestamped correction and update history", + "lineage": "Evidence-led journalism and public explanation", + "tags": [ + "source-provenance", + "confidence", + "update-history" + ] + }, + { + "id": "data-evidence-chronology-explainer", + "form": "chronology explainer, visible source provenance; confidence or verification markers; timestamped correction and update history", + "lineage": "Evidence-led journalism and public explanation", + "tags": [ + "source-provenance", + "confidence", + "update-history" + ] + }, + { + "id": "data-evidence-public-records-request-log", + "form": "public-records request log, visible source provenance; confidence or verification markers; timestamped correction and update history", + "lineage": "Evidence-led journalism and public explanation", + "tags": [ + "source-provenance", + "confidence", + "update-history" + ] + }, + { + "id": "data-evidence-correction-transparency-note", + "form": "correction transparency note, visible source provenance; confidence or verification markers; timestamped correction and update history", + "lineage": "Evidence-led journalism and public explanation", + "tags": [ + "source-provenance", + "confidence", + "update-history" + ] + }, + { + "id": "data-evidence-methodology-disclosure-box", + "form": "methodology disclosure box, visible source provenance; confidence or verification markers; timestamped correction and update history", + "lineage": "Evidence-led journalism and public explanation", + "tags": [ + "source-provenance", + "confidence", + "update-history" + ] + }, + { + "id": "data-evidence-reader-evidence-submission-queue", + "form": "reader evidence submission queue, visible source provenance; confidence or verification markers; timestamped correction and update history", + "lineage": "Evidence-led journalism and public explanation", + "tags": [ + "source-provenance", + "confidence", + "update-history" + ] + }, + { + "id": "data-evidence-punch-card-deck", + "form": "punch-card deck, fixed-position input fields; sequential marks or readings; strong blank-versus-recorded contrast", + "lineage": "Mechanical and paper data capture", + "tags": [ + "fixed-fields", + "sequential-marks", + "blank-contrast" + ] + }, + { + "id": "data-evidence-manual-tally-sheet", + "form": "manual tally sheet, fixed-position input fields; sequential marks or readings; strong blank-versus-recorded contrast", + "lineage": "Mechanical and paper data capture", + "tags": [ + "fixed-fields", + "sequential-marks", + "blank-contrast" + ] + }, + { + "id": "data-evidence-paper-survey-form", + "form": "paper survey form, fixed-position input fields; sequential marks or readings; strong blank-versus-recorded contrast", + "lineage": "Mechanical and paper data capture", + "tags": [ + "fixed-fields", + "sequential-marks", + "blank-contrast" + ] + }, + { + "id": "data-evidence-optical-mark-answer-sheet", + "form": "optical-mark answer sheet, fixed-position input fields; sequential marks or readings; strong blank-versus-recorded contrast", + "lineage": "Mechanical and paper data capture", + "tags": [ + "fixed-fields", + "sequential-marks", + "blank-contrast" + ] + }, + { + "id": "data-evidence-mechanical-counter-register", + "form": "mechanical counter register, fixed-position input fields; sequential marks or readings; strong blank-versus-recorded contrast", + "lineage": "Mechanical and paper data capture", + "tags": [ + "fixed-fields", + "sequential-marks", + "blank-contrast" + ] + }, + { + "id": "data-evidence-seismograph-strip", + "form": "seismograph strip, fixed-position input fields; sequential marks or readings; strong blank-versus-recorded contrast", + "lineage": "Mechanical and paper data capture", + "tags": [ + "fixed-fields", + "sequential-marks", + "blank-contrast" + ] + }, + { + "id": "data-evidence-chart-recorder-roll", + "form": "chart-recorder roll, fixed-position input fields; sequential marks or readings; strong blank-versus-recorded contrast", + "lineage": "Mechanical and paper data capture", + "tags": [ + "fixed-fields", + "sequential-marks", + "blank-contrast" + ] + }, + { + "id": "data-evidence-weather-station-log-sheet", + "form": "weather-station log sheet, fixed-position input fields; sequential marks or readings; strong blank-versus-recorded contrast", + "lineage": "Mechanical and paper data capture", + "tags": [ + "fixed-fields", + "sequential-marks", + "blank-contrast" + ] + }, + { + "id": "data-evidence-traffic-count-board", + "form": "traffic-count board, fixed-position input fields; sequential marks or readings; strong blank-versus-recorded contrast", + "lineage": "Mechanical and paper data capture", + "tags": [ + "fixed-fields", + "sequential-marks", + "blank-contrast" + ] + }, + { + "id": "data-evidence-clipboard-inspection-form", + "form": "clipboard inspection form, fixed-position input fields; sequential marks or readings; strong blank-versus-recorded contrast", + "lineage": "Mechanical and paper data capture", + "tags": [ + "fixed-fields", + "sequential-marks", + "blank-contrast" + ] + }, + { + "id": "data-evidence-warehouse-count-tag", + "form": "warehouse count tag, fixed-position input fields; sequential marks or readings; strong blank-versus-recorded contrast", + "lineage": "Mechanical and paper data capture", + "tags": [ + "fixed-fields", + "sequential-marks", + "blank-contrast" + ] + }, + { + "id": "data-evidence-laboratory-data-punch-tape", + "form": "laboratory data punch tape, fixed-position input fields; sequential marks or readings; strong blank-versus-recorded contrast", + "lineage": "Mechanical and paper data capture", + "tags": [ + "fixed-fields", + "sequential-marks", + "blank-contrast" + ] + }, + { + "id": "data-evidence-database-query-result-table", + "form": "database query result table, dimension and cohort filters; drill-down from summary to records; uncertainty and data-quality signals", + "lineage": "Contemporary analytic systems", + "tags": [ + "filters", + "drill-down", + "uncertainty" + ] + }, + { + "id": "data-evidence-event-analytics-funnel", + "form": "event analytics funnel, dimension and cohort filters; drill-down from summary to records; uncertainty and data-quality signals", + "lineage": "Contemporary analytic systems", + "tags": [ + "filters", + "drill-down", + "uncertainty" + ] + }, + { + "id": "data-evidence-retention-heatmap", + "form": "retention heatmap, dimension and cohort filters; drill-down from summary to records; uncertainty and data-quality signals", + "lineage": "Contemporary analytic systems", + "tags": [ + "filters", + "drill-down", + "uncertainty" + ] + }, + { + "id": "data-evidence-anomaly-detection-panel", + "form": "anomaly detection panel, dimension and cohort filters; drill-down from summary to records; uncertainty and data-quality signals", + "lineage": "Contemporary analytic systems", + "tags": [ + "filters", + "drill-down", + "uncertainty" + ] + } + ] + }, + { + "id": "mapping-navigation", + "label": "Mapping & navigation", + "description": "Spatial and conceptual orientation systems with routes, landmarks, layers, and decision points.", + "concepts": [ + { + "id": "mapping-navigation-metro-system-s-map-and-station-signage", + "form": "a metro system's map and station signage, with colored line routes, transfer nodes, and zone boundaries", + "lineage": "Mapping & navigation", + "tags": [ + "route-choice", + "orientation-cue", + "layered-legend" + ] + }, + { + "id": "mapping-navigation-rail-platform-sign-system", + "form": "a rail-platform sign system, with directional bands, stop sequences, and numbered exits", + "lineage": "Mapping & navigation", + "tags": [ + "route-choice", + "orientation-cue", + "layered-legend" + ] + }, + { + "id": "mapping-navigation-airport-departures-board", + "form": "an airport departures board, with flip-dot rows, gate status alerts, and scheduled timelines", + "lineage": "Mapping & navigation", + "tags": [ + "route-choice", + "orientation-cue", + "layered-legend" + ] + }, + { + "id": "mapping-navigation-motorway-sign-gantry", + "form": "a motorway sign gantry, with lane-aligned choices, route shields, and distance cues", + "lineage": "Mapping & navigation", + "tags": [ + "route-choice", + "orientation-cue", + "layered-legend" + ] + }, + { + "id": "mapping-navigation-emergency-evacuation-plan", + "form": "an emergency evacuation plan, with a fixed you-are-here point, branching routes, and priority exits", + "lineage": "Mapping & navigation", + "tags": [ + "route-choice", + "orientation-cue", + "layered-legend" + ] + }, + { + "id": "mapping-navigation-theater-s-lobby-cards-and-marquee", + "form": "a theater's lobby cards and marquee, with cast lists and act summaries", + "lineage": "Mapping & navigation", + "tags": [ + "route-choice", + "orientation-cue", + "layered-legend" + ] + }, + { + "id": "mapping-navigation-race-circuit-s-pit-wall-timing-screens", + "form": "a race circuit's pit wall timing screens, with sector splits and tyre stints", + "lineage": "Mapping & navigation", + "tags": [ + "route-choice", + "orientation-cue", + "layered-legend" + ] + }, + { + "id": "mapping-navigation-national-park-trailhead-kiosk", + "form": "a national park trailhead kiosk, with route markers, difficulty grades, and safety notices", + "lineage": "Mapping & navigation", + "tags": [ + "route-choice", + "orientation-cue", + "layered-legend" + ] + }, + { + "id": "mapping-navigation-botanical-garden-trail-map", + "form": "a botanical garden trail map, with legend symbols, path grades, and specimen stations", + "lineage": "Mapping & navigation", + "tags": [ + "route-choice", + "orientation-cue", + "layered-legend" + ] + }, + { + "id": "mapping-navigation-museum-gallery-directory", + "form": "a museum gallery directory, with floor-plan shapes, wing names, and room-by-room listings", + "lineage": "Mapping & navigation", + "tags": [ + "route-choice", + "orientation-cue", + "layered-legend" + ] + }, + { + "id": "mapping-navigation-harbor-s-tide-table-board-and-small-craft-advisories", + "form": "a harbor's tide table board and small-craft advisory, with time-indexed water levels, threshold warnings, and vessel-class guidance", + "lineage": "Mapping & navigation", + "tags": [ + "route-choice", + "orientation-cue", + "layered-legend" + ] + }, + { + "id": "mapping-navigation-pilgrimage-route-map", + "form": "a pilgrimage route map, with staged waypoints, distance intervals, and destination seals", + "lineage": "Mapping & navigation", + "tags": [ + "route-choice", + "orientation-cue", + "layered-legend" + ] + }, + { + "id": "mapping-navigation-parking-garage-level-guide", + "form": "a parking garage level guide, with colored floor zones, bay numbering, and exit arrows", + "lineage": "Mapping & navigation", + "tags": [ + "route-choice", + "orientation-cue", + "layered-legend" + ] + }, + { + "id": "mapping-navigation-supermarket-planogram", + "form": "a supermarket planogram, with shelf bands, repeated facings, and priority zones", + "lineage": "Mapping & navigation", + "tags": [ + "route-choice", + "orientation-cue", + "layered-legend" + ] + }, + { + "id": "mapping-navigation-topographic-map", + "form": "topographic map, scale and orientation references; layered symbol legends; route or coordinate indexing", + "lineage": "Surveying and cartographic practice", + "tags": [ + "scale", + "legend", + "route-index" + ] + }, + { + "id": "mapping-navigation-cadastral-parcel-map", + "form": "cadastral parcel map, scale and orientation references; layered symbol legends; route or coordinate indexing", + "lineage": "Surveying and cartographic practice", + "tags": [ + "scale", + "legend", + "route-index" + ] + }, + { + "id": "mapping-navigation-road-atlas", + "form": "road atlas, scale and orientation references; layered symbol legends; route or coordinate indexing", + "lineage": "Surveying and cartographic practice", + "tags": [ + "scale", + "legend", + "route-index" + ] + }, + { + "id": "mapping-navigation-nautical-chart", + "form": "nautical chart, scale and orientation references; layered symbol legends; route or coordinate indexing", + "lineage": "Surveying and cartographic practice", + "tags": [ + "scale", + "legend", + "route-index" + ] + }, + { + "id": "mapping-navigation-aeronautical-chart", + "form": "aeronautical chart, scale and orientation references; layered symbol legends; route or coordinate indexing", + "lineage": "Surveying and cartographic practice", + "tags": [ + "scale", + "legend", + "route-index" + ] + }, + { + "id": "mapping-navigation-geological-map", + "form": "geological map, scale and orientation references; layered symbol legends; route or coordinate indexing", + "lineage": "Surveying and cartographic practice", + "tags": [ + "scale", + "legend", + "route-index" + ] + }, + { + "id": "mapping-navigation-public-transit-map", + "form": "public transit map, scale and orientation references; layered symbol legends; route or coordinate indexing", + "lineage": "Surveying and cartographic practice", + "tags": [ + "scale", + "legend", + "route-index" + ] + }, + { + "id": "mapping-navigation-hiking-trail-map", + "form": "hiking trail map, scale and orientation references; layered symbol legends; route or coordinate indexing", + "lineage": "Surveying and cartographic practice", + "tags": [ + "scale", + "legend", + "route-index" + ] + }, + { + "id": "mapping-navigation-cycling-route-map", + "form": "cycling route map, scale and orientation references; layered symbol legends; route or coordinate indexing", + "lineage": "Surveying and cartographic practice", + "tags": [ + "scale", + "legend", + "route-index" + ] + }, + { + "id": "mapping-navigation-university-campus-map", + "form": "university campus map, scale and orientation references; layered symbol legends; route or coordinate indexing", + "lineage": "Surveying and cartographic practice", + "tags": [ + "scale", + "legend", + "route-index" + ] + }, + { + "id": "mapping-navigation-exhibition-floor-map", + "form": "exhibition floor map, scale and orientation references; layered symbol legends; route or coordinate indexing", + "lineage": "Surveying and cartographic practice", + "tags": [ + "scale", + "legend", + "route-index" + ] + }, + { + "id": "mapping-navigation-emergency-evacuation-map", + "form": "emergency evacuation map, scale and orientation references; layered symbol legends; route or coordinate indexing", + "lineage": "Surveying and cartographic practice", + "tags": [ + "scale", + "legend", + "route-index" + ] + }, + { + "id": "mapping-navigation-airport-sign-system", + "form": "airport sign system, signs placed at decision points; consistent arrows and destination codes; hierarchy by destination importance", + "lineage": "Modern public wayfinding systems", + "tags": [ + "decision-points", + "direction-code", + "destination-rank" + ] + }, + { + "id": "mapping-navigation-rail-station-wayfinding-system", + "form": "rail station wayfinding system, signs placed at decision points; consistent arrows and destination codes; hierarchy by destination importance", + "lineage": "Modern public wayfinding systems", + "tags": [ + "decision-points", + "direction-code", + "destination-rank" + ] + }, + { + "id": "mapping-navigation-hospital-wayfinding-system", + "form": "hospital wayfinding system, signs placed at decision points; consistent arrows and destination codes; hierarchy by destination importance", + "lineage": "Modern public wayfinding systems", + "tags": [ + "decision-points", + "direction-code", + "destination-rank" + ] + }, + { + "id": "mapping-navigation-civic-building-directory", + "form": "civic building directory, signs placed at decision points; consistent arrows and destination codes; hierarchy by destination importance", + "lineage": "Modern public wayfinding systems", + "tags": [ + "decision-points", + "direction-code", + "destination-rank" + ] + }, + { + "id": "mapping-navigation-parking-garage-wayfinding", + "form": "parking garage wayfinding, signs placed at decision points; consistent arrows and destination codes; hierarchy by destination importance", + "lineage": "Modern public wayfinding systems", + "tags": [ + "decision-points", + "direction-code", + "destination-rank" + ] + }, + { + "id": "mapping-navigation-pedestrian-fingerpost-network", + "form": "pedestrian fingerpost network, signs placed at decision points; consistent arrows and destination codes; hierarchy by destination importance", + "lineage": "Modern public wayfinding systems", + "tags": [ + "decision-points", + "direction-code", + "destination-rank" + ] + }, + { + "id": "mapping-navigation-highway-interchange-signage", + "form": "highway interchange signage, signs placed at decision points; consistent arrows and destination codes; hierarchy by destination importance", + "lineage": "Modern public wayfinding systems", + "tags": [ + "decision-points", + "direction-code", + "destination-rank" + ] + }, + { + "id": "mapping-navigation-ferry-terminal-guide", + "form": "ferry terminal guide, signs placed at decision points; consistent arrows and destination codes; hierarchy by destination importance", + "lineage": "Modern public wayfinding systems", + "tags": [ + "decision-points", + "direction-code", + "destination-rank" + ] + }, + { + "id": "mapping-navigation-stadium-section-signage", + "form": "stadium section signage, signs placed at decision points; consistent arrows and destination codes; hierarchy by destination importance", + "lineage": "Modern public wayfinding systems", + "tags": [ + "decision-points", + "direction-code", + "destination-rank" + ] + }, + { + "id": "mapping-navigation-museum-gallery-wayfinding", + "form": "museum gallery wayfinding, signs placed at decision points; consistent arrows and destination codes; hierarchy by destination importance", + "lineage": "Modern public wayfinding systems", + "tags": [ + "decision-points", + "direction-code", + "destination-rank" + ] + }, + { + "id": "mapping-navigation-university-campus-directory", + "form": "university campus directory, signs placed at decision points; consistent arrows and destination codes; hierarchy by destination importance", + "lineage": "Modern public wayfinding systems", + "tags": [ + "decision-points", + "direction-code", + "destination-rank" + ] + }, + { + "id": "mapping-navigation-industrial-site-safety-wayfinding", + "form": "industrial site safety wayfinding, signs placed at decision points; consistent arrows and destination codes; hierarchy by destination importance", + "lineage": "Modern public wayfinding systems", + "tags": [ + "decision-points", + "direction-code", + "destination-rank" + ] + }, + { + "id": "mapping-navigation-railway-timetable", + "form": "railway timetable, sequential stops or legs; transfer and connection cues; distance and elapsed-time markers", + "lineage": "Timetables and journey documentation", + "tags": [ + "stop-sequence", + "transfer-cues", + "distance-time" + ] + }, + { + "id": "mapping-navigation-bus-route-schedule", + "form": "bus route schedule, sequential stops or legs; transfer and connection cues; distance and elapsed-time markers", + "lineage": "Timetables and journey documentation", + "tags": [ + "stop-sequence", + "transfer-cues", + "distance-time" + ] + }, + { + "id": "mapping-navigation-airline-timetable-booklet", + "form": "airline timetable booklet, sequential stops or legs; transfer and connection cues; distance and elapsed-time markers", + "lineage": "Timetables and journey documentation", + "tags": [ + "stop-sequence", + "transfer-cues", + "distance-time" + ] + }, + { + "id": "mapping-navigation-ferry-departure-board", + "form": "ferry departure board, sequential stops or legs; transfer and connection cues; distance and elapsed-time markers", + "lineage": "Timetables and journey documentation", + "tags": [ + "stop-sequence", + "transfer-cues", + "distance-time" + ] + }, + { + "id": "mapping-navigation-multimodal-journey-itinerary", + "form": "multimodal journey itinerary, sequential stops or legs; transfer and connection cues; distance and elapsed-time markers", + "lineage": "Timetables and journey documentation", + "tags": [ + "stop-sequence", + "transfer-cues", + "distance-time" + ] + }, + { + "id": "mapping-navigation-road-trip-strip-map", + "form": "road-trip strip map, sequential stops or legs; transfer and connection cues; distance and elapsed-time markers", + "lineage": "Timetables and journey documentation", + "tags": [ + "stop-sequence", + "transfer-cues", + "distance-time" + ] + }, + { + "id": "mapping-navigation-walking-tour-brochure", + "form": "walking tour brochure, sequential stops or legs; transfer and connection cues; distance and elapsed-time markers", + "lineage": "Timetables and journey documentation", + "tags": [ + "stop-sequence", + "transfer-cues", + "distance-time" + ] + }, + { + "id": "mapping-navigation-bicycle-cue-sheet", + "form": "bicycle cue sheet, sequential stops or legs; transfer and connection cues; distance and elapsed-time markers", + "lineage": "Timetables and journey documentation", + "tags": [ + "stop-sequence", + "transfer-cues", + "distance-time" + ] + }, + { + "id": "mapping-navigation-shipping-passage-plan", + "form": "shipping passage plan, sequential stops or legs; transfer and connection cues; distance and elapsed-time markers", + "lineage": "Timetables and journey documentation", + "tags": [ + "stop-sequence", + "transfer-cues", + "distance-time" + ] + }, + { + "id": "mapping-navigation-field-expedition-route-card", + "form": "field expedition route card, sequential stops or legs; transfer and connection cues; distance and elapsed-time markers", + "lineage": "Timetables and journey documentation", + "tags": [ + "stop-sequence", + "transfer-cues", + "distance-time" + ] + }, + { + "id": "mapping-navigation-school-transport-route-sheet", + "form": "school transport route sheet, sequential stops or legs; transfer and connection cues; distance and elapsed-time markers", + "lineage": "Timetables and journey documentation", + "tags": [ + "stop-sequence", + "transfer-cues", + "distance-time" + ] + }, + { + "id": "mapping-navigation-emergency-response-route-plan", + "form": "emergency response route plan, sequential stops or legs; transfer and connection cues; distance and elapsed-time markers", + "lineage": "Timetables and journey documentation", + "tags": [ + "stop-sequence", + "transfer-cues", + "distance-time" + ] + }, + { + "id": "mapping-navigation-street-gazetteer", + "form": "street gazetteer, alphanumeric spatial grids; encoded administrative boundaries; indexed references to named places", + "lineage": "Administrative spatial indexing", + "tags": [ + "spatial-grid", + "boundaries", + "place-index" + ] + }, + { + "id": "mapping-navigation-postal-delivery-zone-map", + "form": "postal delivery zone map, alphanumeric spatial grids; encoded administrative boundaries; indexed references to named places", + "lineage": "Administrative spatial indexing", + "tags": [ + "spatial-grid", + "boundaries", + "place-index" + ] + }, + { + "id": "mapping-navigation-telephone-area-code-map", + "form": "telephone area-code map, alphanumeric spatial grids; encoded administrative boundaries; indexed references to named places", + "lineage": "Administrative spatial indexing", + "tags": [ + "spatial-grid", + "boundaries", + "place-index" + ] + }, + { + "id": "mapping-navigation-electoral-district-map", + "form": "electoral district map, alphanumeric spatial grids; encoded administrative boundaries; indexed references to named places", + "lineage": "Administrative spatial indexing", + "tags": [ + "spatial-grid", + "boundaries", + "place-index" + ] + }, + { + "id": "mapping-navigation-utility-service-map", + "form": "utility service map, alphanumeric spatial grids; encoded administrative boundaries; indexed references to named places", + "lineage": "Administrative spatial indexing", + "tags": [ + "spatial-grid", + "boundaries", + "place-index" + ] + }, + { + "id": "mapping-navigation-land-use-zoning-map", + "form": "land-use zoning map, alphanumeric spatial grids; encoded administrative boundaries; indexed references to named places", + "lineage": "Administrative spatial indexing", + "tags": [ + "spatial-grid", + "boundaries", + "place-index" + ] + }, + { + "id": "mapping-navigation-weather-warning-zone-map", + "form": "weather warning zone map, alphanumeric spatial grids; encoded administrative boundaries; indexed references to named places", + "lineage": "Administrative spatial indexing", + "tags": [ + "spatial-grid", + "boundaries", + "place-index" + ] + }, + { + "id": "mapping-navigation-maritime-grid-chart", + "form": "maritime grid chart, alphanumeric spatial grids; encoded administrative boundaries; indexed references to named places", + "lineage": "Administrative spatial indexing", + "tags": [ + "spatial-grid", + "boundaries", + "place-index" + ] + }, + { + "id": "mapping-navigation-fire-response-grid", + "form": "fire-response grid, alphanumeric spatial grids; encoded administrative boundaries; indexed references to named places", + "lineage": "Administrative spatial indexing", + "tags": [ + "spatial-grid", + "boundaries", + "place-index" + ] + }, + { + "id": "mapping-navigation-public-works-maintenance-map", + "form": "public works maintenance map, alphanumeric spatial grids; encoded administrative boundaries; indexed references to named places", + "lineage": "Administrative spatial indexing", + "tags": [ + "spatial-grid", + "boundaries", + "place-index" + ] + }, + { + "id": "mapping-navigation-census-tract-map", + "form": "census tract map, alphanumeric spatial grids; encoded administrative boundaries; indexed references to named places", + "lineage": "Administrative spatial indexing", + "tags": [ + "spatial-grid", + "boundaries", + "place-index" + ] + }, + { + "id": "mapping-navigation-venue-seating-plan", + "form": "venue seating plan, alphanumeric spatial grids; encoded administrative boundaries; indexed references to named places", + "lineage": "Administrative spatial indexing", + "tags": [ + "spatial-grid", + "boundaries", + "place-index" + ] + }, + { + "id": "mapping-navigation-turn-by-turn-navigation-screen", + "form": "turn-by-turn navigation screen, a persistent current-position anchor; live movement and status updates; smooth overview-to-detail transitions", + "lineage": "Live digital navigation", + "tags": [ + "current-position", + "live-status", + "overview-detail" + ] + }, + { + "id": "mapping-navigation-on-demand-transport-pickup-map", + "form": "on-demand transport pickup map, a persistent current-position anchor; live movement and status updates; smooth overview-to-detail transitions", + "lineage": "Live digital navigation", + "tags": [ + "current-position", + "live-status", + "overview-detail" + ] + } + ] + }, + { + "id": "civic-legal-governance", + "label": "Civic, legal & governance", + "description": "Public record and decision systems with authority, sequence, evidence, and accountability.", + "concepts": [ + { + "id": "civic-legal-governance-court-transcript", + "form": "a court transcript, with examination, exhibits, and a verdict", + "lineage": "Civic, legal & governance", + "tags": [ + "ordered-record", + "evidence-index", + "verification-mark" + ] + }, + { + "id": "civic-legal-governance-legal-case-file", + "form": "a legal case file, with a cover index, chronologically tabbed evidence, and disposition stamps", + "lineage": "Civic, legal & governance", + "tags": [ + "ordered-record", + "evidence-index", + "verification-mark" + ] + }, + { + "id": "civic-legal-governance-ship-s-log", + "form": "a ship's log, with watch entries, positions, and weather remarks", + "lineage": "Civic, legal & governance", + "tags": [ + "ordered-record", + "evidence-index", + "verification-mark" + ] + }, + { + "id": "civic-legal-governance-expedition-s-field-notebook", + "form": "an expedition's field notebook, with sketches, measurements, and daily entries", + "lineage": "Civic, legal & governance", + "tags": [ + "ordered-record", + "evidence-index", + "verification-mark" + ] + }, + { + "id": "civic-legal-governance-laboratory-notebook", + "form": "a laboratory notebook, with dated experiments, observations, and sign-offs", + "lineage": "Civic, legal & governance", + "tags": [ + "ordered-record", + "evidence-index", + "verification-mark" + ] + }, + { + "id": "civic-legal-governance-botanist-s-herbarium-sheet", + "form": "a botanist's herbarium sheet, with mounted specimens, label cards, and collection metadata", + "lineage": "Civic, legal & governance", + "tags": [ + "ordered-record", + "evidence-index", + "verification-mark" + ] + }, + { + "id": "civic-legal-governance-passport-booklet", + "form": "a passport booklet, with identity front matter, repeated entry fields, and chronological stamps", + "lineage": "Civic, legal & governance", + "tags": [ + "ordered-record", + "evidence-index", + "verification-mark" + ] + }, + { + "id": "civic-legal-governance-customs-declaration-form", + "form": "a customs declaration form, with gated sections, coded responses, and signature checkpoints", + "lineage": "Civic, legal & governance", + "tags": [ + "ordered-record", + "evidence-index", + "verification-mark" + ] + }, + { + "id": "civic-legal-governance-census-questionnaire", + "form": "a census questionnaire, with fill-in boxes, branching yes/no paths, and instruction margins", + "lineage": "Civic, legal & governance", + "tags": [ + "ordered-record", + "evidence-index", + "verification-mark" + ] + }, + { + "id": "civic-legal-governance-ballot-paper", + "form": "a ballot paper, with constrained choices, section-by-section progression, and verification marks", + "lineage": "Civic, legal & governance", + "tags": [ + "ordered-record", + "evidence-index", + "verification-mark" + ] + }, + { + "id": "civic-legal-governance-title-deed-and-property-survey", + "form": "a title deed and property survey, with plot boundaries and easements", + "lineage": "Civic, legal & governance", + "tags": [ + "ordered-record", + "evidence-index", + "verification-mark" + ] + }, + { + "id": "civic-legal-governance-cadastral-survey-map", + "form": "a cadastral survey map, with parcel boundaries, plot numbers, and keyed ownership records", + "lineage": "Civic, legal & governance", + "tags": [ + "ordered-record", + "evidence-index", + "verification-mark" + ] + }, + { + "id": "civic-legal-governance-patent-folio", + "form": "a patent folio, with numbered figures, claim hierarchies, and reference labels", + "lineage": "Civic, legal & governance", + "tags": [ + "ordered-record", + "evidence-index", + "verification-mark" + ] + }, + { + "id": "civic-legal-governance-industrial-standards-sheet", + "form": "an industrial standards sheet, with numbered clauses, dimensioned diagrams, and tolerance tables", + "lineage": "Civic, legal & governance", + "tags": [ + "ordered-record", + "evidence-index", + "verification-mark" + ] + }, + { + "id": "civic-legal-governance-repair-manual-s-exploded-parts-diagrams", + "form": "a repair manual's exploded-parts diagrams, with numbered callouts and lookup tables", + "lineage": "Civic, legal & governance", + "tags": [ + "ordered-record", + "evidence-index", + "verification-mark" + ] + }, + { + "id": "civic-legal-governance-double-entry-ledger", + "form": "a double-entry ledger, with mirrored debit-credit columns, running balances, and period totals", + "lineage": "Civic, legal & governance", + "tags": [ + "ordered-record", + "evidence-index", + "verification-mark" + ] + }, + { + "id": "civic-legal-governance-bank-statement", + "form": "a bank statement, with opening and closing summaries, chronological transactions, and reconciled totals", + "lineage": "Civic, legal & governance", + "tags": [ + "ordered-record", + "evidence-index", + "verification-mark" + ] + }, + { + "id": "civic-legal-governance-bank-passbook", + "form": "a bank passbook, with ruled entries and teller stamps", + "lineage": "Civic, legal & governance", + "tags": [ + "ordered-record", + "evidence-index", + "verification-mark" + ] + }, + { + "id": "civic-legal-governance-utility-bill", + "form": "a utility bill, with historical usage charts, payment-due boxes, and itemized fees", + "lineage": "Civic, legal & governance", + "tags": [ + "ordered-record", + "evidence-index", + "verification-mark" + ] + }, + { + "id": "civic-legal-governance-shipping-manifest", + "form": "a shipping manifest, with grouped consignments, tracking codes, and exception flags", + "lineage": "Civic, legal & governance", + "tags": [ + "ordered-record", + "evidence-index", + "verification-mark" + ] + }, + { + "id": "civic-legal-governance-telephone-directory", + "form": "a telephone directory, with alphabetic columns, index tabs, and compact locator codes", + "lineage": "Civic, legal & governance", + "tags": [ + "ordered-record", + "evidence-index", + "verification-mark" + ] + }, + { + "id": "civic-legal-governance-library-card-catalog", + "form": "a library card catalog, with standardized records, alphabetical dividers, and linked subject codes", + "lineage": "Civic, legal & governance", + "tags": [ + "ordered-record", + "evidence-index", + "verification-mark" + ] + }, + { + "id": "civic-legal-governance-statute-book-page", + "form": "statute book page, stable numbered clauses; dense citation and cross-reference links; visible amendment or version history", + "lineage": "Codified law and judicial publishing", + "tags": [ + "numbered-clauses", + "citations", + "version-history" + ] + }, + { + "id": "civic-legal-governance-court-opinion", + "form": "court opinion, stable numbered clauses; dense citation and cross-reference links; visible amendment or version history", + "lineage": "Codified law and judicial publishing", + "tags": [ + "numbered-clauses", + "citations", + "version-history" + ] + }, + { + "id": "civic-legal-governance-legislative-bill", + "form": "legislative bill, stable numbered clauses; dense citation and cross-reference links; visible amendment or version history", + "lineage": "Codified law and judicial publishing", + "tags": [ + "numbered-clauses", + "citations", + "version-history" + ] + }, + { + "id": "civic-legal-governance-contract-schedule", + "form": "contract schedule, stable numbered clauses; dense citation and cross-reference links; visible amendment or version history", + "lineage": "Codified law and judicial publishing", + "tags": [ + "numbered-clauses", + "citations", + "version-history" + ] + }, + { + "id": "civic-legal-governance-administrative-rule-notice", + "form": "administrative rule notice, stable numbered clauses; dense citation and cross-reference links; visible amendment or version history", + "lineage": "Codified law and judicial publishing", + "tags": [ + "numbered-clauses", + "citations", + "version-history" + ] + }, + { + "id": "civic-legal-governance-municipal-code-section", + "form": "municipal code section, stable numbered clauses; dense citation and cross-reference links; visible amendment or version history", + "lineage": "Codified law and judicial publishing", + "tags": [ + "numbered-clauses", + "citations", + "version-history" + ] + }, + { + "id": "civic-legal-governance-treaty-volume", + "form": "treaty volume, stable numbered clauses; dense citation and cross-reference links; visible amendment or version history", + "lineage": "Codified law and judicial publishing", + "tags": [ + "numbered-clauses", + "citations", + "version-history" + ] + }, + { + "id": "civic-legal-governance-case-reporter-headnote", + "form": "case reporter headnote, stable numbered clauses; dense citation and cross-reference links; visible amendment or version history", + "lineage": "Codified law and judicial publishing", + "tags": [ + "numbered-clauses", + "citations", + "version-history" + ] + }, + { + "id": "civic-legal-governance-legal-brief", + "form": "legal brief, stable numbered clauses; dense citation and cross-reference links; visible amendment or version history", + "lineage": "Codified law and judicial publishing", + "tags": [ + "numbered-clauses", + "citations", + "version-history" + ] + }, + { + "id": "civic-legal-governance-witness-statement", + "form": "witness statement, stable numbered clauses; dense citation and cross-reference links; visible amendment or version history", + "lineage": "Codified law and judicial publishing", + "tags": [ + "numbered-clauses", + "citations", + "version-history" + ] + }, + { + "id": "civic-legal-governance-affidavit-form", + "form": "affidavit form, stable numbered clauses; dense citation and cross-reference links; visible amendment or version history", + "lineage": "Codified law and judicial publishing", + "tags": [ + "numbered-clauses", + "citations", + "version-history" + ] + }, + { + "id": "civic-legal-governance-judicial-docket", + "form": "judicial docket, stable numbered clauses; dense citation and cross-reference links; visible amendment or version history", + "lineage": "Codified law and judicial publishing", + "tags": [ + "numbered-clauses", + "citations", + "version-history" + ] + }, + { + "id": "civic-legal-governance-ballot-paper-25bb36e1", + "form": "ballot paper, equal visual treatment of alternatives; explicit completion instructions; official validation and counting marks", + "lineage": "Democratic participation systems", + "tags": [ + "equal-treatment", + "instructions", + "validation" + ] + }, + { + "id": "civic-legal-governance-voter-information-booklet", + "form": "voter information booklet, equal visual treatment of alternatives; explicit completion instructions; official validation and counting marks", + "lineage": "Democratic participation systems", + "tags": [ + "equal-treatment", + "instructions", + "validation" + ] + }, + { + "id": "civic-legal-governance-referendum-guide", + "form": "referendum guide, equal visual treatment of alternatives; explicit completion instructions; official validation and counting marks", + "lineage": "Democratic participation systems", + "tags": [ + "equal-treatment", + "instructions", + "validation" + ] + }, + { + "id": "civic-legal-governance-legislative-agenda", + "form": "legislative agenda, equal visual treatment of alternatives; explicit completion instructions; official validation and counting marks", + "lineage": "Democratic participation systems", + "tags": [ + "equal-treatment", + "instructions", + "validation" + ] + }, + { + "id": "civic-legal-governance-parliamentary-order-paper", + "form": "parliamentary order paper, equal visual treatment of alternatives; explicit completion instructions; official validation and counting marks", + "lineage": "Democratic participation systems", + "tags": [ + "equal-treatment", + "instructions", + "validation" + ] + }, + { + "id": "civic-legal-governance-committee-hearing-schedule", + "form": "committee hearing schedule, equal visual treatment of alternatives; explicit completion instructions; official validation and counting marks", + "lineage": "Democratic participation systems", + "tags": [ + "equal-treatment", + "instructions", + "validation" + ] + }, + { + "id": "civic-legal-governance-public-consultation-form", + "form": "public consultation form, equal visual treatment of alternatives; explicit completion instructions; official validation and counting marks", + "lineage": "Democratic participation systems", + "tags": [ + "equal-treatment", + "instructions", + "validation" + ] + }, + { + "id": "civic-legal-governance-petition-sheet", + "form": "petition sheet, equal visual treatment of alternatives; explicit completion instructions; official validation and counting marks", + "lineage": "Democratic participation systems", + "tags": [ + "equal-treatment", + "instructions", + "validation" + ] + }, + { + "id": "civic-legal-governance-election-results-canvass", + "form": "election results canvass, equal visual treatment of alternatives; explicit completion instructions; official validation and counting marks", + "lineage": "Democratic participation systems", + "tags": [ + "equal-treatment", + "instructions", + "validation" + ] + }, + { + "id": "civic-legal-governance-candidate-statement-booklet", + "form": "candidate statement booklet, equal visual treatment of alternatives; explicit completion instructions; official validation and counting marks", + "lineage": "Democratic participation systems", + "tags": [ + "equal-treatment", + "instructions", + "validation" + ] + }, + { + "id": "civic-legal-governance-precinct-notice", + "form": "precinct notice, equal visual treatment of alternatives; explicit completion instructions; official validation and counting marks", + "lineage": "Democratic participation systems", + "tags": [ + "equal-treatment", + "instructions", + "validation" + ] + }, + { + "id": "civic-legal-governance-civic-assembly-agenda", + "form": "civic assembly agenda, equal visual treatment of alternatives; explicit completion instructions; official validation and counting marks", + "lineage": "Democratic participation systems", + "tags": [ + "equal-treatment", + "instructions", + "validation" + ] + }, + { + "id": "civic-legal-governance-permit-application", + "form": "permit application, legally defined identity fields; workflow and case-status markers; signature or certification zones", + "lineage": "Public-service administration", + "tags": [ + "identity-fields", + "workflow-status", + "certification" + ] + }, + { + "id": "civic-legal-governance-income-declaration-form", + "form": "income declaration form, legally defined identity fields; workflow and case-status markers; signature or certification zones", + "lineage": "Public-service administration", + "tags": [ + "identity-fields", + "workflow-status", + "certification" + ] + }, + { + "id": "civic-legal-governance-benefits-application", + "form": "benefits application, legally defined identity fields; workflow and case-status markers; signature or certification zones", + "lineage": "Public-service administration", + "tags": [ + "identity-fields", + "workflow-status", + "certification" + ] + }, + { + "id": "civic-legal-governance-license-certificate", + "form": "license certificate, legally defined identity fields; workflow and case-status markers; signature or certification zones", + "lineage": "Public-service administration", + "tags": [ + "identity-fields", + "workflow-status", + "certification" + ] + }, + { + "id": "civic-legal-governance-building-inspection-notice", + "form": "building inspection notice, legally defined identity fields; workflow and case-status markers; signature or certification zones", + "lineage": "Public-service administration", + "tags": [ + "identity-fields", + "workflow-status", + "certification" + ] + }, + { + "id": "civic-legal-governance-public-records-request-form", + "form": "public-records request form, legally defined identity fields; workflow and case-status markers; signature or certification zones", + "lineage": "Public-service administration", + "tags": [ + "identity-fields", + "workflow-status", + "certification" + ] + }, + { + "id": "civic-legal-governance-residency-registration-form", + "form": "residency registration form, legally defined identity fields; workflow and case-status markers; signature or certification zones", + "lineage": "Public-service administration", + "tags": [ + "identity-fields", + "workflow-status", + "certification" + ] + }, + { + "id": "civic-legal-governance-customs-declaration", + "form": "customs declaration, legally defined identity fields; workflow and case-status markers; signature or certification zones", + "lineage": "Public-service administration", + "tags": [ + "identity-fields", + "workflow-status", + "certification" + ] + }, + { + "id": "civic-legal-governance-vital-records-certificate", + "form": "vital-records certificate, legally defined identity fields; workflow and case-status markers; signature or certification zones", + "lineage": "Public-service administration", + "tags": [ + "identity-fields", + "workflow-status", + "certification" + ] + }, + { + "id": "civic-legal-governance-public-service-receipt", + "form": "public-service receipt, legally defined identity fields; workflow and case-status markers; signature or certification zones", + "lineage": "Public-service administration", + "tags": [ + "identity-fields", + "workflow-status", + "certification" + ] + }, + { + "id": "civic-legal-governance-municipal-complaint-form", + "form": "municipal complaint form, legally defined identity fields; workflow and case-status markers; signature or certification zones", + "lineage": "Public-service administration", + "tags": [ + "identity-fields", + "workflow-status", + "certification" + ] + }, + { + "id": "civic-legal-governance-government-appointment-ticket", + "form": "government appointment ticket, legally defined identity fields; workflow and case-status markers; signature or certification zones", + "lineage": "Public-service administration", + "tags": [ + "identity-fields", + "workflow-status", + "certification" + ] + }, + { + "id": "civic-legal-governance-council-meeting-minutes", + "form": "council meeting minutes, proposition-evidence-decision sequencing; attributed positions and speakers; recorded outcomes and next actions", + "lineage": "Deliberative and policy documentation", + "tags": [ + "decision-sequence", + "attribution", + "outcomes" + ] + }, + { + "id": "civic-legal-governance-legislative-committee-report", + "form": "legislative committee report, proposition-evidence-decision sequencing; attributed positions and speakers; recorded outcomes and next actions", + "lineage": "Deliberative and policy documentation", + "tags": [ + "decision-sequence", + "attribution", + "outcomes" + ] + }, + { + "id": "civic-legal-governance-policy-white-paper", + "form": "policy white paper, proposition-evidence-decision sequencing; attributed positions and speakers; recorded outcomes and next actions", + "lineage": "Deliberative and policy documentation", + "tags": [ + "decision-sequence", + "attribution", + "outcomes" + ] + }, + { + "id": "civic-legal-governance-budget-hearing-packet", + "form": "budget hearing packet, proposition-evidence-decision sequencing; attributed positions and speakers; recorded outcomes and next actions", + "lineage": "Deliberative and policy documentation", + "tags": [ + "decision-sequence", + "attribution", + "outcomes" + ] + }, + { + "id": "civic-legal-governance-impact-assessment", + "form": "impact assessment, proposition-evidence-decision sequencing; attributed positions and speakers; recorded outcomes and next actions", + "lineage": "Deliberative and policy documentation", + "tags": [ + "decision-sequence", + "attribution", + "outcomes" + ] + }, + { + "id": "civic-legal-governance-public-comment-digest", + "form": "public comment digest, proposition-evidence-decision sequencing; attributed positions and speakers; recorded outcomes and next actions", + "lineage": "Deliberative and policy documentation", + "tags": [ + "decision-sequence", + "attribution", + "outcomes" + ] + } + ] + }, + { + "id": "finance-accounting", + "label": "Finance & accounting", + "description": "Reconciliation, allocation, balance, and scenario structures that expose consequence and flow.", + "concepts": [ + { + "id": "finance-accounting-double-entry-ledger-page", + "form": "double-entry ledger page, aligned debit and credit columns; running and period balances; audit references to source documents", + "lineage": "Double-entry bookkeeping", + "tags": [ + "debit-credit", + "running-balance", + "audit-ref" + ] + }, + { + "id": "finance-accounting-cashbook", + "form": "cashbook, aligned debit and credit columns; running and period balances; audit references to source documents", + "lineage": "Double-entry bookkeeping", + "tags": [ + "debit-credit", + "running-balance", + "audit-ref" + ] + }, + { + "id": "finance-accounting-accounting-daybook", + "form": "accounting daybook, aligned debit and credit columns; running and period balances; audit references to source documents", + "lineage": "Double-entry bookkeeping", + "tags": [ + "debit-credit", + "running-balance", + "audit-ref" + ] + }, + { + "id": "finance-accounting-general-journal", + "form": "general journal, aligned debit and credit columns; running and period balances; audit references to source documents", + "lineage": "Double-entry bookkeeping", + "tags": [ + "debit-credit", + "running-balance", + "audit-ref" + ] + }, + { + "id": "finance-accounting-trial-balance-worksheet", + "form": "trial balance worksheet, aligned debit and credit columns; running and period balances; audit references to source documents", + "lineage": "Double-entry bookkeeping", + "tags": [ + "debit-credit", + "running-balance", + "audit-ref" + ] + }, + { + "id": "finance-accounting-accounts-receivable-ledger", + "form": "accounts receivable ledger, aligned debit and credit columns; running and period balances; audit references to source documents", + "lineage": "Double-entry bookkeeping", + "tags": [ + "debit-credit", + "running-balance", + "audit-ref" + ] + }, + { + "id": "finance-accounting-accounts-payable-ledger", + "form": "accounts payable ledger, aligned debit and credit columns; running and period balances; audit references to source documents", + "lineage": "Double-entry bookkeeping", + "tags": [ + "debit-credit", + "running-balance", + "audit-ref" + ] + }, + { + "id": "finance-accounting-payroll-register", + "form": "payroll register, aligned debit and credit columns; running and period balances; audit references to source documents", + "lineage": "Double-entry bookkeeping", + "tags": [ + "debit-credit", + "running-balance", + "audit-ref" + ] + }, + { + "id": "finance-accounting-petty-cash-voucher", + "form": "petty-cash voucher, aligned debit and credit columns; running and period balances; audit references to source documents", + "lineage": "Double-entry bookkeeping", + "tags": [ + "debit-credit", + "running-balance", + "audit-ref" + ] + }, + { + "id": "finance-accounting-fixed-asset-register", + "form": "fixed-asset register, aligned debit and credit columns; running and period balances; audit references to source documents", + "lineage": "Double-entry bookkeeping", + "tags": [ + "debit-credit", + "running-balance", + "audit-ref" + ] + }, + { + "id": "finance-accounting-bank-reconciliation-sheet", + "form": "bank reconciliation sheet, aligned debit and credit columns; running and period balances; audit references to source documents", + "lineage": "Double-entry bookkeeping", + "tags": [ + "debit-credit", + "running-balance", + "audit-ref" + ] + }, + { + "id": "finance-accounting-employee-expense-claim", + "form": "employee expense claim, aligned debit and credit columns; running and period balances; audit references to source documents", + "lineage": "Double-entry bookkeeping", + "tags": [ + "debit-credit", + "running-balance", + "audit-ref" + ] + }, + { + "id": "finance-accounting-stock-exchange-price-board", + "form": "stock-exchange price board, precise market timestamps; comparable instrument rows; price and change shown together", + "lineage": "Exchange and market information", + "tags": [ + "market-time", + "comparable-rows", + "price-change" + ] + }, + { + "id": "finance-accounting-commodity-market-report", + "form": "commodity market report, precise market timestamps; comparable instrument rows; price and change shown together", + "lineage": "Exchange and market information", + "tags": [ + "market-time", + "comparable-rows", + "price-change" + ] + }, + { + "id": "finance-accounting-bond-quotation-sheet", + "form": "bond quotation sheet, precise market timestamps; comparable instrument rows; price and change shown together", + "lineage": "Exchange and market information", + "tags": [ + "market-time", + "comparable-rows", + "price-change" + ] + }, + { + "id": "finance-accounting-foreign-exchange-rate-board", + "form": "foreign-exchange rate board, precise market timestamps; comparable instrument rows; price and change shown together", + "lineage": "Exchange and market information", + "tags": [ + "market-time", + "comparable-rows", + "price-change" + ] + }, + { + "id": "finance-accounting-investment-fund-factsheet", + "form": "investment fund factsheet, precise market timestamps; comparable instrument rows; price and change shown together", + "lineage": "Exchange and market information", + "tags": [ + "market-time", + "comparable-rows", + "price-change" + ] + }, + { + "id": "finance-accounting-securities-trade-ticket", + "form": "securities trade ticket, precise market timestamps; comparable instrument rows; price and change shown together", + "lineage": "Exchange and market information", + "tags": [ + "market-time", + "comparable-rows", + "price-change" + ] + }, + { + "id": "finance-accounting-auction-estimate-catalogue-page", + "form": "auction estimate catalogue page, precise market timestamps; comparable instrument rows; price and change shown together", + "lineage": "Exchange and market information", + "tags": [ + "market-time", + "comparable-rows", + "price-change" + ] + }, + { + "id": "finance-accounting-order-book-depth-screen", + "form": "order-book depth screen, precise market timestamps; comparable instrument rows; price and change shown together", + "lineage": "Exchange and market information", + "tags": [ + "market-time", + "comparable-rows", + "price-change" + ] + }, + { + "id": "finance-accounting-market-candlestick-chart", + "form": "market candlestick chart, precise market timestamps; comparable instrument rows; price and change shown together", + "lineage": "Exchange and market information", + "tags": [ + "market-time", + "comparable-rows", + "price-change" + ] + }, + { + "id": "finance-accounting-portfolio-allocation-report", + "form": "portfolio allocation report, precise market timestamps; comparable instrument rows; price and change shown together", + "lineage": "Exchange and market information", + "tags": [ + "market-time", + "comparable-rows", + "price-change" + ] + }, + { + "id": "finance-accounting-investment-research-tear-sheet", + "form": "investment research tear sheet, precise market timestamps; comparable instrument rows; price and change shown together", + "lineage": "Exchange and market information", + "tags": [ + "market-time", + "comparable-rows", + "price-change" + ] + }, + { + "id": "finance-accounting-shareholder-register", + "form": "shareholder register, precise market timestamps; comparable instrument rows; price and change shown together", + "lineage": "Exchange and market information", + "tags": [ + "market-time", + "comparable-rows", + "price-change" + ] + }, + { + "id": "finance-accounting-bank-statement", + "form": "bank statement, chronological transaction sequences; opening and closing totals; account and transaction identifiers", + "lineage": "Retail and commercial banking", + "tags": [ + "chronology", + "balance-totals", + "transaction-id" + ] + }, + { + "id": "finance-accounting-savings-passbook", + "form": "savings passbook, chronological transaction sequences; opening and closing totals; account and transaction identifiers", + "lineage": "Retail and commercial banking", + "tags": [ + "chronology", + "balance-totals", + "transaction-id" + ] + }, + { + "id": "finance-accounting-cash-deposit-slip", + "form": "cash deposit slip, chronological transaction sequences; opening and closing totals; account and transaction identifiers", + "lineage": "Retail and commercial banking", + "tags": [ + "chronology", + "balance-totals", + "transaction-id" + ] + }, + { + "id": "finance-accounting-cash-withdrawal-form", + "form": "cash withdrawal form, chronological transaction sequences; opening and closing totals; account and transaction identifiers", + "lineage": "Retail and commercial banking", + "tags": [ + "chronology", + "balance-totals", + "transaction-id" + ] + }, + { + "id": "finance-accounting-cheque-register", + "form": "cheque register, chronological transaction sequences; opening and closing totals; account and transaction identifiers", + "lineage": "Retail and commercial banking", + "tags": [ + "chronology", + "balance-totals", + "transaction-id" + ] + }, + { + "id": "finance-accounting-wire-transfer-form", + "form": "wire-transfer form, chronological transaction sequences; opening and closing totals; account and transaction identifiers", + "lineage": "Retail and commercial banking", + "tags": [ + "chronology", + "balance-totals", + "transaction-id" + ] + }, + { + "id": "finance-accounting-remittance-advice", + "form": "remittance advice, chronological transaction sequences; opening and closing totals; account and transaction identifiers", + "lineage": "Retail and commercial banking", + "tags": [ + "chronology", + "balance-totals", + "transaction-id" + ] + }, + { + "id": "finance-accounting-loan-amortization-schedule", + "form": "loan amortization schedule, chronological transaction sequences; opening and closing totals; account and transaction identifiers", + "lineage": "Retail and commercial banking", + "tags": [ + "chronology", + "balance-totals", + "transaction-id" + ] + }, + { + "id": "finance-accounting-mortgage-disclosure", + "form": "mortgage disclosure, chronological transaction sequences; opening and closing totals; account and transaction identifiers", + "lineage": "Retail and commercial banking", + "tags": [ + "chronology", + "balance-totals", + "transaction-id" + ] + }, + { + "id": "finance-accounting-credit-card-statement", + "form": "credit-card statement, chronological transaction sequences; opening and closing totals; account and transaction identifiers", + "lineage": "Retail and commercial banking", + "tags": [ + "chronology", + "balance-totals", + "transaction-id" + ] + }, + { + "id": "finance-accounting-safe-deposit-inventory", + "form": "safe-deposit inventory, chronological transaction sequences; opening and closing totals; account and transaction identifiers", + "lineage": "Retail and commercial banking", + "tags": [ + "chronology", + "balance-totals", + "transaction-id" + ] + }, + { + "id": "finance-accounting-currency-exchange-receipt", + "form": "currency-exchange receipt, chronological transaction sequences; opening and closing totals; account and transaction identifiers", + "lineage": "Retail and commercial banking", + "tags": [ + "chronology", + "balance-totals", + "transaction-id" + ] + }, + { + "id": "finance-accounting-household-budget-envelope-system", + "form": "household budget envelope system, planned-versus-actual columns; stable spending categories; rollups from line items to totals", + "lineage": "Budgeting and financial planning", + "tags": [ + "plan-actual", + "categories", + "rollups" + ] + }, + { + "id": "finance-accounting-annual-public-budget-book", + "form": "annual public budget book, planned-versus-actual columns; stable spending categories; rollups from line items to totals", + "lineage": "Budgeting and financial planning", + "tags": [ + "plan-actual", + "categories", + "rollups" + ] + }, + { + "id": "finance-accounting-capital-expenditure-plan", + "form": "capital expenditure plan, planned-versus-actual columns; stable spending categories; rollups from line items to totals", + "lineage": "Budgeting and financial planning", + "tags": [ + "plan-actual", + "categories", + "rollups" + ] + }, + { + "id": "finance-accounting-cash-flow-forecast", + "form": "cash-flow forecast, planned-versus-actual columns; stable spending categories; rollups from line items to totals", + "lineage": "Budgeting and financial planning", + "tags": [ + "plan-actual", + "categories", + "rollups" + ] + }, + { + "id": "finance-accounting-break-even-worksheet", + "form": "break-even worksheet, planned-versus-actual columns; stable spending categories; rollups from line items to totals", + "lineage": "Budgeting and financial planning", + "tags": [ + "plan-actual", + "categories", + "rollups" + ] + }, + { + "id": "finance-accounting-cost-center-report", + "form": "cost-center report, planned-versus-actual columns; stable spending categories; rollups from line items to totals", + "lineage": "Budgeting and financial planning", + "tags": [ + "plan-actual", + "categories", + "rollups" + ] + }, + { + "id": "finance-accounting-project-budget-tracker", + "form": "project budget tracker, planned-versus-actual columns; stable spending categories; rollups from line items to totals", + "lineage": "Budgeting and financial planning", + "tags": [ + "plan-actual", + "categories", + "rollups" + ] + }, + { + "id": "finance-accounting-grant-budget-form", + "form": "grant budget form, planned-versus-actual columns; stable spending categories; rollups from line items to totals", + "lineage": "Budgeting and financial planning", + "tags": [ + "plan-actual", + "categories", + "rollups" + ] + }, + { + "id": "finance-accounting-campaign-finance-report", + "form": "campaign finance report, planned-versus-actual columns; stable spending categories; rollups from line items to totals", + "lineage": "Budgeting and financial planning", + "tags": [ + "plan-actual", + "categories", + "rollups" + ] + }, + { + "id": "finance-accounting-pension-projection", + "form": "pension projection, planned-versus-actual columns; stable spending categories; rollups from line items to totals", + "lineage": "Budgeting and financial planning", + "tags": [ + "plan-actual", + "categories", + "rollups" + ] + }, + { + "id": "finance-accounting-savings-goal-chart", + "form": "savings goal chart, planned-versus-actual columns; stable spending categories; rollups from line items to totals", + "lineage": "Budgeting and financial planning", + "tags": [ + "plan-actual", + "categories", + "rollups" + ] + }, + { + "id": "finance-accounting-financial-scenario-table", + "form": "financial scenario table, planned-versus-actual columns; stable spending categories; rollups from line items to totals", + "lineage": "Budgeting and financial planning", + "tags": [ + "plan-actual", + "categories", + "rollups" + ] + }, + { + "id": "finance-accounting-income-tax-return", + "form": "income tax return, declared entity and reporting period; compliance checkpoints and statuses; signed declarations of accuracy", + "lineage": "Tax and financial compliance", + "tags": [ + "entity-period", + "compliance-check", + "declaration" + ] + }, + { + "id": "finance-accounting-sales-tax-register", + "form": "sales-tax register, declared entity and reporting period; compliance checkpoints and statuses; signed declarations of accuracy", + "lineage": "Tax and financial compliance", + "tags": [ + "entity-period", + "compliance-check", + "declaration" + ] + }, + { + "id": "finance-accounting-customs-duty-schedule", + "form": "customs duty schedule, declared entity and reporting period; compliance checkpoints and statuses; signed declarations of accuracy", + "lineage": "Tax and financial compliance", + "tags": [ + "entity-period", + "compliance-check", + "declaration" + ] + }, + { + "id": "finance-accounting-payroll-tax-form", + "form": "payroll tax form, declared entity and reporting period; compliance checkpoints and statuses; signed declarations of accuracy", + "lineage": "Tax and financial compliance", + "tags": [ + "entity-period", + "compliance-check", + "declaration" + ] + }, + { + "id": "finance-accounting-withholding-statement", + "form": "withholding statement, declared entity and reporting period; compliance checkpoints and statuses; signed declarations of accuracy", + "lineage": "Tax and financial compliance", + "tags": [ + "entity-period", + "compliance-check", + "declaration" + ] + }, + { + "id": "finance-accounting-charitable-donation-receipt", + "form": "charitable donation receipt, declared entity and reporting period; compliance checkpoints and statuses; signed declarations of accuracy", + "lineage": "Tax and financial compliance", + "tags": [ + "entity-period", + "compliance-check", + "declaration" + ] + }, + { + "id": "finance-accounting-invoice-register", + "form": "invoice register, declared entity and reporting period; compliance checkpoints and statuses; signed declarations of accuracy", + "lineage": "Tax and financial compliance", + "tags": [ + "entity-period", + "compliance-check", + "declaration" + ] + }, + { + "id": "finance-accounting-audit-confirmation-letter", + "form": "audit confirmation letter, declared entity and reporting period; compliance checkpoints and statuses; signed declarations of accuracy", + "lineage": "Tax and financial compliance", + "tags": [ + "entity-period", + "compliance-check", + "declaration" + ] + }, + { + "id": "finance-accounting-insurance-premium-schedule", + "form": "insurance premium schedule, declared entity and reporting period; compliance checkpoints and statuses; signed declarations of accuracy", + "lineage": "Tax and financial compliance", + "tags": [ + "entity-period", + "compliance-check", + "declaration" + ] + }, + { + "id": "finance-accounting-solvency-ratio-report", + "form": "solvency ratio report, declared entity and reporting period; compliance checkpoints and statuses; signed declarations of accuracy", + "lineage": "Tax and financial compliance", + "tags": [ + "entity-period", + "compliance-check", + "declaration" + ] + }, + { + "id": "finance-accounting-financial-crime-case-file", + "form": "financial crime case file, declared entity and reporting period; compliance checkpoints and statuses; signed declarations of accuracy", + "lineage": "Tax and financial compliance", + "tags": [ + "entity-period", + "compliance-check", + "declaration" + ] + }, + { + "id": "finance-accounting-beneficial-ownership-register", + "form": "beneficial ownership register, declared entity and reporting period; compliance checkpoints and statuses; signed declarations of accuracy", + "lineage": "Tax and financial compliance", + "tags": [ + "entity-period", + "compliance-check", + "declaration" + ] + }, + { + "id": "finance-accounting-retail-receipt", + "form": "retail receipt, itemized quantity and price lines; subtotal tax and final-total hierarchy; payment or fulfillment status", + "lineage": "Invoices and transactional documents", + "tags": [ + "itemized-lines", + "total-hierarchy", + "payment-status" + ] + }, + { + "id": "finance-accounting-commercial-invoice", + "form": "commercial invoice, itemized quantity and price lines; subtotal tax and final-total hierarchy; payment or fulfillment status", + "lineage": "Invoices and transactional documents", + "tags": [ + "itemized-lines", + "total-hierarchy", + "payment-status" + ] + }, + { + "id": "finance-accounting-purchase-order", + "form": "purchase order, itemized quantity and price lines; subtotal tax and final-total hierarchy; payment or fulfillment status", + "lineage": "Invoices and transactional documents", + "tags": [ + "itemized-lines", + "total-hierarchy", + "payment-status" + ] + }, + { + "id": "finance-accounting-credit-note", + "form": "credit note, itemized quantity and price lines; subtotal tax and final-total hierarchy; payment or fulfillment status", + "lineage": "Invoices and transactional documents", + "tags": [ + "itemized-lines", + "total-hierarchy", + "payment-status" + ] + } + ] + }, + { + "id": "logistics-supply-chain", + "label": "Logistics & supply chain", + "description": "Movement and custody systems organized around stages, dependencies, exceptions, and handoffs.", + "concepts": [ + { + "id": "logistics-supply-chain-ocean-bill-of-lading", + "form": "ocean bill of lading, persistent shipment identifiers; named origin destination and parties; signed custody checkpoints", + "lineage": "International freight documentation", + "tags": [ + "shipment-id", + "route-parties", + "custody" + ] + }, + { + "id": "logistics-supply-chain-air-waybill", + "form": "air waybill, persistent shipment identifiers; named origin destination and parties; signed custody checkpoints", + "lineage": "International freight documentation", + "tags": [ + "shipment-id", + "route-parties", + "custody" + ] + }, + { + "id": "logistics-supply-chain-rail-consignment-note", + "form": "rail consignment note, persistent shipment identifiers; named origin destination and parties; signed custody checkpoints", + "lineage": "International freight documentation", + "tags": [ + "shipment-id", + "route-parties", + "custody" + ] + }, + { + "id": "logistics-supply-chain-delivery-note", + "form": "delivery note, persistent shipment identifiers; named origin destination and parties; signed custody checkpoints", + "lineage": "International freight documentation", + "tags": [ + "shipment-id", + "route-parties", + "custody" + ] + }, + { + "id": "logistics-supply-chain-cargo-packing-list", + "form": "cargo packing list, persistent shipment identifiers; named origin destination and parties; signed custody checkpoints", + "lineage": "International freight documentation", + "tags": [ + "shipment-id", + "route-parties", + "custody" + ] + }, + { + "id": "logistics-supply-chain-ship-cargo-manifest", + "form": "ship cargo manifest, persistent shipment identifiers; named origin destination and parties; signed custody checkpoints", + "lineage": "International freight documentation", + "tags": [ + "shipment-id", + "route-parties", + "custody" + ] + }, + { + "id": "logistics-supply-chain-customs-cargo-declaration", + "form": "customs cargo declaration, persistent shipment identifiers; named origin destination and parties; signed custody checkpoints", + "lineage": "International freight documentation", + "tags": [ + "shipment-id", + "route-parties", + "custody" + ] + }, + { + "id": "logistics-supply-chain-certificate-of-origin", + "form": "certificate of origin, persistent shipment identifiers; named origin destination and parties; signed custody checkpoints", + "lineage": "International freight documentation", + "tags": [ + "shipment-id", + "route-parties", + "custody" + ] + }, + { + "id": "logistics-supply-chain-dangerous-goods-declaration", + "form": "dangerous-goods declaration, persistent shipment identifiers; named origin destination and parties; signed custody checkpoints", + "lineage": "International freight documentation", + "tags": [ + "shipment-id", + "route-parties", + "custody" + ] + }, + { + "id": "logistics-supply-chain-freight-invoice", + "form": "freight invoice, persistent shipment identifiers; named origin destination and parties; signed custody checkpoints", + "lineage": "International freight documentation", + "tags": [ + "shipment-id", + "route-parties", + "custody" + ] + }, + { + "id": "logistics-supply-chain-dock-receipt", + "form": "dock receipt, persistent shipment identifiers; named origin destination and parties; signed custody checkpoints", + "lineage": "International freight documentation", + "tags": [ + "shipment-id", + "route-parties", + "custody" + ] + }, + { + "id": "logistics-supply-chain-container-interchange-report", + "form": "container interchange report, persistent shipment identifiers; named origin destination and parties; signed custody checkpoints", + "lineage": "International freight documentation", + "tags": [ + "shipment-id", + "route-parties", + "custody" + ] + }, + { + "id": "logistics-supply-chain-warehouse-bin-location-label", + "form": "warehouse bin-location label, coded storage locations; expected and confirmed quantities; prominent handling status", + "lineage": "Warehouse and inventory operations", + "tags": [ + "location-code", + "quantity-check", + "handling-status" + ] + }, + { + "id": "logistics-supply-chain-pallet-license-plate", + "form": "pallet license plate, coded storage locations; expected and confirmed quantities; prominent handling status", + "lineage": "Warehouse and inventory operations", + "tags": [ + "location-code", + "quantity-check", + "handling-status" + ] + }, + { + "id": "logistics-supply-chain-warehouse-pick-list", + "form": "warehouse pick list, coded storage locations; expected and confirmed quantities; prominent handling status", + "lineage": "Warehouse and inventory operations", + "tags": [ + "location-code", + "quantity-check", + "handling-status" + ] + }, + { + "id": "logistics-supply-chain-put-away-task-sheet", + "form": "put-away task sheet, coded storage locations; expected and confirmed quantities; prominent handling status", + "lineage": "Warehouse and inventory operations", + "tags": [ + "location-code", + "quantity-check", + "handling-status" + ] + }, + { + "id": "logistics-supply-chain-cycle-count-card", + "form": "cycle-count card, coded storage locations; expected and confirmed quantities; prominent handling status", + "lineage": "Warehouse and inventory operations", + "tags": [ + "location-code", + "quantity-check", + "handling-status" + ] + }, + { + "id": "logistics-supply-chain-stock-transfer-order", + "form": "stock transfer order, coded storage locations; expected and confirmed quantities; prominent handling status", + "lineage": "Warehouse and inventory operations", + "tags": [ + "location-code", + "quantity-check", + "handling-status" + ] + }, + { + "id": "logistics-supply-chain-goods-received-note", + "form": "goods-received note, coded storage locations; expected and confirmed quantities; prominent handling status", + "lineage": "Warehouse and inventory operations", + "tags": [ + "location-code", + "quantity-check", + "handling-status" + ] + }, + { + "id": "logistics-supply-chain-warehouse-slot-map", + "form": "warehouse slot map, coded storage locations; expected and confirmed quantities; prominent handling status", + "lineage": "Warehouse and inventory operations", + "tags": [ + "location-code", + "quantity-check", + "handling-status" + ] + }, + { + "id": "logistics-supply-chain-replenishment-queue", + "form": "replenishment queue, coded storage locations; expected and confirmed quantities; prominent handling status", + "lineage": "Warehouse and inventory operations", + "tags": [ + "location-code", + "quantity-check", + "handling-status" + ] + }, + { + "id": "logistics-supply-chain-returns-authorization", + "form": "returns authorization, coded storage locations; expected and confirmed quantities; prominent handling status", + "lineage": "Warehouse and inventory operations", + "tags": [ + "location-code", + "quantity-check", + "handling-status" + ] + }, + { + "id": "logistics-supply-chain-inventory-quarantine-tag", + "form": "inventory quarantine tag, coded storage locations; expected and confirmed quantities; prominent handling status", + "lineage": "Warehouse and inventory operations", + "tags": [ + "location-code", + "quantity-check", + "handling-status" + ] + }, + { + "id": "logistics-supply-chain-cross-dock-routing-sheet", + "form": "cross-dock routing sheet, coded storage locations; expected and confirmed quantities; prominent handling status", + "lineage": "Warehouse and inventory operations", + "tags": [ + "location-code", + "quantity-check", + "handling-status" + ] + }, + { + "id": "logistics-supply-chain-transport-dispatch-board", + "form": "transport dispatch board, time-bound resource assignments; ordered departure or handling sequence; exception and delay annotations", + "lineage": "Fleet and terminal operations", + "tags": [ + "resource-time", + "operation-sequence", + "delay-notes" + ] + }, + { + "id": "logistics-supply-chain-fleet-route-sheet", + "form": "fleet route sheet, time-bound resource assignments; ordered departure or handling sequence; exception and delay annotations", + "lineage": "Fleet and terminal operations", + "tags": [ + "resource-time", + "operation-sequence", + "delay-notes" + ] + }, + { + "id": "logistics-supply-chain-driver-logbook", + "form": "driver logbook, time-bound resource assignments; ordered departure or handling sequence; exception and delay annotations", + "lineage": "Fleet and terminal operations", + "tags": [ + "resource-time", + "operation-sequence", + "delay-notes" + ] + }, + { + "id": "logistics-supply-chain-vehicle-inspection-checklist", + "form": "vehicle inspection checklist, time-bound resource assignments; ordered departure or handling sequence; exception and delay annotations", + "lineage": "Fleet and terminal operations", + "tags": [ + "resource-time", + "operation-sequence", + "delay-notes" + ] + }, + { + "id": "logistics-supply-chain-fuel-issue-record", + "form": "fuel issue record, time-bound resource assignments; ordered departure or handling sequence; exception and delay annotations", + "lineage": "Fleet and terminal operations", + "tags": [ + "resource-time", + "operation-sequence", + "delay-notes" + ] + }, + { + "id": "logistics-supply-chain-maintenance-scheduler", + "form": "maintenance scheduler, time-bound resource assignments; ordered departure or handling sequence; exception and delay annotations", + "lineage": "Fleet and terminal operations", + "tags": [ + "resource-time", + "operation-sequence", + "delay-notes" + ] + }, + { + "id": "logistics-supply-chain-load-planning-chart", + "form": "load-planning chart, time-bound resource assignments; ordered departure or handling sequence; exception and delay annotations", + "lineage": "Fleet and terminal operations", + "tags": [ + "resource-time", + "operation-sequence", + "delay-notes" + ] + }, + { + "id": "logistics-supply-chain-taxi-rank-queue-board", + "form": "taxi-rank queue board, time-bound resource assignments; ordered departure or handling sequence; exception and delay annotations", + "lineage": "Fleet and terminal operations", + "tags": [ + "resource-time", + "operation-sequence", + "delay-notes" + ] + }, + { + "id": "logistics-supply-chain-bus-depot-pullout-sheet", + "form": "bus-depot pullout sheet, time-bound resource assignments; ordered departure or handling sequence; exception and delay annotations", + "lineage": "Fleet and terminal operations", + "tags": [ + "resource-time", + "operation-sequence", + "delay-notes" + ] + }, + { + "id": "logistics-supply-chain-rail-yard-consist-list", + "form": "rail-yard consist list, time-bound resource assignments; ordered departure or handling sequence; exception and delay annotations", + "lineage": "Fleet and terminal operations", + "tags": [ + "resource-time", + "operation-sequence", + "delay-notes" + ] + }, + { + "id": "logistics-supply-chain-port-berth-plan", + "form": "port berth plan, time-bound resource assignments; ordered departure or handling sequence; exception and delay annotations", + "lineage": "Fleet and terminal operations", + "tags": [ + "resource-time", + "operation-sequence", + "delay-notes" + ] + }, + { + "id": "logistics-supply-chain-aircraft-turnaround-sheet", + "form": "aircraft turnaround sheet, time-bound resource assignments; ordered departure or handling sequence; exception and delay annotations", + "lineage": "Fleet and terminal operations", + "tags": [ + "resource-time", + "operation-sequence", + "delay-notes" + ] + }, + { + "id": "logistics-supply-chain-postal-sorting-frame", + "form": "postal sorting frame, machine-readable route codes; timestamped scan events; handoff and delivery proof", + "lineage": "Postal and parcel networks", + "tags": [ + "route-code", + "scan-events", + "handoff-proof" + ] + }, + { + "id": "logistics-supply-chain-postal-address-label", + "form": "postal address label, machine-readable route codes; timestamped scan events; handoff and delivery proof", + "lineage": "Postal and parcel networks", + "tags": [ + "route-code", + "scan-events", + "handoff-proof" + ] + }, + { + "id": "logistics-supply-chain-registered-mail-receipt", + "form": "registered-mail receipt, machine-readable route codes; timestamped scan events; handoff and delivery proof", + "lineage": "Postal and parcel networks", + "tags": [ + "route-code", + "scan-events", + "handoff-proof" + ] + }, + { + "id": "logistics-supply-chain-parcel-tracking-scan-log", + "form": "parcel tracking scan log, machine-readable route codes; timestamped scan events; handoff and delivery proof", + "lineage": "Postal and parcel networks", + "tags": [ + "route-code", + "scan-events", + "handoff-proof" + ] + }, + { + "id": "logistics-supply-chain-courier-route-manifest", + "form": "courier route manifest, machine-readable route codes; timestamped scan events; handoff and delivery proof", + "lineage": "Postal and parcel networks", + "tags": [ + "route-code", + "scan-events", + "handoff-proof" + ] + }, + { + "id": "logistics-supply-chain-mail-sack-tag", + "form": "mail-sack tag, machine-readable route codes; timestamped scan events; handoff and delivery proof", + "lineage": "Postal and parcel networks", + "tags": [ + "route-code", + "scan-events", + "handoff-proof" + ] + }, + { + "id": "logistics-supply-chain-postmark-cancellation-layout", + "form": "postmark cancellation layout, machine-readable route codes; timestamped scan events; handoff and delivery proof", + "lineage": "Postal and parcel networks", + "tags": [ + "route-code", + "scan-events", + "handoff-proof" + ] + }, + { + "id": "logistics-supply-chain-undeliverable-mail-sticker", + "form": "undeliverable-mail sticker, machine-readable route codes; timestamped scan events; handoff and delivery proof", + "lineage": "Postal and parcel networks", + "tags": [ + "route-code", + "scan-events", + "handoff-proof" + ] + }, + { + "id": "logistics-supply-chain-return-to-sender-label", + "form": "return-to-sender label, machine-readable route codes; timestamped scan events; handoff and delivery proof", + "lineage": "Postal and parcel networks", + "tags": [ + "route-code", + "scan-events", + "handoff-proof" + ] + }, + { + "id": "logistics-supply-chain-delivery-attempt-notice", + "form": "delivery-attempt notice, machine-readable route codes; timestamped scan events; handoff and delivery proof", + "lineage": "Postal and parcel networks", + "tags": [ + "route-code", + "scan-events", + "handoff-proof" + ] + }, + { + "id": "logistics-supply-chain-post-office-box-directory", + "form": "post-office-box directory, machine-readable route codes; timestamped scan events; handoff and delivery proof", + "lineage": "Postal and parcel networks", + "tags": [ + "route-code", + "scan-events", + "handoff-proof" + ] + }, + { + "id": "logistics-supply-chain-proof-of-delivery-screen", + "form": "proof-of-delivery screen, machine-readable route codes; timestamped scan events; handoff and delivery proof", + "lineage": "Postal and parcel networks", + "tags": [ + "route-code", + "scan-events", + "handoff-proof" + ] + }, + { + "id": "logistics-supply-chain-supply-network-map", + "form": "supply network map, forecast-versus-capacity comparison; upstream and downstream dependencies; threshold alerts for shortages or delay", + "lineage": "Supply planning systems", + "tags": [ + "forecast-capacity", + "dependencies", + "threshold-alerts" + ] + }, + { + "id": "logistics-supply-chain-demand-forecast-chart", + "form": "demand forecast chart, forecast-versus-capacity comparison; upstream and downstream dependencies; threshold alerts for shortages or delay", + "lineage": "Supply planning systems", + "tags": [ + "forecast-capacity", + "dependencies", + "threshold-alerts" + ] + }, + { + "id": "logistics-supply-chain-safety-stock-calculator", + "form": "safety-stock calculator, forecast-versus-capacity comparison; upstream and downstream dependencies; threshold alerts for shortages or delay", + "lineage": "Supply planning systems", + "tags": [ + "forecast-capacity", + "dependencies", + "threshold-alerts" + ] + }, + { + "id": "logistics-supply-chain-material-requirements-plan", + "form": "material requirements plan, forecast-versus-capacity comparison; upstream and downstream dependencies; threshold alerts for shortages or delay", + "lineage": "Supply planning systems", + "tags": [ + "forecast-capacity", + "dependencies", + "threshold-alerts" + ] + }, + { + "id": "logistics-supply-chain-vendor-lead-time-matrix", + "form": "vendor lead-time matrix, forecast-versus-capacity comparison; upstream and downstream dependencies; threshold alerts for shortages or delay", + "lineage": "Supply planning systems", + "tags": [ + "forecast-capacity", + "dependencies", + "threshold-alerts" + ] + }, + { + "id": "logistics-supply-chain-distribution-requirements-plan", + "form": "distribution requirements plan, forecast-versus-capacity comparison; upstream and downstream dependencies; threshold alerts for shortages or delay", + "lineage": "Supply planning systems", + "tags": [ + "forecast-capacity", + "dependencies", + "threshold-alerts" + ] + }, + { + "id": "logistics-supply-chain-capacity-planning-board", + "form": "capacity planning board, forecast-versus-capacity comparison; upstream and downstream dependencies; threshold alerts for shortages or delay", + "lineage": "Supply planning systems", + "tags": [ + "forecast-capacity", + "dependencies", + "threshold-alerts" + ] + }, + { + "id": "logistics-supply-chain-cold-chain-temperature-log", + "form": "cold-chain temperature log, forecast-versus-capacity comparison; upstream and downstream dependencies; threshold alerts for shortages or delay", + "lineage": "Supply planning systems", + "tags": [ + "forecast-capacity", + "dependencies", + "threshold-alerts" + ] + }, + { + "id": "logistics-supply-chain-seasonal-allocation-table", + "form": "seasonal allocation table, forecast-versus-capacity comparison; upstream and downstream dependencies; threshold alerts for shortages or delay", + "lineage": "Supply planning systems", + "tags": [ + "forecast-capacity", + "dependencies", + "threshold-alerts" + ] + }, + { + "id": "logistics-supply-chain-disruption-scenario-map", + "form": "disruption scenario map, forecast-versus-capacity comparison; upstream and downstream dependencies; threshold alerts for shortages or delay", + "lineage": "Supply planning systems", + "tags": [ + "forecast-capacity", + "dependencies", + "threshold-alerts" + ] + }, + { + "id": "logistics-supply-chain-procurement-pipeline", + "form": "procurement pipeline, forecast-versus-capacity comparison; upstream and downstream dependencies; threshold alerts for shortages or delay", + "lineage": "Supply planning systems", + "tags": [ + "forecast-capacity", + "dependencies", + "threshold-alerts" + ] + }, + { + "id": "logistics-supply-chain-service-level-dashboard", + "form": "service-level dashboard, forecast-versus-capacity comparison; upstream and downstream dependencies; threshold alerts for shortages or delay", + "lineage": "Supply planning systems", + "tags": [ + "forecast-capacity", + "dependencies", + "threshold-alerts" + ] + }, + { + "id": "logistics-supply-chain-relief-distribution-card", + "form": "relief distribution card, kit route and recipient matching; custody handoffs at each stage; shortage damage and exception logs", + "lineage": "Field and institutional logistics", + "tags": [ + "recipient-match", + "stage-handoff", + "exception-log" + ] + }, + { + "id": "logistics-supply-chain-mobile-clinic-supply-kit-list", + "form": "mobile-clinic supply kit list, kit route and recipient matching; custody handoffs at each stage; shortage damage and exception logs", + "lineage": "Field and institutional logistics", + "tags": [ + "recipient-match", + "stage-handoff", + "exception-log" + ] + }, + { + "id": "logistics-supply-chain-construction-site-material-call-off", + "form": "construction-site material call-off, kit route and recipient matching; custody handoffs at each stage; shortage damage and exception logs", + "lineage": "Field and institutional logistics", + "tags": [ + "recipient-match", + "stage-handoff", + "exception-log" + ] + }, + { + "id": "logistics-supply-chain-film-production-logistics-sheet", + "form": "film-production logistics sheet, kit route and recipient matching; custody handoffs at each stage; shortage damage and exception logs", + "lineage": "Field and institutional logistics", + "tags": [ + "recipient-match", + "stage-handoff", + "exception-log" + ] + } + ] + }, + { + "id": "retail-packaging-service", + "label": "Retail, packaging & service", + "description": "Choice, specification, ordering, fulfillment, and service artifacts with clear transactional grammar.", + "concepts": [ + { + "id": "retail-packaging-service-seed-packet-s-front-and-back-panels", + "form": "a seed packet's front-and-back panels, with sowing instructions and zone tables", + "lineage": "Retail, packaging & service", + "tags": [ + "choice-grid", + "specification", + "transaction-step" + ] + }, + { + "id": "retail-packaging-service-mail-order-seed-catalog", + "form": "a mail-order seed catalog, with variety grids and growing-zone tables", + "lineage": "Retail, packaging & service", + "tags": [ + "choice-grid", + "specification", + "transaction-step" + ] + }, + { + "id": "retail-packaging-service-hardware-store-s-parts-drawers", + "form": "a hardware store's parts drawers, with bin labels and spec cards", + "lineage": "Retail, packaging & service", + "tags": [ + "choice-grid", + "specification", + "transaction-step" + ] + }, + { + "id": "retail-packaging-service-pharmacy-prescription-label-and-patient-information", + "form": "a pharmacy prescription label and patient-information leaflet, with dosage hierarchy, identity verification, and contraindication guidance", + "lineage": "Retail, packaging & service", + "tags": [ + "choice-grid", + "specification", + "transaction-step" + ] + }, + { + "id": "retail-packaging-service-matchbook-and-cigar-band-graphics", + "form": "a matchbook and cigar-band graphics, with foil stamping and tiny type", + "lineage": "Retail, packaging & service", + "tags": [ + "choice-grid", + "specification", + "transaction-step" + ] + }, + { + "id": "retail-packaging-service-produce-market-s-chalkboard-price-signs-and-crate-si", + "form": "a produce market's chalkboard price signs and crate-side stencils, with commodity grouping, unit pricing, and origin marks", + "lineage": "Retail, packaging & service", + "tags": [ + "choice-grid", + "specification", + "transaction-step" + ] + }, + { + "id": "retail-packaging-service-market-price-board", + "form": "a market price board, with commodity rows, live rate changes, and unit legends", + "lineage": "Retail, packaging & service", + "tags": [ + "choice-grid", + "specification", + "transaction-step" + ] + }, + { + "id": "retail-packaging-service-restaurant-order-rail", + "form": "a restaurant order rail, with time-ordered tickets, station assignments, and completion marks", + "lineage": "Retail, packaging & service", + "tags": [ + "choice-grid", + "specification", + "transaction-step" + ] + }, + { + "id": "retail-packaging-service-restaurant-order-ticket", + "form": "a restaurant order ticket, with seat numbers, modifier checkmarks, and timestamps", + "lineage": "Retail, packaging & service", + "tags": [ + "choice-grid", + "specification", + "transaction-step" + ] + }, + { + "id": "retail-packaging-service-auction-house-catalog", + "form": "an auction house catalog, with lot numbers, estimate bands, and condition reports", + "lineage": "Retail, packaging & service", + "tags": [ + "choice-grid", + "specification", + "transaction-step" + ] + }, + { + "id": "retail-packaging-service-coupon-sheet", + "form": "a coupon sheet, with perforated modules, denomination hierarchy, and redemption conditions", + "lineage": "Retail, packaging & service", + "tags": [ + "choice-grid", + "specification", + "transaction-step" + ] + }, + { + "id": "retail-packaging-service-recipe-card", + "form": "a recipe card, with an ingredient inventory, ordered steps, and timing checkpoints", + "lineage": "Retail, packaging & service", + "tags": [ + "choice-grid", + "specification", + "transaction-step" + ] + }, + { + "id": "retail-packaging-service-folding-carton-panel-layout", + "form": "folding-carton panel layout, front-back-side information hierarchy; reserved regulatory and code zones; continuous alignment across folds or wraps", + "lineage": "Commercial package graphics", + "tags": [ + "panel-hierarchy", + "regulatory-zone", + "fold-continuity" + ] + }, + { + "id": "retail-packaging-service-medicine-carton-information-panel", + "form": "medicine carton information panel, front-back-side information hierarchy; reserved regulatory and code zones; continuous alignment across folds or wraps", + "lineage": "Commercial package graphics", + "tags": [ + "panel-hierarchy", + "regulatory-zone", + "fold-continuity" + ] + }, + { + "id": "retail-packaging-service-food-can-label", + "form": "food-can label, front-back-side information hierarchy; reserved regulatory and code zones; continuous alignment across folds or wraps", + "lineage": "Commercial package graphics", + "tags": [ + "panel-hierarchy", + "regulatory-zone", + "fold-continuity" + ] + }, + { + "id": "retail-packaging-service-glass-bottle-wrap-label", + "form": "glass-bottle wrap label, front-back-side information hierarchy; reserved regulatory and code zones; continuous alignment across folds or wraps", + "lineage": "Commercial package graphics", + "tags": [ + "panel-hierarchy", + "regulatory-zone", + "fold-continuity" + ] + }, + { + "id": "retail-packaging-service-paper-shopping-bag-print-layout", + "form": "paper shopping-bag print layout, front-back-side information hierarchy; reserved regulatory and code zones; continuous alignment across folds or wraps", + "lineage": "Commercial package graphics", + "tags": [ + "panel-hierarchy", + "regulatory-zone", + "fold-continuity" + ] + }, + { + "id": "retail-packaging-service-shipping-box-handling-panel", + "form": "shipping-box handling panel, front-back-side information hierarchy; reserved regulatory and code zones; continuous alignment across folds or wraps", + "lineage": "Commercial package graphics", + "tags": [ + "panel-hierarchy", + "regulatory-zone", + "fold-continuity" + ] + }, + { + "id": "retail-packaging-service-cosmetic-tube-label", + "form": "cosmetic tube label, front-back-side information hierarchy; reserved regulatory and code zones; continuous alignment across folds or wraps", + "lineage": "Commercial package graphics", + "tags": [ + "panel-hierarchy", + "regulatory-zone", + "fold-continuity" + ] + }, + { + "id": "retail-packaging-service-blister-pack-backing-card", + "form": "blister-pack backing card, front-back-side information hierarchy; reserved regulatory and code zones; continuous alignment across folds or wraps", + "lineage": "Commercial package graphics", + "tags": [ + "panel-hierarchy", + "regulatory-zone", + "fold-continuity" + ] + }, + { + "id": "retail-packaging-service-refill-pouch-label", + "form": "refill-pouch label, front-back-side information hierarchy; reserved regulatory and code zones; continuous alignment across folds or wraps", + "lineage": "Commercial package graphics", + "tags": [ + "panel-hierarchy", + "regulatory-zone", + "fold-continuity" + ] + }, + { + "id": "retail-packaging-service-multipack-sleeve", + "form": "multipack sleeve, front-back-side information hierarchy; reserved regulatory and code zones; continuous alignment across folds or wraps", + "lineage": "Commercial package graphics", + "tags": [ + "panel-hierarchy", + "regulatory-zone", + "fold-continuity" + ] + }, + { + "id": "retail-packaging-service-reusable-container-deposit-label", + "form": "reusable-container deposit label, front-back-side information hierarchy; reserved regulatory and code zones; continuous alignment across folds or wraps", + "lineage": "Commercial package graphics", + "tags": [ + "panel-hierarchy", + "regulatory-zone", + "fold-continuity" + ] + }, + { + "id": "retail-packaging-service-gift-wrap-pattern-system", + "form": "gift-wrap pattern system, front-back-side information hierarchy; reserved regulatory and code zones; continuous alignment across folds or wraps", + "lineage": "Commercial package graphics", + "tags": [ + "panel-hierarchy", + "regulatory-zone", + "fold-continuity" + ] + }, + { + "id": "retail-packaging-service-supermarket-shelf-edge-label", + "form": "supermarket shelf-edge label, clear item-to-price hierarchy; consistent category and variant coding; fast side-by-side comparison", + "lineage": "Shelf and market merchandising", + "tags": [ + "item-price", + "variant-code", + "comparison" + ] + }, + { + "id": "retail-packaging-service-pharmacy-shelf-talker", + "form": "pharmacy shelf talker, clear item-to-price hierarchy; consistent category and variant coding; fast side-by-side comparison", + "lineage": "Shelf and market merchandising", + "tags": [ + "item-price", + "variant-code", + "comparison" + ] + }, + { + "id": "retail-packaging-service-bookstore-shelf-card", + "form": "bookstore shelf card, clear item-to-price hierarchy; consistent category and variant coding; fast side-by-side comparison", + "lineage": "Shelf and market merchandising", + "tags": [ + "item-price", + "variant-code", + "comparison" + ] + }, + { + "id": "retail-packaging-service-electronics-specification-label", + "form": "electronics specification label, clear item-to-price hierarchy; consistent category and variant coding; fast side-by-side comparison", + "lineage": "Shelf and market merchandising", + "tags": [ + "item-price", + "variant-code", + "comparison" + ] + }, + { + "id": "retail-packaging-service-clothing-size-rail-marker", + "form": "clothing size-rail marker, clear item-to-price hierarchy; consistent category and variant coding; fast side-by-side comparison", + "lineage": "Shelf and market merchandising", + "tags": [ + "item-price", + "variant-code", + "comparison" + ] + }, + { + "id": "retail-packaging-service-produce-crate-sign", + "form": "produce crate sign, clear item-to-price hierarchy; consistent category and variant coding; fast side-by-side comparison", + "lineage": "Shelf and market merchandising", + "tags": [ + "item-price", + "variant-code", + "comparison" + ] + }, + { + "id": "retail-packaging-service-market-stall-price-card", + "form": "market-stall price card, clear item-to-price hierarchy; consistent category and variant coding; fast side-by-side comparison", + "lineage": "Shelf and market merchandising", + "tags": [ + "item-price", + "variant-code", + "comparison" + ] + }, + { + "id": "retail-packaging-service-bakery-display-ticket", + "form": "bakery display ticket, clear item-to-price hierarchy; consistent category and variant coding; fast side-by-side comparison", + "lineage": "Shelf and market merchandising", + "tags": [ + "item-price", + "variant-code", + "comparison" + ] + }, + { + "id": "retail-packaging-service-hardware-bin-label", + "form": "hardware bin label, clear item-to-price hierarchy; consistent category and variant coding; fast side-by-side comparison", + "lineage": "Shelf and market merchandising", + "tags": [ + "item-price", + "variant-code", + "comparison" + ] + }, + { + "id": "retail-packaging-service-department-store-directory", + "form": "department-store directory, clear item-to-price hierarchy; consistent category and variant coding; fast side-by-side comparison", + "lineage": "Shelf and market merchandising", + "tags": [ + "item-price", + "variant-code", + "comparison" + ] + }, + { + "id": "retail-packaging-service-vending-machine-selection-panel", + "form": "vending-machine selection panel, clear item-to-price hierarchy; consistent category and variant coding; fast side-by-side comparison", + "lineage": "Shelf and market merchandising", + "tags": [ + "item-price", + "variant-code", + "comparison" + ] + }, + { + "id": "retail-packaging-service-travel-retail-price-display", + "form": "travel-retail price display, clear item-to-price hierarchy; consistent category and variant coding; fast side-by-side comparison", + "lineage": "Shelf and market merchandising", + "tags": [ + "item-price", + "variant-code", + "comparison" + ] + }, + { + "id": "retail-packaging-service-mail-order-catalogue-spread", + "form": "mail-order catalogue spread, stable stock or reference identifiers; modular repeated product blocks; explicit selection and ordering cues", + "lineage": "Catalogue and mail-order systems", + "tags": [ + "stock-id", + "product-modules", + "order-cues" + ] + }, + { + "id": "retail-packaging-service-wholesale-line-sheet", + "form": "wholesale line sheet, stable stock or reference identifiers; modular repeated product blocks; explicit selection and ordering cues", + "lineage": "Catalogue and mail-order systems", + "tags": [ + "stock-id", + "product-modules", + "order-cues" + ] + }, + { + "id": "retail-packaging-service-seasonal-retail-lookbook", + "form": "seasonal retail lookbook, stable stock or reference identifiers; modular repeated product blocks; explicit selection and ordering cues", + "lineage": "Catalogue and mail-order systems", + "tags": [ + "stock-id", + "product-modules", + "order-cues" + ] + }, + { + "id": "retail-packaging-service-product-comparison-table", + "form": "product comparison table, stable stock or reference identifiers; modular repeated product blocks; explicit selection and ordering cues", + "lineage": "Catalogue and mail-order systems", + "tags": [ + "stock-id", + "product-modules", + "order-cues" + ] + }, + { + "id": "retail-packaging-service-sample-swatch-book", + "form": "sample swatch book, stable stock or reference identifiers; modular repeated product blocks; explicit selection and ordering cues", + "lineage": "Catalogue and mail-order systems", + "tags": [ + "stock-id", + "product-modules", + "order-cues" + ] + }, + { + "id": "retail-packaging-service-service-parts-catalogue-page", + "form": "service-parts catalogue page, stable stock or reference identifiers; modular repeated product blocks; explicit selection and ordering cues", + "lineage": "Catalogue and mail-order systems", + "tags": [ + "stock-id", + "product-modules", + "order-cues" + ] + }, + { + "id": "retail-packaging-service-auction-lot-catalogue", + "form": "auction lot catalogue, stable stock or reference identifiers; modular repeated product blocks; explicit selection and ordering cues", + "lineage": "Catalogue and mail-order systems", + "tags": [ + "stock-id", + "product-modules", + "order-cues" + ] + }, + { + "id": "retail-packaging-service-restaurant-supply-catalogue", + "form": "restaurant-supply catalogue, stable stock or reference identifiers; modular repeated product blocks; explicit selection and ordering cues", + "lineage": "Catalogue and mail-order systems", + "tags": [ + "stock-id", + "product-modules", + "order-cues" + ] + }, + { + "id": "retail-packaging-service-seed-catalogue-spread", + "form": "seed catalogue spread, stable stock or reference identifiers; modular repeated product blocks; explicit selection and ordering cues", + "lineage": "Catalogue and mail-order systems", + "tags": [ + "stock-id", + "product-modules", + "order-cues" + ] + }, + { + "id": "retail-packaging-service-furniture-catalogue-room-index", + "form": "furniture catalogue room index, stable stock or reference identifiers; modular repeated product blocks; explicit selection and ordering cues", + "lineage": "Catalogue and mail-order systems", + "tags": [ + "stock-id", + "product-modules", + "order-cues" + ] + }, + { + "id": "retail-packaging-service-subscription-parcel-insert", + "form": "subscription parcel insert, stable stock or reference identifiers; modular repeated product blocks; explicit selection and ordering cues", + "lineage": "Catalogue and mail-order systems", + "tags": [ + "stock-id", + "product-modules", + "order-cues" + ] + }, + { + "id": "retail-packaging-service-dealer-order-guide", + "form": "dealer order guide, stable stock or reference identifiers; modular repeated product blocks; explicit selection and ordering cues", + "lineage": "Catalogue and mail-order systems", + "tags": [ + "stock-id", + "product-modules", + "order-cues" + ] + }, + { + "id": "retail-packaging-service-point-of-sale-receipt", + "form": "point-of-sale receipt, sequential transaction steps; visible confirmation and completion states; adjacent exception and help routes", + "lineage": "Point-of-sale transactions", + "tags": [ + "transaction-steps", + "confirmation", + "exception-route" + ] + }, + { + "id": "retail-packaging-service-cash-register-key-map", + "form": "cash-register key map, sequential transaction steps; visible confirmation and completion states; adjacent exception and help routes", + "lineage": "Point-of-sale transactions", + "tags": [ + "transaction-steps", + "confirmation", + "exception-route" + ] + }, + { + "id": "retail-packaging-service-self-checkout-interface", + "form": "self-checkout interface, sequential transaction steps; visible confirmation and completion states; adjacent exception and help routes", + "lineage": "Point-of-sale transactions", + "tags": [ + "transaction-steps", + "confirmation", + "exception-route" + ] + }, + { + "id": "retail-packaging-service-counter-order-ticket", + "form": "counter order ticket, sequential transaction steps; visible confirmation and completion states; adjacent exception and help routes", + "lineage": "Point-of-sale transactions", + "tags": [ + "transaction-steps", + "confirmation", + "exception-route" + ] + }, + { + "id": "retail-packaging-service-queue-number-system", + "form": "queue number system, sequential transaction steps; visible confirmation and completion states; adjacent exception and help routes", + "lineage": "Point-of-sale transactions", + "tags": [ + "transaction-steps", + "confirmation", + "exception-route" + ] + }, + { + "id": "retail-packaging-service-service-bell-placard", + "form": "service bell placard, sequential transaction steps; visible confirmation and completion states; adjacent exception and help routes", + "lineage": "Point-of-sale transactions", + "tags": [ + "transaction-steps", + "confirmation", + "exception-route" + ] + }, + { + "id": "retail-packaging-service-return-policy-board", + "form": "return policy board, sequential transaction steps; visible confirmation and completion states; adjacent exception and help routes", + "lineage": "Point-of-sale transactions", + "tags": [ + "transaction-steps", + "confirmation", + "exception-route" + ] + }, + { + "id": "retail-packaging-service-gift-receipt", + "form": "gift receipt, sequential transaction steps; visible confirmation and completion states; adjacent exception and help routes", + "lineage": "Point-of-sale transactions", + "tags": [ + "transaction-steps", + "confirmation", + "exception-route" + ] + }, + { + "id": "retail-packaging-service-layaway-agreement", + "form": "layaway agreement, sequential transaction steps; visible confirmation and completion states; adjacent exception and help routes", + "lineage": "Point-of-sale transactions", + "tags": [ + "transaction-steps", + "confirmation", + "exception-route" + ] + }, + { + "id": "retail-packaging-service-warranty-registration-card", + "form": "warranty registration card, sequential transaction steps; visible confirmation and completion states; adjacent exception and help routes", + "lineage": "Point-of-sale transactions", + "tags": [ + "transaction-steps", + "confirmation", + "exception-route" + ] + }, + { + "id": "retail-packaging-service-customer-stamp-card", + "form": "customer stamp card, sequential transaction steps; visible confirmation and completion states; adjacent exception and help routes", + "lineage": "Point-of-sale transactions", + "tags": [ + "transaction-steps", + "confirmation", + "exception-route" + ] + }, + { + "id": "retail-packaging-service-cash-drawer-count-sheet", + "form": "cash-drawer count sheet, sequential transaction steps; visible confirmation and completion states; adjacent exception and help routes", + "lineage": "Point-of-sale transactions", + "tags": [ + "transaction-steps", + "confirmation", + "exception-route" + ] + }, + { + "id": "retail-packaging-service-hotel-check-in-folio", + "form": "hotel check-in folio, paired customer and item identifiers; service-stage status markers; prominent pickup or deadline cues", + "lineage": "Hospitality and personal-service tickets", + "tags": [ + "paired-id", + "service-stage", + "pickup-deadline" + ] + }, + { + "id": "retail-packaging-service-repair-intake-ticket", + "form": "repair intake ticket, paired customer and item identifiers; service-stage status markers; prominent pickup or deadline cues", + "lineage": "Hospitality and personal-service tickets", + "tags": [ + "paired-id", + "service-stage", + "pickup-deadline" + ] + }, + { + "id": "retail-packaging-service-dry-cleaning-claim-ticket", + "form": "dry-cleaning claim ticket, paired customer and item identifiers; service-stage status markers; prominent pickup or deadline cues", + "lineage": "Hospitality and personal-service tickets", + "tags": [ + "paired-id", + "service-stage", + "pickup-deadline" + ] + }, + { + "id": "retail-packaging-service-coat-check-token-system", + "form": "coat-check token system, paired customer and item identifiers; service-stage status markers; prominent pickup or deadline cues", + "lineage": "Hospitality and personal-service tickets", + "tags": [ + "paired-id", + "service-stage", + "pickup-deadline" + ] + } + ] + }, + { + "id": "food-agriculture-hospitality", + "label": "Food, agriculture & hospitality", + "description": "Seasonal, procedural, hosting, preparation, and service systems grounded in everyday practice.", + "concepts": [ + { + "id": "food-agriculture-hospitality-fixed-price-restaurant-menu", + "form": "fixed-price restaurant menu, course or category hierarchy; price and dietary annotations; a clear ordering sequence", + "lineage": "Menu and meal-service publishing", + "tags": [ + "course-hierarchy", + "dietary-notes", + "order-sequence" + ] + }, + { + "id": "food-agriculture-hospitality-cafeteria-menu-board", + "form": "cafeteria menu board, course or category hierarchy; price and dietary annotations; a clear ordering sequence", + "lineage": "Menu and meal-service publishing", + "tags": [ + "course-hierarchy", + "dietary-notes", + "order-sequence" + ] + }, + { + "id": "food-agriculture-hospitality-street-food-stall-menu", + "form": "street-food stall menu, course or category hierarchy; price and dietary annotations; a clear ordering sequence", + "lineage": "Menu and meal-service publishing", + "tags": [ + "course-hierarchy", + "dietary-notes", + "order-sequence" + ] + }, + { + "id": "food-agriculture-hospitality-hotel-breakfast-card", + "form": "hotel breakfast card, course or category hierarchy; price and dietary annotations; a clear ordering sequence", + "lineage": "Menu and meal-service publishing", + "tags": [ + "course-hierarchy", + "dietary-notes", + "order-sequence" + ] + }, + { + "id": "food-agriculture-hospitality-tasting-sequence-card", + "form": "tasting sequence card, course or category hierarchy; price and dietary annotations; a clear ordering sequence", + "lineage": "Menu and meal-service publishing", + "tags": [ + "course-hierarchy", + "dietary-notes", + "order-sequence" + ] + }, + { + "id": "food-agriculture-hospitality-room-service-menu", + "form": "room-service menu, course or category hierarchy; price and dietary annotations; a clear ordering sequence", + "lineage": "Menu and meal-service publishing", + "tags": [ + "course-hierarchy", + "dietary-notes", + "order-sequence" + ] + }, + { + "id": "food-agriculture-hospitality-banquet-menu", + "form": "banquet menu, course or category hierarchy; price and dietary annotations; a clear ordering sequence", + "lineage": "Menu and meal-service publishing", + "tags": [ + "course-hierarchy", + "dietary-notes", + "order-sequence" + ] + }, + { + "id": "food-agriculture-hospitality-school-lunch-calendar", + "form": "school lunch calendar, course or category hierarchy; price and dietary annotations; a clear ordering sequence", + "lineage": "Menu and meal-service publishing", + "tags": [ + "course-hierarchy", + "dietary-notes", + "order-sequence" + ] + }, + { + "id": "food-agriculture-hospitality-airline-meal-card", + "form": "airline meal card, course or category hierarchy; price and dietary annotations; a clear ordering sequence", + "lineage": "Menu and meal-service publishing", + "tags": [ + "course-hierarchy", + "dietary-notes", + "order-sequence" + ] + }, + { + "id": "food-agriculture-hospitality-tea-room-menu", + "form": "tea-room menu, course or category hierarchy; price and dietary annotations; a clear ordering sequence", + "lineage": "Menu and meal-service publishing", + "tags": [ + "course-hierarchy", + "dietary-notes", + "order-sequence" + ] + }, + { + "id": "food-agriculture-hospitality-bakery-product-list", + "form": "bakery product list, course or category hierarchy; price and dietary annotations; a clear ordering sequence", + "lineage": "Menu and meal-service publishing", + "tags": [ + "course-hierarchy", + "dietary-notes", + "order-sequence" + ] + }, + { + "id": "food-agriculture-hospitality-bar-drinks-ledger", + "form": "bar drinks ledger, course or category hierarchy; price and dietary annotations; a clear ordering sequence", + "lineage": "Menu and meal-service publishing", + "tags": [ + "course-hierarchy", + "dietary-notes", + "order-sequence" + ] + }, + { + "id": "food-agriculture-hospitality-kitchen-order-ticket", + "form": "kitchen order ticket, ingredient item and quantity fields; ordered time-sensitive steps; verification initials and checkpoints", + "lineage": "Professional kitchen operations", + "tags": [ + "item-quantity", + "timed-steps", + "verification" + ] + }, + { + "id": "food-agriculture-hospitality-standard-recipe-card", + "form": "standard recipe card, ingredient item and quantity fields; ordered time-sensitive steps; verification initials and checkpoints", + "lineage": "Professional kitchen operations", + "tags": [ + "item-quantity", + "timed-steps", + "verification" + ] + }, + { + "id": "food-agriculture-hospitality-daily-preparation-list", + "form": "daily preparation list, ingredient item and quantity fields; ordered time-sensitive steps; verification initials and checkpoints", + "lineage": "Professional kitchen operations", + "tags": [ + "item-quantity", + "timed-steps", + "verification" + ] + }, + { + "id": "food-agriculture-hospitality-station-setup-chart", + "form": "station setup chart, ingredient item and quantity fields; ordered time-sensitive steps; verification initials and checkpoints", + "lineage": "Professional kitchen operations", + "tags": [ + "item-quantity", + "timed-steps", + "verification" + ] + }, + { + "id": "food-agriculture-hospitality-food-safety-temperature-log", + "form": "food-safety temperature log, ingredient item and quantity fields; ordered time-sensitive steps; verification initials and checkpoints", + "lineage": "Professional kitchen operations", + "tags": [ + "item-quantity", + "timed-steps", + "verification" + ] + }, + { + "id": "food-agriculture-hospitality-allergen-matrix", + "form": "allergen matrix, ingredient item and quantity fields; ordered time-sensitive steps; verification initials and checkpoints", + "lineage": "Professional kitchen operations", + "tags": [ + "item-quantity", + "timed-steps", + "verification" + ] + }, + { + "id": "food-agriculture-hospitality-production-batch-sheet", + "form": "production batch sheet, ingredient item and quantity fields; ordered time-sensitive steps; verification initials and checkpoints", + "lineage": "Professional kitchen operations", + "tags": [ + "item-quantity", + "timed-steps", + "verification" + ] + }, + { + "id": "food-agriculture-hospitality-butcher-cut-chart", + "form": "butcher cut chart, ingredient item and quantity fields; ordered time-sensitive steps; verification initials and checkpoints", + "lineage": "Professional kitchen operations", + "tags": [ + "item-quantity", + "timed-steps", + "verification" + ] + }, + { + "id": "food-agriculture-hospitality-bakery-proofing-schedule", + "form": "bakery proofing schedule, ingredient item and quantity fields; ordered time-sensitive steps; verification initials and checkpoints", + "lineage": "Professional kitchen operations", + "tags": [ + "item-quantity", + "timed-steps", + "verification" + ] + }, + { + "id": "food-agriculture-hospitality-brewing-batch-record", + "form": "brewing batch record, ingredient item and quantity fields; ordered time-sensitive steps; verification initials and checkpoints", + "lineage": "Professional kitchen operations", + "tags": [ + "item-quantity", + "timed-steps", + "verification" + ] + }, + { + "id": "food-agriculture-hospitality-catering-function-sheet", + "form": "catering function sheet, ingredient item and quantity fields; ordered time-sensitive steps; verification initials and checkpoints", + "lineage": "Professional kitchen operations", + "tags": [ + "item-quantity", + "timed-steps", + "verification" + ] + }, + { + "id": "food-agriculture-hospitality-dishwashing-sanitation-checklist", + "form": "dishwashing sanitation checklist, ingredient item and quantity fields; ordered time-sensitive steps; verification initials and checkpoints", + "lineage": "Professional kitchen operations", + "tags": [ + "item-quantity", + "timed-steps", + "verification" + ] + }, + { + "id": "food-agriculture-hospitality-crop-rotation-chart", + "form": "crop-rotation chart, coded spatial plots or groups; seasonal cycle markers; measured inputs and recorded outcomes", + "lineage": "Agricultural planning and field records", + "tags": [ + "spatial-plots", + "seasonal-cycle", + "input-output" + ] + }, + { + "id": "food-agriculture-hospitality-field-planting-map", + "form": "field planting map, coded spatial plots or groups; seasonal cycle markers; measured inputs and recorded outcomes", + "lineage": "Agricultural planning and field records", + "tags": [ + "spatial-plots", + "seasonal-cycle", + "input-output" + ] + }, + { + "id": "food-agriculture-hospitality-seed-packet-instruction-panel", + "form": "seed-packet instruction panel, coded spatial plots or groups; seasonal cycle markers; measured inputs and recorded outcomes", + "lineage": "Agricultural planning and field records", + "tags": [ + "spatial-plots", + "seasonal-cycle", + "input-output" + ] + }, + { + "id": "food-agriculture-hospitality-irrigation-schedule", + "form": "irrigation schedule, coded spatial plots or groups; seasonal cycle markers; measured inputs and recorded outcomes", + "lineage": "Agricultural planning and field records", + "tags": [ + "spatial-plots", + "seasonal-cycle", + "input-output" + ] + }, + { + "id": "food-agriculture-hospitality-harvest-tally-sheet", + "form": "harvest tally sheet, coded spatial plots or groups; seasonal cycle markers; measured inputs and recorded outcomes", + "lineage": "Agricultural planning and field records", + "tags": [ + "spatial-plots", + "seasonal-cycle", + "input-output" + ] + }, + { + "id": "food-agriculture-hospitality-livestock-feed-chart", + "form": "livestock feed chart, coded spatial plots or groups; seasonal cycle markers; measured inputs and recorded outcomes", + "lineage": "Agricultural planning and field records", + "tags": [ + "spatial-plots", + "seasonal-cycle", + "input-output" + ] + }, + { + "id": "food-agriculture-hospitality-greenhouse-bench-map", + "form": "greenhouse bench map, coded spatial plots or groups; seasonal cycle markers; measured inputs and recorded outcomes", + "lineage": "Agricultural planning and field records", + "tags": [ + "spatial-plots", + "seasonal-cycle", + "input-output" + ] + }, + { + "id": "food-agriculture-hospitality-orchard-block-register", + "form": "orchard block register, coded spatial plots or groups; seasonal cycle markers; measured inputs and recorded outcomes", + "lineage": "Agricultural planning and field records", + "tags": [ + "spatial-plots", + "seasonal-cycle", + "input-output" + ] + }, + { + "id": "food-agriculture-hospitality-soil-sampling-grid", + "form": "soil sampling grid, coded spatial plots or groups; seasonal cycle markers; measured inputs and recorded outcomes", + "lineage": "Agricultural planning and field records", + "tags": [ + "spatial-plots", + "seasonal-cycle", + "input-output" + ] + }, + { + "id": "food-agriculture-hospitality-fertilizer-application-log", + "form": "fertilizer application log, coded spatial plots or groups; seasonal cycle markers; measured inputs and recorded outcomes", + "lineage": "Agricultural planning and field records", + "tags": [ + "spatial-plots", + "seasonal-cycle", + "input-output" + ] + }, + { + "id": "food-agriculture-hospitality-pest-scouting-sheet", + "form": "pest scouting sheet, coded spatial plots or groups; seasonal cycle markers; measured inputs and recorded outcomes", + "lineage": "Agricultural planning and field records", + "tags": [ + "spatial-plots", + "seasonal-cycle", + "input-output" + ] + }, + { + "id": "food-agriculture-hospitality-farm-equipment-maintenance-board", + "form": "farm-equipment maintenance board, coded spatial plots or groups; seasonal cycle markers; measured inputs and recorded outcomes", + "lineage": "Agricultural planning and field records", + "tags": [ + "spatial-plots", + "seasonal-cycle", + "input-output" + ] + }, + { + "id": "food-agriculture-hospitality-wholesale-produce-price-board", + "form": "wholesale produce price board, grade lot and origin identifiers; harvest packing or auction dates; quantity and price comparisons", + "lineage": "Food commodity and wholesale trade", + "tags": [ + "grade-origin", + "dated-lot", + "quantity-price" + ] + }, + { + "id": "food-agriculture-hospitality-fish-auction-lot-ticket", + "form": "fish auction lot ticket, grade lot and origin identifiers; harvest packing or auction dates; quantity and price comparisons", + "lineage": "Food commodity and wholesale trade", + "tags": [ + "grade-origin", + "dated-lot", + "quantity-price" + ] + }, + { + "id": "food-agriculture-hospitality-grain-elevator-receipt", + "form": "grain-elevator receipt, grade lot and origin identifiers; harvest packing or auction dates; quantity and price comparisons", + "lineage": "Food commodity and wholesale trade", + "tags": [ + "grade-origin", + "dated-lot", + "quantity-price" + ] + }, + { + "id": "food-agriculture-hospitality-livestock-market-catalogue", + "form": "livestock market catalogue, grade lot and origin identifiers; harvest packing or auction dates; quantity and price comparisons", + "lineage": "Food commodity and wholesale trade", + "tags": [ + "grade-origin", + "dated-lot", + "quantity-price" + ] + }, + { + "id": "food-agriculture-hospitality-farm-share-box-list", + "form": "farm-share box list, grade lot and origin identifiers; harvest packing or auction dates; quantity and price comparisons", + "lineage": "Food commodity and wholesale trade", + "tags": [ + "grade-origin", + "dated-lot", + "quantity-price" + ] + }, + { + "id": "food-agriculture-hospitality-farmers-market-stall-sign", + "form": "farmers-market stall sign, grade lot and origin identifiers; harvest packing or auction dates; quantity and price comparisons", + "lineage": "Food commodity and wholesale trade", + "tags": [ + "grade-origin", + "dated-lot", + "quantity-price" + ] + }, + { + "id": "food-agriculture-hospitality-restaurant-produce-order-sheet", + "form": "restaurant produce order sheet, grade lot and origin identifiers; harvest packing or auction dates; quantity and price comparisons", + "lineage": "Food commodity and wholesale trade", + "tags": [ + "grade-origin", + "dated-lot", + "quantity-price" + ] + }, + { + "id": "food-agriculture-hospitality-cold-storage-inventory-tag", + "form": "cold-storage inventory tag, grade lot and origin identifiers; harvest packing or auction dates; quantity and price comparisons", + "lineage": "Food commodity and wholesale trade", + "tags": [ + "grade-origin", + "dated-lot", + "quantity-price" + ] + }, + { + "id": "food-agriculture-hospitality-dairy-collection-route-card", + "form": "dairy collection route card, grade lot and origin identifiers; harvest packing or auction dates; quantity and price comparisons", + "lineage": "Food commodity and wholesale trade", + "tags": [ + "grade-origin", + "dated-lot", + "quantity-price" + ] + }, + { + "id": "food-agriculture-hospitality-coffee-grading-sheet", + "form": "coffee grading sheet, grade lot and origin identifiers; harvest packing or auction dates; quantity and price comparisons", + "lineage": "Food commodity and wholesale trade", + "tags": [ + "grade-origin", + "dated-lot", + "quantity-price" + ] + }, + { + "id": "food-agriculture-hospitality-tea-auction-sample-label", + "form": "tea auction sample label, grade lot and origin identifiers; harvest packing or auction dates; quantity and price comparisons", + "lineage": "Food commodity and wholesale trade", + "tags": [ + "grade-origin", + "dated-lot", + "quantity-price" + ] + }, + { + "id": "food-agriculture-hospitality-spice-warehouse-bale-mark", + "form": "spice warehouse bale mark, grade lot and origin identifiers; harvest packing or auction dates; quantity and price comparisons", + "lineage": "Food commodity and wholesale trade", + "tags": [ + "grade-origin", + "dated-lot", + "quantity-price" + ] + }, + { + "id": "food-agriculture-hospitality-hotel-room-key-rack", + "form": "hotel room-key rack, guest-to-resource matching; time and readiness status; location and wayfinding cues", + "lineage": "Guest and venue operations", + "tags": [ + "resource-match", + "readiness", + "wayfinding" + ] + }, + { + "id": "food-agriculture-hospitality-guest-registration-card", + "form": "guest registration card, guest-to-resource matching; time and readiness status; location and wayfinding cues", + "lineage": "Guest and venue operations", + "tags": [ + "resource-match", + "readiness", + "wayfinding" + ] + }, + { + "id": "food-agriculture-hospitality-concierge-message-slip", + "form": "concierge message slip, guest-to-resource matching; time and readiness status; location and wayfinding cues", + "lineage": "Guest and venue operations", + "tags": [ + "resource-match", + "readiness", + "wayfinding" + ] + }, + { + "id": "food-agriculture-hospitality-housekeeping-room-status-board", + "form": "housekeeping room-status board, guest-to-resource matching; time and readiness status; location and wayfinding cues", + "lineage": "Guest and venue operations", + "tags": [ + "resource-match", + "readiness", + "wayfinding" + ] + }, + { + "id": "food-agriculture-hospitality-restaurant-table-plan", + "form": "restaurant table plan, guest-to-resource matching; time and readiness status; location and wayfinding cues", + "lineage": "Guest and venue operations", + "tags": [ + "resource-match", + "readiness", + "wayfinding" + ] + }, + { + "id": "food-agriculture-hospitality-banquet-seating-chart", + "form": "banquet seating chart, guest-to-resource matching; time and readiness status; location and wayfinding cues", + "lineage": "Guest and venue operations", + "tags": [ + "resource-match", + "readiness", + "wayfinding" + ] + }, + { + "id": "food-agriculture-hospitality-cloakroom-ticket", + "form": "cloakroom ticket, guest-to-resource matching; time and readiness status; location and wayfinding cues", + "lineage": "Guest and venue operations", + "tags": [ + "resource-match", + "readiness", + "wayfinding" + ] + }, + { + "id": "food-agriculture-hospitality-spa-treatment-schedule", + "form": "spa treatment schedule, guest-to-resource matching; time and readiness status; location and wayfinding cues", + "lineage": "Guest and venue operations", + "tags": [ + "resource-match", + "readiness", + "wayfinding" + ] + }, + { + "id": "food-agriculture-hospitality-campground-pitch-map", + "form": "campground pitch map, guest-to-resource matching; time and readiness status; location and wayfinding cues", + "lineage": "Guest and venue operations", + "tags": [ + "resource-match", + "readiness", + "wayfinding" + ] + }, + { + "id": "food-agriculture-hospitality-hostel-bed-allocation-board", + "form": "hostel bed-allocation board, guest-to-resource matching; time and readiness status; location and wayfinding cues", + "lineage": "Guest and venue operations", + "tags": [ + "resource-match", + "readiness", + "wayfinding" + ] + }, + { + "id": "food-agriculture-hospitality-passenger-ship-daily-program", + "form": "passenger ship daily program, guest-to-resource matching; time and readiness status; location and wayfinding cues", + "lineage": "Guest and venue operations", + "tags": [ + "resource-match", + "readiness", + "wayfinding" + ] + }, + { + "id": "food-agriculture-hospitality-resort-activity-timetable", + "form": "resort activity timetable, guest-to-resource matching; time and readiness status; location and wayfinding cues", + "lineage": "Guest and venue operations", + "tags": [ + "resource-match", + "readiness", + "wayfinding" + ] + }, + { + "id": "food-agriculture-hospitality-seasonal-produce-calendar", + "form": "seasonal produce calendar, ordered categories or scales; sensory and technical annotations; time-based progression markers", + "lineage": "Culinary reference and process knowledge", + "tags": [ + "category-scale", + "annotations", + "time-progression" + ] + }, + { + "id": "food-agriculture-hospitality-sensory-flavor-wheel", + "form": "sensory flavor wheel, ordered categories or scales; sensory and technical annotations; time-based progression markers", + "lineage": "Culinary reference and process knowledge", + "tags": [ + "category-scale", + "annotations", + "time-progression" + ] + }, + { + "id": "food-agriculture-hospitality-cheese-aging-chart", + "form": "cheese aging chart, ordered categories or scales; sensory and technical annotations; time-based progression markers", + "lineage": "Culinary reference and process knowledge", + "tags": [ + "category-scale", + "annotations", + "time-progression" + ] + }, + { + "id": "food-agriculture-hospitality-coffee-roast-curve", + "form": "coffee roast curve, ordered categories or scales; sensory and technical annotations; time-based progression markers", + "lineage": "Culinary reference and process knowledge", + "tags": [ + "category-scale", + "annotations", + "time-progression" + ] + } + ] + }, + { + "id": "industrial-control", + "label": "Industrial control", + "description": "Operational panels and status systems with repeated channels, thresholds, alarms, and intervention.", + "concepts": [ + { + "id": "industrial-control-mission-control-status-wall", + "form": "a mission-control status wall, with countdown clocks and go/no-go polls", + "lineage": "Industrial control", + "tags": [ + "status-field", + "calibrated-control", + "exception-signal" + ] + }, + { + "id": "industrial-control-ship-s-bridge-instrument-panel", + "form": "a ship's bridge instrument panel, with engine-order telegraph and compass repeaters", + "lineage": "Industrial control", + "tags": [ + "status-field", + "calibrated-control", + "exception-signal" + ] + }, + { + "id": "industrial-control-recording-studio-mixing-console", + "form": "a recording studio mixing console, with repeated channel strips, shared buses, and master controls", + "lineage": "Industrial control", + "tags": [ + "status-field", + "calibrated-control", + "exception-signal" + ] + }, + { + "id": "industrial-control-patch-bay-diagram", + "form": "a patch-bay diagram, with source-destination matrices and signal groupings", + "lineage": "Industrial control", + "tags": [ + "status-field", + "calibrated-control", + "exception-signal" + ] + }, + { + "id": "industrial-control-aircraft-preflight-checklist", + "form": "an aircraft preflight checklist, with phased sections, binary confirmations, and hold points", + "lineage": "Industrial control", + "tags": [ + "status-field", + "calibrated-control", + "exception-signal" + ] + }, + { + "id": "industrial-control-oscilloscope-faceplate", + "form": "an oscilloscope faceplate, with a calibrated grid, overlaid traces, and channel controls", + "lineage": "Industrial control", + "tags": [ + "status-field", + "calibrated-control", + "exception-signal" + ] + }, + { + "id": "industrial-control-sonar-sweep-display", + "form": "a sonar sweep display, with concentric distance rings, sweep lines, and blip markers", + "lineage": "Industrial control", + "tags": [ + "status-field", + "calibrated-control", + "exception-signal" + ] + }, + { + "id": "industrial-control-weather-station-s-synoptic-chart", + "form": "a weather station's synoptic chart, with fronts, isobars, and station models", + "lineage": "Industrial control", + "tags": [ + "status-field", + "calibrated-control", + "exception-signal" + ] + }, + { + "id": "industrial-control-seismograph-station-s-drum-recorders-and-event-logs", + "form": "a seismograph station's drum recorders and event logs, with synchronized traces, anomaly markers, and timestamped annotations", + "lineage": "Industrial control", + "tags": [ + "status-field", + "calibrated-control", + "exception-signal" + ] + }, + { + "id": "industrial-control-darkroom-contact-sheet", + "form": "a darkroom contact sheet, with thumbnail grids, frame numbers, and grease-pencil selection marks", + "lineage": "Industrial control", + "tags": [ + "status-field", + "calibrated-control", + "exception-signal" + ] + }, + { + "id": "industrial-control-camera-viewfinder-overlay", + "form": "a camera viewfinder overlay, with framing grid lines, exposure bars, and status indicators", + "lineage": "Industrial control", + "tags": [ + "status-field", + "calibrated-control", + "exception-signal" + ] + }, + { + "id": "industrial-control-medical-triage-chart", + "form": "a medical triage chart, with color-coded priority tiers, vital-sign thresholds, and symptom checklists", + "lineage": "Industrial control", + "tags": [ + "status-field", + "calibrated-control", + "exception-signal" + ] + }, + { + "id": "industrial-control-hospital-patient-chart", + "form": "a hospital patient chart, with a summary header, time-series observations, and alert annotations", + "lineage": "Industrial control", + "tags": [ + "status-field", + "calibrated-control", + "exception-signal" + ] + }, + { + "id": "industrial-control-periodic-table", + "form": "a periodic table, with categorical families, ordered coordinates, and encoded properties", + "lineage": "Industrial control", + "tags": [ + "status-field", + "calibrated-control", + "exception-signal" + ] + }, + { + "id": "industrial-control-analog-refinery-control-panel", + "form": "analog refinery control panel, a spatial process-flow mimic; state lamps adjacent to equipment; controls grouped by subsystem", + "lineage": "Twentieth-century process control panels", + "tags": [ + "process-mimic", + "state-lamps", + "control-groups" + ] + }, + { + "id": "industrial-control-power-station-mimic-panel", + "form": "power-station mimic panel, a spatial process-flow mimic; state lamps adjacent to equipment; controls grouped by subsystem", + "lineage": "Twentieth-century process control panels", + "tags": [ + "process-mimic", + "state-lamps", + "control-groups" + ] + }, + { + "id": "industrial-control-water-treatment-control-board", + "form": "water-treatment control board, a spatial process-flow mimic; state lamps adjacent to equipment; controls grouped by subsystem", + "lineage": "Twentieth-century process control panels", + "tags": [ + "process-mimic", + "state-lamps", + "control-groups" + ] + }, + { + "id": "industrial-control-chemical-plant-annunciator", + "form": "chemical-plant annunciator, a spatial process-flow mimic; state lamps adjacent to equipment; controls grouped by subsystem", + "lineage": "Twentieth-century process control panels", + "tags": [ + "process-mimic", + "state-lamps", + "control-groups" + ] + }, + { + "id": "industrial-control-food-processing-line-panel", + "form": "food-processing line panel, a spatial process-flow mimic; state lamps adjacent to equipment; controls grouped by subsystem", + "lineage": "Twentieth-century process control panels", + "tags": [ + "process-mimic", + "state-lamps", + "control-groups" + ] + }, + { + "id": "industrial-control-paper-mill-control-desk", + "form": "paper-mill control desk, a spatial process-flow mimic; state lamps adjacent to equipment; controls grouped by subsystem", + "lineage": "Twentieth-century process control panels", + "tags": [ + "process-mimic", + "state-lamps", + "control-groups" + ] + }, + { + "id": "industrial-control-cement-kiln-operator-panel", + "form": "cement-kiln operator panel, a spatial process-flow mimic; state lamps adjacent to equipment; controls grouped by subsystem", + "lineage": "Twentieth-century process control panels", + "tags": [ + "process-mimic", + "state-lamps", + "control-groups" + ] + }, + { + "id": "industrial-control-mine-ventilation-board", + "form": "mine ventilation board, a spatial process-flow mimic; state lamps adjacent to equipment; controls grouped by subsystem", + "lineage": "Twentieth-century process control panels", + "tags": [ + "process-mimic", + "state-lamps", + "control-groups" + ] + }, + { + "id": "industrial-control-district-heating-control-board", + "form": "district-heating control board, a spatial process-flow mimic; state lamps adjacent to equipment; controls grouped by subsystem", + "lineage": "Twentieth-century process control panels", + "tags": [ + "process-mimic", + "state-lamps", + "control-groups" + ] + }, + { + "id": "industrial-control-wastewater-pumping-station-panel", + "form": "wastewater pumping-station panel, a spatial process-flow mimic; state lamps adjacent to equipment; controls grouped by subsystem", + "lineage": "Twentieth-century process control panels", + "tags": [ + "process-mimic", + "state-lamps", + "control-groups" + ] + }, + { + "id": "industrial-control-grain-elevator-control-board", + "form": "grain-elevator control board, a spatial process-flow mimic; state lamps adjacent to equipment; controls grouped by subsystem", + "lineage": "Twentieth-century process control panels", + "tags": [ + "process-mimic", + "state-lamps", + "control-groups" + ] + }, + { + "id": "industrial-control-cold-storage-refrigeration-panel", + "form": "cold-storage refrigeration panel, a spatial process-flow mimic; state lamps adjacent to equipment; controls grouped by subsystem", + "lineage": "Twentieth-century process control panels", + "tags": [ + "process-mimic", + "state-lamps", + "control-groups" + ] + }, + { + "id": "industrial-control-pressure-gauge-cluster", + "form": "pressure-gauge cluster, standardized scales and units; marked normal and limit zones; adjacent needle or value comparison", + "lineage": "Mechanical instrumentation", + "tags": [ + "scales", + "limit-zones", + "value-compare" + ] + }, + { + "id": "industrial-control-engine-instrument-panel", + "form": "engine instrument panel, standardized scales and units; marked normal and limit zones; adjacent needle or value comparison", + "lineage": "Mechanical instrumentation", + "tags": [ + "scales", + "limit-zones", + "value-compare" + ] + }, + { + "id": "industrial-control-electrical-switchboard-meter-bank", + "form": "electrical switchboard meter bank, standardized scales and units; marked normal and limit zones; adjacent needle or value comparison", + "lineage": "Mechanical instrumentation", + "tags": [ + "scales", + "limit-zones", + "value-compare" + ] + }, + { + "id": "industrial-control-boiler-gauge-glass-panel", + "form": "boiler gauge-glass panel, standardized scales and units; marked normal and limit zones; adjacent needle or value comparison", + "lineage": "Mechanical instrumentation", + "tags": [ + "scales", + "limit-zones", + "value-compare" + ] + }, + { + "id": "industrial-control-laboratory-instrument-rack", + "form": "laboratory instrument rack, standardized scales and units; marked normal and limit zones; adjacent needle or value comparison", + "lineage": "Mechanical instrumentation", + "tags": [ + "scales", + "limit-zones", + "value-compare" + ] + }, + { + "id": "industrial-control-machine-tool-dial-panel", + "form": "machine-tool dial panel, standardized scales and units; marked normal and limit zones; adjacent needle or value comparison", + "lineage": "Mechanical instrumentation", + "tags": [ + "scales", + "limit-zones", + "value-compare" + ] + }, + { + "id": "industrial-control-ship-engine-room-gauge-board", + "form": "ship engine-room gauge board, standardized scales and units; marked normal and limit zones; adjacent needle or value comparison", + "lineage": "Mechanical instrumentation", + "tags": [ + "scales", + "limit-zones", + "value-compare" + ] + }, + { + "id": "industrial-control-railway-signal-box-indicator", + "form": "railway signal-box indicator, standardized scales and units; marked normal and limit zones; adjacent needle or value comparison", + "lineage": "Mechanical instrumentation", + "tags": [ + "scales", + "limit-zones", + "value-compare" + ] + }, + { + "id": "industrial-control-aircraft-maintenance-test-panel", + "form": "aircraft maintenance test panel, standardized scales and units; marked normal and limit zones; adjacent needle or value comparison", + "lineage": "Mechanical instrumentation", + "tags": [ + "scales", + "limit-zones", + "value-compare" + ] + }, + { + "id": "industrial-control-building-plant-room-gauge-panel", + "form": "building plant-room gauge panel, standardized scales and units; marked normal and limit zones; adjacent needle or value comparison", + "lineage": "Mechanical instrumentation", + "tags": [ + "scales", + "limit-zones", + "value-compare" + ] + }, + { + "id": "industrial-control-weather-station-instrument-panel", + "form": "weather-station instrument panel, standardized scales and units; marked normal and limit zones; adjacent needle or value comparison", + "lineage": "Mechanical instrumentation", + "tags": [ + "scales", + "limit-zones", + "value-compare" + ] + }, + { + "id": "industrial-control-hydroelectric-turbine-monitor", + "form": "hydroelectric turbine monitor, standardized scales and units; marked normal and limit zones; adjacent needle or value comparison", + "lineage": "Mechanical instrumentation", + "tags": [ + "scales", + "limit-zones", + "value-compare" + ] + }, + { + "id": "industrial-control-annunciator-tile-wall", + "form": "annunciator tile wall, priority-coded alarm levels; explicit acknowledgment state; physical or spatial fail-safe separation", + "lineage": "Industrial alarm and safety systems", + "tags": [ + "alarm-priority", + "acknowledgment", + "fail-safe" + ] + }, + { + "id": "industrial-control-emergency-shutdown-panel", + "form": "emergency shutdown panel, priority-coded alarm levels; explicit acknowledgment state; physical or spatial fail-safe separation", + "lineage": "Industrial alarm and safety systems", + "tags": [ + "alarm-priority", + "acknowledgment", + "fail-safe" + ] + }, + { + "id": "industrial-control-fire-alarm-control-panel", + "form": "fire-alarm control panel, priority-coded alarm levels; explicit acknowledgment state; physical or spatial fail-safe separation", + "lineage": "Industrial alarm and safety systems", + "tags": [ + "alarm-priority", + "acknowledgment", + "fail-safe" + ] + }, + { + "id": "industrial-control-gas-detection-console", + "form": "gas-detection console, priority-coded alarm levels; explicit acknowledgment state; physical or spatial fail-safe separation", + "lineage": "Industrial alarm and safety systems", + "tags": [ + "alarm-priority", + "acknowledgment", + "fail-safe" + ] + }, + { + "id": "industrial-control-mine-refuge-status-board", + "form": "mine refuge status board, priority-coded alarm levels; explicit acknowledgment state; physical or spatial fail-safe separation", + "lineage": "Industrial alarm and safety systems", + "tags": [ + "alarm-priority", + "acknowledgment", + "fail-safe" + ] + }, + { + "id": "industrial-control-maritime-distress-console", + "form": "maritime distress console, priority-coded alarm levels; explicit acknowledgment state; physical or spatial fail-safe separation", + "lineage": "Industrial alarm and safety systems", + "tags": [ + "alarm-priority", + "acknowledgment", + "fail-safe" + ] + }, + { + "id": "industrial-control-industrial-siren-code-chart", + "form": "industrial siren code chart, priority-coded alarm levels; explicit acknowledgment state; physical or spatial fail-safe separation", + "lineage": "Industrial alarm and safety systems", + "tags": [ + "alarm-priority", + "acknowledgment", + "fail-safe" + ] + }, + { + "id": "industrial-control-hazard-permit-board", + "form": "hazard permit board, priority-coded alarm levels; explicit acknowledgment state; physical or spatial fail-safe separation", + "lineage": "Industrial alarm and safety systems", + "tags": [ + "alarm-priority", + "acknowledgment", + "fail-safe" + ] + }, + { + "id": "industrial-control-lockout-tagout-station", + "form": "lockout-tagout station, priority-coded alarm levels; explicit acknowledgment state; physical or spatial fail-safe separation", + "lineage": "Industrial alarm and safety systems", + "tags": [ + "alarm-priority", + "acknowledgment", + "fail-safe" + ] + }, + { + "id": "industrial-control-confined-space-entry-board", + "form": "confined-space entry board, priority-coded alarm levels; explicit acknowledgment state; physical or spatial fail-safe separation", + "lineage": "Industrial alarm and safety systems", + "tags": [ + "alarm-priority", + "acknowledgment", + "fail-safe" + ] + }, + { + "id": "industrial-control-evacuation-muster-board", + "form": "evacuation muster board, priority-coded alarm levels; explicit acknowledgment state; physical or spatial fail-safe separation", + "lineage": "Industrial alarm and safety systems", + "tags": [ + "alarm-priority", + "acknowledgment", + "fail-safe" + ] + }, + { + "id": "industrial-control-safety-incident-command-board", + "form": "safety incident command board, priority-coded alarm levels; explicit acknowledgment state; physical or spatial fail-safe separation", + "lineage": "Industrial alarm and safety systems", + "tags": [ + "alarm-priority", + "acknowledgment", + "fail-safe" + ] + }, + { + "id": "industrial-control-supervisory-process-control-overview", + "form": "supervisory process-control overview, overview-to-equipment drill-down; live trend and state encoding; alarm layers that interrupt routine data", + "lineage": "Networked operations control rooms", + "tags": [ + "drill-down", + "live-trends", + "alarm-layer" + ] + }, + { + "id": "industrial-control-distributed-control-faceplate-grid", + "form": "distributed-control faceplate grid, overview-to-equipment drill-down; live trend and state encoding; alarm layers that interrupt routine data", + "lineage": "Networked operations control rooms", + "tags": [ + "drill-down", + "live-trends", + "alarm-layer" + ] + }, + { + "id": "industrial-control-building-management-dashboard", + "form": "building-management dashboard, overview-to-equipment drill-down; live trend and state encoding; alarm layers that interrupt routine data", + "lineage": "Networked operations control rooms", + "tags": [ + "drill-down", + "live-trends", + "alarm-layer" + ] + }, + { + "id": "industrial-control-electric-grid-one-line-display", + "form": "electric-grid one-line display, overview-to-equipment drill-down; live trend and state encoding; alarm layers that interrupt routine data", + "lineage": "Networked operations control rooms", + "tags": [ + "drill-down", + "live-trends", + "alarm-layer" + ] + }, + { + "id": "industrial-control-traffic-control-center-wall", + "form": "traffic-control center wall, overview-to-equipment drill-down; live trend and state encoding; alarm layers that interrupt routine data", + "lineage": "Networked operations control rooms", + "tags": [ + "drill-down", + "live-trends", + "alarm-layer" + ] + }, + { + "id": "industrial-control-rail-operations-control-screen", + "form": "rail-operations control screen, overview-to-equipment drill-down; live trend and state encoding; alarm layers that interrupt routine data", + "lineage": "Networked operations control rooms", + "tags": [ + "drill-down", + "live-trends", + "alarm-layer" + ] + }, + { + "id": "industrial-control-tunnel-ventilation-dashboard", + "form": "tunnel-ventilation dashboard, overview-to-equipment drill-down; live trend and state encoding; alarm layers that interrupt routine data", + "lineage": "Networked operations control rooms", + "tags": [ + "drill-down", + "live-trends", + "alarm-layer" + ] + }, + { + "id": "industrial-control-airport-baggage-control-screen", + "form": "airport baggage-control screen, overview-to-equipment drill-down; live trend and state encoding; alarm layers that interrupt routine data", + "lineage": "Networked operations control rooms", + "tags": [ + "drill-down", + "live-trends", + "alarm-layer" + ] + }, + { + "id": "industrial-control-port-crane-operations-screen", + "form": "port-crane operations screen, overview-to-equipment drill-down; live trend and state encoding; alarm layers that interrupt routine data", + "lineage": "Networked operations control rooms", + "tags": [ + "drill-down", + "live-trends", + "alarm-layer" + ] + }, + { + "id": "industrial-control-pipeline-leak-detection-dashboard", + "form": "pipeline leak-detection dashboard, overview-to-equipment drill-down; live trend and state encoding; alarm layers that interrupt routine data", + "lineage": "Networked operations control rooms", + "tags": [ + "drill-down", + "live-trends", + "alarm-layer" + ] + }, + { + "id": "industrial-control-cleanroom-environment-monitor", + "form": "cleanroom environment monitor, overview-to-equipment drill-down; live trend and state encoding; alarm layers that interrupt routine data", + "lineage": "Networked operations control rooms", + "tags": [ + "drill-down", + "live-trends", + "alarm-layer" + ] + }, + { + "id": "industrial-control-renewable-energy-farm-monitor", + "form": "renewable-energy farm monitor, overview-to-equipment drill-down; live trend and state encoding; alarm layers that interrupt routine data", + "lineage": "Networked operations control rooms", + "tags": [ + "drill-down", + "live-trends", + "alarm-layer" + ] + }, + { + "id": "industrial-control-industrial-startup-checklist", + "form": "industrial startup checklist, gated sequential steps; role-based initials or signoff; exception branches with recovery actions", + "lineage": "Operator procedure and handover", + "tags": [ + "step-gates", + "role-signoff", + "recovery-branch" + ] + }, + { + "id": "industrial-control-industrial-shutdown-sequence-card", + "form": "industrial shutdown sequence card, gated sequential steps; role-based initials or signoff; exception branches with recovery actions", + "lineage": "Operator procedure and handover", + "tags": [ + "step-gates", + "role-signoff", + "recovery-branch" + ] + } + ] + }, + { + "id": "manufacturing-assembly-repair", + "label": "Manufacturing, assembly & repair", + "description": "Making and maintenance systems with parts, tolerances, sequence, diagnostics, and verification.", + "concepts": [ + { + "id": "manufacturing-assembly-repair-exploded-parts-assembly-diagram", + "form": "exploded-parts assembly diagram, numbered construction steps; parts callouts tied to a manifest; orientation and movement arrows", + "lineage": "Pictorial assembly and installation manuals", + "tags": [ + "numbered-steps", + "parts-callouts", + "orientation" + ] + }, + { + "id": "manufacturing-assembly-repair-flat-pack-assembly-sheet", + "form": "flat-pack assembly sheet, numbered construction steps; parts callouts tied to a manifest; orientation and movement arrows", + "lineage": "Pictorial assembly and installation manuals", + "tags": [ + "numbered-steps", + "parts-callouts", + "orientation" + ] + }, + { + "id": "manufacturing-assembly-repair-appliance-installation-guide", + "form": "appliance installation guide, numbered construction steps; parts callouts tied to a manifest; orientation and movement arrows", + "lineage": "Pictorial assembly and installation manuals", + "tags": [ + "numbered-steps", + "parts-callouts", + "orientation" + ] + }, + { + "id": "manufacturing-assembly-repair-bicycle-assembly-manual", + "form": "bicycle assembly manual, numbered construction steps; parts callouts tied to a manifest; orientation and movement arrows", + "lineage": "Pictorial assembly and installation manuals", + "tags": [ + "numbered-steps", + "parts-callouts", + "orientation" + ] + }, + { + "id": "manufacturing-assembly-repair-machinery-erection-drawing", + "form": "machinery erection drawing, numbered construction steps; parts callouts tied to a manifest; orientation and movement arrows", + "lineage": "Pictorial assembly and installation manuals", + "tags": [ + "numbered-steps", + "parts-callouts", + "orientation" + ] + }, + { + "id": "manufacturing-assembly-repair-garment-sewing-instruction", + "form": "garment sewing instruction, numbered construction steps; parts callouts tied to a manifest; orientation and movement arrows", + "lineage": "Pictorial assembly and installation manuals", + "tags": [ + "numbered-steps", + "parts-callouts", + "orientation" + ] + }, + { + "id": "manufacturing-assembly-repair-electronics-kit-assembly-card", + "form": "electronics kit assembly card, numbered construction steps; parts callouts tied to a manifest; orientation and movement arrows", + "lineage": "Pictorial assembly and installation manuals", + "tags": [ + "numbered-steps", + "parts-callouts", + "orientation" + ] + }, + { + "id": "manufacturing-assembly-repair-modular-shelving-instruction", + "form": "modular shelving instruction, numbered construction steps; parts callouts tied to a manifest; orientation and movement arrows", + "lineage": "Pictorial assembly and installation manuals", + "tags": [ + "numbered-steps", + "parts-callouts", + "orientation" + ] + }, + { + "id": "manufacturing-assembly-repair-playground-equipment-assembly-plan", + "form": "playground equipment assembly plan, numbered construction steps; parts callouts tied to a manifest; orientation and movement arrows", + "lineage": "Pictorial assembly and installation manuals", + "tags": [ + "numbered-steps", + "parts-callouts", + "orientation" + ] + }, + { + "id": "manufacturing-assembly-repair-plumbing-fixture-installation-sheet", + "form": "plumbing fixture installation sheet, numbered construction steps; parts callouts tied to a manifest; orientation and movement arrows", + "lineage": "Pictorial assembly and installation manuals", + "tags": [ + "numbered-steps", + "parts-callouts", + "orientation" + ] + }, + { + "id": "manufacturing-assembly-repair-lighting-fixture-wiring-guide", + "form": "lighting fixture wiring guide, numbered construction steps; parts callouts tied to a manifest; orientation and movement arrows", + "lineage": "Pictorial assembly and installation manuals", + "tags": [ + "numbered-steps", + "parts-callouts", + "orientation" + ] + }, + { + "id": "manufacturing-assembly-repair-laboratory-apparatus-setup-guide", + "form": "laboratory apparatus setup guide, numbered construction steps; parts callouts tied to a manifest; orientation and movement arrows", + "lineage": "Pictorial assembly and installation manuals", + "tags": [ + "numbered-steps", + "parts-callouts", + "orientation" + ] + }, + { + "id": "manufacturing-assembly-repair-shop-floor-production-traveler", + "form": "shop-floor production traveler, persistent job and batch identifiers; ordered work-center operations; operator and quality signoffs", + "lineage": "Factory routing and production control", + "tags": [ + "job-id", + "operation-order", + "quality-signoff" + ] + }, + { + "id": "manufacturing-assembly-repair-manufacturing-routing-card", + "form": "manufacturing routing card, persistent job and batch identifiers; ordered work-center operations; operator and quality signoffs", + "lineage": "Factory routing and production control", + "tags": [ + "job-id", + "operation-order", + "quality-signoff" + ] + }, + { + "id": "manufacturing-assembly-repair-batch-manufacturing-record", + "form": "batch manufacturing record, persistent job and batch identifiers; ordered work-center operations; operator and quality signoffs", + "lineage": "Factory routing and production control", + "tags": [ + "job-id", + "operation-order", + "quality-signoff" + ] + }, + { + "id": "manufacturing-assembly-repair-factory-work-order", + "form": "factory work order, persistent job and batch identifiers; ordered work-center operations; operator and quality signoffs", + "lineage": "Factory routing and production control", + "tags": [ + "job-id", + "operation-order", + "quality-signoff" + ] + }, + { + "id": "manufacturing-assembly-repair-job-ticket", + "form": "job ticket, persistent job and batch identifiers; ordered work-center operations; operator and quality signoffs", + "lineage": "Factory routing and production control", + "tags": [ + "job-id", + "operation-order", + "quality-signoff" + ] + }, + { + "id": "manufacturing-assembly-repair-operation-sequence-sheet", + "form": "operation sequence sheet, persistent job and batch identifiers; ordered work-center operations; operator and quality signoffs", + "lineage": "Factory routing and production control", + "tags": [ + "job-id", + "operation-order", + "quality-signoff" + ] + }, + { + "id": "manufacturing-assembly-repair-kanban-production-card", + "form": "kanban production card, persistent job and batch identifiers; ordered work-center operations; operator and quality signoffs", + "lineage": "Factory routing and production control", + "tags": [ + "job-id", + "operation-order", + "quality-signoff" + ] + }, + { + "id": "manufacturing-assembly-repair-tool-preset-sheet", + "form": "tool preset sheet, persistent job and batch identifiers; ordered work-center operations; operator and quality signoffs", + "lineage": "Factory routing and production control", + "tags": [ + "job-id", + "operation-order", + "quality-signoff" + ] + }, + { + "id": "manufacturing-assembly-repair-material-cutting-list", + "form": "material cutting list, persistent job and batch identifiers; ordered work-center operations; operator and quality signoffs", + "lineage": "Factory routing and production control", + "tags": [ + "job-id", + "operation-order", + "quality-signoff" + ] + }, + { + "id": "manufacturing-assembly-repair-bill-of-materials", + "form": "bill of materials, persistent job and batch identifiers; ordered work-center operations; operator and quality signoffs", + "lineage": "Factory routing and production control", + "tags": [ + "job-id", + "operation-order", + "quality-signoff" + ] + }, + { + "id": "manufacturing-assembly-repair-fabrication-checklist", + "form": "fabrication checklist, persistent job and batch identifiers; ordered work-center operations; operator and quality signoffs", + "lineage": "Factory routing and production control", + "tags": [ + "job-id", + "operation-order", + "quality-signoff" + ] + }, + { + "id": "manufacturing-assembly-repair-first-article-inspection-sheet", + "form": "first-article inspection sheet, persistent job and batch identifiers; ordered work-center operations; operator and quality signoffs", + "lineage": "Factory routing and production control", + "tags": [ + "job-id", + "operation-order", + "quality-signoff" + ] + }, + { + "id": "manufacturing-assembly-repair-orthographic-engineering-drawing", + "form": "orthographic engineering drawing, coordinated multiple views; dimensions tolerances and material notes; revision and approval blocks", + "lineage": "Engineering drawing standards", + "tags": [ + "multiple-views", + "tolerances", + "revision-block" + ] + }, + { + "id": "manufacturing-assembly-repair-section-engineering-drawing", + "form": "section engineering drawing, coordinated multiple views; dimensions tolerances and material notes; revision and approval blocks", + "lineage": "Engineering drawing standards", + "tags": [ + "multiple-views", + "tolerances", + "revision-block" + ] + }, + { + "id": "manufacturing-assembly-repair-dimensioned-fabrication-print", + "form": "dimensioned fabrication print, coordinated multiple views; dimensions tolerances and material notes; revision and approval blocks", + "lineage": "Engineering drawing standards", + "tags": [ + "multiple-views", + "tolerances", + "revision-block" + ] + }, + { + "id": "manufacturing-assembly-repair-tolerance-stack-diagram", + "form": "tolerance-stack diagram, coordinated multiple views; dimensions tolerances and material notes; revision and approval blocks", + "lineage": "Engineering drawing standards", + "tags": [ + "multiple-views", + "tolerances", + "revision-block" + ] + }, + { + "id": "manufacturing-assembly-repair-weld-symbol-drawing", + "form": "weld-symbol drawing, coordinated multiple views; dimensions tolerances and material notes; revision and approval blocks", + "lineage": "Engineering drawing standards", + "tags": [ + "multiple-views", + "tolerances", + "revision-block" + ] + }, + { + "id": "manufacturing-assembly-repair-piping-isometric", + "form": "piping isometric, coordinated multiple views; dimensions tolerances and material notes; revision and approval blocks", + "lineage": "Engineering drawing standards", + "tags": [ + "multiple-views", + "tolerances", + "revision-block" + ] + }, + { + "id": "manufacturing-assembly-repair-sheet-metal-flat-pattern", + "form": "sheet-metal flat pattern, coordinated multiple views; dimensions tolerances and material notes; revision and approval blocks", + "lineage": "Engineering drawing standards", + "tags": [ + "multiple-views", + "tolerances", + "revision-block" + ] + }, + { + "id": "manufacturing-assembly-repair-electrical-wiring-diagram", + "form": "electrical wiring diagram, coordinated multiple views; dimensions tolerances and material notes; revision and approval blocks", + "lineage": "Engineering drawing standards", + "tags": [ + "multiple-views", + "tolerances", + "revision-block" + ] + }, + { + "id": "manufacturing-assembly-repair-circuit-board-assembly-drawing", + "form": "circuit-board assembly drawing, coordinated multiple views; dimensions tolerances and material notes; revision and approval blocks", + "lineage": "Engineering drawing standards", + "tags": [ + "multiple-views", + "tolerances", + "revision-block" + ] + }, + { + "id": "manufacturing-assembly-repair-architectural-shop-drawing", + "form": "architectural shop drawing, coordinated multiple views; dimensions tolerances and material notes; revision and approval blocks", + "lineage": "Engineering drawing standards", + "tags": [ + "multiple-views", + "tolerances", + "revision-block" + ] + }, + { + "id": "manufacturing-assembly-repair-mold-design-drawing", + "form": "mold design drawing, coordinated multiple views; dimensions tolerances and material notes; revision and approval blocks", + "lineage": "Engineering drawing standards", + "tags": [ + "multiple-views", + "tolerances", + "revision-block" + ] + }, + { + "id": "manufacturing-assembly-repair-jig-and-fixture-plan", + "form": "jig-and-fixture plan, coordinated multiple views; dimensions tolerances and material notes; revision and approval blocks", + "lineage": "Engineering drawing standards", + "tags": [ + "multiple-views", + "tolerances", + "revision-block" + ] + }, + { + "id": "manufacturing-assembly-repair-service-manual-troubleshooting-tree", + "form": "service-manual troubleshooting tree, symptom-to-cause branching; test and measurement checkpoints; recorded closure and verification", + "lineage": "Maintenance and fault diagnosis", + "tags": [ + "diagnostic-branch", + "test-checkpoint", + "closure-record" + ] + }, + { + "id": "manufacturing-assembly-repair-machine-fault-code-table", + "form": "machine fault-code table, symptom-to-cause branching; test and measurement checkpoints; recorded closure and verification", + "lineage": "Maintenance and fault diagnosis", + "tags": [ + "diagnostic-branch", + "test-checkpoint", + "closure-record" + ] + }, + { + "id": "manufacturing-assembly-repair-equipment-maintenance-log", + "form": "equipment maintenance log, symptom-to-cause branching; test and measurement checkpoints; recorded closure and verification", + "lineage": "Maintenance and fault diagnosis", + "tags": [ + "diagnostic-branch", + "test-checkpoint", + "closure-record" + ] + }, + { + "id": "manufacturing-assembly-repair-condition-inspection-checklist", + "form": "condition inspection checklist, symptom-to-cause branching; test and measurement checkpoints; recorded closure and verification", + "lineage": "Maintenance and fault diagnosis", + "tags": [ + "diagnostic-branch", + "test-checkpoint", + "closure-record" + ] + }, + { + "id": "manufacturing-assembly-repair-repair-estimate-sheet", + "form": "repair estimate sheet, symptom-to-cause branching; test and measurement checkpoints; recorded closure and verification", + "lineage": "Maintenance and fault diagnosis", + "tags": [ + "diagnostic-branch", + "test-checkpoint", + "closure-record" + ] + }, + { + "id": "manufacturing-assembly-repair-parts-replacement-record", + "form": "parts replacement record, symptom-to-cause branching; test and measurement checkpoints; recorded closure and verification", + "lineage": "Maintenance and fault diagnosis", + "tags": [ + "diagnostic-branch", + "test-checkpoint", + "closure-record" + ] + }, + { + "id": "manufacturing-assembly-repair-diagnostic-flowchart", + "form": "diagnostic flowchart, symptom-to-cause branching; test and measurement checkpoints; recorded closure and verification", + "lineage": "Maintenance and fault diagnosis", + "tags": [ + "diagnostic-branch", + "test-checkpoint", + "closure-record" + ] + }, + { + "id": "manufacturing-assembly-repair-lubrication-schedule", + "form": "lubrication schedule, symptom-to-cause branching; test and measurement checkpoints; recorded closure and verification", + "lineage": "Maintenance and fault diagnosis", + "tags": [ + "diagnostic-branch", + "test-checkpoint", + "closure-record" + ] + }, + { + "id": "manufacturing-assembly-repair-wear-limit-chart", + "form": "wear-limit chart, symptom-to-cause branching; test and measurement checkpoints; recorded closure and verification", + "lineage": "Maintenance and fault diagnosis", + "tags": [ + "diagnostic-branch", + "test-checkpoint", + "closure-record" + ] + }, + { + "id": "manufacturing-assembly-repair-calibration-procedure", + "form": "calibration procedure, symptom-to-cause branching; test and measurement checkpoints; recorded closure and verification", + "lineage": "Maintenance and fault diagnosis", + "tags": [ + "diagnostic-branch", + "test-checkpoint", + "closure-record" + ] + }, + { + "id": "manufacturing-assembly-repair-warranty-repair-ticket", + "form": "warranty repair ticket, symptom-to-cause branching; test and measurement checkpoints; recorded closure and verification", + "lineage": "Maintenance and fault diagnosis", + "tags": [ + "diagnostic-branch", + "test-checkpoint", + "closure-record" + ] + }, + { + "id": "manufacturing-assembly-repair-field-service-report", + "form": "field-service report, symptom-to-cause branching; test and measurement checkpoints; recorded closure and verification", + "lineage": "Maintenance and fault diagnosis", + "tags": [ + "diagnostic-branch", + "test-checkpoint", + "closure-record" + ] + }, + { + "id": "manufacturing-assembly-repair-coded-parts-bin-system", + "form": "coded parts-bin system, coded physical locations; visual matching by silhouette or dimension; inventory and checkout status", + "lineage": "Workshop parts and tool organization", + "tags": [ + "location-code", + "visual-match", + "inventory-status" + ] + }, + { + "id": "manufacturing-assembly-repair-shadow-tool-board", + "form": "shadow tool board, coded physical locations; visual matching by silhouette or dimension; inventory and checkout status", + "lineage": "Workshop parts and tool organization", + "tags": [ + "location-code", + "visual-match", + "inventory-status" + ] + }, + { + "id": "manufacturing-assembly-repair-fastener-size-chart", + "form": "fastener size chart, coded physical locations; visual matching by silhouette or dimension; inventory and checkout status", + "lineage": "Workshop parts and tool organization", + "tags": [ + "location-code", + "visual-match", + "inventory-status" + ] + }, + { + "id": "manufacturing-assembly-repair-spare-parts-catalogue", + "form": "spare-parts catalogue, coded physical locations; visual matching by silhouette or dimension; inventory and checkout status", + "lineage": "Workshop parts and tool organization", + "tags": [ + "location-code", + "visual-match", + "inventory-status" + ] + }, + { + "id": "manufacturing-assembly-repair-exploded-service-parts-list", + "form": "exploded service-parts list, coded physical locations; visual matching by silhouette or dimension; inventory and checkout status", + "lineage": "Workshop parts and tool organization", + "tags": [ + "location-code", + "visual-match", + "inventory-status" + ] + }, + { + "id": "manufacturing-assembly-repair-tool-crib-checkout-board", + "form": "tool-crib checkout board, coded physical locations; visual matching by silhouette or dimension; inventory and checkout status", + "lineage": "Workshop parts and tool organization", + "tags": [ + "location-code", + "visual-match", + "inventory-status" + ] + }, + { + "id": "manufacturing-assembly-repair-material-rack-label", + "form": "material-rack label, coded physical locations; visual matching by silhouette or dimension; inventory and checkout status", + "lineage": "Workshop parts and tool organization", + "tags": [ + "location-code", + "visual-match", + "inventory-status" + ] + }, + { + "id": "manufacturing-assembly-repair-cable-identification-scheme", + "form": "cable identification scheme, coded physical locations; visual matching by silhouette or dimension; inventory and checkout status", + "lineage": "Workshop parts and tool organization", + "tags": [ + "location-code", + "visual-match", + "inventory-status" + ] + }, + { + "id": "manufacturing-assembly-repair-pipe-fitting-reference-chart", + "form": "pipe-fitting reference chart, coded physical locations; visual matching by silhouette or dimension; inventory and checkout status", + "lineage": "Workshop parts and tool organization", + "tags": [ + "location-code", + "visual-match", + "inventory-status" + ] + }, + { + "id": "manufacturing-assembly-repair-bearing-selection-table", + "form": "bearing selection table, coded physical locations; visual matching by silhouette or dimension; inventory and checkout status", + "lineage": "Workshop parts and tool organization", + "tags": [ + "location-code", + "visual-match", + "inventory-status" + ] + }, + { + "id": "manufacturing-assembly-repair-gauge-block-set-index", + "form": "gauge-block set index, coded physical locations; visual matching by silhouette or dimension; inventory and checkout status", + "lineage": "Workshop parts and tool organization", + "tags": [ + "location-code", + "visual-match", + "inventory-status" + ] + }, + { + "id": "manufacturing-assembly-repair-workshop-wall-standard", + "form": "workshop wall standard, coded physical locations; visual matching by silhouette or dimension; inventory and checkout status", + "lineage": "Workshop parts and tool organization", + "tags": [ + "location-code", + "visual-match", + "inventory-status" + ] + }, + { + "id": "manufacturing-assembly-repair-letterpress-imposition-plan", + "form": "letterpress imposition plan, material transformation sequence; measured fit heat or timing constraints; quality checkpoints at irreversible stages", + "lineage": "Craft and industrial process planning", + "tags": [ + "material-sequence", + "measured-constraint", + "quality-gate" + ] + }, + { + "id": "manufacturing-assembly-repair-bookbinding-folding-diagram", + "form": "bookbinding folding diagram, material transformation sequence; measured fit heat or timing constraints; quality checkpoints at irreversible stages", + "lineage": "Craft and industrial process planning", + "tags": [ + "material-sequence", + "measured-constraint", + "quality-gate" + ] + }, + { + "id": "manufacturing-assembly-repair-textile-loom-draft", + "form": "textile loom draft, material transformation sequence; measured fit heat or timing constraints; quality checkpoints at irreversible stages", + "lineage": "Craft and industrial process planning", + "tags": [ + "material-sequence", + "measured-constraint", + "quality-gate" + ] + }, + { + "id": "manufacturing-assembly-repair-garment-cutting-marker", + "form": "garment cutting marker, material transformation sequence; measured fit heat or timing constraints; quality checkpoints at irreversible stages", + "lineage": "Craft and industrial process planning", + "tags": [ + "material-sequence", + "measured-constraint", + "quality-gate" + ] + } + ] + }, + { + "id": "architecture-construction", + "label": "Architecture & construction", + "description": "Spatial documents and site systems built from layers, datums, sections, phases, and constraints.", + "concepts": [ + { + "id": "architecture-construction-building-section-drawing", + "form": "a building section drawing, with cut-plane hierarchy, level datum lines, and material hatches", + "lineage": "architectural documentation", + "tags": [ + "sectioning", + "annotation", + "scale" + ] + }, + { + "id": "architecture-construction-live-digital-system-modeled-on-a-building-section-dr", + "form": "a live digital system modeled on a building section drawing, with material hatches, dimension chains, and detail callouts", + "lineage": "architectural documentation; native digital", + "tags": [ + "sectioning", + "annotation", + "scale" + ] + }, + { + "id": "architecture-construction-shared-participatory-system-modeled-on-a-building-se", + "form": "a shared participatory system modeled on a building section drawing, with detail callouts, cut-plane hierarchy, and dimension chains", + "lineage": "architectural documentation; participatory", + "tags": [ + "sectioning", + "annotation", + "scale" + ] + }, + { + "id": "architecture-construction-site-plan", + "form": "a site plan, with property boundaries, north orientation, and access hierarchy", + "lineage": "architectural planning", + "tags": [ + "mapping", + "orientation", + "boundaries" + ] + }, + { + "id": "architecture-construction-live-digital-system-modeled-on-a-site-plan", + "form": "a live digital system modeled on a site plan, with access hierarchy, setback bands, and landscape symbols", + "lineage": "architectural planning; native digital", + "tags": [ + "mapping", + "orientation", + "boundaries" + ] + }, + { + "id": "architecture-construction-shared-participatory-system-modeled-on-a-site-plan", + "form": "a shared participatory system modeled on a site plan, with landscape symbols, property boundaries, and setback bands", + "lineage": "architectural planning; participatory", + "tags": [ + "mapping", + "orientation", + "boundaries" + ] + }, + { + "id": "architecture-construction-floor-plan", + "form": "a floor plan, with room adjacencies, circulation paths, and door swings", + "lineage": "architectural planning", + "tags": [ + "adjacency", + "circulation", + "scale" + ] + }, + { + "id": "architecture-construction-live-digital-system-modeled-on-a-floor-plan", + "form": "a live digital system modeled on a floor plan, with door swings, scale grids, and keyed notes", + "lineage": "architectural planning; native digital", + "tags": [ + "adjacency", + "circulation", + "scale" + ] + }, + { + "id": "architecture-construction-shared-participatory-system-modeled-on-a-floor-plan", + "form": "a shared participatory system modeled on a floor plan, with keyed notes, room adjacencies, and scale grids", + "lineage": "architectural planning; participatory", + "tags": [ + "adjacency", + "circulation", + "scale" + ] + }, + { + "id": "architecture-construction-elevation-sheet", + "form": "an elevation sheet, with facade bays, datum lines, and material zones", + "lineage": "architectural documentation", + "tags": [ + "rhythm", + "datum", + "materials" + ] + }, + { + "id": "architecture-construction-live-digital-system-modeled-on-an-elevation-sheet", + "form": "a live digital system modeled on an elevation sheet, with material zones, opening rhythms, and height dimensions", + "lineage": "architectural documentation; native digital", + "tags": [ + "rhythm", + "datum", + "materials" + ] + }, + { + "id": "architecture-construction-shared-participatory-system-modeled-on-an-elevation", + "form": "a shared participatory system modeled on an elevation sheet, with height dimensions, facade bays, and opening rhythms", + "lineage": "architectural documentation; participatory", + "tags": [ + "rhythm", + "datum", + "materials" + ] + }, + { + "id": "architecture-construction-reflected-ceiling-plan", + "form": "a reflected ceiling plan, with overhead layers, fixture grids, and switching zones", + "lineage": "building coordination", + "tags": [ + "overlays", + "grids", + "coordination" + ] + }, + { + "id": "architecture-construction-live-digital-system-modeled-on-a-reflected-ceiling-p", + "form": "a live digital system modeled on a reflected ceiling plan, with switching zones, ceiling heights, and coordination tags", + "lineage": "building coordination; native digital", + "tags": [ + "overlays", + "grids", + "coordination" + ] + }, + { + "id": "architecture-construction-shared-participatory-system-modeled-on-a-reflected-c", + "form": "a shared participatory system modeled on a reflected ceiling plan, with coordination tags, overhead layers, and ceiling heights", + "lineage": "building coordination; participatory", + "tags": [ + "overlays", + "grids", + "coordination" + ] + }, + { + "id": "architecture-construction-construction-detail", + "form": "a construction detail, with enlarged junctions, layer sequencing, and fastener callouts", + "lineage": "building craft", + "tags": [ + "layers", + "junctions", + "callouts" + ] + }, + { + "id": "architecture-construction-live-digital-system-modeled-on-a-construction-detail", + "form": "a live digital system modeled on a construction detail, with fastener callouts, waterproofing paths, and keyed references", + "lineage": "building craft; native digital", + "tags": [ + "layers", + "junctions", + "callouts" + ] + }, + { + "id": "architecture-construction-shared-participatory-system-modeled-on-a-constructio", + "form": "a shared participatory system modeled on a construction detail, with keyed references, enlarged junctions, and waterproofing paths", + "lineage": "building craft; participatory", + "tags": [ + "layers", + "junctions", + "callouts" + ] + }, + { + "id": "architecture-construction-exploded-axonometric-drawing", + "form": "an exploded axonometric drawing, with separated assemblies, alignment axes, and numbered parts", + "lineage": "technical illustration", + "tags": [ + "assemblies", + "axes", + "sequence" + ] + }, + { + "id": "architecture-construction-live-digital-system-modeled-on-an-exploded-axonometr", + "form": "a live digital system modeled on an exploded axonometric drawing, with numbered parts, assembly order, and visible interfaces", + "lineage": "technical illustration; native digital", + "tags": [ + "assemblies", + "axes", + "sequence" + ] + }, + { + "id": "architecture-construction-shared-participatory-system-modeled-on-an-exploded-a", + "form": "a shared participatory system modeled on an exploded axonometric drawing, with visible interfaces, separated assemblies, and assembly order", + "lineage": "technical illustration; participatory", + "tags": [ + "assemblies", + "axes", + "sequence" + ] + }, + { + "id": "architecture-construction-zoning-envelope-diagram", + "form": "a zoning envelope diagram, with allowable mass, setback planes, and height caps", + "lineage": "urban regulation", + "tags": [ + "constraints", + "overlays", + "boundaries" + ] + }, + { + "id": "architecture-construction-live-digital-system-modeled-on-a-zoning-envelope-dia", + "form": "a live digital system modeled on a zoning envelope diagram, with height caps, parcel limits, and exception overlays", + "lineage": "urban regulation; native digital", + "tags": [ + "constraints", + "overlays", + "boundaries" + ] + }, + { + "id": "architecture-construction-shared-participatory-system-modeled-on-a-zoning-enve", + "form": "a shared participatory system modeled on a zoning envelope diagram, with exception overlays, allowable mass, and parcel limits", + "lineage": "urban regulation; participatory", + "tags": [ + "constraints", + "overlays", + "boundaries" + ] + }, + { + "id": "architecture-construction-structural-load-path-diagram", + "form": "a structural load-path diagram, with force arrows, primary and secondary members, and support nodes", + "lineage": "structural engineering", + "tags": [ + "flows", + "nodes", + "capacity" + ] + }, + { + "id": "architecture-construction-live-digital-system-modeled-on-a-structural-load-pat", + "form": "a live digital system modeled on a structural load-path diagram, with support nodes, span groups, and capacity labels", + "lineage": "structural engineering; native digital", + "tags": [ + "flows", + "nodes", + "capacity" + ] + }, + { + "id": "architecture-construction-shared-participatory-system-modeled-on-a-structural", + "form": "a shared participatory system modeled on a structural load-path diagram, with capacity labels, force arrows, and span groups", + "lineage": "structural engineering; participatory", + "tags": [ + "flows", + "nodes", + "capacity" + ] + }, + { + "id": "architecture-construction-materials-sample-board", + "form": "a materials sample board, with grouped samples, finish codes, and adjacency pairings", + "lineage": "interior specification", + "tags": [ + "samples", + "grouping", + "approval" + ] + }, + { + "id": "architecture-construction-live-digital-system-modeled-on-a-materials-sample-bo", + "form": "a live digital system modeled on a materials sample board, with adjacency pairings, tactile scales, and approval markers", + "lineage": "interior specification; native digital", + "tags": [ + "samples", + "grouping", + "approval" + ] + }, + { + "id": "architecture-construction-shared-participatory-system-modeled-on-a-materials-s", + "form": "a shared participatory system modeled on a materials sample board, with approval markers, grouped samples, and tactile scales", + "lineage": "interior specification; participatory", + "tags": [ + "samples", + "grouping", + "approval" + ] + }, + { + "id": "architecture-construction-tender-drawing-register", + "form": "a tender drawing register, with sheet indices, revision clouds, and issue stamps", + "lineage": "construction procurement", + "tags": [ + "indexing", + "revision", + "status" + ] + }, + { + "id": "architecture-construction-live-digital-system-modeled-on-a-tender-drawing-regi", + "form": "a live digital system modeled on a tender drawing register, with issue stamps, discipline codes, and supersession logs", + "lineage": "construction procurement; native digital", + "tags": [ + "indexing", + "revision", + "status" + ] + }, + { + "id": "architecture-construction-shared-participatory-system-modeled-on-a-tender-draw", + "form": "a shared participatory system modeled on a tender drawing register, with supersession logs, sheet indices, and discipline codes", + "lineage": "construction procurement; participatory", + "tags": [ + "indexing", + "revision", + "status" + ] + }, + { + "id": "architecture-construction-construction-punch-list", + "form": "a construction punch list, with location codes, defect evidence, and responsibility fields", + "lineage": "site inspection", + "tags": [ + "queues", + "ownership", + "closure" + ] + }, + { + "id": "architecture-construction-live-digital-system-modeled-on-a-construction-punch", + "form": "a live digital system modeled on a construction punch list, with responsibility fields, priority levels, and closure checks", + "lineage": "site inspection; native digital", + "tags": [ + "queues", + "ownership", + "closure" + ] + }, + { + "id": "architecture-construction-shared-participatory-system-modeled-on-a-constructio-50dd1c3f", + "form": "a shared participatory system modeled on a construction punch list, with closure checks, location codes, and priority levels", + "lineage": "site inspection; participatory", + "tags": [ + "queues", + "ownership", + "closure" + ] + }, + { + "id": "architecture-construction-site-logistics-plan", + "form": "a site logistics plan, with crane arcs, delivery gates, and exclusion zones", + "lineage": "construction operations", + "tags": [ + "zones", + "routes", + "phases" + ] + }, + { + "id": "architecture-construction-live-digital-system-modeled-on-a-site-logistics-plan", + "form": "a live digital system modeled on a site logistics plan, with exclusion zones, temporary routes, and phase overlays", + "lineage": "construction operations; native digital", + "tags": [ + "zones", + "routes", + "phases" + ] + }, + { + "id": "architecture-construction-shared-participatory-system-modeled-on-a-site-logist", + "form": "a shared participatory system modeled on a site logistics plan, with phase overlays, crane arcs, and temporary routes", + "lineage": "construction operations; participatory", + "tags": [ + "zones", + "routes", + "phases" + ] + }, + { + "id": "architecture-construction-construction-sequencing-chart", + "form": "a construction sequencing chart, with phase bands, dependency arrows, and milestone gates", + "lineage": "project planning", + "tags": [ + "dependencies", + "lanes", + "gates" + ] + }, + { + "id": "architecture-construction-live-digital-system-modeled-on-a-construction-sequen", + "form": "a live digital system modeled on a construction sequencing chart, with milestone gates, crew lanes, and handoff checks", + "lineage": "project planning; native digital", + "tags": [ + "dependencies", + "lanes", + "gates" + ] + }, + { + "id": "architecture-construction-shared-participatory-system-modeled-on-a-constructio-3d970fb9", + "form": "a shared participatory system modeled on a construction sequencing chart, with handoff checks, phase bands, and crew lanes", + "lineage": "project planning; participatory", + "tags": [ + "dependencies", + "lanes", + "gates" + ] + }, + { + "id": "architecture-construction-building-permit-review-set", + "form": "a building permit review set, with code citations, authority stamps, and revision deltas", + "lineage": "civic review", + "tags": [ + "compliance", + "revision", + "decisions" + ] + }, + { + "id": "architecture-construction-live-digital-system-modeled-on-a-building-permit-rev", + "form": "a live digital system modeled on a building permit review set, with revision deltas, compliance matrices, and decision records", + "lineage": "civic review; native digital", + "tags": [ + "compliance", + "revision", + "decisions" + ] + }, + { + "id": "architecture-construction-shared-participatory-system-modeled-on-a-building-pe", + "form": "a shared participatory system modeled on a building permit review set, with decision records, code citations, and compliance matrices", + "lineage": "civic review; participatory", + "tags": [ + "compliance", + "revision", + "decisions" + ] + }, + { + "id": "architecture-construction-quantity-survey-bill", + "form": "a quantity survey bill, with trade sections, measured quantities, and unit rates", + "lineage": "cost planning", + "tags": [ + "quantities", + "rates", + "totals" + ] + }, + { + "id": "architecture-construction-live-digital-system-modeled-on-a-quantity-survey-bil", + "form": "a live digital system modeled on a quantity survey bill, with unit rates, subtotal bands, and variance flags", + "lineage": "cost planning; native digital", + "tags": [ + "quantities", + "rates", + "totals" + ] + }, + { + "id": "architecture-construction-shared-participatory-system-modeled-on-a-quantity-su", + "form": "a shared participatory system modeled on a quantity survey bill, with variance flags, trade sections, and subtotal bands", + "lineage": "cost planning; participatory", + "tags": [ + "quantities", + "rates", + "totals" + ] + }, + { + "id": "architecture-construction-room-data-sheet", + "form": "a room data sheet, with room identifiers, finish schedules, and equipment lists", + "lineage": "building specification", + "tags": [ + "records", + "requirements", + "signoff" + ] + }, + { + "id": "architecture-construction-live-digital-system-modeled-on-a-room-data-sheet", + "form": "a live digital system modeled on a room data sheet, with equipment lists, performance requirements, and sign-off fields", + "lineage": "building specification; native digital", + "tags": [ + "records", + "requirements", + "signoff" + ] + }, + { + "id": "architecture-construction-shared-participatory-system-modeled-on-a-room-data-s", + "form": "a shared participatory system modeled on a room data sheet, with sign-off fields, room identifiers, and performance requirements", + "lineage": "building specification; participatory", + "tags": [ + "records", + "requirements", + "signoff" + ] + }, + { + "id": "architecture-construction-wayfinding-masterplan", + "form": "a wayfinding masterplan, with destination hierarchy, decision points, and sign families", + "lineage": "environmental graphics", + "tags": [ + "hierarchy", + "routes", + "placement" + ] + }, + { + "id": "architecture-construction-live-digital-system-modeled-on-a-wayfinding-masterpl", + "form": "a live digital system modeled on a wayfinding masterplan, with sign families, route colors, and placement schedules", + "lineage": "environmental graphics; native digital", + "tags": [ + "hierarchy", + "routes", + "placement" + ] + }, + { + "id": "architecture-construction-shared-participatory-system-modeled-on-a-wayfinding", + "form": "a shared participatory system modeled on a wayfinding masterplan, with placement schedules, destination hierarchy, and route colors", + "lineage": "environmental graphics; participatory", + "tags": [ + "hierarchy", + "routes", + "placement" + ] + }, + { + "id": "architecture-construction-facade-bay-study", + "form": "a facade bay study, with modular repeats, panel joints, and opening rules", + "lineage": "architectural composition", + "tags": [ + "modules", + "rules", + "exceptions" + ] + }, + { + "id": "architecture-construction-live-digital-system-modeled-on-a-facade-bay-study", + "form": "a live digital system modeled on a facade bay study, with opening rules, shading depths, and corner exceptions", + "lineage": "architectural composition; native digital", + "tags": [ + "modules", + "rules", + "exceptions" + ] + }, + { + "id": "architecture-construction-shared-participatory-system-modeled-on-a-facade-bay", + "form": "a shared participatory system modeled on a facade bay study, with corner exceptions, modular repeats, and shading depths", + "lineage": "architectural composition; participatory", + "tags": [ + "modules", + "rules", + "exceptions" + ] + }, + { + "id": "architecture-construction-urban-figure-ground-map", + "form": "an urban figure-ground map, with solid building footprints, open-space voids, and block grain", + "lineage": "urban morphology", + "tags": [ + "positive-negative", + "grain", + "edges" + ] + }, + { + "id": "architecture-construction-live-digital-system-modeled-on-an-urban-figure-groun", + "form": "a live digital system modeled on an urban figure-ground map, with block grain, street edges, and scale comparisons", + "lineage": "urban morphology; native digital", + "tags": [ + "positive-negative", + "grain", + "edges" + ] + }, + { + "id": "architecture-construction-shared-participatory-system-modeled-on-an-urban-figu", + "form": "a shared participatory system modeled on an urban figure-ground map, with scale comparisons, solid building footprints, and street edges", + "lineage": "urban morphology; participatory", + "tags": [ + "positive-negative", + "grain", + "edges" + ] + }, + { + "id": "architecture-construction-construction-daily-report", + "form": "a construction daily report, with weather headers, workforce counts, and work zones", + "lineage": "site reporting", + "tags": [ + "time", + "evidence", + "exceptions" + ] + }, + { + "id": "architecture-construction-live-digital-system-modeled-on-a-construction-daily", + "form": "a live digital system modeled on a construction daily report, with work zones, delay notes, and photo evidence", + "lineage": "site reporting; native digital", + "tags": [ + "time", + "evidence", + "exceptions" + ] + }, + { + "id": "architecture-construction-shared-participatory-system-modeled-on-a-constructio-6e2b8824", + "form": "a shared participatory system modeled on a construction daily report, with photo evidence, weather headers, and delay notes", + "lineage": "site reporting; participatory", + "tags": [ + "time", + "evidence", + "exceptions" + ] + }, + { + "id": "architecture-construction-building-maintenance-manual", + "form": "a building maintenance manual, with asset hierarchy, service intervals, and troubleshooting paths", + "lineage": "facilities operations", + "tags": [ + "hierarchy", + "intervals", + "diagnosis" + ] + } + ] + }, + { + "id": "transport-mobility", + "label": "Transport & mobility", + "description": "Vehicle, route, interchange, dispatch, and passenger systems shaped by time and movement.", + "concepts": [ + { + "id": "transport-mobility-transit-network-diagram", + "form": "a transit network diagram, with line color coding, transfer nodes, and ordered stops", + "lineage": "public transport", + "tags": [ + "routes", + "nodes", + "zones" + ] + }, + { + "id": "transport-mobility-live-digital-system-modeled-on-a-transit-network-dia", + "form": "a live digital system modeled on a transit network diagram, with ordered stops, service branches, and fare zones", + "lineage": "public transport; native digital", + "tags": [ + "routes", + "nodes", + "zones" + ] + }, + { + "id": "transport-mobility-shared-participatory-system-modeled-on-a-transit-net", + "form": "a shared participatory system modeled on a transit network diagram, with fare zones, line color coding, and service branches", + "lineage": "public transport; participatory", + "tags": [ + "routes", + "nodes", + "zones" + ] + }, + { + "id": "transport-mobility-railway-timetable", + "form": "a railway timetable, with station rows, departure columns, and service classes", + "lineage": "rail operations", + "tags": [ + "timegrid", + "connections", + "exceptions" + ] + }, + { + "id": "transport-mobility-live-digital-system-modeled-on-a-railway-timetable", + "form": "a live digital system modeled on a railway timetable, with service classes, footnote exceptions, and connection windows", + "lineage": "rail operations; native digital", + "tags": [ + "timegrid", + "connections", + "exceptions" + ] + }, + { + "id": "transport-mobility-shared-participatory-system-modeled-on-a-railway-tim", + "form": "a shared participatory system modeled on a railway timetable, with connection windows, station rows, and footnote exceptions", + "lineage": "rail operations; participatory", + "tags": [ + "timegrid", + "connections", + "exceptions" + ] + }, + { + "id": "transport-mobility-flight-progress-strip", + "form": "a flight progress strip, with route segments, altitude blocks, and handoff markers", + "lineage": "air traffic control", + "tags": [ + "handoffs", + "segments", + "status" + ] + }, + { + "id": "transport-mobility-live-digital-system-modeled-on-a-flight-progress-str", + "form": "a live digital system modeled on a flight progress strip, with handoff markers, timing estimates, and status annotations", + "lineage": "air traffic control; native digital", + "tags": [ + "handoffs", + "segments", + "status" + ] + }, + { + "id": "transport-mobility-shared-participatory-system-modeled-on-a-flight-prog", + "form": "a shared participatory system modeled on a flight progress strip, with status annotations, route segments, and timing estimates", + "lineage": "air traffic control; participatory", + "tags": [ + "handoffs", + "segments", + "status" + ] + }, + { + "id": "transport-mobility-harbor-traffic-plot", + "form": "a harbor traffic plot, with vessel tracks, channel boundaries, and anchorage zones", + "lineage": "maritime operations", + "tags": [ + "tracks", + "zones", + "alerts" + ] + }, + { + "id": "transport-mobility-live-digital-system-modeled-on-a-harbor-traffic-plot", + "form": "a live digital system modeled on a harbor traffic plot, with anchorage zones, tide windows, and conflict alerts", + "lineage": "maritime operations; native digital", + "tags": [ + "tracks", + "zones", + "alerts" + ] + }, + { + "id": "transport-mobility-shared-participatory-system-modeled-on-a-harbor-traf", + "form": "a shared participatory system modeled on a harbor traffic plot, with conflict alerts, vessel tracks, and tide windows", + "lineage": "maritime operations; participatory", + "tags": [ + "tracks", + "zones", + "alerts" + ] + }, + { + "id": "transport-mobility-long-distance-roadbook", + "form": "a long-distance roadbook, with turn diagrams, cumulative distances, and hazard notes", + "lineage": "overland navigation", + "tags": [ + "sequence", + "distance", + "checkpoints" + ] + }, + { + "id": "transport-mobility-live-digital-system-modeled-on-a-long-distance-roadb", + "form": "a live digital system modeled on a long-distance roadbook, with hazard notes, turn indices, and checkpoint stamps", + "lineage": "overland navigation; native digital", + "tags": [ + "sequence", + "distance", + "checkpoints" + ] + }, + { + "id": "transport-mobility-shared-participatory-system-modeled-on-a-long-distan", + "form": "a shared participatory system modeled on a long-distance roadbook, with checkpoint stamps, turn diagrams, and turn indices", + "lineage": "overland navigation; participatory", + "tags": [ + "sequence", + "distance", + "checkpoints" + ] + }, + { + "id": "transport-mobility-rally-pace-note-booklet", + "form": "a rally pace-note booklet, with sequential corner codes, distance intervals, and severity marks", + "lineage": "motorsport navigation", + "tags": [ + "codes", + "intervals", + "severity" + ] + }, + { + "id": "transport-mobility-live-digital-system-modeled-on-a-rally-pace-note-boo", + "form": "a live digital system modeled on a rally pace-note booklet, with severity marks, landmark cues, and confirmation calls", + "lineage": "motorsport navigation; native digital", + "tags": [ + "codes", + "intervals", + "severity" + ] + }, + { + "id": "transport-mobility-shared-participatory-system-modeled-on-a-rally-pace", + "form": "a shared participatory system modeled on a rally pace-note booklet, with confirmation calls, sequential corner codes, and landmark cues", + "lineage": "motorsport navigation; participatory", + "tags": [ + "codes", + "intervals", + "severity" + ] + }, + { + "id": "transport-mobility-vehicle-instrument-cluster", + "form": "a vehicle instrument cluster, with primary speed readout, warning hierarchy, and gauge arcs", + "lineage": "vehicle interfaces", + "tags": [ + "gauges", + "warnings", + "modes" + ] + }, + { + "id": "transport-mobility-live-digital-system-modeled-on-a-vehicle-instrument", + "form": "a live digital system modeled on a vehicle instrument cluster, with gauge arcs, mode indicators, and trip summaries", + "lineage": "vehicle interfaces; native digital", + "tags": [ + "gauges", + "warnings", + "modes" + ] + }, + { + "id": "transport-mobility-shared-participatory-system-modeled-on-a-vehicle-ins", + "form": "a shared participatory system modeled on a vehicle instrument cluster, with trip summaries, primary speed readout, and mode indicators", + "lineage": "vehicle interfaces; participatory", + "tags": [ + "gauges", + "warnings", + "modes" + ] + }, + { + "id": "transport-mobility-bicycle-route-map", + "form": "a bicycle route map, with protected and unprotected segments, grade profiles, and repair stations", + "lineage": "active mobility", + "tags": [ + "segments", + "grades", + "services" + ] + }, + { + "id": "transport-mobility-live-digital-system-modeled-on-a-bicycle-route-map", + "form": "a live digital system modeled on a bicycle route map, with repair stations, intersection warnings, and route alternates", + "lineage": "active mobility; native digital", + "tags": [ + "segments", + "grades", + "services" + ] + }, + { + "id": "transport-mobility-shared-participatory-system-modeled-on-a-bicycle-rou", + "form": "a shared participatory system modeled on a bicycle route map, with route alternates, protected and unprotected segments, and intersection warnings", + "lineage": "active mobility; participatory", + "tags": [ + "segments", + "grades", + "services" + ] + }, + { + "id": "transport-mobility-curb-allocation-plan", + "form": "a curb allocation plan, with time-based zones, vehicle classes, and loading windows", + "lineage": "street management", + "tags": [ + "timebands", + "classes", + "enforcement" + ] + }, + { + "id": "transport-mobility-live-digital-system-modeled-on-a-curb-allocation-pla", + "form": "a live digital system modeled on a curb allocation plan, with loading windows, conflict markings, and enforcement codes", + "lineage": "street management; native digital", + "tags": [ + "timebands", + "classes", + "enforcement" + ] + }, + { + "id": "transport-mobility-shared-participatory-system-modeled-on-a-curb-alloca", + "form": "a shared participatory system modeled on a curb allocation plan, with enforcement codes, time-based zones, and conflict markings", + "lineage": "street management; participatory", + "tags": [ + "timebands", + "classes", + "enforcement" + ] + }, + { + "id": "transport-mobility-fleet-dispatch-board", + "form": "a fleet dispatch board, with vehicle lanes, job cards, and priority ordering", + "lineage": "transport logistics", + "tags": [ + "lanes", + "cards", + "exceptions" + ] + }, + { + "id": "transport-mobility-live-digital-system-modeled-on-a-fleet-dispatch-boar", + "form": "a live digital system modeled on a fleet dispatch board, with priority ordering, status transitions, and exception flags", + "lineage": "transport logistics; native digital", + "tags": [ + "lanes", + "cards", + "exceptions" + ] + }, + { + "id": "transport-mobility-shared-participatory-system-modeled-on-a-fleet-dispa", + "form": "a shared participatory system modeled on a fleet dispatch board, with exception flags, vehicle lanes, and status transitions", + "lineage": "transport logistics; participatory", + "tags": [ + "lanes", + "cards", + "exceptions" + ] + }, + { + "id": "transport-mobility-container-yard-map", + "form": "a container yard map, with block coordinates, stack heights, and crane zones", + "lineage": "freight logistics", + "tags": [ + "coordinates", + "stacks", + "queues" + ] + }, + { + "id": "transport-mobility-live-digital-system-modeled-on-a-container-yard-map", + "form": "a live digital system modeled on a container yard map, with crane zones, pickup queues, and hazardous separation", + "lineage": "freight logistics; native digital", + "tags": [ + "coordinates", + "stacks", + "queues" + ] + }, + { + "id": "transport-mobility-shared-participatory-system-modeled-on-a-container-y", + "form": "a shared participatory system modeled on a container yard map, with hazardous separation, block coordinates, and pickup queues", + "lineage": "freight logistics; participatory", + "tags": [ + "coordinates", + "stacks", + "queues" + ] + }, + { + "id": "transport-mobility-airport-movement-map", + "form": "an airport movement map, with runway hierarchy, taxiway codes, and hold points", + "lineage": "airport operations", + "tags": [ + "routes", + "codes", + "holds" + ] + }, + { + "id": "transport-mobility-live-digital-system-modeled-on-an-airport-movement-m", + "form": "a live digital system modeled on an airport movement map, with hold points, restricted zones, and ground routes", + "lineage": "airport operations; native digital", + "tags": [ + "routes", + "codes", + "holds" + ] + }, + { + "id": "transport-mobility-shared-participatory-system-modeled-on-an-airport-mo", + "form": "a shared participatory system modeled on an airport movement map, with ground routes, runway hierarchy, and restricted zones", + "lineage": "airport operations; participatory", + "tags": [ + "routes", + "codes", + "holds" + ] + }, + { + "id": "transport-mobility-taxi-rank-queue-system", + "form": "a taxi rank queue system, with numbered bays, next-up ordering, and demand signals", + "lineage": "urban mobility", + "tags": [ + "ordering", + "bays", + "priority" + ] + }, + { + "id": "transport-mobility-live-digital-system-modeled-on-a-taxi-rank-queue-sys", + "form": "a live digital system modeled on a taxi rank queue system, with demand signals, accessibility priority, and timeout rules", + "lineage": "urban mobility; native digital", + "tags": [ + "ordering", + "bays", + "priority" + ] + }, + { + "id": "transport-mobility-shared-participatory-system-modeled-on-a-taxi-rank-q", + "form": "a shared participatory system modeled on a taxi rank queue system, with timeout rules, numbered bays, and accessibility priority", + "lineage": "urban mobility; participatory", + "tags": [ + "ordering", + "bays", + "priority" + ] + }, + { + "id": "transport-mobility-bus-headway-board", + "form": "a bus headway board, with interval targets, bunching alerts, and vehicle spacing", + "lineage": "transit control", + "tags": [ + "intervals", + "spacing", + "recovery" + ] + }, + { + "id": "transport-mobility-live-digital-system-modeled-on-a-bus-headway-board", + "form": "a live digital system modeled on a bus headway board, with vehicle spacing, recovery actions, and route clocks", + "lineage": "transit control; native digital", + "tags": [ + "intervals", + "spacing", + "recovery" + ] + }, + { + "id": "transport-mobility-shared-participatory-system-modeled-on-a-bus-headway", + "form": "a shared participatory system modeled on a bus headway board, with route clocks, interval targets, and recovery actions", + "lineage": "transit control; participatory", + "tags": [ + "intervals", + "spacing", + "recovery" + ] + }, + { + "id": "transport-mobility-ticket-validation-flow", + "form": "a ticket validation flow, with fare selection, zone rules, and eligibility checkpoints", + "lineage": "fare systems", + "tags": [ + "gates", + "eligibility", + "confirmation" + ] + }, + { + "id": "transport-mobility-live-digital-system-modeled-on-a-ticket-validation-f", + "form": "a live digital system modeled on a ticket validation flow, with eligibility checkpoints, confirmation states, and transfer rules", + "lineage": "fare systems; native digital", + "tags": [ + "gates", + "eligibility", + "confirmation" + ] + }, + { + "id": "transport-mobility-shared-participatory-system-modeled-on-a-ticket-vali", + "form": "a shared participatory system modeled on a ticket validation flow, with transfer rules, fare selection, and confirmation states", + "lineage": "fare systems; participatory", + "tags": [ + "gates", + "eligibility", + "confirmation" + ] + }, + { + "id": "transport-mobility-mobility-hub-directory", + "form": "a mobility hub directory, with mode grouping, departure proximity, and accessible paths", + "lineage": "intermodal transport", + "tags": [ + "grouping", + "proximity", + "access" + ] + }, + { + "id": "transport-mobility-live-digital-system-modeled-on-a-mobility-hub-direct", + "form": "a live digital system modeled on a mobility hub directory, with accessible paths, platform codes, and last-mile links", + "lineage": "intermodal transport; native digital", + "tags": [ + "grouping", + "proximity", + "access" + ] + }, + { + "id": "transport-mobility-shared-participatory-system-modeled-on-a-mobility-hu", + "form": "a shared participatory system modeled on a mobility hub directory, with last-mile links, mode grouping, and platform codes", + "lineage": "intermodal transport; participatory", + "tags": [ + "grouping", + "proximity", + "access" + ] + }, + { + "id": "transport-mobility-trail-blaze-system", + "form": "a trail blaze system, with repeated route marks, junction confirmations, and distance intervals", + "lineage": "route finding", + "tags": [ + "repetition", + "junctions", + "difficulty" + ] + }, + { + "id": "transport-mobility-live-digital-system-modeled-on-a-trail-blaze-system", + "form": "a live digital system modeled on a trail blaze system, with distance intervals, difficulty codes, and emergency exits", + "lineage": "route finding; native digital", + "tags": [ + "repetition", + "junctions", + "difficulty" + ] + }, + { + "id": "transport-mobility-shared-participatory-system-modeled-on-a-trail-blaze", + "form": "a shared participatory system modeled on a trail blaze system, with emergency exits, repeated route marks, and difficulty codes", + "lineage": "route finding; participatory", + "tags": [ + "repetition", + "junctions", + "difficulty" + ] + }, + { + "id": "transport-mobility-canal-lock-schedule", + "form": "a canal lock schedule, with chamber sequences, water-level states, and vessel queues", + "lineage": "water transport", + "tags": [ + "sequence", + "levels", + "windows" + ] + }, + { + "id": "transport-mobility-live-digital-system-modeled-on-a-canal-lock-schedule", + "form": "a live digital system modeled on a canal lock schedule, with vessel queues, transit windows, and closure notices", + "lineage": "water transport; native digital", + "tags": [ + "sequence", + "levels", + "windows" + ] + }, + { + "id": "transport-mobility-shared-participatory-system-modeled-on-a-canal-lock", + "form": "a shared participatory system modeled on a canal lock schedule, with closure notices, chamber sequences, and transit windows", + "lineage": "water transport; participatory", + "tags": [ + "sequence", + "levels", + "windows" + ] + }, + { + "id": "transport-mobility-fleet-maintenance-board", + "form": "a fleet maintenance board, with service intervals, vehicle cards, and defect severity", + "lineage": "fleet operations", + "tags": [ + "intervals", + "defects", + "release" + ] + }, + { + "id": "transport-mobility-live-digital-system-modeled-on-a-fleet-maintenance-b", + "form": "a live digital system modeled on a fleet maintenance board, with defect severity, part dependencies, and release checks", + "lineage": "fleet operations; native digital", + "tags": [ + "intervals", + "defects", + "release" + ] + }, + { + "id": "transport-mobility-shared-participatory-system-modeled-on-a-fleet-maint", + "form": "a shared participatory system modeled on a fleet maintenance board, with release checks, service intervals, and part dependencies", + "lineage": "fleet operations; participatory", + "tags": [ + "intervals", + "defects", + "release" + ] + }, + { + "id": "transport-mobility-shipping-lane-chart", + "form": "a shipping lane chart, with traffic separation lanes, navigation marks, and depth bands", + "lineage": "maritime navigation", + "tags": [ + "lanes", + "marks", + "depth" + ] + }, + { + "id": "transport-mobility-live-digital-system-modeled-on-a-shipping-lane-chart", + "form": "a live digital system modeled on a shipping lane chart, with depth bands, reporting points, and weather cautions", + "lineage": "maritime navigation; native digital", + "tags": [ + "lanes", + "marks", + "depth" + ] + }, + { + "id": "transport-mobility-shared-participatory-system-modeled-on-a-shipping-la", + "form": "a shared participatory system modeled on a shipping lane chart, with weather cautions, traffic separation lanes, and reporting points", + "lineage": "maritime navigation; participatory", + "tags": [ + "lanes", + "marks", + "depth" + ] + }, + { + "id": "transport-mobility-car-share-availability-map", + "form": "a car-share availability map, with live vehicle pins, battery or fuel levels, and reservation windows", + "lineage": "shared mobility", + "tags": [ + "live-map", + "booking", + "zones" + ] + }, + { + "id": "transport-mobility-live-digital-system-modeled-on-a-car-share-availabil", + "form": "a live digital system modeled on a car-share availability map, with reservation windows, geofenced returns, and price tiers", + "lineage": "shared mobility; native digital", + "tags": [ + "live-map", + "booking", + "zones" + ] + }, + { + "id": "transport-mobility-shared-participatory-system-modeled-on-a-car-share-a", + "form": "a shared participatory system modeled on a car-share availability map, with price tiers, live vehicle pins, and geofenced returns", + "lineage": "shared mobility; participatory", + "tags": [ + "live-map", + "booking", + "zones" + ] + }, + { + "id": "transport-mobility-baggage-routing-diagram", + "form": "a baggage routing diagram, with bag identifiers, sortation branches, and transfer deadlines", + "lineage": "airport logistics", + "tags": [ + "identifiers", + "branches", + "deadlines" + ] + } + ] + }, + { + "id": "safety-emergency", + "label": "Safety & emergency", + "description": "High-stakes coordination systems with priorities, roles, routes, escalation, and recovery.", + "concepts": [ + { + "id": "safety-emergency-emergency-evacuation-plan", + "form": "an emergency evacuation plan, with you-are-here anchors, branching escape routes, and priority exits", + "lineage": "life safety", + "tags": [ + "routes", + "priority", + "location" + ] + }, + { + "id": "safety-emergency-live-digital-system-modeled-on-an-emergency-evacuati", + "form": "a live digital system modeled on an emergency evacuation plan, with priority exits, assembly points, and blocked-path alternates", + "lineage": "life safety; native digital", + "tags": [ + "routes", + "priority", + "location" + ] + }, + { + "id": "safety-emergency-shared-participatory-system-modeled-on-an-emergency", + "form": "a shared participatory system modeled on an emergency evacuation plan, with blocked-path alternates, you-are-here anchors, and assembly points", + "lineage": "life safety; participatory", + "tags": [ + "routes", + "priority", + "location" + ] + }, + { + "id": "safety-emergency-medical-triage-tag", + "form": "a medical triage tag, with color priority bands, vital-sign fields, and injury checklists", + "lineage": "emergency medicine", + "tags": [ + "priority", + "vitals", + "handoff" + ] + }, + { + "id": "safety-emergency-live-digital-system-modeled-on-a-medical-triage-tag", + "form": "a live digital system modeled on a medical triage tag, with injury checklists, treatment timestamps, and destination stubs", + "lineage": "emergency medicine; native digital", + "tags": [ + "priority", + "vitals", + "handoff" + ] + }, + { + "id": "safety-emergency-shared-participatory-system-modeled-on-a-medical-tri", + "form": "a shared participatory system modeled on a medical triage tag, with destination stubs, color priority bands, and treatment timestamps", + "lineage": "emergency medicine; participatory", + "tags": [ + "priority", + "vitals", + "handoff" + ] + }, + { + "id": "safety-emergency-incident-command-board", + "form": "an incident command board, with command roles, incident objectives, and resource assignments", + "lineage": "emergency management", + "tags": [ + "roles", + "status", + "resources" + ] + }, + { + "id": "safety-emergency-live-digital-system-modeled-on-an-incident-command-b", + "form": "a live digital system modeled on an incident command board, with resource assignments, operational periods, and status updates", + "lineage": "emergency management; native digital", + "tags": [ + "roles", + "status", + "resources" + ] + }, + { + "id": "safety-emergency-shared-participatory-system-modeled-on-an-incident-c", + "form": "a shared participatory system modeled on an incident command board, with status updates, command roles, and operational periods", + "lineage": "emergency management; participatory", + "tags": [ + "roles", + "status", + "resources" + ] + }, + { + "id": "safety-emergency-hazard-classification-diamond", + "form": "a hazard classification diamond, with fixed hazard quadrants, numeric severity levels, and special-condition codes", + "lineage": "industrial safety", + "tags": [ + "quadrants", + "severity", + "codes" + ] + }, + { + "id": "safety-emergency-live-digital-system-modeled-on-a-hazard-classificati", + "form": "a live digital system modeled on a hazard classification diamond, with special-condition codes, high-contrast encoding, and at-a-glance comparison", + "lineage": "industrial safety; native digital", + "tags": [ + "quadrants", + "severity", + "codes" + ] + }, + { + "id": "safety-emergency-shared-participatory-system-modeled-on-a-hazard-clas", + "form": "a shared participatory system modeled on a hazard classification diamond, with at-a-glance comparison, fixed hazard quadrants, and high-contrast encoding", + "lineage": "industrial safety; participatory", + "tags": [ + "quadrants", + "severity", + "codes" + ] + }, + { + "id": "safety-emergency-fire-control-panel", + "form": "a fire control panel, with zone indicators, alarm hierarchy, and fault states", + "lineage": "building safety", + "tags": [ + "zones", + "alarms", + "acknowledgment" + ] + }, + { + "id": "safety-emergency-live-digital-system-modeled-on-a-fire-control-panel", + "form": "a live digital system modeled on a fire control panel, with fault states, acknowledgment controls, and event history", + "lineage": "building safety; native digital", + "tags": [ + "zones", + "alarms", + "acknowledgment" + ] + }, + { + "id": "safety-emergency-shared-participatory-system-modeled-on-a-fire-contro", + "form": "a shared participatory system modeled on a fire control panel, with event history, zone indicators, and acknowledgment controls", + "lineage": "building safety; participatory", + "tags": [ + "zones", + "alarms", + "acknowledgment" + ] + }, + { + "id": "safety-emergency-emergency-action-checklist", + "form": "an emergency action checklist, with phased actions, binary confirmations, and decision holds", + "lineage": "crisis operations", + "tags": [ + "sequence", + "confirmation", + "holds" + ] + }, + { + "id": "safety-emergency-live-digital-system-modeled-on-an-emergency-action-c", + "form": "a live digital system modeled on an emergency action checklist, with decision holds, escalation triggers, and completion sign-offs", + "lineage": "crisis operations; native digital", + "tags": [ + "sequence", + "confirmation", + "holds" + ] + }, + { + "id": "safety-emergency-shared-participatory-system-modeled-on-an-emergency-561e3fe1", + "form": "a shared participatory system modeled on an emergency action checklist, with completion sign-offs, phased actions, and escalation triggers", + "lineage": "crisis operations; participatory", + "tags": [ + "sequence", + "confirmation", + "holds" + ] + }, + { + "id": "safety-emergency-disaster-shelter-registry", + "form": "a disaster shelter registry, with shelter locations, live capacity counts, and accessibility fields", + "lineage": "humanitarian response", + "tags": [ + "capacity", + "needs", + "status" + ] + }, + { + "id": "safety-emergency-live-digital-system-modeled-on-a-disaster-shelter-re", + "form": "a live digital system modeled on a disaster shelter registry, with accessibility fields, supply status, and intake cutoffs", + "lineage": "humanitarian response; native digital", + "tags": [ + "capacity", + "needs", + "status" + ] + }, + { + "id": "safety-emergency-shared-participatory-system-modeled-on-a-disaster-sh", + "form": "a shared participatory system modeled on a disaster shelter registry, with intake cutoffs, shelter locations, and supply status", + "lineage": "humanitarian response; participatory", + "tags": [ + "capacity", + "needs", + "status" + ] + }, + { + "id": "safety-emergency-severe-storm-track-map", + "form": "a severe-storm track map, with forecast track cones, time-stamped positions, and uncertainty bands", + "lineage": "weather warning", + "tags": [ + "trajectory", + "uncertainty", + "time" + ] + }, + { + "id": "safety-emergency-live-digital-system-modeled-on-a-severe-storm-track", + "form": "a live digital system modeled on a severe-storm track map, with uncertainty bands, warning zones, and arrival estimates", + "lineage": "weather warning; native digital", + "tags": [ + "trajectory", + "uncertainty", + "time" + ] + }, + { + "id": "safety-emergency-shared-participatory-system-modeled-on-a-severe-stor", + "form": "a shared participatory system modeled on a severe-storm track map, with arrival estimates, forecast track cones, and warning zones", + "lineage": "weather warning; participatory", + "tags": [ + "trajectory", + "uncertainty", + "time" + ] + }, + { + "id": "safety-emergency-avalanche-danger-bulletin", + "form": "an avalanche danger bulletin, with danger levels, slope-aspect diagrams, and elevation bands", + "lineage": "mountain safety", + "tags": [ + "levels", + "aspect", + "forecast" + ] + }, + { + "id": "safety-emergency-live-digital-system-modeled-on-an-avalanche-danger-b", + "form": "a live digital system modeled on an avalanche danger bulletin, with elevation bands, problem types, and time-based outlooks", + "lineage": "mountain safety; native digital", + "tags": [ + "levels", + "aspect", + "forecast" + ] + }, + { + "id": "safety-emergency-shared-participatory-system-modeled-on-an-avalanche", + "form": "a shared participatory system modeled on an avalanche danger bulletin, with time-based outlooks, danger levels, and problem types", + "lineage": "mountain safety; participatory", + "tags": [ + "levels", + "aspect", + "forecast" + ] + }, + { + "id": "safety-emergency-maritime-signal-flag-codebook", + "form": "a maritime signal-flag codebook, with distinct flag patterns, single-signal meanings, and multi-flag sequences", + "lineage": "maritime safety", + "tags": [ + "codes", + "sequence", + "recognition" + ] + }, + { + "id": "safety-emergency-live-digital-system-modeled-on-a-maritime-signal-fla", + "form": "a live digital system modeled on a maritime signal-flag codebook, with multi-flag sequences, distance recognition, and acknowledgment rules", + "lineage": "maritime safety; native digital", + "tags": [ + "codes", + "sequence", + "recognition" + ] + }, + { + "id": "safety-emergency-shared-participatory-system-modeled-on-a-maritime-si", + "form": "a shared participatory system modeled on a maritime signal-flag codebook, with acknowledgment rules, distinct flag patterns, and distance recognition", + "lineage": "maritime safety; participatory", + "tags": [ + "codes", + "sequence", + "recognition" + ] + }, + { + "id": "safety-emergency-aviation-hazard-bulletin", + "form": "an aviation hazard bulletin, with coded locations, validity windows, and altitude bands", + "lineage": "flight safety", + "tags": [ + "location", + "validity", + "severity" + ] + }, + { + "id": "safety-emergency-live-digital-system-modeled-on-an-aviation-hazard-bu", + "form": "a live digital system modeled on an aviation hazard bulletin, with altitude bands, hazard descriptions, and cancellation notices", + "lineage": "flight safety; native digital", + "tags": [ + "location", + "validity", + "severity" + ] + }, + { + "id": "safety-emergency-shared-participatory-system-modeled-on-an-aviation-h", + "form": "a shared participatory system modeled on an aviation hazard bulletin, with cancellation notices, coded locations, and hazard descriptions", + "lineage": "flight safety; participatory", + "tags": [ + "location", + "validity", + "severity" + ] + }, + { + "id": "safety-emergency-beach-safety-flag-system", + "form": "a beach safety flag system, with condition colors, zoned placement, and plain-language meanings", + "lineage": "public safety", + "tags": [ + "colorcode", + "zones", + "conditions" + ] + }, + { + "id": "safety-emergency-live-digital-system-modeled-on-a-beach-safety-flag-s", + "form": "a live digital system modeled on a beach safety flag system, with plain-language meanings, change thresholds, and all-clear signals", + "lineage": "public safety; native digital", + "tags": [ + "colorcode", + "zones", + "conditions" + ] + }, + { + "id": "safety-emergency-shared-participatory-system-modeled-on-a-beach-safet", + "form": "a shared participatory system modeled on a beach safety flag system, with all-clear signals, condition colors, and change thresholds", + "lineage": "public safety; participatory", + "tags": [ + "colorcode", + "zones", + "conditions" + ] + }, + { + "id": "safety-emergency-laboratory-safety-data-sheet", + "form": "a laboratory safety data sheet, with hazard identification, exposure limits, and handling instructions", + "lineage": "chemical safety", + "tags": [ + "sections", + "thresholds", + "response" + ] + }, + { + "id": "safety-emergency-live-digital-system-modeled-on-a-laboratory-safety-d", + "form": "a live digital system modeled on a laboratory safety data sheet, with handling instructions, first-aid steps, and disposal rules", + "lineage": "chemical safety; native digital", + "tags": [ + "sections", + "thresholds", + "response" + ] + }, + { + "id": "safety-emergency-shared-participatory-system-modeled-on-a-laboratory", + "form": "a shared participatory system modeled on a laboratory safety data sheet, with disposal rules, hazard identification, and first-aid steps", + "lineage": "chemical safety; participatory", + "tags": [ + "sections", + "thresholds", + "response" + ] + }, + { + "id": "safety-emergency-product-recall-notice", + "form": "a product recall notice, with affected-item identifiers, risk summaries, and stop-use instructions", + "lineage": "consumer safety", + "tags": [ + "identification", + "action", + "contact" + ] + }, + { + "id": "safety-emergency-live-digital-system-modeled-on-a-product-recall-noti", + "form": "a live digital system modeled on a product recall notice, with stop-use instructions, return pathways, and contact escalation", + "lineage": "consumer safety; native digital", + "tags": [ + "identification", + "action", + "contact" + ] + }, + { + "id": "safety-emergency-shared-participatory-system-modeled-on-a-product-rec", + "form": "a shared participatory system modeled on a product recall notice, with contact escalation, affected-item identifiers, and return pathways", + "lineage": "consumer safety; participatory", + "tags": [ + "identification", + "action", + "contact" + ] + }, + { + "id": "safety-emergency-public-health-alert-dashboard", + "form": "a public-health alert dashboard, with alert levels, trend lines, and geographic incidence", + "lineage": "public health", + "tags": [ + "levels", + "trends", + "guidance" + ] + }, + { + "id": "safety-emergency-live-digital-system-modeled-on-a-public-health-alert", + "form": "a live digital system modeled on a public-health alert dashboard, with geographic incidence, action thresholds, and audience-specific guidance", + "lineage": "public health; native digital", + "tags": [ + "levels", + "trends", + "guidance" + ] + }, + { + "id": "safety-emergency-shared-participatory-system-modeled-on-a-public-heal", + "form": "a shared participatory system modeled on a public-health alert dashboard, with audience-specific guidance, alert levels, and action thresholds", + "lineage": "public health; participatory", + "tags": [ + "levels", + "trends", + "guidance" + ] + }, + { + "id": "safety-emergency-building-occupancy-status-board", + "form": "a building occupancy status board, with live occupant counts, zone capacities, and entry-exit deltas", + "lineage": "facility safety", + "tags": [ + "counts", + "zones", + "limits" + ] + }, + { + "id": "safety-emergency-live-digital-system-modeled-on-a-building-occupancy", + "form": "a live digital system modeled on a building occupancy status board, with entry-exit deltas, threshold warnings, and roll-call gaps", + "lineage": "facility safety; native digital", + "tags": [ + "counts", + "zones", + "limits" + ] + }, + { + "id": "safety-emergency-shared-participatory-system-modeled-on-a-building-oc", + "form": "a shared participatory system modeled on a building occupancy status board, with roll-call gaps, live occupant counts, and threshold warnings", + "lineage": "facility safety; participatory", + "tags": [ + "counts", + "zones", + "limits" + ] + }, + { + "id": "safety-emergency-search-and-rescue-grid-map", + "form": "a search-and-rescue grid map, with numbered search cells, team assignments, and coverage traces", + "lineage": "rescue operations", + "tags": [ + "grid", + "coverage", + "evidence" + ] + }, + { + "id": "safety-emergency-live-digital-system-modeled-on-a-search-and-rescue-g", + "form": "a live digital system modeled on a search-and-rescue grid map, with coverage traces, clue markers, and priority revisions", + "lineage": "rescue operations; native digital", + "tags": [ + "grid", + "coverage", + "evidence" + ] + }, + { + "id": "safety-emergency-shared-participatory-system-modeled-on-a-search-and", + "form": "a shared participatory system modeled on a search-and-rescue grid map, with priority revisions, numbered search cells, and clue markers", + "lineage": "rescue operations; participatory", + "tags": [ + "grid", + "coverage", + "evidence" + ] + }, + { + "id": "safety-emergency-missing-person-incident-board", + "form": "a missing-person incident board, with last-known-point anchors, chronological sightings, and search assignments", + "lineage": "rescue coordination", + "tags": [ + "timeline", + "evidence", + "assignments" + ] + }, + { + "id": "safety-emergency-live-digital-system-modeled-on-a-missing-person-inci", + "form": "a live digital system modeled on a missing-person incident board, with search assignments, evidence confidence, and contact logs", + "lineage": "rescue coordination; native digital", + "tags": [ + "timeline", + "evidence", + "assignments" + ] + }, + { + "id": "safety-emergency-shared-participatory-system-modeled-on-a-missing-per", + "form": "a shared participatory system modeled on a missing-person incident board, with contact logs, last-known-point anchors, and evidence confidence", + "lineage": "rescue coordination; participatory", + "tags": [ + "timeline", + "evidence", + "assignments" + ] + }, + { + "id": "safety-emergency-crisis-hotline-decision-tree", + "form": "a crisis hotline decision tree, with opening assessment prompts, risk branches, and de-escalation steps", + "lineage": "crisis support", + "tags": [ + "branching", + "severity", + "handoff" + ] + }, + { + "id": "safety-emergency-live-digital-system-modeled-on-a-crisis-hotline-deci", + "form": "a live digital system modeled on a crisis hotline decision tree, with de-escalation steps, warm-handoff points, and documentation checks", + "lineage": "crisis support; native digital", + "tags": [ + "branching", + "severity", + "handoff" + ] + }, + { + "id": "safety-emergency-shared-participatory-system-modeled-on-a-crisis-hotl", + "form": "a shared participatory system modeled on a crisis hotline decision tree, with documentation checks, opening assessment prompts, and warm-handoff points", + "lineage": "crisis support; participatory", + "tags": [ + "branching", + "severity", + "handoff" + ] + }, + { + "id": "safety-emergency-emergency-broadcast-crawl", + "form": "an emergency broadcast crawl, with interrupt priority, compact action text, and location qualifiers", + "lineage": "public warning", + "tags": [ + "priority", + "brevity", + "repetition" + ] + }, + { + "id": "safety-emergency-live-digital-system-modeled-on-an-emergency-broadcas", + "form": "a live digital system modeled on an emergency broadcast crawl, with location qualifiers, repeated cycles, and expiry timestamps", + "lineage": "public warning; native digital", + "tags": [ + "priority", + "brevity", + "repetition" + ] + }, + { + "id": "safety-emergency-shared-participatory-system-modeled-on-an-emergency-de56a9a4", + "form": "a shared participatory system modeled on an emergency broadcast crawl, with expiry timestamps, interrupt priority, and repeated cycles", + "lineage": "public warning; participatory", + "tags": [ + "priority", + "brevity", + "repetition" + ] + }, + { + "id": "safety-emergency-wildfire-containment-map", + "form": "a wildfire containment map, with active-fire perimeters, containment segments, and resource locations", + "lineage": "wildland response", + "tags": [ + "perimeter", + "resources", + "change" + ] + }, + { + "id": "safety-emergency-live-digital-system-modeled-on-a-wildfire-containmen", + "form": "a live digital system modeled on a wildfire containment map, with resource locations, wind overlays, and time-stamped changes", + "lineage": "wildland response; native digital", + "tags": [ + "perimeter", + "resources", + "change" + ] + }, + { + "id": "safety-emergency-shared-participatory-system-modeled-on-a-wildfire-co", + "form": "a shared participatory system modeled on a wildfire containment map, with time-stamped changes, active-fire perimeters, and wind overlays", + "lineage": "wildland response; participatory", + "tags": [ + "perimeter", + "resources", + "change" + ] + }, + { + "id": "safety-emergency-flood-stage-gauge", + "form": "a flood-stage gauge, with physical level marks, named warning thresholds, and current-level pointer", + "lineage": "hydrology warning", + "tags": [ + "thresholds", + "levels", + "history" + ] + } + ] + }, + { + "id": "broadcast-programming", + "label": "Broadcast & programming", + "description": "Scheduled and live media systems with channels, segments, timing, cues, and continuity.", + "concepts": [ + { + "id": "broadcast-programming-teletext-service", + "form": "a teletext service, with page numbers, block graphics, and channel colors", + "lineage": "Broadcast & programming", + "tags": [ + "time-grid", + "channel-structure", + "program-cue" + ] + }, + { + "id": "broadcast-programming-printed-tv-programme-guide", + "form": "a printed TV programme guide, with time grids and circled listings", + "lineage": "Broadcast & programming", + "tags": [ + "time-grid", + "channel-structure", + "program-cue" + ] + }, + { + "id": "broadcast-programming-radio-station-s-program-log-and-request-line-cards", + "form": "a radio station's program log and request-line cards, with scheduled segments, caller queues, and transmission cues", + "lineage": "Broadcast & programming", + "tags": [ + "time-grid", + "channel-structure", + "program-cue" + ] + }, + { + "id": "broadcast-programming-vintage-radio-receiver-plate", + "form": "a vintage radio receiver plate, with slide tuning bands, frequency markings, and signal meters", + "lineage": "Broadcast & programming", + "tags": [ + "time-grid", + "channel-structure", + "program-cue" + ] + }, + { + "id": "broadcast-programming-cinema-s-projection-booth-reel-change-cue-sheets", + "form": "a cinema's projection-booth reel-change cue sheet, with timed changeovers, equipment checks, and interruption notes", + "lineage": "Broadcast & programming", + "tags": [ + "time-grid", + "channel-structure", + "program-cue" + ] + }, + { + "id": "broadcast-programming-cinema-ticket-stub", + "form": "a cinema ticket stub, with seat coordinates, screen numbers, and entry barcodes", + "lineage": "Broadcast & programming", + "tags": [ + "time-grid", + "channel-structure", + "program-cue" + ] + }, + { + "id": "broadcast-programming-vinyl-double-album-gatefold", + "form": "a vinyl double-album gatefold, with liner notes, track listing, and credits panel", + "lineage": "Broadcast & programming", + "tags": [ + "time-grid", + "channel-structure", + "program-cue" + ] + }, + { + "id": "broadcast-programming-video-rental-shop", + "form": "a video-rental shop, with hand-labeled cassettes and membership cards", + "lineage": "Broadcast & programming", + "tags": [ + "time-grid", + "channel-structure", + "program-cue" + ] + }, + { + "id": "broadcast-programming-shortwave-listener-s-qsl-card-collection-and-frequen", + "form": "a shortwave listener's QSL card collection and frequency schedule, with station records, reception conditions, and time-zone bands", + "lineage": "Broadcast & programming", + "tags": [ + "time-grid", + "channel-structure", + "program-cue" + ] + }, + { + "id": "broadcast-programming-photographic-slide-carousel-and-its-typed-index-card", + "form": "a photographic slide carousel and typed index card, with numbered frames, ordered sequences, and caption cross-references", + "lineage": "Broadcast & programming", + "tags": [ + "time-grid", + "channel-structure", + "program-cue" + ] + }, + { + "id": "broadcast-programming-television-schedule-grid", + "form": "a television schedule grid, with time rows, channel columns, and program blocks", + "lineage": "broadcast programming", + "tags": [ + "timegrid", + "channels", + "episodes" + ] + }, + { + "id": "broadcast-programming-live-digital-system-modeled-on-a-television-schedule", + "form": "a live digital system modeled on a television schedule grid, with program blocks, duration spans, and schedule exceptions", + "lineage": "broadcast programming; native digital", + "tags": [ + "timegrid", + "channels", + "episodes" + ] + }, + { + "id": "broadcast-programming-shared-participatory-system-modeled-on-a-television", + "form": "a shared participatory system modeled on a television schedule grid, with schedule exceptions, time rows, and duration spans", + "lineage": "broadcast programming; participatory", + "tags": [ + "timegrid", + "channels", + "episodes" + ] + }, + { + "id": "broadcast-programming-radio-programming-clock", + "form": "a radio programming clock, with circular hour timing, segment wedges, and fixed breaks", + "lineage": "radio operations", + "tags": [ + "cycle", + "segments", + "timing" + ] + }, + { + "id": "broadcast-programming-live-digital-system-modeled-on-a-radio-programming-c", + "form": "a live digital system modeled on a radio programming clock, with fixed breaks, flexible windows, and reset points", + "lineage": "radio operations; native digital", + "tags": [ + "cycle", + "segments", + "timing" + ] + }, + { + "id": "broadcast-programming-shared-participatory-system-modeled-on-a-radio-progr", + "form": "a shared participatory system modeled on a radio programming clock, with reset points, circular hour timing, and flexible windows", + "lineage": "radio operations; participatory", + "tags": [ + "cycle", + "segments", + "timing" + ] + }, + { + "id": "broadcast-programming-live-show-rundown-sheet", + "form": "a live-show rundown sheet, with item numbers, planned durations, and source assignments", + "lineage": "broadcast production", + "tags": [ + "sequence", + "timing", + "status" + ] + }, + { + "id": "broadcast-programming-live-digital-system-modeled-on-a-live-show-rundown-s", + "form": "a live digital system modeled on a live-show rundown sheet, with source assignments, live status marks, and overrun calculations", + "lineage": "broadcast production; native digital", + "tags": [ + "sequence", + "timing", + "status" + ] + }, + { + "id": "broadcast-programming-shared-participatory-system-modeled-on-a-live-show-r", + "form": "a shared participatory system modeled on a live-show rundown sheet, with overrun calculations, item numbers, and live status marks", + "lineage": "broadcast production; participatory", + "tags": [ + "sequence", + "timing", + "status" + ] + }, + { + "id": "broadcast-programming-lower-third-graphics-system", + "form": "a lower-third graphics system, with name-title hierarchy, safe-area bounds, and template variants", + "lineage": "broadcast graphics", + "tags": [ + "hierarchy", + "templates", + "timing" + ] + }, + { + "id": "broadcast-programming-live-digital-system-modeled-on-a-lower-third-graphic", + "form": "a live digital system modeled on a lower-third graphics system, with template variants, entrance timing, and update rules", + "lineage": "broadcast graphics; native digital", + "tags": [ + "hierarchy", + "templates", + "timing" + ] + }, + { + "id": "broadcast-programming-shared-participatory-system-modeled-on-a-lower-third", + "form": "a shared participatory system modeled on a lower-third graphics system, with update rules, name-title hierarchy, and entrance timing", + "lineage": "broadcast graphics; participatory", + "tags": [ + "hierarchy", + "templates", + "timing" + ] + }, + { + "id": "broadcast-programming-teletext-information-page", + "form": "a teletext information page, with numeric page addresses, block graphics, and limited color roles", + "lineage": "digital broadcasting", + "tags": [ + "pages", + "blocks", + "navigation" + ] + }, + { + "id": "broadcast-programming-live-digital-system-modeled-on-a-teletext-informatio", + "form": "a live digital system modeled on a teletext information page, with limited color roles, topic indices, and next-page links", + "lineage": "digital broadcasting; native digital", + "tags": [ + "pages", + "blocks", + "navigation" + ] + }, + { + "id": "broadcast-programming-shared-participatory-system-modeled-on-a-teletext-in", + "form": "a shared participatory system modeled on a teletext information page, with next-page links, numeric page addresses, and topic indices", + "lineage": "digital broadcasting; participatory", + "tags": [ + "pages", + "blocks", + "navigation" + ] + }, + { + "id": "broadcast-programming-electronic-program-guide", + "form": "an electronic program guide, with channel lanes, time-scaled cards, and focus movement", + "lineage": "television interfaces", + "tags": [ + "timegrid", + "focus", + "detail" + ] + }, + { + "id": "broadcast-programming-live-digital-system-modeled-on-an-electronic-program", + "form": "a live digital system modeled on an electronic program guide, with focus movement, now markers, and detail overlays", + "lineage": "television interfaces; native digital", + "tags": [ + "timegrid", + "focus", + "detail" + ] + }, + { + "id": "broadcast-programming-shared-participatory-system-modeled-on-an-electronic", + "form": "a shared participatory system modeled on an electronic program guide, with detail overlays, channel lanes, and now markers", + "lineage": "television interfaces; participatory", + "tags": [ + "timegrid", + "focus", + "detail" + ] + }, + { + "id": "broadcast-programming-playout-automation-queue", + "form": "a playout automation queue, with scheduled assets, hard and soft start times, and transition rules", + "lineage": "broadcast operations", + "tags": [ + "queue", + "timing", + "exceptions" + ] + }, + { + "id": "broadcast-programming-live-digital-system-modeled-on-a-playout-automation", + "form": "a live digital system modeled on a playout automation queue, with transition rules, missing-media alerts, and manual overrides", + "lineage": "broadcast operations; native digital", + "tags": [ + "queue", + "timing", + "exceptions" + ] + }, + { + "id": "broadcast-programming-shared-participatory-system-modeled-on-a-playout-aut", + "form": "a shared participatory system modeled on a playout automation queue, with manual overrides, scheduled assets, and missing-media alerts", + "lineage": "broadcast operations; participatory", + "tags": [ + "queue", + "timing", + "exceptions" + ] + }, + { + "id": "broadcast-programming-broadcast-cue-sheet", + "form": "a broadcast cue sheet, with numbered cues, department columns, and trigger phrases", + "lineage": "production coordination", + "tags": [ + "cues", + "roles", + "sequence" + ] + }, + { + "id": "broadcast-programming-live-digital-system-modeled-on-a-broadcast-cue-sheet", + "form": "a live digital system modeled on a broadcast cue sheet, with trigger phrases, standby states, and completion marks", + "lineage": "production coordination; native digital", + "tags": [ + "cues", + "roles", + "sequence" + ] + }, + { + "id": "broadcast-programming-shared-participatory-system-modeled-on-a-broadcast-c", + "form": "a shared participatory system modeled on a broadcast cue sheet, with completion marks, numbered cues, and standby states", + "lineage": "production coordination; participatory", + "tags": [ + "cues", + "roles", + "sequence" + ] + }, + { + "id": "broadcast-programming-news-assignment-desk-board", + "form": "a news assignment desk board, with story cards, desk ownership, and location fields", + "lineage": "newsroom operations", + "tags": [ + "stories", + "ownership", + "status" + ] + }, + { + "id": "broadcast-programming-live-digital-system-modeled-on-a-news-assignment-des", + "form": "a live digital system modeled on a news assignment desk board, with location fields, deadline ordering, and coverage status", + "lineage": "newsroom operations; native digital", + "tags": [ + "stories", + "ownership", + "status" + ] + }, + { + "id": "broadcast-programming-shared-participatory-system-modeled-on-a-news-assign", + "form": "a shared participatory system modeled on a news assignment desk board, with coverage status, story cards, and deadline ordering", + "lineage": "newsroom operations; participatory", + "tags": [ + "stories", + "ownership", + "status" + ] + }, + { + "id": "broadcast-programming-live-switcher-multiview", + "form": "a live switcher multiview, with source tile grid, preview-program distinction, and audio meters", + "lineage": "live production", + "tags": [ + "sources", + "preview", + "program" + ] + }, + { + "id": "broadcast-programming-live-digital-system-modeled-on-a-live-switcher-multi", + "form": "a live digital system modeled on a live switcher multiview, with audio meters, tally borders, and scene labels", + "lineage": "live production; native digital", + "tags": [ + "sources", + "preview", + "program" + ] + }, + { + "id": "broadcast-programming-shared-participatory-system-modeled-on-a-live-switch", + "form": "a shared participatory system modeled on a live switcher multiview, with scene labels, source tile grid, and tally borders", + "lineage": "live production; participatory", + "tags": [ + "sources", + "preview", + "program" + ] + }, + { + "id": "broadcast-programming-election-results-board", + "form": "an election results board, with contest hierarchy, geographic grouping, and counted percentages", + "lineage": "news graphics", + "tags": [ + "hierarchy", + "geography", + "progress" + ] + }, + { + "id": "broadcast-programming-live-digital-system-modeled-on-an-election-results-b", + "form": "a live digital system modeled on an election results board, with counted percentages, lead changes, and projection status", + "lineage": "news graphics; native digital", + "tags": [ + "hierarchy", + "geography", + "progress" + ] + }, + { + "id": "broadcast-programming-shared-participatory-system-modeled-on-an-election-r", + "form": "a shared participatory system modeled on an election results board, with projection status, contest hierarchy, and lead changes", + "lineage": "news graphics; participatory", + "tags": [ + "hierarchy", + "geography", + "progress" + ] + }, + { + "id": "broadcast-programming-sports-broadcast-scorebug", + "form": "a sports broadcast scorebug, with team identifiers, score hierarchy, and game clock", + "lineage": "sports broadcasting", + "tags": [ + "persistent-state", + "clock", + "events" + ] + }, + { + "id": "broadcast-programming-live-digital-system-modeled-on-a-sports-broadcast-sc", + "form": "a live digital system modeled on a sports broadcast scorebug, with game clock, period state, and event indicators", + "lineage": "sports broadcasting; native digital", + "tags": [ + "persistent-state", + "clock", + "events" + ] + }, + { + "id": "broadcast-programming-shared-participatory-system-modeled-on-a-sports-broa", + "form": "a shared participatory system modeled on a sports broadcast scorebug, with event indicators, team identifiers, and period state", + "lineage": "sports broadcasting; participatory", + "tags": [ + "persistent-state", + "clock", + "events" + ] + }, + { + "id": "broadcast-programming-weather-broadcast-layer-stack", + "form": "a weather broadcast layer stack, with base maps, radar overlays, and forecast frames", + "lineage": "weather broadcasting", + "tags": [ + "overlays", + "time", + "geography" + ] + }, + { + "id": "broadcast-programming-live-digital-system-modeled-on-a-weather-broadcast-l", + "form": "a live digital system modeled on a weather broadcast layer stack, with forecast frames, legend keys, and timeline scrubbing", + "lineage": "weather broadcasting; native digital", + "tags": [ + "overlays", + "time", + "geography" + ] + }, + { + "id": "broadcast-programming-shared-participatory-system-modeled-on-a-weather-bro", + "form": "a shared participatory system modeled on a weather broadcast layer stack, with timeline scrubbing, base maps, and legend keys", + "lineage": "weather broadcasting; participatory", + "tags": [ + "overlays", + "time", + "geography" + ] + }, + { + "id": "broadcast-programming-closed-caption-authoring-track", + "form": "a closed-caption authoring track, with time-coded segments, speaker labels, and reading-speed limits", + "lineage": "accessible media", + "tags": [ + "timecode", + "speakers", + "linebreaks" + ] + }, + { + "id": "broadcast-programming-live-digital-system-modeled-on-a-closed-caption-auth", + "form": "a live digital system modeled on a closed-caption authoring track, with reading-speed limits, semantic line breaks, and sound descriptions", + "lineage": "accessible media; native digital", + "tags": [ + "timecode", + "speakers", + "linebreaks" + ] + }, + { + "id": "broadcast-programming-shared-participatory-system-modeled-on-a-closed-capt", + "form": "a shared participatory system modeled on a closed-caption authoring track, with sound descriptions, time-coded segments, and semantic line breaks", + "lineage": "accessible media; participatory", + "tags": [ + "timecode", + "speakers", + "linebreaks" + ] + }, + { + "id": "broadcast-programming-live-call-in-queue", + "form": "a live call-in queue, with caller cards, screening notes, and priority order", + "lineage": "talk broadcasting", + "tags": [ + "queue", + "screening", + "handoff" + ] + }, + { + "id": "broadcast-programming-live-digital-system-modeled-on-a-live-call-in-queue", + "form": "a live digital system modeled on a live call-in queue, with priority order, on-air status, and follow-up disposition", + "lineage": "talk broadcasting; native digital", + "tags": [ + "queue", + "screening", + "handoff" + ] + }, + { + "id": "broadcast-programming-shared-participatory-system-modeled-on-a-live-call-i", + "form": "a shared participatory system modeled on a live call-in queue, with follow-up disposition, caller cards, and on-air status", + "lineage": "talk broadcasting; participatory", + "tags": [ + "queue", + "screening", + "handoff" + ] + }, + { + "id": "broadcast-programming-podcast-chapter-map", + "form": "a podcast chapter map, with chapter markers, time ranges, and topic summaries", + "lineage": "on-demand audio", + "tags": [ + "chapters", + "timecode", + "links" + ] + }, + { + "id": "broadcast-programming-live-digital-system-modeled-on-a-podcast-chapter-map", + "form": "a live digital system modeled on a podcast chapter map, with topic summaries, linked references, and play-position memory", + "lineage": "on-demand audio; native digital", + "tags": [ + "chapters", + "timecode", + "links" + ] + }, + { + "id": "broadcast-programming-shared-participatory-system-modeled-on-a-podcast-cha", + "form": "a shared participatory system modeled on a podcast chapter map, with play-position memory, chapter markers, and linked references", + "lineage": "on-demand audio; participatory", + "tags": [ + "chapters", + "timecode", + "links" + ] + }, + { + "id": "broadcast-programming-streaming-channel-rail", + "form": "a streaming channel rail, with thematic rows, horizontal browsing, and preview states", + "lineage": "streaming interfaces", + "tags": [ + "rows", + "preview", + "continuity" + ] + }, + { + "id": "broadcast-programming-live-digital-system-modeled-on-a-streaming-channel-r", + "form": "a live digital system modeled on a streaming channel rail, with preview states, resume positions, and availability badges", + "lineage": "streaming interfaces; native digital", + "tags": [ + "rows", + "preview", + "continuity" + ] + }, + { + "id": "broadcast-programming-shared-participatory-system-modeled-on-a-streaming-c", + "form": "a shared participatory system modeled on a streaming channel rail, with availability badges, thematic rows, and resume positions", + "lineage": "streaming interfaces; participatory", + "tags": [ + "rows", + "preview", + "continuity" + ] + }, + { + "id": "broadcast-programming-transmission-waveform-monitor", + "form": "a transmission waveform monitor, with calibrated scopes, signal traces, and legal-range thresholds", + "lineage": "broadcast engineering", + "tags": [ + "scopes", + "thresholds", + "alerts" + ] + }, + { + "id": "broadcast-programming-live-digital-system-modeled-on-a-transmission-wavefo", + "form": "a live digital system modeled on a transmission waveform monitor, with legal-range thresholds, channel isolation, and fault alerts", + "lineage": "broadcast engineering; native digital", + "tags": [ + "scopes", + "thresholds", + "alerts" + ] + }, + { + "id": "broadcast-programming-shared-participatory-system-modeled-on-a-transmissio", + "form": "a shared participatory system modeled on a transmission waveform monitor, with fault alerts, calibrated scopes, and channel isolation", + "lineage": "broadcast engineering; participatory", + "tags": [ + "scopes", + "thresholds", + "alerts" + ] + } + ] + }, + { + "id": "cinema-storytelling", + "label": "Cinema & storytelling", + "description": "Narrative production and presentation structures with scenes, beats, viewpoints, and reveal.", + "concepts": [ + { + "id": "cinema-storytelling-storyboard-sheet", + "form": "a storyboard sheet, with shot panels, sequence numbers, and camera notes", + "lineage": "film preproduction", + "tags": [ + "panels", + "sequence", + "annotations" + ] + }, + { + "id": "cinema-storytelling-live-digital-system-modeled-on-a-storyboard-sheet", + "form": "a live digital system modeled on a storyboard sheet, with camera notes, dialogue captions, and transition arrows", + "lineage": "film preproduction; native digital", + "tags": [ + "panels", + "sequence", + "annotations" + ] + }, + { + "id": "cinema-storytelling-shared-participatory-system-modeled-on-a-storyboard", + "form": "a shared participatory system modeled on a storyboard sheet, with transition arrows, shot panels, and dialogue captions", + "lineage": "film preproduction; participatory", + "tags": [ + "panels", + "sequence", + "annotations" + ] + }, + { + "id": "cinema-storytelling-screenplay-page", + "form": "a screenplay page, with scene headings, action blocks, and speaker cues", + "lineage": "screenwriting", + "tags": [ + "scenes", + "dialogue", + "actions" + ] + }, + { + "id": "cinema-storytelling-live-digital-system-modeled-on-a-screenplay-page", + "form": "a live digital system modeled on a screenplay page, with speaker cues, dialogue indents, and page timing", + "lineage": "screenwriting; native digital", + "tags": [ + "scenes", + "dialogue", + "actions" + ] + }, + { + "id": "cinema-storytelling-shared-participatory-system-modeled-on-a-screenplay", + "form": "a shared participatory system modeled on a screenplay page, with page timing, scene headings, and dialogue indents", + "lineage": "screenwriting; participatory", + "tags": [ + "scenes", + "dialogue", + "actions" + ] + }, + { + "id": "cinema-storytelling-production-shot-list", + "form": "a production shot list, with shot identifiers, lens and movement fields, and coverage grouping", + "lineage": "film production", + "tags": [ + "indexing", + "coverage", + "status" + ] + }, + { + "id": "cinema-storytelling-live-digital-system-modeled-on-a-production-shot-lis", + "form": "a live digital system modeled on a production shot list, with coverage grouping, setup order, and completion checks", + "lineage": "film production; native digital", + "tags": [ + "indexing", + "coverage", + "status" + ] + }, + { + "id": "cinema-storytelling-shared-participatory-system-modeled-on-a-production", + "form": "a shared participatory system modeled on a production shot list, with completion checks, shot identifiers, and setup order", + "lineage": "film production; participatory", + "tags": [ + "indexing", + "coverage", + "status" + ] + }, + { + "id": "cinema-storytelling-continuity-log", + "form": "a continuity log, with scene and take codes, wardrobe states, and prop positions", + "lineage": "film production", + "tags": [ + "chronology", + "matching", + "evidence" + ] + }, + { + "id": "cinema-storytelling-live-digital-system-modeled-on-a-continuity-log", + "form": "a live digital system modeled on a continuity log, with prop positions, timing notes, and reference images", + "lineage": "film production; native digital", + "tags": [ + "chronology", + "matching", + "evidence" + ] + }, + { + "id": "cinema-storytelling-shared-participatory-system-modeled-on-a-continuity", + "form": "a shared participatory system modeled on a continuity log, with reference images, scene and take codes, and timing notes", + "lineage": "film production; participatory", + "tags": [ + "chronology", + "matching", + "evidence" + ] + }, + { + "id": "cinema-storytelling-edit-decision-list", + "form": "an edit decision list, with source timecodes, record positions, and clip order", + "lineage": "film editing", + "tags": [ + "timecode", + "sequence", + "transitions" + ] + }, + { + "id": "cinema-storytelling-live-digital-system-modeled-on-an-edit-decision-list", + "form": "a live digital system modeled on an edit decision list, with clip order, transition codes, and reel references", + "lineage": "film editing; native digital", + "tags": [ + "timecode", + "sequence", + "transitions" + ] + }, + { + "id": "cinema-storytelling-shared-participatory-system-modeled-on-an-edit-decis", + "form": "a shared participatory system modeled on an edit decision list, with reel references, source timecodes, and transition codes", + "lineage": "film editing; participatory", + "tags": [ + "timecode", + "sequence", + "transitions" + ] + }, + { + "id": "cinema-storytelling-filmstrip-contact-sheet", + "form": "a filmstrip contact sheet, with chronological frames, edge numbering, and take groupings", + "lineage": "film review", + "tags": [ + "frames", + "sequence", + "selection" + ] + }, + { + "id": "cinema-storytelling-live-digital-system-modeled-on-a-filmstrip-contact-s", + "form": "a live digital system modeled on a filmstrip contact sheet, with take groupings, selection marks, and exposure comparisons", + "lineage": "film review; native digital", + "tags": [ + "frames", + "sequence", + "selection" + ] + }, + { + "id": "cinema-storytelling-shared-participatory-system-modeled-on-a-filmstrip-c", + "form": "a shared participatory system modeled on a filmstrip contact sheet, with exposure comparisons, chronological frames, and selection marks", + "lineage": "film review; participatory", + "tags": [ + "frames", + "sequence", + "selection" + ] + }, + { + "id": "cinema-storytelling-title-card-sequence", + "form": "a title-card sequence, with credit hierarchy, card order, and screen duration", + "lineage": "motion graphics", + "tags": [ + "hierarchy", + "rhythm", + "sequence" + ] + }, + { + "id": "cinema-storytelling-live-digital-system-modeled-on-a-title-card-sequence", + "form": "a live digital system modeled on a title-card sequence, with screen duration, typographic rhythm, and music sync points", + "lineage": "motion graphics; native digital", + "tags": [ + "hierarchy", + "rhythm", + "sequence" + ] + }, + { + "id": "cinema-storytelling-shared-participatory-system-modeled-on-a-title-card", + "form": "a shared participatory system modeled on a title-card sequence, with music sync points, credit hierarchy, and typographic rhythm", + "lineage": "motion graphics; participatory", + "tags": [ + "hierarchy", + "rhythm", + "sequence" + ] + }, + { + "id": "cinema-storytelling-montage-map", + "form": "a montage map, with parallel action lanes, beat markers, and recurring motifs", + "lineage": "film editing", + "tags": [ + "parallelism", + "rhythm", + "motifs" + ] + }, + { + "id": "cinema-storytelling-live-digital-system-modeled-on-a-montage-map", + "form": "a live digital system modeled on a montage map, with recurring motifs, tempo changes, and convergence points", + "lineage": "film editing; native digital", + "tags": [ + "parallelism", + "rhythm", + "motifs" + ] + }, + { + "id": "cinema-storytelling-shared-participatory-system-modeled-on-a-montage-map", + "form": "a shared participatory system modeled on a montage map, with convergence points, parallel action lanes, and tempo changes", + "lineage": "film editing; participatory", + "tags": [ + "parallelism", + "rhythm", + "motifs" + ] + }, + { + "id": "cinema-storytelling-scene-beat-sheet", + "form": "a scene beat sheet, with ordered beat cards, turning points, and character objectives", + "lineage": "story development", + "tags": [ + "beats", + "turns", + "stakes" + ] + }, + { + "id": "cinema-storytelling-live-digital-system-modeled-on-a-scene-beat-sheet", + "form": "a live digital system modeled on a scene beat sheet, with character objectives, stakes shifts, and scene outcomes", + "lineage": "story development; native digital", + "tags": [ + "beats", + "turns", + "stakes" + ] + }, + { + "id": "cinema-storytelling-shared-participatory-system-modeled-on-a-scene-beat", + "form": "a shared participatory system modeled on a scene beat sheet, with scene outcomes, ordered beat cards, and stakes shifts", + "lineage": "story development; participatory", + "tags": [ + "beats", + "turns", + "stakes" + ] + }, + { + "id": "cinema-storytelling-narrative-act-map", + "form": "a narrative act map, with act bands, inciting turns, and rising complications", + "lineage": "story structure", + "tags": [ + "acts", + "arcs", + "turns" + ] + }, + { + "id": "cinema-storytelling-live-digital-system-modeled-on-a-narrative-act-map", + "form": "a live digital system modeled on a narrative act map, with rising complications, climax markers, and resolution threads", + "lineage": "story structure; native digital", + "tags": [ + "acts", + "arcs", + "turns" + ] + }, + { + "id": "cinema-storytelling-shared-participatory-system-modeled-on-a-narrative-a", + "form": "a shared participatory system modeled on a narrative act map, with resolution threads, act bands, and climax markers", + "lineage": "story structure; participatory", + "tags": [ + "acts", + "arcs", + "turns" + ] + }, + { + "id": "cinema-storytelling-prop-continuity-board", + "form": "a prop continuity board, with prop inventories, scene assignments, and state changes", + "lineage": "film art department", + "tags": [ + "objects", + "states", + "scenes" + ] + }, + { + "id": "cinema-storytelling-live-digital-system-modeled-on-a-prop-continuity-boa", + "form": "a live digital system modeled on a prop continuity board, with state changes, duplicate tracking, and reset checks", + "lineage": "film art department; native digital", + "tags": [ + "objects", + "states", + "scenes" + ] + }, + { + "id": "cinema-storytelling-shared-participatory-system-modeled-on-a-prop-contin", + "form": "a shared participatory system modeled on a prop continuity board, with reset checks, prop inventories, and duplicate tracking", + "lineage": "film art department; participatory", + "tags": [ + "objects", + "states", + "scenes" + ] + }, + { + "id": "cinema-storytelling-costume-breakdown-chart", + "form": "a costume breakdown chart, with character rows, scene columns, and look identifiers", + "lineage": "costume production", + "tags": [ + "characters", + "scenes", + "changes" + ] + }, + { + "id": "cinema-storytelling-live-digital-system-modeled-on-a-costume-breakdown-c", + "form": "a live digital system modeled on a costume breakdown chart, with look identifiers, change timing, and continuity links", + "lineage": "costume production; native digital", + "tags": [ + "characters", + "scenes", + "changes" + ] + }, + { + "id": "cinema-storytelling-shared-participatory-system-modeled-on-a-costume-bre", + "form": "a shared participatory system modeled on a costume breakdown chart, with continuity links, character rows, and change timing", + "lineage": "costume production; participatory", + "tags": [ + "characters", + "scenes", + "changes" + ] + }, + { + "id": "cinema-storytelling-location-scout-dossier", + "form": "a location scout dossier, with location plates, access notes, and light orientation", + "lineage": "film preproduction", + "tags": [ + "places", + "constraints", + "evidence" + ] + }, + { + "id": "cinema-storytelling-live-digital-system-modeled-on-a-location-scout-doss", + "form": "a live digital system modeled on a location scout dossier, with light orientation, sound constraints, and permit status", + "lineage": "film preproduction; native digital", + "tags": [ + "places", + "constraints", + "evidence" + ] + }, + { + "id": "cinema-storytelling-shared-participatory-system-modeled-on-a-location-sc", + "form": "a shared participatory system modeled on a location scout dossier, with permit status, location plates, and sound constraints", + "lineage": "film preproduction; participatory", + "tags": [ + "places", + "constraints", + "evidence" + ] + }, + { + "id": "cinema-storytelling-casting-side-packet", + "form": "a casting side packet, with role context, selected excerpts, and line cues", + "lineage": "casting", + "tags": [ + "scenes", + "roles", + "annotations" + ] + }, + { + "id": "cinema-storytelling-live-digital-system-modeled-on-a-casting-side-packet", + "form": "a live digital system modeled on a casting side packet, with line cues, page breaks, and reader annotations", + "lineage": "casting; native digital", + "tags": [ + "scenes", + "roles", + "annotations" + ] + }, + { + "id": "cinema-storytelling-shared-participatory-system-modeled-on-a-casting-sid", + "form": "a shared participatory system modeled on a casting side packet, with reader annotations, role context, and page breaks", + "lineage": "casting; participatory", + "tags": [ + "scenes", + "roles", + "annotations" + ] + }, + { + "id": "cinema-storytelling-daily-production-call-sheet", + "form": "a daily production call sheet, with day header, scene schedule, and cast call times", + "lineage": "film operations", + "tags": [ + "schedule", + "roles", + "logistics" + ] + }, + { + "id": "cinema-storytelling-live-digital-system-modeled-on-a-daily-production-ca", + "form": "a live digital system modeled on a daily production call sheet, with cast call times, location details, and safety notices", + "lineage": "film operations; native digital", + "tags": [ + "schedule", + "roles", + "logistics" + ] + }, + { + "id": "cinema-storytelling-shared-participatory-system-modeled-on-a-daily-produ", + "form": "a shared participatory system modeled on a daily production call sheet, with safety notices, day header, and location details", + "lineage": "film operations; participatory", + "tags": [ + "schedule", + "roles", + "logistics" + ] + }, + { + "id": "cinema-storytelling-clapperboard-slate", + "form": "a clapperboard slate, with production identifiers, scene and take fields, and camera-roll codes", + "lineage": "film production", + "tags": [ + "identity", + "synchronization", + "takes" + ] + }, + { + "id": "cinema-storytelling-live-digital-system-modeled-on-a-clapperboard-slate", + "form": "a live digital system modeled on a clapperboard slate, with camera-roll codes, sync strike, and date markers", + "lineage": "film production; native digital", + "tags": [ + "identity", + "synchronization", + "takes" + ] + }, + { + "id": "cinema-storytelling-shared-participatory-system-modeled-on-a-clapperboar", + "form": "a shared participatory system modeled on a clapperboard slate, with date markers, production identifiers, and sync strike", + "lineage": "film production; participatory", + "tags": [ + "identity", + "synchronization", + "takes" + ] + }, + { + "id": "cinema-storytelling-projection-changeover-cue-sheet", + "form": "a projection changeover cue sheet, with reel sequence, changeover marks, and dowser cues", + "lineage": "film exhibition", + "tags": [ + "reels", + "cues", + "timing" + ] + }, + { + "id": "cinema-storytelling-live-digital-system-modeled-on-a-projection-changeov", + "form": "a live digital system modeled on a projection changeover cue sheet, with dowser cues, sound-format notes, and fault contingencies", + "lineage": "film exhibition; native digital", + "tags": [ + "reels", + "cues", + "timing" + ] + }, + { + "id": "cinema-storytelling-shared-participatory-system-modeled-on-a-projection", + "form": "a shared participatory system modeled on a projection changeover cue sheet, with fault contingencies, reel sequence, and sound-format notes", + "lineage": "film exhibition; participatory", + "tags": [ + "reels", + "cues", + "timing" + ] + }, + { + "id": "cinema-storytelling-subtitle-spotting-list", + "form": "a subtitle spotting list, with in and out timecodes, dialogue text, and reading-speed checks", + "lineage": "screen localization", + "tags": [ + "timecode", + "reading", + "dialogue" + ] + }, + { + "id": "cinema-storytelling-live-digital-system-modeled-on-a-subtitle-spotting-l", + "form": "a live digital system modeled on a subtitle spotting list, with reading-speed checks, speaker changes, and shot-change constraints", + "lineage": "screen localization; native digital", + "tags": [ + "timecode", + "reading", + "dialogue" + ] + }, + { + "id": "cinema-storytelling-shared-participatory-system-modeled-on-a-subtitle-sp", + "form": "a shared participatory system modeled on a subtitle spotting list, with shot-change constraints, in and out timecodes, and speaker changes", + "lineage": "screen localization; participatory", + "tags": [ + "timecode", + "reading", + "dialogue" + ] + }, + { + "id": "cinema-storytelling-animatic-timeline", + "form": "an animatic timeline, with storyboard clips, temporary audio, and shot durations", + "lineage": "animation production", + "tags": [ + "panels", + "audio", + "timing" + ] + }, + { + "id": "cinema-storytelling-live-digital-system-modeled-on-an-animatic-timeline", + "form": "a live digital system modeled on an animatic timeline, with shot durations, camera-move tracks, and revision markers", + "lineage": "animation production; native digital", + "tags": [ + "panels", + "audio", + "timing" + ] + }, + { + "id": "cinema-storytelling-shared-participatory-system-modeled-on-an-animatic-t", + "form": "a shared participatory system modeled on an animatic timeline, with revision markers, storyboard clips, and camera-move tracks", + "lineage": "animation production; participatory", + "tags": [ + "panels", + "audio", + "timing" + ] + }, + { + "id": "cinema-storytelling-story-reel-review-board", + "form": "a story reel review board, with sequence bins, version stacks, and review timestamps", + "lineage": "animation production", + "tags": [ + "sequence", + "versions", + "notes" + ] + }, + { + "id": "cinema-storytelling-live-digital-system-modeled-on-a-story-reel-review-b", + "form": "a live digital system modeled on a story reel review board, with review timestamps, frame-specific notes, and approval states", + "lineage": "animation production; native digital", + "tags": [ + "sequence", + "versions", + "notes" + ] + }, + { + "id": "cinema-storytelling-shared-participatory-system-modeled-on-a-story-reel", + "form": "a shared participatory system modeled on a story reel review board, with approval states, sequence bins, and frame-specific notes", + "lineage": "animation production; participatory", + "tags": [ + "sequence", + "versions", + "notes" + ] + }, + { + "id": "cinema-storytelling-branching-narrative-map", + "form": "a branching narrative map, with choice nodes, state conditions, and branch paths", + "lineage": "interactive storytelling", + "tags": [ + "branches", + "state", + "endings" + ] + }, + { + "id": "cinema-storytelling-live-digital-system-modeled-on-a-branching-narrative", + "form": "a live digital system modeled on a branching narrative map, with branch paths, rejoin points, and ending outcomes", + "lineage": "interactive storytelling; native digital", + "tags": [ + "branches", + "state", + "endings" + ] + }, + { + "id": "cinema-storytelling-shared-participatory-system-modeled-on-a-branching-n", + "form": "a shared participatory system modeled on a branching narrative map, with ending outcomes, choice nodes, and rejoin points", + "lineage": "interactive storytelling; participatory", + "tags": [ + "branches", + "state", + "endings" + ] + }, + { + "id": "cinema-storytelling-documentary-evidence-wall", + "form": "a documentary evidence wall, with source cards, chronological bands, and theme clusters", + "lineage": "documentary research", + "tags": [ + "sources", + "themes", + "chronology" + ] + } + ] + }, + { + "id": "music-performance", + "label": "Music & performance", + "description": "Time-based scores and production systems coordinating layers, performers, cues, and variation.", + "concepts": [ + { + "id": "music-performance-full-orchestral-score", + "form": "a full orchestral score, with synchronized staves, measure numbering, and dynamic markings", + "lineage": "music notation", + "tags": [ + "staves", + "synchronization", + "motifs" + ] + }, + { + "id": "music-performance-live-digital-system-modeled-on-a-full-orchestral-sco", + "form": "a live digital system modeled on a full orchestral score, with dynamic markings, section cues, and recurring motifs", + "lineage": "music notation; native digital", + "tags": [ + "staves", + "synchronization", + "motifs" + ] + }, + { + "id": "music-performance-shared-participatory-system-modeled-on-a-full-orches", + "form": "a shared participatory system modeled on a full orchestral score, with recurring motifs, synchronized staves, and section cues", + "lineage": "music notation; participatory", + "tags": [ + "staves", + "synchronization", + "motifs" + ] + }, + { + "id": "music-performance-lead-sheet", + "form": "a lead sheet, with melody staff, chord symbols, and lyric alignment", + "lineage": "popular music notation", + "tags": [ + "melody", + "harmony", + "form" + ] + }, + { + "id": "music-performance-live-digital-system-modeled-on-a-lead-sheet", + "form": "a live digital system modeled on a lead sheet, with lyric alignment, section labels, and repeat signs", + "lineage": "popular music notation; native digital", + "tags": [ + "melody", + "harmony", + "form" + ] + }, + { + "id": "music-performance-shared-participatory-system-modeled-on-a-lead-sheet", + "form": "a shared participatory system modeled on a lead sheet, with repeat signs, melody staff, and section labels", + "lineage": "popular music notation; participatory", + "tags": [ + "melody", + "harmony", + "form" + ] + }, + { + "id": "music-performance-performance-setlist", + "form": "a performance setlist, with ordered songs, key and tempo notes, and transition cues", + "lineage": "live music", + "tags": [ + "sequence", + "energy", + "notes" + ] + }, + { + "id": "music-performance-live-digital-system-modeled-on-a-performance-setlist", + "form": "a live digital system modeled on a performance setlist, with transition cues, encore branches, and completion marks", + "lineage": "live music; native digital", + "tags": [ + "sequence", + "energy", + "notes" + ] + }, + { + "id": "music-performance-shared-participatory-system-modeled-on-a-performance", + "form": "a shared participatory system modeled on a performance setlist, with completion marks, ordered songs, and encore branches", + "lineage": "live music; participatory", + "tags": [ + "sequence", + "energy", + "notes" + ] + }, + { + "id": "music-performance-recording-mixing-console", + "form": "a recording mixing console, with repeated channel strips, shared buses, and mute and solo states", + "lineage": "audio production", + "tags": [ + "channels", + "buses", + "master" + ] + }, + { + "id": "music-performance-live-digital-system-modeled-on-a-recording-mixing-co", + "form": "a live digital system modeled on a recording mixing console, with mute and solo states, level meters, and master controls", + "lineage": "audio production; native digital", + "tags": [ + "channels", + "buses", + "master" + ] + }, + { + "id": "music-performance-shared-participatory-system-modeled-on-a-recording-m", + "form": "a shared participatory system modeled on a recording mixing console, with master controls, repeated channel strips, and level meters", + "lineage": "audio production; participatory", + "tags": [ + "channels", + "buses", + "master" + ] + }, + { + "id": "music-performance-audio-patch-bay", + "form": "an audio patch bay, with source-destination matrix, normalized routes, and signal groupings", + "lineage": "audio engineering", + "tags": [ + "routing", + "matrix", + "groups" + ] + }, + { + "id": "music-performance-live-digital-system-modeled-on-an-audio-patch-bay", + "form": "a live digital system modeled on an audio patch bay, with signal groupings, patch overrides, and fault tracing", + "lineage": "audio engineering; native digital", + "tags": [ + "routing", + "matrix", + "groups" + ] + }, + { + "id": "music-performance-shared-participatory-system-modeled-on-an-audio-patc", + "form": "a shared participatory system modeled on an audio patch bay, with fault tracing, source-destination matrix, and patch overrides", + "lineage": "audio engineering; participatory", + "tags": [ + "routing", + "matrix", + "groups" + ] + }, + { + "id": "music-performance-step-sequencer", + "form": "a step sequencer, with time-step columns, voice rows, and active-step toggles", + "lineage": "electronic music", + "tags": [ + "grid", + "loop", + "states" + ] + }, + { + "id": "music-performance-live-digital-system-modeled-on-a-step-sequencer", + "form": "a live digital system modeled on a step sequencer, with active-step toggles, loop boundaries, and pattern chaining", + "lineage": "electronic music; native digital", + "tags": [ + "grid", + "loop", + "states" + ] + }, + { + "id": "music-performance-shared-participatory-system-modeled-on-a-step-sequen", + "form": "a shared participatory system modeled on a step sequencer, with pattern chaining, time-step columns, and loop boundaries", + "lineage": "electronic music; participatory", + "tags": [ + "grid", + "loop", + "states" + ] + }, + { + "id": "music-performance-piano-roll-editor", + "form": "a piano-roll editor, with pitch rows, time grid, and note blocks", + "lineage": "digital music", + "tags": [ + "pitch", + "time", + "velocity" + ] + }, + { + "id": "music-performance-live-digital-system-modeled-on-a-piano-roll-editor", + "form": "a live digital system modeled on a piano-roll editor, with note blocks, velocity encoding, and loop regions", + "lineage": "digital music; native digital", + "tags": [ + "pitch", + "time", + "velocity" + ] + }, + { + "id": "music-performance-shared-participatory-system-modeled-on-a-piano-roll", + "form": "a shared participatory system modeled on a piano-roll editor, with loop regions, pitch rows, and velocity encoding", + "lineage": "digital music; participatory", + "tags": [ + "pitch", + "time", + "velocity" + ] + }, + { + "id": "music-performance-drum-machine-pattern-grid", + "form": "a drum-machine pattern grid, with instrument rows, beat subdivisions, and hit toggles", + "lineage": "electronic music", + "tags": [ + "rhythm", + "steps", + "variation" + ] + }, + { + "id": "music-performance-live-digital-system-modeled-on-a-drum-machine-patter", + "form": "a live digital system modeled on a drum-machine pattern grid, with hit toggles, accent states, and pattern variations", + "lineage": "electronic music; native digital", + "tags": [ + "rhythm", + "steps", + "variation" + ] + }, + { + "id": "music-performance-shared-participatory-system-modeled-on-a-drum-machin", + "form": "a shared participatory system modeled on a drum-machine pattern grid, with pattern variations, instrument rows, and accent states", + "lineage": "electronic music; participatory", + "tags": [ + "rhythm", + "steps", + "variation" + ] + }, + { + "id": "music-performance-live-loop-station", + "form": "a live-loop station, with stacked loop tracks, record-overdub states, and cycle lengths", + "lineage": "live electronic music", + "tags": [ + "layers", + "recording", + "cycles" + ] + }, + { + "id": "music-performance-live-digital-system-modeled-on-a-live-loop-station", + "form": "a live digital system modeled on a live-loop station, with cycle lengths, mute controls, and clear gestures", + "lineage": "live electronic music; native digital", + "tags": [ + "layers", + "recording", + "cycles" + ] + }, + { + "id": "music-performance-shared-participatory-system-modeled-on-a-live-loop-s", + "form": "a shared participatory system modeled on a live-loop station, with clear gestures, stacked loop tracks, and mute controls", + "lineage": "live electronic music; participatory", + "tags": [ + "layers", + "recording", + "cycles" + ] + }, + { + "id": "music-performance-multitrack-waveform-arrangement", + "form": "a multitrack waveform arrangement, with parallel tracks, time-scaled clips, and waveform previews", + "lineage": "digital audio", + "tags": [ + "tracks", + "clips", + "time" + ] + }, + { + "id": "music-performance-live-digital-system-modeled-on-a-multitrack-waveform", + "form": "a live digital system modeled on a multitrack waveform arrangement, with waveform previews, edit boundaries, and automation lanes", + "lineage": "digital audio; native digital", + "tags": [ + "tracks", + "clips", + "time" + ] + }, + { + "id": "music-performance-shared-participatory-system-modeled-on-a-multitrack", + "form": "a shared participatory system modeled on a multitrack waveform arrangement, with automation lanes, parallel tracks, and edit boundaries", + "lineage": "digital audio; participatory", + "tags": [ + "tracks", + "clips", + "time" + ] + }, + { + "id": "music-performance-stage-plot", + "form": "a stage plot, with performer positions, equipment symbols, and input labels", + "lineage": "live production", + "tags": [ + "spatial", + "roles", + "connections" + ] + }, + { + "id": "music-performance-live-digital-system-modeled-on-a-stage-plot", + "form": "a live digital system modeled on a stage plot, with input labels, power locations, and monitor mixes", + "lineage": "live production; native digital", + "tags": [ + "spatial", + "roles", + "connections" + ] + }, + { + "id": "music-performance-shared-participatory-system-modeled-on-a-stage-plot", + "form": "a shared participatory system modeled on a stage plot, with monitor mixes, performer positions, and power locations", + "lineage": "live production; participatory", + "tags": [ + "spatial", + "roles", + "connections" + ] + }, + { + "id": "music-performance-performance-cue-stack", + "form": "a performance cue stack, with numbered cues, standby states, and trigger actions", + "lineage": "show control", + "tags": [ + "sequence", + "triggers", + "status" + ] + }, + { + "id": "music-performance-live-digital-system-modeled-on-a-performance-cue-sta", + "form": "a live digital system modeled on a performance cue stack, with trigger actions, follow times, and completed-cue history", + "lineage": "show control; native digital", + "tags": [ + "sequence", + "triggers", + "status" + ] + }, + { + "id": "music-performance-shared-participatory-system-modeled-on-a-performance-5cf88e6b", + "form": "a shared participatory system modeled on a performance cue stack, with completed-cue history, numbered cues, and follow times", + "lineage": "show control; participatory", + "tags": [ + "sequence", + "triggers", + "status" + ] + }, + { + "id": "music-performance-lighting-chase-sheet", + "form": "a lighting chase sheet, with fixture groups, scene numbers, and fade durations", + "lineage": "concert production", + "tags": [ + "channels", + "timing", + "scenes" + ] + }, + { + "id": "music-performance-live-digital-system-modeled-on-a-lighting-chase-shee", + "form": "a live digital system modeled on a lighting chase sheet, with fade durations, beat triggers, and override notes", + "lineage": "concert production; native digital", + "tags": [ + "channels", + "timing", + "scenes" + ] + }, + { + "id": "music-performance-shared-participatory-system-modeled-on-a-lighting-ch", + "form": "a shared participatory system modeled on a lighting chase sheet, with override notes, fixture groups, and beat triggers", + "lineage": "concert production; participatory", + "tags": [ + "channels", + "timing", + "scenes" + ] + }, + { + "id": "music-performance-effects-pedal-signal-chain", + "form": "an effects pedal signal chain, with ordered effect blocks, input-output path, and bypass states", + "lineage": "instrument performance", + "tags": [ + "routing", + "order", + "bypass" + ] + }, + { + "id": "music-performance-live-digital-system-modeled-on-an-effects-pedal-sign", + "form": "a live digital system modeled on an effects pedal signal chain, with bypass states, parameter presets, and branch loops", + "lineage": "instrument performance; native digital", + "tags": [ + "routing", + "order", + "bypass" + ] + }, + { + "id": "music-performance-shared-participatory-system-modeled-on-an-effects-pe", + "form": "a shared participatory system modeled on an effects pedal signal chain, with branch loops, ordered effect blocks, and parameter presets", + "lineage": "instrument performance; participatory", + "tags": [ + "routing", + "order", + "bypass" + ] + }, + { + "id": "music-performance-disc-jockey-crate-index", + "form": "a disc-jockey crate index, with genre dividers, tempo ranges, and key codes", + "lineage": "record performance", + "tags": [ + "collections", + "tempo", + "sequence" + ] + }, + { + "id": "music-performance-live-digital-system-modeled-on-a-disc-jockey-crate-i", + "form": "a live digital system modeled on a disc-jockey crate index, with key codes, energy notes, and transition pairings", + "lineage": "record performance; native digital", + "tags": [ + "collections", + "tempo", + "sequence" + ] + }, + { + "id": "music-performance-shared-participatory-system-modeled-on-a-disc-jockey", + "form": "a shared participatory system modeled on a disc-jockey crate index, with transition pairings, genre dividers, and energy notes", + "lineage": "record performance; participatory", + "tags": [ + "collections", + "tempo", + "sequence" + ] + }, + { + "id": "music-performance-turntable-mixer-layout", + "form": "a turntable mixer layout, with mirrored deck controls, crossfader axis, and cue channels", + "lineage": "record performance", + "tags": [ + "decks", + "crossfade", + "monitoring" + ] + }, + { + "id": "music-performance-live-digital-system-modeled-on-a-turntable-mixer-lay", + "form": "a live digital system modeled on a turntable mixer layout, with cue channels, level meters, and effect sends", + "lineage": "record performance; native digital", + "tags": [ + "decks", + "crossfade", + "monitoring" + ] + }, + { + "id": "music-performance-shared-participatory-system-modeled-on-a-turntable-m", + "form": "a shared participatory system modeled on a turntable mixer layout, with effect sends, mirrored deck controls, and level meters", + "lineage": "record performance; participatory", + "tags": [ + "decks", + "crossfade", + "monitoring" + ] + }, + { + "id": "music-performance-choral-part-book", + "form": "a choral part book, with voice-specific lines, entry cues, and breath marks", + "lineage": "vocal performance", + "tags": [ + "voices", + "entries", + "phrasing" + ] + }, + { + "id": "music-performance-live-digital-system-modeled-on-a-choral-part-book", + "form": "a live digital system modeled on a choral part book, with breath marks, pronunciation notes, and rehearsal numbers", + "lineage": "vocal performance; native digital", + "tags": [ + "voices", + "entries", + "phrasing" + ] + }, + { + "id": "music-performance-shared-participatory-system-modeled-on-a-choral-part", + "form": "a shared participatory system modeled on a choral part book, with rehearsal numbers, voice-specific lines, and pronunciation notes", + "lineage": "vocal performance; participatory", + "tags": [ + "voices", + "entries", + "phrasing" + ] + }, + { + "id": "music-performance-call-and-response-song-chart", + "form": "a call-and-response song chart, with leader prompts, group responses, and repeating cycles", + "lineage": "participatory music", + "tags": [ + "leader-response", + "cycles", + "variation" + ] + }, + { + "id": "music-performance-live-digital-system-modeled-on-a-call-and-response-s", + "form": "a live digital system modeled on a call-and-response song chart, with repeating cycles, variation slots, and closing signals", + "lineage": "participatory music; native digital", + "tags": [ + "leader-response", + "cycles", + "variation" + ] + }, + { + "id": "music-performance-shared-participatory-system-modeled-on-a-call-and-re", + "form": "a shared participatory system modeled on a call-and-response song chart, with closing signals, leader prompts, and variation slots", + "lineage": "participatory music; participatory", + "tags": [ + "leader-response", + "cycles", + "variation" + ] + }, + { + "id": "music-performance-concert-program", + "form": "a concert program, with performance order, movement hierarchy, and context notes", + "lineage": "music presentation", + "tags": [ + "sequence", + "context", + "credits" + ] + }, + { + "id": "music-performance-live-digital-system-modeled-on-a-concert-program", + "form": "a live digital system modeled on a concert program, with context notes, performer credits, and interval markers", + "lineage": "music presentation; native digital", + "tags": [ + "sequence", + "context", + "credits" + ] + }, + { + "id": "music-performance-shared-participatory-system-modeled-on-a-concert-pro", + "form": "a shared participatory system modeled on a concert program, with interval markers, performance order, and performer credits", + "lineage": "music presentation; participatory", + "tags": [ + "sequence", + "context", + "credits" + ] + }, + { + "id": "music-performance-rehearsal-mark-map", + "form": "a rehearsal mark map, with lettered landmarks, section ranges, and repeat loops", + "lineage": "music rehearsal", + "tags": [ + "landmarks", + "loops", + "notes" + ] + }, + { + "id": "music-performance-live-digital-system-modeled-on-a-rehearsal-mark-map", + "form": "a live digital system modeled on a rehearsal mark map, with repeat loops, problem annotations, and restart points", + "lineage": "music rehearsal; native digital", + "tags": [ + "landmarks", + "loops", + "notes" + ] + }, + { + "id": "music-performance-shared-participatory-system-modeled-on-a-rehearsal-m", + "form": "a shared participatory system modeled on a rehearsal mark map, with restart points, lettered landmarks, and problem annotations", + "lineage": "music rehearsal; participatory", + "tags": [ + "landmarks", + "loops", + "notes" + ] + }, + { + "id": "music-performance-tuning-reference-chart", + "form": "a tuning reference chart, with reference pitches, frequency values, and interval relationships", + "lineage": "instrument setup", + "tags": [ + "frequency", + "comparison", + "tolerance" + ] + }, + { + "id": "music-performance-live-digital-system-modeled-on-a-tuning-reference-ch", + "form": "a live digital system modeled on a tuning reference chart, with interval relationships, deviation indicators, and instrument ranges", + "lineage": "instrument setup; native digital", + "tags": [ + "frequency", + "comparison", + "tolerance" + ] + }, + { + "id": "music-performance-shared-participatory-system-modeled-on-a-tuning-refe", + "form": "a shared participatory system modeled on a tuning reference chart, with instrument ranges, reference pitches, and deviation indicators", + "lineage": "instrument setup; participatory", + "tags": [ + "frequency", + "comparison", + "tolerance" + ] + }, + { + "id": "music-performance-modular-synthesizer-rack", + "form": "a modular synthesizer rack, with functional modules, signal cables, and control-voltage routes", + "lineage": "sound synthesis", + "tags": [ + "modules", + "routing", + "modulation" + ] + } + ] + }, + { + "id": "photography-archive", + "label": "Photography & archive", + "description": "Selection and memory systems with sequences, metadata, contact views, provenance, and retrieval.", + "concepts": [ + { + "id": "photography-archive-photographic-contact-sheet", + "form": "a photographic contact sheet, with thumbnail grid, frame numbers, and chronological order", + "lineage": "photo editing", + "tags": [ + "grid", + "sequence", + "selection" + ] + }, + { + "id": "photography-archive-live-digital-system-modeled-on-a-photographic-contac", + "form": "a live digital system modeled on a photographic contact sheet, with chronological order, selection marks, and rating annotations", + "lineage": "photo editing; native digital", + "tags": [ + "grid", + "sequence", + "selection" + ] + }, + { + "id": "photography-archive-shared-participatory-system-modeled-on-a-photographi", + "form": "a shared participatory system modeled on a photographic contact sheet, with rating annotations, thumbnail grid, and selection marks", + "lineage": "photo editing; participatory", + "tags": [ + "grid", + "sequence", + "selection" + ] + }, + { + "id": "photography-archive-negative-sleeve-index", + "form": "a negative-sleeve index, with sleeve rows, roll identifiers, and frame ranges", + "lineage": "photo archiving", + "tags": [ + "containers", + "identifiers", + "condition" + ] + }, + { + "id": "photography-archive-live-digital-system-modeled-on-a-negative-sleeve-ind", + "form": "a live digital system modeled on a negative-sleeve index, with frame ranges, date fields, and condition notes", + "lineage": "photo archiving; native digital", + "tags": [ + "containers", + "identifiers", + "condition" + ] + }, + { + "id": "photography-archive-shared-participatory-system-modeled-on-a-negative-sl", + "form": "a shared participatory system modeled on a negative-sleeve index, with condition notes, sleeve rows, and date fields", + "lineage": "photo archiving; participatory", + "tags": [ + "containers", + "identifiers", + "condition" + ] + }, + { + "id": "photography-archive-slide-carousel-tray", + "form": "a slide-carousel tray, with numbered slots, fixed sequence, and orientation marks", + "lineage": "slide presentation", + "tags": [ + "sequence", + "slots", + "index" + ] + }, + { + "id": "photography-archive-live-digital-system-modeled-on-a-slide-carousel-tray", + "form": "a live digital system modeled on a slide-carousel tray, with orientation marks, missing-slide gaps, and typed index cards", + "lineage": "slide presentation; native digital", + "tags": [ + "sequence", + "slots", + "index" + ] + }, + { + "id": "photography-archive-shared-participatory-system-modeled-on-a-slide-carou", + "form": "a shared participatory system modeled on a slide-carousel tray, with typed index cards, numbered slots, and missing-slide gaps", + "lineage": "slide presentation; participatory", + "tags": [ + "sequence", + "slots", + "index" + ] + }, + { + "id": "photography-archive-darkroom-exposure-test-strip", + "form": "a darkroom exposure test strip, with stepped exposure bands, constant subject crop, and contrast comparison", + "lineage": "photographic printing", + "tags": [ + "increments", + "comparison", + "selection" + ] + }, + { + "id": "photography-archive-live-digital-system-modeled-on-a-darkroom-exposure-t", + "form": "a live digital system modeled on a darkroom exposure test strip, with contrast comparison, timing notes, and chosen setting marks", + "lineage": "photographic printing; native digital", + "tags": [ + "increments", + "comparison", + "selection" + ] + }, + { + "id": "photography-archive-shared-participatory-system-modeled-on-a-darkroom-ex", + "form": "a shared participatory system modeled on a darkroom exposure test strip, with chosen setting marks, stepped exposure bands, and timing notes", + "lineage": "photographic printing; participatory", + "tags": [ + "increments", + "comparison", + "selection" + ] + }, + { + "id": "photography-archive-photographic-archive-card", + "form": "a photographic archive card, with accession identifiers, subject headings, and date and maker fields", + "lineage": "photo cataloging", + "tags": [ + "metadata", + "crossreference", + "location" + ] + }, + { + "id": "photography-archive-live-digital-system-modeled-on-a-photographic-archiv", + "form": "a live digital system modeled on a photographic archive card, with date and maker fields, storage locations, and cross-references", + "lineage": "photo cataloging; native digital", + "tags": [ + "metadata", + "crossreference", + "location" + ] + }, + { + "id": "photography-archive-shared-participatory-system-modeled-on-a-photographi-62ff2c4c", + "form": "a shared participatory system modeled on a photographic archive card, with cross-references, accession identifiers, and storage locations", + "lineage": "photo cataloging; participatory", + "tags": [ + "metadata", + "crossreference", + "location" + ] + }, + { + "id": "photography-archive-family-photo-album-spread", + "form": "a family photo album spread, with page chronology, image clusters, and handwritten captions", + "lineage": "vernacular photography", + "tags": [ + "chronology", + "captions", + "grouping" + ] + }, + { + "id": "photography-archive-live-digital-system-modeled-on-a-family-photo-album", + "form": "a live digital system modeled on a family photo album spread, with handwritten captions, keepsake pockets, and blank memory gaps", + "lineage": "vernacular photography; native digital", + "tags": [ + "chronology", + "captions", + "grouping" + ] + }, + { + "id": "photography-archive-shared-participatory-system-modeled-on-a-family-phot", + "form": "a shared participatory system modeled on a family photo album spread, with blank memory gaps, page chronology, and keepsake pockets", + "lineage": "vernacular photography; participatory", + "tags": [ + "chronology", + "captions", + "grouping" + ] + }, + { + "id": "photography-archive-photo-essay-sequence", + "form": "a photo-essay sequence, with opening image, wide-detail alternation, and caption tiers", + "lineage": "editorial photography", + "tags": [ + "narrative", + "scale", + "rhythm" + ] + }, + { + "id": "photography-archive-live-digital-system-modeled-on-a-photo-essay-sequenc", + "form": "a live digital system modeled on a photo-essay sequence, with caption tiers, page-turn reveals, and closing image", + "lineage": "editorial photography; native digital", + "tags": [ + "narrative", + "scale", + "rhythm" + ] + }, + { + "id": "photography-archive-shared-participatory-system-modeled-on-a-photo-essay", + "form": "a shared participatory system modeled on a photo-essay sequence, with closing image, opening image, and page-turn reveals", + "lineage": "editorial photography; participatory", + "tags": [ + "narrative", + "scale", + "rhythm" + ] + }, + { + "id": "photography-archive-crop-marked-photographic-proof", + "form": "a crop-marked photographic proof, with crop boundaries, scale percentages, and rotation marks", + "lineage": "print production", + "tags": [ + "boundaries", + "scale", + "annotation" + ] + }, + { + "id": "photography-archive-live-digital-system-modeled-on-a-crop-marked-photogr", + "form": "a live digital system modeled on a crop-marked photographic proof, with rotation marks, color notes, and approval signatures", + "lineage": "print production; native digital", + "tags": [ + "boundaries", + "scale", + "annotation" + ] + }, + { + "id": "photography-archive-shared-participatory-system-modeled-on-a-crop-marked", + "form": "a shared participatory system modeled on a crop-marked photographic proof, with approval signatures, crop boundaries, and color notes", + "lineage": "print production; participatory", + "tags": [ + "boundaries", + "scale", + "annotation" + ] + }, + { + "id": "photography-archive-light-table-review-layout", + "form": "a light-table review layout, with loose image groups, side-by-side comparisons, and candidate stacks", + "lineage": "photo editing", + "tags": [ + "sorting", + "comparison", + "stacks" + ] + }, + { + "id": "photography-archive-live-digital-system-modeled-on-a-light-table-review", + "form": "a live digital system modeled on a light-table review layout, with candidate stacks, reject zones, and final sequence row", + "lineage": "photo editing; native digital", + "tags": [ + "sorting", + "comparison", + "stacks" + ] + }, + { + "id": "photography-archive-shared-participatory-system-modeled-on-a-light-table", + "form": "a shared participatory system modeled on a light-table review layout, with final sequence row, loose image groups, and reject zones", + "lineage": "photo editing; participatory", + "tags": [ + "sorting", + "comparison", + "stacks" + ] + }, + { + "id": "photography-archive-camera-viewfinder-overlay", + "form": "a camera viewfinder overlay, with framing guides, focus points, and exposure scale", + "lineage": "camera interfaces", + "tags": [ + "framing", + "exposure", + "status" + ] + }, + { + "id": "photography-archive-live-digital-system-modeled-on-a-camera-viewfinder-o", + "form": "a live digital system modeled on a camera viewfinder overlay, with exposure scale, level indicator, and capture status", + "lineage": "camera interfaces; native digital", + "tags": [ + "framing", + "exposure", + "status" + ] + }, + { + "id": "photography-archive-shared-participatory-system-modeled-on-a-camera-view", + "form": "a shared participatory system modeled on a camera viewfinder overlay, with capture status, framing guides, and level indicator", + "lineage": "camera interfaces; participatory", + "tags": [ + "framing", + "exposure", + "status" + ] + }, + { + "id": "photography-archive-tonal-zone-exposure-chart", + "form": "a tonal-zone exposure chart, with ordered tonal bands, metered-value mapping, and texture thresholds", + "lineage": "photographic technique", + "tags": [ + "zones", + "mapping", + "range" + ] + }, + { + "id": "photography-archive-live-digital-system-modeled-on-a-tonal-zone-exposure", + "form": "a live digital system modeled on a tonal-zone exposure chart, with texture thresholds, development adjustments, and print targets", + "lineage": "photographic technique; native digital", + "tags": [ + "zones", + "mapping", + "range" + ] + }, + { + "id": "photography-archive-shared-participatory-system-modeled-on-a-tonal-zone", + "form": "a shared participatory system modeled on a tonal-zone exposure chart, with print targets, ordered tonal bands, and development adjustments", + "lineage": "photographic technique; participatory", + "tags": [ + "zones", + "mapping", + "range" + ] + }, + { + "id": "photography-archive-photographic-color-reference-chart", + "form": "a photographic color-reference chart, with standardized color patches, neutral steps, and exposure checks", + "lineage": "color management", + "tags": [ + "patches", + "calibration", + "comparison" + ] + }, + { + "id": "photography-archive-live-digital-system-modeled-on-a-photographic-color", + "form": "a live digital system modeled on a photographic color-reference chart, with exposure checks, profile targets, and before-after comparison", + "lineage": "color management; native digital", + "tags": [ + "patches", + "calibration", + "comparison" + ] + }, + { + "id": "photography-archive-shared-participatory-system-modeled-on-a-photographi-93fcbc4a", + "form": "a shared participatory system modeled on a photographic color-reference chart, with before-after comparison, standardized color patches, and profile targets", + "lineage": "color management; participatory", + "tags": [ + "patches", + "calibration", + "comparison" + ] + }, + { + "id": "photography-archive-studio-shot-list", + "form": "a studio shot list, with shot identifiers, angle and crop notes, and lighting setups", + "lineage": "commercial photography", + "tags": [ + "shots", + "setup", + "status" + ] + }, + { + "id": "photography-archive-live-digital-system-modeled-on-a-studio-shot-list", + "form": "a live digital system modeled on a studio shot list, with lighting setups, prop requirements, and completion checks", + "lineage": "commercial photography; native digital", + "tags": [ + "shots", + "setup", + "status" + ] + }, + { + "id": "photography-archive-shared-participatory-system-modeled-on-a-studio-shot", + "form": "a shared participatory system modeled on a studio shot list, with completion checks, shot identifiers, and prop requirements", + "lineage": "commercial photography; participatory", + "tags": [ + "shots", + "setup", + "status" + ] + }, + { + "id": "photography-archive-photograph-condition-report", + "form": "a photograph condition report, with object identifiers, damage diagrams, and severity scales", + "lineage": "conservation", + "tags": [ + "damage", + "location", + "change" + ] + }, + { + "id": "photography-archive-live-digital-system-modeled-on-a-photograph-conditio", + "form": "a live digital system modeled on a photograph condition report, with severity scales, treatment notes, and dated comparisons", + "lineage": "conservation; native digital", + "tags": [ + "damage", + "location", + "change" + ] + }, + { + "id": "photography-archive-shared-participatory-system-modeled-on-a-photograph", + "form": "a shared participatory system modeled on a photograph condition report, with dated comparisons, object identifiers, and treatment notes", + "lineage": "conservation; participatory", + "tags": [ + "damage", + "location", + "change" + ] + }, + { + "id": "photography-archive-rights-and-reproduction-ledger", + "form": "a rights-and-reproduction ledger, with asset identifiers, rights holders, and permitted uses", + "lineage": "photo licensing", + "tags": [ + "rights", + "uses", + "expiry" + ] + }, + { + "id": "photography-archive-live-digital-system-modeled-on-a-rights-and-reproduc", + "form": "a live digital system modeled on a rights-and-reproduction ledger, with permitted uses, territory and term fields, and expiry alerts", + "lineage": "photo licensing; native digital", + "tags": [ + "rights", + "uses", + "expiry" + ] + }, + { + "id": "photography-archive-shared-participatory-system-modeled-on-a-rights-and", + "form": "a shared participatory system modeled on a rights-and-reproduction ledger, with expiry alerts, asset identifiers, and territory and term fields", + "lineage": "photo licensing; participatory", + "tags": [ + "rights", + "uses", + "expiry" + ] + }, + { + "id": "photography-archive-geotagged-photo-map", + "form": "a geotagged photo map, with location pins, density clusters, and time filters", + "lineage": "digital photography", + "tags": [ + "map", + "clusters", + "time" + ] + }, + { + "id": "photography-archive-live-digital-system-modeled-on-a-geotagged-photo-map", + "form": "a live digital system modeled on a geotagged photo map, with time filters, route traces, and place-based albums", + "lineage": "digital photography; native digital", + "tags": [ + "map", + "clusters", + "time" + ] + }, + { + "id": "photography-archive-shared-participatory-system-modeled-on-a-geotagged-p", + "form": "a shared participatory system modeled on a geotagged photo map, with place-based albums, location pins, and route traces", + "lineage": "digital photography; participatory", + "tags": [ + "map", + "clusters", + "time" + ] + }, + { + "id": "photography-archive-chronological-image-archive", + "form": "a chronological image archive, with time bands, dated images, and event clusters", + "lineage": "photo history", + "tags": [ + "timeline", + "events", + "gaps" + ] + }, + { + "id": "photography-archive-live-digital-system-modeled-on-a-chronological-image", + "form": "a live digital system modeled on a chronological image archive, with event clusters, uncertain-date ranges, and missing-period gaps", + "lineage": "photo history; native digital", + "tags": [ + "timeline", + "events", + "gaps" + ] + }, + { + "id": "photography-archive-shared-participatory-system-modeled-on-a-chronologic", + "form": "a shared participatory system modeled on a chronological image archive, with missing-period gaps, time bands, and uncertain-date ranges", + "lineage": "photo history; participatory", + "tags": [ + "timeline", + "events", + "gaps" + ] + }, + { + "id": "photography-archive-press-photo-caption-desk", + "form": "a press-photo caption desk, with image queue, who-what-where fields, and fact checks", + "lineage": "photojournalism", + "tags": [ + "caption", + "verification", + "distribution" + ] + }, + { + "id": "photography-archive-live-digital-system-modeled-on-a-press-photo-caption", + "form": "a live digital system modeled on a press-photo caption desk, with fact checks, rights status, and distribution priority", + "lineage": "photojournalism; native digital", + "tags": [ + "caption", + "verification", + "distribution" + ] + }, + { + "id": "photography-archive-shared-participatory-system-modeled-on-a-press-photo", + "form": "a shared participatory system modeled on a press-photo caption desk, with distribution priority, image queue, and rights status", + "lineage": "photojournalism; participatory", + "tags": [ + "caption", + "verification", + "distribution" + ] + }, + { + "id": "photography-archive-panoramic-stitch-grid", + "form": "a panoramic stitch grid, with overlapping frames, alignment points, and projection guides", + "lineage": "computational photography", + "tags": [ + "overlap", + "alignment", + "seams" + ] + }, + { + "id": "photography-archive-live-digital-system-modeled-on-a-panoramic-stitch-gr", + "form": "a live digital system modeled on a panoramic stitch grid, with projection guides, seam masks, and coverage gaps", + "lineage": "computational photography; native digital", + "tags": [ + "overlap", + "alignment", + "seams" + ] + }, + { + "id": "photography-archive-shared-participatory-system-modeled-on-a-panoramic-s", + "form": "a shared participatory system modeled on a panoramic stitch grid, with coverage gaps, overlapping frames, and seam masks", + "lineage": "computational photography; participatory", + "tags": [ + "overlap", + "alignment", + "seams" + ] + }, + { + "id": "photography-archive-stereoscopic-image-pair", + "form": "a stereoscopic image pair, with paired viewpoints, matched crop, and horizontal alignment", + "lineage": "early photography", + "tags": [ + "pairing", + "alignment", + "depth" + ] + }, + { + "id": "photography-archive-live-digital-system-modeled-on-a-stereoscopic-image", + "form": "a live digital system modeled on a stereoscopic image pair, with horizontal alignment, depth cues, and viewing frame", + "lineage": "early photography; native digital", + "tags": [ + "pairing", + "alignment", + "depth" + ] + }, + { + "id": "photography-archive-shared-participatory-system-modeled-on-a-stereoscopi", + "form": "a shared participatory system modeled on a stereoscopic image pair, with viewing frame, paired viewpoints, and depth cues", + "lineage": "early photography; participatory", + "tags": [ + "pairing", + "alignment", + "depth" + ] + }, + { + "id": "photography-archive-photogram-composition", + "form": "a photogram composition, with object silhouettes, overlap layers, and exposure gradients", + "lineage": "camera-less photography", + "tags": [ + "silhouette", + "overlap", + "exposure" + ] + }, + { + "id": "photography-archive-live-digital-system-modeled-on-a-photogram-compositi", + "form": "a live digital system modeled on a photogram composition, with exposure gradients, negative space, and contact edges", + "lineage": "camera-less photography; native digital", + "tags": [ + "silhouette", + "overlap", + "exposure" + ] + }, + { + "id": "photography-archive-shared-participatory-system-modeled-on-a-photogram-c", + "form": "a shared participatory system modeled on a photogram composition, with contact edges, object silhouettes, and negative space", + "lineage": "camera-less photography; participatory", + "tags": [ + "silhouette", + "overlap", + "exposure" + ] + }, + { + "id": "photography-archive-blueprint-process-specimen-sheet", + "form": "a blueprint-process specimen sheet, with contact silhouettes, chemical-blue field, and specimen labels", + "lineage": "alternative photography", + "tags": [ + "specimens", + "labels", + "contact" + ] + } + ] + }, + { + "id": "computational-heritage", + "label": "Computational heritage", + "description": "Historic and enduring software forms whose structure teaches state, constraint, and manipulation.", + "concepts": [ + { + "id": "computational-heritage-punched-card-program-deck", + "form": "a punched-card program deck, with fixed-column encoding, physical card order, and separator cards", + "lineage": "early computing", + "tags": [ + "sequence", + "encoding", + "batches" + ] + }, + { + "id": "computational-heritage-live-digital-system-modeled-on-a-punched-card-progra", + "form": "a live digital system modeled on a punched-card program deck, with separator cards, job headers, and error-recovery markers", + "lineage": "early computing; native digital", + "tags": [ + "sequence", + "encoding", + "batches" + ] + }, + { + "id": "computational-heritage-shared-participatory-system-modeled-on-a-punched-car", + "form": "a shared participatory system modeled on a punched-card program deck, with error-recovery markers, fixed-column encoding, and job headers", + "lineage": "early computing; participatory", + "tags": [ + "sequence", + "encoding", + "batches" + ] + }, + { + "id": "computational-heritage-perforated-paper-tape-stream", + "form": "a perforated paper-tape stream, with linear hole patterns, character groups, and feed direction", + "lineage": "early computing", + "tags": [ + "sequence", + "holes", + "checks" + ] + }, + { + "id": "computational-heritage-live-digital-system-modeled-on-a-perforated-paper-ta", + "form": "a live digital system modeled on a perforated paper-tape stream, with feed direction, leader sections, and parity checks", + "lineage": "early computing; native digital", + "tags": [ + "sequence", + "holes", + "checks" + ] + }, + { + "id": "computational-heritage-shared-participatory-system-modeled-on-a-perforated", + "form": "a shared participatory system modeled on a perforated paper-tape stream, with parity checks, linear hole patterns, and leader sections", + "lineage": "early computing; participatory", + "tags": [ + "sequence", + "holes", + "checks" + ] + }, + { + "id": "computational-heritage-telephone-switchboard-patch-panel", + "form": "a telephone switchboard patch panel, with labeled jack fields, visible patch cords, and operator positions", + "lineage": "telecommunications", + "tags": [ + "ports", + "routing", + "status" + ] + }, + { + "id": "computational-heritage-live-digital-system-modeled-on-a-telephone-switchboa", + "form": "a live digital system modeled on a telephone switchboard patch panel, with operator positions, busy indicators, and manual rerouting", + "lineage": "telecommunications; native digital", + "tags": [ + "ports", + "routing", + "status" + ] + }, + { + "id": "computational-heritage-shared-participatory-system-modeled-on-a-telephone-s", + "form": "a shared participatory system modeled on a telephone switchboard patch panel, with manual rerouting, labeled jack fields, and busy indicators", + "lineage": "telecommunications; participatory", + "tags": [ + "ports", + "routing", + "status" + ] + }, + { + "id": "computational-heritage-relay-logic-ladder-diagram", + "form": "a relay logic ladder diagram, with parallel power rails, ordered logic rungs, and contact states", + "lineage": "electromechanical control", + "tags": [ + "rungs", + "states", + "flows" + ] + }, + { + "id": "computational-heritage-live-digital-system-modeled-on-a-relay-logic-ladder", + "form": "a live digital system modeled on a relay logic ladder diagram, with contact states, coil outputs, and fault tracing", + "lineage": "electromechanical control; native digital", + "tags": [ + "rungs", + "states", + "flows" + ] + }, + { + "id": "computational-heritage-shared-participatory-system-modeled-on-a-relay-logic", + "form": "a shared participatory system modeled on a relay logic ladder diagram, with fault tracing, parallel power rails, and coil outputs", + "lineage": "electromechanical control; participatory", + "tags": [ + "rungs", + "states", + "flows" + ] + }, + { + "id": "computational-heritage-vacuum-tube-computer-rack", + "form": "a vacuum-tube computer rack, with repeated functional bays, signal lamps, and plug-in modules", + "lineage": "early electronic computing", + "tags": [ + "modules", + "signals", + "maintenance" + ] + }, + { + "id": "computational-heritage-live-digital-system-modeled-on-a-vacuum-tube-compute", + "form": "a live digital system modeled on a vacuum-tube computer rack, with plug-in modules, cooling zones, and maintenance labels", + "lineage": "early electronic computing; native digital", + "tags": [ + "modules", + "signals", + "maintenance" + ] + }, + { + "id": "computational-heritage-shared-participatory-system-modeled-on-a-vacuum-tube", + "form": "a shared participatory system modeled on a vacuum-tube computer rack, with maintenance labels, repeated functional bays, and cooling zones", + "lineage": "early electronic computing; participatory", + "tags": [ + "modules", + "signals", + "maintenance" + ] + }, + { + "id": "computational-heritage-magnetic-core-memory-plane", + "form": "a magnetic core-memory plane, with orthogonal wire grid, address intersections, and binary core states", + "lineage": "computer memory", + "tags": [ + "matrix", + "addressing", + "state" + ] + }, + { + "id": "computational-heritage-live-digital-system-modeled-on-a-magnetic-core-memor", + "form": "a live digital system modeled on a magnetic core-memory plane, with binary core states, read-write paths, and module tiling", + "lineage": "computer memory; native digital", + "tags": [ + "matrix", + "addressing", + "state" + ] + }, + { + "id": "computational-heritage-shared-participatory-system-modeled-on-a-magnetic-co", + "form": "a shared participatory system modeled on a magnetic core-memory plane, with module tiling, orthogonal wire grid, and read-write paths", + "lineage": "computer memory; participatory", + "tags": [ + "matrix", + "addressing", + "state" + ] + }, + { + "id": "computational-heritage-mainframe-batch-job-console", + "form": "a mainframe batch-job console, with queued jobs, resource classes, and operator messages", + "lineage": "mainframe operations", + "tags": [ + "queue", + "logs", + "operators" + ] + }, + { + "id": "computational-heritage-live-digital-system-modeled-on-a-mainframe-batch-job", + "form": "a live digital system modeled on a mainframe batch-job console, with operator messages, completion codes, and rerun controls", + "lineage": "mainframe operations; native digital", + "tags": [ + "queue", + "logs", + "operators" + ] + }, + { + "id": "computational-heritage-shared-participatory-system-modeled-on-a-mainframe-b", + "form": "a shared participatory system modeled on a mainframe batch-job console, with rerun controls, queued jobs, and completion codes", + "lineage": "mainframe operations; participatory", + "tags": [ + "queue", + "logs", + "operators" + ] + }, + { + "id": "computational-heritage-monochrome-text-terminal", + "form": "a monochrome text terminal, with fixed-width character grid, command prompt, and scrolling output", + "lineage": "terminal computing", + "tags": [ + "lines", + "prompt", + "feedback" + ] + }, + { + "id": "computational-heritage-live-digital-system-modeled-on-a-monochrome-text-ter", + "form": "a live digital system modeled on a monochrome text terminal, with scrolling output, cursor position, and compact status line", + "lineage": "terminal computing; native digital", + "tags": [ + "lines", + "prompt", + "feedback" + ] + }, + { + "id": "computational-heritage-shared-participatory-system-modeled-on-a-monochrome", + "form": "a shared participatory system modeled on a monochrome text terminal, with compact status line, fixed-width character grid, and cursor position", + "lineage": "terminal computing; participatory", + "tags": [ + "lines", + "prompt", + "feedback" + ] + }, + { + "id": "computational-heritage-flowchart-stencil-sheet", + "form": "a flowchart stencil sheet, with standard process shapes, directional connectors, and decision branches", + "lineage": "systems analysis", + "tags": [ + "shapes", + "connectors", + "decisions" + ] + }, + { + "id": "computational-heritage-live-digital-system-modeled-on-a-flowchart-stencil-s", + "form": "a live digital system modeled on a flowchart stencil sheet, with decision branches, start-end terminals, and off-page links", + "lineage": "systems analysis; native digital", + "tags": [ + "shapes", + "connectors", + "decisions" + ] + }, + { + "id": "computational-heritage-shared-participatory-system-modeled-on-a-flowchart-s", + "form": "a shared participatory system modeled on a flowchart stencil sheet, with off-page links, standard process shapes, and start-end terminals", + "lineage": "systems analysis; participatory", + "tags": [ + "shapes", + "connectors", + "decisions" + ] + }, + { + "id": "computational-heritage-structured-programming-diagram", + "form": "a structured-programming diagram, with nested blocks, top-down sequence, and conditional splits", + "lineage": "software design", + "tags": [ + "nesting", + "sequence", + "branches" + ] + }, + { + "id": "computational-heritage-live-digital-system-modeled-on-a-structured-programm", + "form": "a live digital system modeled on a structured-programming diagram, with conditional splits, loop enclosures, and single entry-exit paths", + "lineage": "software design; native digital", + "tags": [ + "nesting", + "sequence", + "branches" + ] + }, + { + "id": "computational-heritage-shared-participatory-system-modeled-on-a-structured", + "form": "a shared participatory system modeled on a structured-programming diagram, with single entry-exit paths, nested blocks, and loop enclosures", + "lineage": "software design; participatory", + "tags": [ + "nesting", + "sequence", + "branches" + ] + }, + { + "id": "computational-heritage-finite-state-transition-table", + "form": "a finite-state transition table, with state rows, event columns, and next-state cells", + "lineage": "computer science", + "tags": [ + "states", + "events", + "transitions" + ] + }, + { + "id": "computational-heritage-live-digital-system-modeled-on-a-finite-state-transi", + "form": "a live digital system modeled on a finite-state transition table, with next-state cells, invalid combinations, and terminal states", + "lineage": "computer science; native digital", + "tags": [ + "states", + "events", + "transitions" + ] + }, + { + "id": "computational-heritage-shared-participatory-system-modeled-on-a-finite-stat", + "form": "a shared participatory system modeled on a finite-state transition table, with terminal states, state rows, and invalid combinations", + "lineage": "computer science; participatory", + "tags": [ + "states", + "events", + "transitions" + ] + }, + { + "id": "computational-heritage-boolean-truth-table", + "form": "a Boolean truth table, with ordered input combinations, binary columns, and computed outputs", + "lineage": "digital logic", + "tags": [ + "inputs", + "outputs", + "combinations" + ] + }, + { + "id": "computational-heritage-live-digital-system-modeled-on-a-boolean-truth-table", + "form": "a live digital system modeled on a Boolean truth table, with computed outputs, grouped equivalences, and edge-condition rows", + "lineage": "digital logic; native digital", + "tags": [ + "inputs", + "outputs", + "combinations" + ] + }, + { + "id": "computational-heritage-shared-participatory-system-modeled-on-a-boolean-tru", + "form": "a shared participatory system modeled on a Boolean truth table, with edge-condition rows, ordered input combinations, and grouped equivalences", + "lineage": "digital logic; participatory", + "tags": [ + "inputs", + "outputs", + "combinations" + ] + }, + { + "id": "computational-heritage-logic-gate-schematic", + "form": "a logic-gate schematic, with functional gate symbols, directed signal wires, and named inputs", + "lineage": "digital electronics", + "tags": [ + "gates", + "wires", + "signals" + ] + }, + { + "id": "computational-heritage-live-digital-system-modeled-on-a-logic-gate-schemati", + "form": "a live digital system modeled on a logic-gate schematic, with named inputs, intermediate nodes, and output probes", + "lineage": "digital electronics; native digital", + "tags": [ + "gates", + "wires", + "signals" + ] + }, + { + "id": "computational-heritage-shared-participatory-system-modeled-on-a-logic-gate", + "form": "a shared participatory system modeled on a logic-gate schematic, with output probes, functional gate symbols, and intermediate nodes", + "lineage": "digital electronics; participatory", + "tags": [ + "gates", + "wires", + "signals" + ] + }, + { + "id": "computational-heritage-hexadecimal-memory-dump", + "form": "a hexadecimal memory dump, with address offsets, byte grids, and group separators", + "lineage": "systems debugging", + "tags": [ + "addresses", + "bytes", + "decoding" + ] + }, + { + "id": "computational-heritage-live-digital-system-modeled-on-a-hexadecimal-memory", + "form": "a live digital system modeled on a hexadecimal memory dump, with group separators, text decoding column, and highlighted changes", + "lineage": "systems debugging; native digital", + "tags": [ + "addresses", + "bytes", + "decoding" + ] + }, + { + "id": "computational-heritage-shared-participatory-system-modeled-on-a-hexadecimal", + "form": "a shared participatory system modeled on a hexadecimal memory dump, with highlighted changes, address offsets, and text decoding column", + "lineage": "systems debugging; participatory", + "tags": [ + "addresses", + "bytes", + "decoding" + ] + }, + { + "id": "computational-heritage-assembly-language-listing", + "form": "an assembly-language listing, with memory addresses, operation codes, and operand fields", + "lineage": "low-level programming", + "tags": [ + "addresses", + "instructions", + "comments" + ] + }, + { + "id": "computational-heritage-live-digital-system-modeled-on-an-assembly-language", + "form": "a live digital system modeled on an assembly-language listing, with operand fields, symbol labels, and inline comments", + "lineage": "low-level programming; native digital", + "tags": [ + "addresses", + "instructions", + "comments" + ] + }, + { + "id": "computational-heritage-shared-participatory-system-modeled-on-an-assembly-l", + "form": "a shared participatory system modeled on an assembly-language listing, with inline comments, memory addresses, and symbol labels", + "lineage": "low-level programming; participatory", + "tags": [ + "addresses", + "instructions", + "comments" + ] + }, + { + "id": "computational-heritage-filesystem-allocation-map", + "form": "a filesystem allocation map, with numbered storage blocks, file ownership, and contiguous runs", + "lineage": "computer storage", + "tags": [ + "blocks", + "ownership", + "gaps" + ] + }, + { + "id": "computational-heritage-live-digital-system-modeled-on-a-filesystem-allocati", + "form": "a live digital system modeled on a filesystem allocation map, with contiguous runs, free-space gaps, and fragmentation links", + "lineage": "computer storage; native digital", + "tags": [ + "blocks", + "ownership", + "gaps" + ] + }, + { + "id": "computational-heritage-shared-participatory-system-modeled-on-a-filesystem", + "form": "a shared participatory system modeled on a filesystem allocation map, with fragmentation links, numbered storage blocks, and free-space gaps", + "lineage": "computer storage; participatory", + "tags": [ + "blocks", + "ownership", + "gaps" + ] + }, + { + "id": "computational-heritage-version-history-graph", + "form": "a version-history graph, with chronological commits, parallel branches, and merge nodes", + "lineage": "software collaboration", + "tags": [ + "commits", + "branches", + "merges" + ] + }, + { + "id": "computational-heritage-live-digital-system-modeled-on-a-version-history-gra", + "form": "a live digital system modeled on a version-history graph, with merge nodes, author markers, and release tags", + "lineage": "software collaboration; native digital", + "tags": [ + "commits", + "branches", + "merges" + ] + }, + { + "id": "computational-heritage-shared-participatory-system-modeled-on-a-version-his", + "form": "a shared participatory system modeled on a version-history graph, with release tags, chronological commits, and author markers", + "lineage": "software collaboration; participatory", + "tags": [ + "commits", + "branches", + "merges" + ] + }, + { + "id": "computational-heritage-command-line-shell-session", + "form": "a command-line shell session, with prompt-response rhythm, command history, and piped streams", + "lineage": "interactive computing", + "tags": [ + "prompt", + "history", + "streams" + ] + }, + { + "id": "computational-heritage-live-digital-system-modeled-on-a-command-line-shell", + "form": "a live digital system modeled on a command-line shell session, with piped streams, error output, and completion feedback", + "lineage": "interactive computing; native digital", + "tags": [ + "prompt", + "history", + "streams" + ] + }, + { + "id": "computational-heritage-shared-participatory-system-modeled-on-a-command-lin", + "form": "a shared participatory system modeled on a command-line shell session, with completion feedback, prompt-response rhythm, and error output", + "lineage": "interactive computing; participatory", + "tags": [ + "prompt", + "history", + "streams" + ] + }, + { + "id": "computational-heritage-early-spreadsheet-grid", + "form": "an early spreadsheet grid, with lettered columns, numbered rows, and cell references", + "lineage": "personal computing", + "tags": [ + "cells", + "formulas", + "recalculation" + ] + }, + { + "id": "computational-heritage-live-digital-system-modeled-on-an-early-spreadsheet", + "form": "a live digital system modeled on an early spreadsheet grid, with cell references, visible formulas, and automatic recalculation", + "lineage": "personal computing; native digital", + "tags": [ + "cells", + "formulas", + "recalculation" + ] + }, + { + "id": "computational-heritage-shared-participatory-system-modeled-on-an-early-spre", + "form": "a shared participatory system modeled on an early spreadsheet grid, with automatic recalculation, lettered columns, and visible formulas", + "lineage": "personal computing; participatory", + "tags": [ + "cells", + "formulas", + "recalculation" + ] + }, + { + "id": "computational-heritage-hypertext-node-map", + "form": "a hypertext node map, with document nodes, typed links, and backlinks", + "lineage": "networked documents", + "tags": [ + "nodes", + "links", + "navigation" + ] + }, + { + "id": "computational-heritage-live-digital-system-modeled-on-a-hypertext-node-map", + "form": "a live digital system modeled on a hypertext node map, with backlinks, visited paths, and orphan detection", + "lineage": "networked documents; native digital", + "tags": [ + "nodes", + "links", + "navigation" + ] + }, + { + "id": "computational-heritage-shared-participatory-system-modeled-on-a-hypertext-n", + "form": "a shared participatory system modeled on a hypertext node map, with orphan detection, document nodes, and visited paths", + "lineage": "networked documents; participatory", + "tags": [ + "nodes", + "links", + "navigation" + ] + }, + { + "id": "computational-heritage-desktop-window-stack", + "form": "a desktop window stack, with overlapping windows, title bars, and focus ordering", + "lineage": "graphical computing", + "tags": [ + "windows", + "focus", + "overlap" + ] + }, + { + "id": "computational-heritage-live-digital-system-modeled-on-a-desktop-window-stac", + "form": "a live digital system modeled on a desktop window stack, with focus ordering, resize boundaries, and desktop background", + "lineage": "graphical computing; native digital", + "tags": [ + "windows", + "focus", + "overlap" + ] + }, + { + "id": "computational-heritage-shared-participatory-system-modeled-on-a-desktop-win", + "form": "a shared participatory system modeled on a desktop window stack, with desktop background, overlapping windows, and resize boundaries", + "lineage": "graphical computing; participatory", + "tags": [ + "windows", + "focus", + "overlap" + ] + }, + { + "id": "computational-heritage-mechanical-calculating-register", + "form": "a mechanical calculating register, with aligned digit wheels, carry propagation, and operation controls", + "lineage": "mechanical computation", + "tags": [ + "digits", + "carry", + "operations" + ] + } + ] + }, + { + "id": "digital-productivity-creation", + "label": "Digital productivity & creation", + "description": "Native digital workspaces for composing, connecting, revising, simulating, and publishing.", + "concepts": [ + { + "id": "digital-productivity-creation-command-palette", + "form": "a command palette, with invoked overlay, fuzzy command search, and categorized actions", + "lineage": "software interfaces", + "tags": [ + "search", + "commands", + "shortcuts" + ] + }, + { + "id": "digital-productivity-creation-live-digital-system-modeled-on-a-command-palette", + "form": "a live digital system modeled on a command palette, with categorized actions, keyboard traversal, and shortcut hints", + "lineage": "software interfaces; native digital", + "tags": [ + "search", + "commands", + "shortcuts" + ] + }, + { + "id": "digital-productivity-creation-shared-participatory-system-modeled-on-a-command-pal", + "form": "a shared participatory system modeled on a command palette, with shortcut hints, invoked overlay, and keyboard traversal", + "lineage": "software interfaces; participatory", + "tags": [ + "search", + "commands", + "shortcuts" + ] + }, + { + "id": "digital-productivity-creation-kanban-work-board", + "form": "a kanban work board, with staged columns, movable task cards, and work-in-progress limits", + "lineage": "project management", + "tags": [ + "lanes", + "cards", + "limits" + ] + }, + { + "id": "digital-productivity-creation-live-digital-system-modeled-on-a-kanban-work-board", + "form": "a live digital system modeled on a kanban work board, with work-in-progress limits, assignee markers, and blocked states", + "lineage": "project management; native digital", + "tags": [ + "lanes", + "cards", + "limits" + ] + }, + { + "id": "digital-productivity-creation-shared-participatory-system-modeled-on-a-kanban-work", + "form": "a shared participatory system modeled on a kanban work board, with blocked states, staged columns, and assignee markers", + "lineage": "project management; participatory", + "tags": [ + "lanes", + "cards", + "limits" + ] + }, + { + "id": "digital-productivity-creation-spreadsheet-workbook", + "form": "a spreadsheet workbook, with addressable cell grid, formulas and references, and sheet tabs", + "lineage": "productivity software", + "tags": [ + "grid", + "formulas", + "sheets" + ] + }, + { + "id": "digital-productivity-creation-live-digital-system-modeled-on-a-spreadsheet-workboo", + "form": "a live digital system modeled on a spreadsheet workbook, with sheet tabs, fill operations, and recalculated totals", + "lineage": "productivity software; native digital", + "tags": [ + "grid", + "formulas", + "sheets" + ] + }, + { + "id": "digital-productivity-creation-shared-participatory-system-modeled-on-a-spreadsheet", + "form": "a shared participatory system modeled on a spreadsheet workbook, with recalculated totals, addressable cell grid, and fill operations", + "lineage": "productivity software; participatory", + "tags": [ + "grid", + "formulas", + "sheets" + ] + }, + { + "id": "digital-productivity-creation-infinite-whiteboard-canvas", + "form": "an infinite whiteboard canvas, with unbounded spatial canvas, freeform objects, and zoom levels", + "lineage": "collaborative software", + "tags": [ + "canvas", + "objects", + "zoom" + ] + }, + { + "id": "digital-productivity-creation-live-digital-system-modeled-on-an-infinite-whiteboar", + "form": "a live digital system modeled on an infinite whiteboard canvas, with zoom levels, grouping frames, and presence cursors", + "lineage": "collaborative software; native digital", + "tags": [ + "canvas", + "objects", + "zoom" + ] + }, + { + "id": "digital-productivity-creation-shared-participatory-system-modeled-on-an-infinite-w", + "form": "a shared participatory system modeled on an infinite whiteboard canvas, with presence cursors, unbounded spatial canvas, and grouping frames", + "lineage": "collaborative software; participatory", + "tags": [ + "canvas", + "objects", + "zoom" + ] + }, + { + "id": "digital-productivity-creation-node-graph-editor", + "form": "a node-graph editor, with typed nodes, input-output ports, and visible connections", + "lineage": "creative software", + "tags": [ + "nodes", + "ports", + "connections" + ] + }, + { + "id": "digital-productivity-creation-live-digital-system-modeled-on-a-node-graph-editor", + "form": "a live digital system modeled on a node-graph editor, with visible connections, group frames, and live previews", + "lineage": "creative software; native digital", + "tags": [ + "nodes", + "ports", + "connections" + ] + }, + { + "id": "digital-productivity-creation-shared-participatory-system-modeled-on-a-node-graph", + "form": "a shared participatory system modeled on a node-graph editor, with live previews, typed nodes, and group frames", + "lineage": "creative software; participatory", + "tags": [ + "nodes", + "ports", + "connections" + ] + }, + { + "id": "digital-productivity-creation-timeline-editor", + "form": "a timeline editor, with parallel tracks, time-scaled clips, and playhead", + "lineage": "creative software", + "tags": [ + "tracks", + "clips", + "time" + ] + }, + { + "id": "digital-productivity-creation-live-digital-system-modeled-on-a-timeline-editor", + "form": "a live digital system modeled on a timeline editor, with playhead, trim handles, and keyframe markers", + "lineage": "creative software; native digital", + "tags": [ + "tracks", + "clips", + "time" + ] + }, + { + "id": "digital-productivity-creation-shared-participatory-system-modeled-on-a-timeline-ed", + "form": "a shared participatory system modeled on a timeline editor, with keyframe markers, parallel tracks, and trim handles", + "lineage": "creative software; participatory", + "tags": [ + "tracks", + "clips", + "time" + ] + }, + { + "id": "digital-productivity-creation-layer-panel", + "form": "a layer panel, with ordered layer stack, visibility toggles, and nested groups", + "lineage": "graphics software", + "tags": [ + "stack", + "visibility", + "nesting" + ] + }, + { + "id": "digital-productivity-creation-live-digital-system-modeled-on-a-layer-panel", + "form": "a live digital system modeled on a layer panel, with nested groups, lock states, and blend controls", + "lineage": "graphics software; native digital", + "tags": [ + "stack", + "visibility", + "nesting" + ] + }, + { + "id": "digital-productivity-creation-shared-participatory-system-modeled-on-a-layer-panel", + "form": "a shared participatory system modeled on a layer panel, with blend controls, ordered layer stack, and lock states", + "lineage": "graphics software; participatory", + "tags": [ + "stack", + "visibility", + "nesting" + ] + }, + { + "id": "digital-productivity-creation-outline-editor", + "form": "an outline editor, with nested text levels, disclosure controls, and drag reordering", + "lineage": "writing software", + "tags": [ + "hierarchy", + "folding", + "reorder" + ] + }, + { + "id": "digital-productivity-creation-live-digital-system-modeled-on-an-outline-editor", + "form": "a live digital system modeled on an outline editor, with drag reordering, focus mode, and breadcrumb context", + "lineage": "writing software; native digital", + "tags": [ + "hierarchy", + "folding", + "reorder" + ] + }, + { + "id": "digital-productivity-creation-shared-participatory-system-modeled-on-an-outline-ed", + "form": "a shared participatory system modeled on an outline editor, with breadcrumb context, nested text levels, and focus mode", + "lineage": "writing software; participatory", + "tags": [ + "hierarchy", + "folding", + "reorder" + ] + }, + { + "id": "digital-productivity-creation-calendar-week-grid", + "form": "a calendar week grid, with day columns, hour rows, and duration blocks", + "lineage": "scheduling software", + "tags": [ + "timegrid", + "events", + "conflicts" + ] + }, + { + "id": "digital-productivity-creation-live-digital-system-modeled-on-a-calendar-week-grid", + "form": "a live digital system modeled on a calendar week grid, with duration blocks, overlap packing, and current-time marker", + "lineage": "scheduling software; native digital", + "tags": [ + "timegrid", + "events", + "conflicts" + ] + }, + { + "id": "digital-productivity-creation-shared-participatory-system-modeled-on-a-calendar-we", + "form": "a shared participatory system modeled on a calendar week grid, with current-time marker, day columns, and overlap packing", + "lineage": "scheduling software; participatory", + "tags": [ + "timegrid", + "events", + "conflicts" + ] + }, + { + "id": "digital-productivity-creation-inbox-triage-queue", + "form": "an inbox triage queue, with chronological message queue, priority flags, and thread grouping", + "lineage": "communication software", + "tags": [ + "queue", + "priority", + "actions" + ] + }, + { + "id": "digital-productivity-creation-live-digital-system-modeled-on-an-inbox-triage-queue", + "form": "a live digital system modeled on an inbox triage queue, with thread grouping, quick actions, and snooze states", + "lineage": "communication software; native digital", + "tags": [ + "queue", + "priority", + "actions" + ] + }, + { + "id": "digital-productivity-creation-shared-participatory-system-modeled-on-an-inbox-tria", + "form": "a shared participatory system modeled on an inbox triage queue, with snooze states, chronological message queue, and quick actions", + "lineage": "communication software; participatory", + "tags": [ + "queue", + "priority", + "actions" + ] + }, + { + "id": "digital-productivity-creation-version-history-browser", + "form": "a version-history browser, with dated snapshots, author markers, and change summaries", + "lineage": "collaboration software", + "tags": [ + "timeline", + "snapshots", + "restore" + ] + }, + { + "id": "digital-productivity-creation-live-digital-system-modeled-on-a-version-history-bro", + "form": "a live digital system modeled on a version-history browser, with change summaries, side-by-side comparison, and restore points", + "lineage": "collaboration software; native digital", + "tags": [ + "timeline", + "snapshots", + "restore" + ] + }, + { + "id": "digital-productivity-creation-shared-participatory-system-modeled-on-a-version-his", + "form": "a shared participatory system modeled on a version-history browser, with restore points, dated snapshots, and side-by-side comparison", + "lineage": "collaboration software; participatory", + "tags": [ + "timeline", + "snapshots", + "restore" + ] + }, + { + "id": "digital-productivity-creation-code-diff-viewer", + "form": "a code-diff viewer, with paired file panes, line numbering, and addition-deletion coloring", + "lineage": "developer tools", + "tags": [ + "comparison", + "lines", + "changes" + ] + }, + { + "id": "digital-productivity-creation-live-digital-system-modeled-on-a-code-diff-viewer", + "form": "a live digital system modeled on a code-diff viewer, with addition-deletion coloring, change hunks, and inline comments", + "lineage": "developer tools; native digital", + "tags": [ + "comparison", + "lines", + "changes" + ] + }, + { + "id": "digital-productivity-creation-shared-participatory-system-modeled-on-a-code-diff-v", + "form": "a shared participatory system modeled on a code-diff viewer, with inline comments, paired file panes, and change hunks", + "lineage": "developer tools; participatory", + "tags": [ + "comparison", + "lines", + "changes" + ] + }, + { + "id": "digital-productivity-creation-multiplayer-document-editor", + "form": "a multiplayer document editor, with live cursors, remote selections, and anchored comments", + "lineage": "collaborative software", + "tags": [ + "presence", + "selection", + "comments" + ] + }, + { + "id": "digital-productivity-creation-live-digital-system-modeled-on-a-multiplayer-documen", + "form": "a live digital system modeled on a multiplayer document editor, with anchored comments, suggestion mode, and revision history", + "lineage": "collaborative software; native digital", + "tags": [ + "presence", + "selection", + "comments" + ] + }, + { + "id": "digital-productivity-creation-shared-participatory-system-modeled-on-a-multiplayer", + "form": "a shared participatory system modeled on a multiplayer document editor, with revision history, live cursors, and suggestion mode", + "lineage": "collaborative software; participatory", + "tags": [ + "presence", + "selection", + "comments" + ] + }, + { + "id": "digital-productivity-creation-file-tree-navigator", + "form": "a file-tree navigator, with nested folders, expand-collapse controls, and file-type markers", + "lineage": "software navigation", + "tags": [ + "hierarchy", + "disclosure", + "selection" + ] + }, + { + "id": "digital-productivity-creation-live-digital-system-modeled-on-a-file-tree-navigator", + "form": "a live digital system modeled on a file-tree navigator, with file-type markers, selected path, and context actions", + "lineage": "software navigation; native digital", + "tags": [ + "hierarchy", + "disclosure", + "selection" + ] + }, + { + "id": "digital-productivity-creation-shared-participatory-system-modeled-on-a-file-tree-n", + "form": "a shared participatory system modeled on a file-tree navigator, with context actions, nested folders, and selected path", + "lineage": "software navigation; participatory", + "tags": [ + "hierarchy", + "disclosure", + "selection" + ] + }, + { + "id": "digital-productivity-creation-faceted-tag-browser", + "form": "a faceted tag browser, with tag groups, result counts, and multi-select filters", + "lineage": "information retrieval", + "tags": [ + "facets", + "counts", + "filters" + ] + }, + { + "id": "digital-productivity-creation-live-digital-system-modeled-on-a-faceted-tag-browser", + "form": "a live digital system modeled on a faceted tag browser, with multi-select filters, active-filter chips, and empty-result recovery", + "lineage": "information retrieval; native digital", + "tags": [ + "facets", + "counts", + "filters" + ] + }, + { + "id": "digital-productivity-creation-shared-participatory-system-modeled-on-a-faceted-tag", + "form": "a shared participatory system modeled on a faceted tag browser, with empty-result recovery, tag groups, and active-filter chips", + "lineage": "information retrieval; participatory", + "tags": [ + "facets", + "counts", + "filters" + ] + }, + { + "id": "digital-productivity-creation-saved-filter-builder", + "form": "a saved-filter builder, with field-operator-value rows, Boolean grouping, and live result preview", + "lineage": "productivity software", + "tags": [ + "rules", + "preview", + "reuse" + ] + }, + { + "id": "digital-productivity-creation-live-digital-system-modeled-on-a-saved-filter-builde", + "form": "a live digital system modeled on a saved-filter builder, with live result preview, named presets, and share controls", + "lineage": "productivity software; native digital", + "tags": [ + "rules", + "preview", + "reuse" + ] + }, + { + "id": "digital-productivity-creation-shared-participatory-system-modeled-on-a-saved-filte", + "form": "a shared participatory system modeled on a saved-filter builder, with share controls, field-operator-value rows, and named presets", + "lineage": "productivity software; participatory", + "tags": [ + "rules", + "preview", + "reuse" + ] + }, + { + "id": "digital-productivity-creation-automation-recipe-builder", + "form": "an automation recipe builder, with trigger block, ordered actions, and conditional branches", + "lineage": "workflow software", + "tags": [ + "triggers", + "actions", + "conditions" + ] + }, + { + "id": "digital-productivity-creation-live-digital-system-modeled-on-an-automation-recipe", + "form": "a live digital system modeled on an automation recipe builder, with conditional branches, data mappings, and run history", + "lineage": "workflow software; native digital", + "tags": [ + "triggers", + "actions", + "conditions" + ] + }, + { + "id": "digital-productivity-creation-shared-participatory-system-modeled-on-an-automation", + "form": "a shared participatory system modeled on an automation recipe builder, with run history, trigger block, and data mappings", + "lineage": "workflow software; participatory", + "tags": [ + "triggers", + "actions", + "conditions" + ] + }, + { + "id": "digital-productivity-creation-form-builder", + "form": "a form builder, with field palette, ordered form canvas, and property inspector", + "lineage": "creation software", + "tags": [ + "fields", + "canvas", + "validation" + ] + }, + { + "id": "digital-productivity-creation-live-digital-system-modeled-on-a-form-builder", + "form": "a live digital system modeled on a form builder, with property inspector, validation rules, and preview mode", + "lineage": "creation software; native digital", + "tags": [ + "fields", + "canvas", + "validation" + ] + }, + { + "id": "digital-productivity-creation-shared-participatory-system-modeled-on-a-form-builde", + "form": "a shared participatory system modeled on a form builder, with preview mode, field palette, and validation rules", + "lineage": "creation software; participatory", + "tags": [ + "fields", + "canvas", + "validation" + ] + }, + { + "id": "digital-productivity-creation-relational-database-table", + "form": "a relational database table, with record rows, typed fields, and linked-record cells", + "lineage": "data software", + "tags": [ + "records", + "fields", + "relations" + ] + }, + { + "id": "digital-productivity-creation-live-digital-system-modeled-on-a-relational-database", + "form": "a live digital system modeled on a relational database table, with linked-record cells, sort and filter rules, and computed views", + "lineage": "data software; native digital", + "tags": [ + "records", + "fields", + "relations" + ] + }, + { + "id": "digital-productivity-creation-shared-participatory-system-modeled-on-a-relational", + "form": "a shared participatory system modeled on a relational database table, with computed views, record rows, and sort and filter rules", + "lineage": "data software; participatory", + "tags": [ + "records", + "fields", + "relations" + ] + }, + { + "id": "digital-productivity-creation-pivot-table-workspace", + "form": "a pivot-table workspace, with row dimensions, column dimensions, and aggregated measures", + "lineage": "data analysis", + "tags": [ + "dimensions", + "measures", + "aggregation" + ] + }, + { + "id": "digital-productivity-creation-live-digital-system-modeled-on-a-pivot-table-workspa", + "form": "a live digital system modeled on a pivot-table workspace, with aggregated measures, subtotal bands, and drill-down paths", + "lineage": "data analysis; native digital", + "tags": [ + "dimensions", + "measures", + "aggregation" + ] + }, + { + "id": "digital-productivity-creation-shared-participatory-system-modeled-on-a-pivot-table", + "form": "a shared participatory system modeled on a pivot-table workspace, with drill-down paths, row dimensions, and subtotal bands", + "lineage": "data analysis; participatory", + "tags": [ + "dimensions", + "measures", + "aggregation" + ] + }, + { + "id": "digital-productivity-creation-modular-dashboard-grid", + "form": "a modular dashboard grid, with resizable widget grid, shared filters, and summary metrics", + "lineage": "analytics software", + "tags": [ + "widgets", + "layout", + "signals" + ] + }, + { + "id": "digital-productivity-creation-live-digital-system-modeled-on-a-modular-dashboard-g", + "form": "a live digital system modeled on a modular dashboard grid, with summary metrics, detail views, and threshold alerts", + "lineage": "analytics software; native digital", + "tags": [ + "widgets", + "layout", + "signals" + ] + }, + { + "id": "digital-productivity-creation-shared-participatory-system-modeled-on-a-modular-das", + "form": "a shared participatory system modeled on a modular dashboard grid, with threshold alerts, resizable widget grid, and detail views", + "lineage": "analytics software; participatory", + "tags": [ + "widgets", + "layout", + "signals" + ] + }, + { + "id": "digital-productivity-creation-focus-timer-workspace", + "form": "a focus-timer workspace, with timed focus cycles, visible countdown, and break intervals", + "lineage": "attention tools", + "tags": [ + "cycles", + "progress", + "interruptions" + ] + } + ] + }, + { + "id": "network-community", + "label": "Network & community", + "description": "Social and distributed systems shaped by participation, roles, relationships, moderation, and trust.", + "concepts": [ + { + "id": "network-community-online-forum-thread", + "form": "an online forum thread, with topic header, nested replies, and author metadata", + "lineage": "online community", + "tags": [ + "threads", + "replies", + "moderation" + ] + }, + { + "id": "network-community-live-digital-system-modeled-on-an-online-forum-threa", + "form": "a live digital system modeled on an online forum thread, with author metadata, quote links, and moderation states", + "lineage": "online community; native digital", + "tags": [ + "threads", + "replies", + "moderation" + ] + }, + { + "id": "network-community-shared-participatory-system-modeled-on-an-online-for", + "form": "a shared participatory system modeled on an online forum thread, with moderation states, topic header, and quote links", + "lineage": "online community; participatory", + "tags": [ + "threads", + "replies", + "moderation" + ] + }, + { + "id": "network-community-persistent-chat-channel", + "form": "a persistent chat channel, with chronological message stream, threaded side conversations, and presence indicators", + "lineage": "group communication", + "tags": [ + "stream", + "threads", + "presence" + ] + }, + { + "id": "network-community-live-digital-system-modeled-on-a-persistent-chat-cha", + "form": "a live digital system modeled on a persistent chat channel, with presence indicators, reactions, and unread markers", + "lineage": "group communication; native digital", + "tags": [ + "stream", + "threads", + "presence" + ] + }, + { + "id": "network-community-shared-participatory-system-modeled-on-a-persistent", + "form": "a shared participatory system modeled on a persistent chat channel, with unread markers, chronological message stream, and reactions", + "lineage": "group communication; participatory", + "tags": [ + "stream", + "threads", + "presence" + ] + }, + { + "id": "network-community-mailing-list-digest", + "form": "a mailing-list digest, with batched message summary, topic sections, and sender headers", + "lineage": "network community", + "tags": [ + "digest", + "topics", + "replies" + ] + }, + { + "id": "network-community-live-digital-system-modeled-on-a-mailing-list-digest", + "form": "a live digital system modeled on a mailing-list digest, with sender headers, reply links, and subscription controls", + "lineage": "network community; native digital", + "tags": [ + "digest", + "topics", + "replies" + ] + }, + { + "id": "network-community-shared-participatory-system-modeled-on-a-mailing-lis", + "form": "a shared participatory system modeled on a mailing-list digest, with subscription controls, batched message summary, and reply links", + "lineage": "network community; participatory", + "tags": [ + "digest", + "topics", + "replies" + ] + }, + { + "id": "network-community-public-bulletin-board", + "form": "a public bulletin board, with pinned notices, category zones, and dated postings", + "lineage": "community communication", + "tags": [ + "cards", + "categories", + "expiry" + ] + }, + { + "id": "network-community-live-digital-system-modeled-on-a-public-bulletin-boa", + "form": "a live digital system modeled on a public bulletin board, with dated postings, contact stubs, and expiry removal", + "lineage": "community communication; native digital", + "tags": [ + "cards", + "categories", + "expiry" + ] + }, + { + "id": "network-community-shared-participatory-system-modeled-on-a-public-bull", + "form": "a shared participatory system modeled on a public bulletin board, with expiry removal, pinned notices, and contact stubs", + "lineage": "community communication; participatory", + "tags": [ + "cards", + "categories", + "expiry" + ] + }, + { + "id": "network-community-linked-site-ring", + "form": "a linked-site ring, with member-site sequence, previous-next links, and central directory", + "lineage": "early web communities", + "tags": [ + "sequence", + "neighbors", + "discovery" + ] + }, + { + "id": "network-community-live-digital-system-modeled-on-a-linked-site-ring", + "form": "a live digital system modeled on a linked-site ring, with central directory, random jump, and membership badge", + "lineage": "early web communities; native digital", + "tags": [ + "sequence", + "neighbors", + "discovery" + ] + }, + { + "id": "network-community-shared-participatory-system-modeled-on-a-linked-site", + "form": "a shared participatory system modeled on a linked-site ring, with membership badge, member-site sequence, and random jump", + "lineage": "early web communities; participatory", + "tags": [ + "sequence", + "neighbors", + "discovery" + ] + }, + { + "id": "network-community-blogroll-directory", + "form": "a blogroll directory, with curated site links, topic grouping, and recent-update marks", + "lineage": "independent publishing", + "tags": [ + "lists", + "categories", + "updates" + ] + }, + { + "id": "network-community-live-digital-system-modeled-on-a-blogroll-directory", + "form": "a live digital system modeled on a blogroll directory, with recent-update marks, short annotations, and reciprocal links", + "lineage": "independent publishing; native digital", + "tags": [ + "lists", + "categories", + "updates" + ] + }, + { + "id": "network-community-shared-participatory-system-modeled-on-a-blogroll-di", + "form": "a shared participatory system modeled on a blogroll directory, with reciprocal links, curated site links, and short annotations", + "lineage": "independent publishing; participatory", + "tags": [ + "lists", + "categories", + "updates" + ] + }, + { + "id": "network-community-social-activity-feed", + "form": "a social activity feed, with mixed-content stream, ranking signals, and author context", + "lineage": "social software", + "tags": [ + "stream", + "ranking", + "actions" + ] + }, + { + "id": "network-community-live-digital-system-modeled-on-a-social-activity-fee", + "form": "a live digital system modeled on a social activity feed, with author context, reaction actions, and pagination boundaries", + "lineage": "social software; native digital", + "tags": [ + "stream", + "ranking", + "actions" + ] + }, + { + "id": "network-community-shared-participatory-system-modeled-on-a-social-acti", + "form": "a shared participatory system modeled on a social activity feed, with pagination boundaries, mixed-content stream, and reaction actions", + "lineage": "social software; participatory", + "tags": [ + "stream", + "ranking", + "actions" + ] + }, + { + "id": "network-community-group-calendar", + "form": "a group calendar, with shared time grid, event ownership, and attendance states", + "lineage": "community coordination", + "tags": [ + "events", + "availability", + "roles" + ] + }, + { + "id": "network-community-live-digital-system-modeled-on-a-group-calendar", + "form": "a live digital system modeled on a group calendar, with attendance states, availability overlays, and reminder rules", + "lineage": "community coordination; native digital", + "tags": [ + "events", + "availability", + "roles" + ] + }, + { + "id": "network-community-shared-participatory-system-modeled-on-a-group-calen", + "form": "a shared participatory system modeled on a group calendar, with reminder rules, shared time grid, and availability overlays", + "lineage": "community coordination; participatory", + "tags": [ + "events", + "availability", + "roles" + ] + }, + { + "id": "network-community-community-member-directory", + "form": "a community member directory, with member cards, role labels, and skill tags", + "lineage": "community infrastructure", + "tags": [ + "profiles", + "filters", + "contact" + ] + }, + { + "id": "network-community-live-digital-system-modeled-on-a-community-member-di", + "form": "a live digital system modeled on a community member directory, with skill tags, faceted search, and contact permissions", + "lineage": "community infrastructure; native digital", + "tags": [ + "profiles", + "filters", + "contact" + ] + }, + { + "id": "network-community-shared-participatory-system-modeled-on-a-community-m", + "form": "a shared participatory system modeled on a community member directory, with contact permissions, member cards, and faceted search", + "lineage": "community infrastructure; participatory", + "tags": [ + "profiles", + "filters", + "contact" + ] + }, + { + "id": "network-community-reputation-ledger", + "form": "a reputation ledger, with contribution history, peer signals, and earned levels", + "lineage": "online trust", + "tags": [ + "history", + "signals", + "appeals" + ] + }, + { + "id": "network-community-live-digital-system-modeled-on-a-reputation-ledger", + "form": "a live digital system modeled on a reputation ledger, with earned levels, contextual badges, and appeal records", + "lineage": "online trust; native digital", + "tags": [ + "history", + "signals", + "appeals" + ] + }, + { + "id": "network-community-shared-participatory-system-modeled-on-a-reputation", + "form": "a shared participatory system modeled on a reputation ledger, with appeal records, contribution history, and contextual badges", + "lineage": "online trust; participatory", + "tags": [ + "history", + "signals", + "appeals" + ] + }, + { + "id": "network-community-moderation-queue", + "form": "a moderation queue, with flagged-item queue, policy citations, and evidence context", + "lineage": "community governance", + "tags": [ + "queue", + "evidence", + "decisions" + ] + }, + { + "id": "network-community-live-digital-system-modeled-on-a-moderation-queue", + "form": "a live digital system modeled on a moderation queue, with evidence context, decision controls, and appeal status", + "lineage": "community governance; native digital", + "tags": [ + "queue", + "evidence", + "decisions" + ] + }, + { + "id": "network-community-shared-participatory-system-modeled-on-a-moderation", + "form": "a shared participatory system modeled on a moderation queue, with appeal status, flagged-item queue, and decision controls", + "lineage": "community governance; participatory", + "tags": [ + "queue", + "evidence", + "decisions" + ] + }, + { + "id": "network-community-mutual-aid-request-board", + "form": "a mutual-aid request board, with need cards, offer cards, and location radius", + "lineage": "community care", + "tags": [ + "needs", + "offers", + "matching" + ] + }, + { + "id": "network-community-live-digital-system-modeled-on-a-mutual-aid-request", + "form": "a live digital system modeled on a mutual-aid request board, with location radius, urgency levels, and matched status", + "lineage": "community care; native digital", + "tags": [ + "needs", + "offers", + "matching" + ] + }, + { + "id": "network-community-shared-participatory-system-modeled-on-a-mutual-aid", + "form": "a shared participatory system modeled on a mutual-aid request board, with matched status, need cards, and urgency levels", + "lineage": "community care; participatory", + "tags": [ + "needs", + "offers", + "matching" + ] + }, + { + "id": "network-community-neighborhood-resource-map", + "form": "a neighborhood resource map, with place pins, resource categories, and service hours", + "lineage": "local community", + "tags": [ + "map", + "resources", + "updates" + ] + }, + { + "id": "network-community-live-digital-system-modeled-on-a-neighborhood-resour", + "form": "a live digital system modeled on a neighborhood resource map, with service hours, access notes, and community updates", + "lineage": "local community; native digital", + "tags": [ + "map", + "resources", + "updates" + ] + }, + { + "id": "network-community-shared-participatory-system-modeled-on-a-neighborhoo", + "form": "a shared participatory system modeled on a neighborhood resource map, with community updates, place pins, and access notes", + "lineage": "local community; participatory", + "tags": [ + "map", + "resources", + "updates" + ] + }, + { + "id": "network-community-event-rsvp-roster", + "form": "an event RSVP roster, with attendee list, response states, and capacity counter", + "lineage": "community events", + "tags": [ + "attendance", + "capacity", + "waitlist" + ] + }, + { + "id": "network-community-live-digital-system-modeled-on-an-event-rsvp-roster", + "form": "a live digital system modeled on an event RSVP roster, with capacity counter, waitlist order, and guest allowances", + "lineage": "community events; native digital", + "tags": [ + "attendance", + "capacity", + "waitlist" + ] + }, + { + "id": "network-community-shared-participatory-system-modeled-on-an-event-rsvp", + "form": "a shared participatory system modeled on an event RSVP roster, with guest allowances, attendee list, and waitlist order", + "lineage": "community events; participatory", + "tags": [ + "attendance", + "capacity", + "waitlist" + ] + }, + { + "id": "network-community-collective-annotation-layer", + "form": "a collective annotation layer, with content anchors, margin notes, and reply threads", + "lineage": "social reading", + "tags": [ + "anchors", + "notes", + "threads" + ] + }, + { + "id": "network-community-live-digital-system-modeled-on-a-collective-annotati", + "form": "a live digital system modeled on a collective annotation layer, with reply threads, visibility scopes, and resolved states", + "lineage": "social reading; native digital", + "tags": [ + "anchors", + "notes", + "threads" + ] + }, + { + "id": "network-community-shared-participatory-system-modeled-on-a-collective", + "form": "a shared participatory system modeled on a collective annotation layer, with resolved states, content anchors, and visibility scopes", + "lineage": "social reading; participatory", + "tags": [ + "anchors", + "notes", + "threads" + ] + }, + { + "id": "network-community-federated-timeline", + "form": "a federated timeline, with multi-server sources, chronological posts, and content warnings", + "lineage": "decentralized social software", + "tags": [ + "sources", + "chronology", + "controls" + ] + }, + { + "id": "network-community-live-digital-system-modeled-on-a-federated-timeline", + "form": "a live digital system modeled on a federated timeline, with content warnings, boost links, and local filtering", + "lineage": "decentralized social software; native digital", + "tags": [ + "sources", + "chronology", + "controls" + ] + }, + { + "id": "network-community-shared-participatory-system-modeled-on-a-federated-t", + "form": "a shared participatory system modeled on a federated timeline, with local filtering, multi-server sources, and boost links", + "lineage": "decentralized social software; participatory", + "tags": [ + "sources", + "chronology", + "controls" + ] + }, + { + "id": "network-community-topic-tag-cloud", + "form": "a topic-tag cloud, with topic labels, frequency weighting, and selectable terms", + "lineage": "community discovery", + "tags": [ + "tags", + "weight", + "navigation" + ] + }, + { + "id": "network-community-live-digital-system-modeled-on-a-topic-tag-cloud", + "form": "a live digital system modeled on a topic-tag cloud, with selectable terms, related-tag transitions, and time filters", + "lineage": "community discovery; native digital", + "tags": [ + "tags", + "weight", + "navigation" + ] + }, + { + "id": "network-community-shared-participatory-system-modeled-on-a-topic-tag-c", + "form": "a shared participatory system modeled on a topic-tag cloud, with time filters, topic labels, and related-tag transitions", + "lineage": "community discovery; participatory", + "tags": [ + "tags", + "weight", + "navigation" + ] + }, + { + "id": "network-community-collaborative-change-log", + "form": "a collaborative change log, with reverse-chronological edits, author markers, and diff previews", + "lineage": "community knowledge", + "tags": [ + "changes", + "authors", + "review" + ] + }, + { + "id": "network-community-live-digital-system-modeled-on-a-collaborative-chang", + "form": "a live digital system modeled on a collaborative change log, with diff previews, review states, and rollback links", + "lineage": "community knowledge; native digital", + "tags": [ + "changes", + "authors", + "review" + ] + }, + { + "id": "network-community-shared-participatory-system-modeled-on-a-collaborati", + "form": "a shared participatory system modeled on a collaborative change log, with rollback links, reverse-chronological edits, and review states", + "lineage": "community knowledge; participatory", + "tags": [ + "changes", + "authors", + "review" + ] + }, + { + "id": "network-community-shared-playlist-queue", + "form": "a shared playlist queue, with ordered media queue, contributor labels, and vote controls", + "lineage": "social audio", + "tags": [ + "queue", + "votes", + "playback" + ] + }, + { + "id": "network-community-live-digital-system-modeled-on-a-shared-playlist-que", + "form": "a live digital system modeled on a shared playlist queue, with vote controls, now-playing state, and duplicate handling", + "lineage": "social audio; native digital", + "tags": [ + "queue", + "votes", + "playback" + ] + }, + { + "id": "network-community-shared-participatory-system-modeled-on-a-shared-play", + "form": "a shared participatory system modeled on a shared playlist queue, with duplicate handling, ordered media queue, and now-playing state", + "lineage": "social audio; participatory", + "tags": [ + "queue", + "votes", + "playback" + ] + }, + { + "id": "network-community-peer-review-queue", + "form": "a peer-review queue, with submission cards, reviewer assignments, and deadline states", + "lineage": "knowledge community", + "tags": [ + "submissions", + "assignments", + "decisions" + ] + }, + { + "id": "network-community-live-digital-system-modeled-on-a-peer-review-queue", + "form": "a live digital system modeled on a peer-review queue, with deadline states, feedback rounds, and decision outcomes", + "lineage": "knowledge community; native digital", + "tags": [ + "submissions", + "assignments", + "decisions" + ] + }, + { + "id": "network-community-shared-participatory-system-modeled-on-a-peer-review", + "form": "a shared participatory system modeled on a peer-review queue, with decision outcomes, submission cards, and feedback rounds", + "lineage": "knowledge community; participatory", + "tags": [ + "submissions", + "assignments", + "decisions" + ] + }, + { + "id": "network-community-community-marketplace-trust-profile", + "form": "a community marketplace trust profile, with verified identity fields, transaction history, and rating distributions", + "lineage": "peer commerce", + "tags": [ + "identity", + "history", + "trust" + ] + }, + { + "id": "network-community-live-digital-system-modeled-on-a-community-marketpla", + "form": "a live digital system modeled on a community marketplace trust profile, with rating distributions, response times, and dispute records", + "lineage": "peer commerce; native digital", + "tags": [ + "identity", + "history", + "trust" + ] + }, + { + "id": "network-community-shared-participatory-system-modeled-on-a-community-m-13267f6a", + "form": "a shared participatory system modeled on a community marketplace trust profile, with dispute records, verified identity fields, and response times", + "lineage": "peer commerce; participatory", + "tags": [ + "identity", + "history", + "trust" + ] + }, + { + "id": "network-community-live-presence-room", + "form": "a live-presence room, with participant roster, spatial groupings, and speaking indicators", + "lineage": "synchronous community", + "tags": [ + "presence", + "spaces", + "signals" + ] + } + ] + }, + { + "id": "games-play", + "label": "Games & play", + "description": "Rule-bound systems with state, turns, progression, probability, feedback, and shared attention.", + "concepts": [ + { + "id": "games-play-chess-annotation-sheet", + "form": "a chess annotation sheet, with move pairs and evaluation symbols", + "lineage": "Games & play", + "tags": [ + "rule-state", + "turn-sequence", + "outcome-feedback" + ] + }, + { + "id": "games-play-tournament-bracket", + "form": "a tournament bracket, with converging match paths, round-by-round hierarchy, and a single outcome", + "lineage": "Games & play", + "tags": [ + "rule-state", + "turn-sequence", + "outcome-feedback" + ] + }, + { + "id": "games-play-scorekeeper-s-baseball-scorecard", + "form": "a scorekeeper's baseball scorecard, with position numbers and inning grids", + "lineage": "Games & play", + "tags": [ + "rule-state", + "turn-sequence", + "outcome-feedback" + ] + }, + { + "id": "games-play-bingo-hall-s-number-board-and-dabbed-cards", + "form": "a bingo hall's number board and dabbed cards, with called-state tracking, repeated grids, and shared progression", + "lineage": "Games & play", + "tags": [ + "rule-state", + "turn-sequence", + "outcome-feedback" + ] + }, + { + "id": "games-play-loteri-a-board", + "form": "a lotería board, with a numbered image grid, compact labels, and call-and-response progression", + "lineage": "Games & play", + "tags": [ + "rule-state", + "turn-sequence", + "outcome-feedback" + ] + }, + { + "id": "games-play-tarot-spread", + "form": "a tarot spread, with card positions and a reading order", + "lineage": "Games & play", + "tags": [ + "rule-state", + "turn-sequence", + "outcome-feedback" + ] + }, + { + "id": "games-play-board-game-s-rulebook", + "form": "a board game's rulebook, with setup diagrams and turn order", + "lineage": "Games & play", + "tags": [ + "rule-state", + "turn-sequence", + "outcome-feedback" + ] + }, + { + "id": "games-play-crossword-page", + "form": "a crossword page, with grid, clues across and down, and a setter's note", + "lineage": "Games & play", + "tags": [ + "rule-state", + "turn-sequence", + "outcome-feedback" + ] + }, + { + "id": "games-play-kanban-board", + "form": "a kanban board, with staged columns, movable work cards, and explicit capacity limits", + "lineage": "Games & play", + "tags": [ + "rule-state", + "turn-sequence", + "outcome-feedback" + ] + }, + { + "id": "games-play-perpetual-calendar", + "form": "a perpetual calendar, with nested time scales, repeating cycles, and movable indicators", + "lineage": "Games & play", + "tags": [ + "rule-state", + "turn-sequence", + "outcome-feedback" + ] + }, + { + "id": "games-play-chess-notation-sheet", + "form": "a chess notation sheet, with paired move columns, board coordinates, and capture notation", + "lineage": "strategy games", + "tags": [ + "turns", + "coordinates", + "evaluation" + ] + }, + { + "id": "games-play-live-digital-system-modeled-on-a-chess-notation-shee", + "form": "a live digital system modeled on a chess notation sheet, with capture notation, evaluation symbols, and result field", + "lineage": "strategy games; native digital", + "tags": [ + "turns", + "coordinates", + "evaluation" + ] + }, + { + "id": "games-play-shared-participatory-system-modeled-on-a-chess-notat", + "form": "a shared participatory system modeled on a chess notation sheet, with result field, paired move columns, and evaluation symbols", + "lineage": "strategy games; participatory", + "tags": [ + "turns", + "coordinates", + "evaluation" + ] + }, + { + "id": "games-play-grid-game-match-record", + "form": "a grid-game match record, with numbered move sequence, board coordinates, and capture markers", + "lineage": "strategy games", + "tags": [ + "grid", + "sequence", + "territory" + ] + }, + { + "id": "games-play-live-digital-system-modeled-on-a-grid-game-match-rec", + "form": "a live digital system modeled on a grid-game match record, with capture markers, territory estimates, and turn annotations", + "lineage": "strategy games; native digital", + "tags": [ + "grid", + "sequence", + "territory" + ] + }, + { + "id": "games-play-shared-participatory-system-modeled-on-a-grid-game-m", + "form": "a shared participatory system modeled on a grid-game match record, with turn annotations, numbered move sequence, and territory estimates", + "lineage": "strategy games; participatory", + "tags": [ + "grid", + "sequence", + "territory" + ] + }, + { + "id": "games-play-card-table-tableau", + "form": "a card-table tableau, with shared play area, private hand zones, and discard pile", + "lineage": "tabletop games", + "tags": [ + "zones", + "hands", + "turns" + ] + }, + { + "id": "games-play-live-digital-system-modeled-on-a-card-table-tableau", + "form": "a live digital system modeled on a card-table tableau, with discard pile, turn marker, and scoring area", + "lineage": "tabletop games; native digital", + "tags": [ + "zones", + "hands", + "turns" + ] + }, + { + "id": "games-play-shared-participatory-system-modeled-on-a-card-table", + "form": "a shared participatory system modeled on a card-table tableau, with scoring area, shared play area, and turn marker", + "lineage": "tabletop games; participatory", + "tags": [ + "zones", + "hands", + "turns" + ] + }, + { + "id": "games-play-tabletop-rulebook", + "form": "a tabletop rulebook, with component inventory, setup diagrams, and turn structure", + "lineage": "tabletop games", + "tags": [ + "setup", + "sequence", + "examples" + ] + }, + { + "id": "games-play-live-digital-system-modeled-on-a-tabletop-rulebook", + "form": "a live digital system modeled on a tabletop rulebook, with turn structure, illustrated examples, and edge-case glossary", + "lineage": "tabletop games; native digital", + "tags": [ + "setup", + "sequence", + "examples" + ] + }, + { + "id": "games-play-shared-participatory-system-modeled-on-a-tabletop-ru", + "form": "a shared participatory system modeled on a tabletop rulebook, with edge-case glossary, component inventory, and illustrated examples", + "lineage": "tabletop games; participatory", + "tags": [ + "setup", + "sequence", + "examples" + ] + }, + { + "id": "games-play-tournament-bracket-fd7b61d3", + "form": "a tournament bracket, with paired entrants, converging match paths, and round hierarchy", + "lineage": "competitive play", + "tags": [ + "rounds", + "paths", + "outcome" + ] + }, + { + "id": "games-play-live-digital-system-modeled-on-a-tournament-bracket", + "form": "a live digital system modeled on a tournament bracket, with round hierarchy, score fields, and single champion", + "lineage": "competitive play; native digital", + "tags": [ + "rounds", + "paths", + "outcome" + ] + }, + { + "id": "games-play-shared-participatory-system-modeled-on-a-tournament", + "form": "a shared participatory system modeled on a tournament bracket, with single champion, paired entrants, and score fields", + "lineage": "competitive play; participatory", + "tags": [ + "rounds", + "paths", + "outcome" + ] + }, + { + "id": "games-play-bingo-number-board", + "form": "a bingo number board, with number grid, called-number states, and card patterns", + "lineage": "social games", + "tags": [ + "grid", + "calls", + "patterns" + ] + }, + { + "id": "games-play-live-digital-system-modeled-on-a-bingo-number-board", + "form": "a live digital system modeled on a bingo number board, with card patterns, progress markers, and win verification", + "lineage": "social games; native digital", + "tags": [ + "grid", + "calls", + "patterns" + ] + }, + { + "id": "games-play-shared-participatory-system-modeled-on-a-bingo-numbe", + "form": "a shared participatory system modeled on a bingo number board, with win verification, number grid, and progress markers", + "lineage": "social games; participatory", + "tags": [ + "grid", + "calls", + "patterns" + ] + }, + { + "id": "games-play-crossword-puzzle-page", + "form": "a crossword puzzle page, with numbered letter grid, across clues, and down clues", + "lineage": "word games", + "tags": [ + "grid", + "clues", + "crossings" + ] + }, + { + "id": "games-play-live-digital-system-modeled-on-a-crossword-puzzle-pa", + "form": "a live digital system modeled on a crossword puzzle page, with down clues, crossing constraints, and completion checks", + "lineage": "word games; native digital", + "tags": [ + "grid", + "clues", + "crossings" + ] + }, + { + "id": "games-play-shared-participatory-system-modeled-on-a-crossword-p", + "form": "a shared participatory system modeled on a crossword puzzle page, with completion checks, numbered letter grid, and crossing constraints", + "lineage": "word games; participatory", + "tags": [ + "grid", + "clues", + "crossings" + ] + }, + { + "id": "games-play-escape-room-clue-wall", + "form": "an escape-room clue wall, with clue artifacts, visible relationship lines, and code slots", + "lineage": "puzzle games", + "tags": [ + "evidence", + "links", + "locks" + ] + }, + { + "id": "games-play-live-digital-system-modeled-on-an-escape-room-clue-w", + "form": "a live digital system modeled on an escape-room clue wall, with code slots, solved markers, and remaining-lock count", + "lineage": "puzzle games; native digital", + "tags": [ + "evidence", + "links", + "locks" + ] + }, + { + "id": "games-play-shared-participatory-system-modeled-on-an-escape-roo", + "form": "a shared participatory system modeled on an escape-room clue wall, with remaining-lock count, clue artifacts, and solved markers", + "lineage": "puzzle games; participatory", + "tags": [ + "evidence", + "links", + "locks" + ] + }, + { + "id": "games-play-procedural-dungeon-map", + "form": "a procedural dungeon map, with connected rooms, branching corridors, and hidden areas", + "lineage": "digital games", + "tags": [ + "rooms", + "paths", + "fog" + ] + }, + { + "id": "games-play-live-digital-system-modeled-on-a-procedural-dungeon", + "form": "a live digital system modeled on a procedural dungeon map, with hidden areas, fog-of-war reveal, and exit gates", + "lineage": "digital games; native digital", + "tags": [ + "rooms", + "paths", + "fog" + ] + }, + { + "id": "games-play-shared-participatory-system-modeled-on-a-procedural", + "form": "a shared participatory system modeled on a procedural dungeon map, with exit gates, connected rooms, and fog-of-war reveal", + "lineage": "digital games; participatory", + "tags": [ + "rooms", + "paths", + "fog" + ] + }, + { + "id": "games-play-technology-progression-tree", + "form": "a technology progression tree, with unlock nodes, prerequisite edges, and parallel branches", + "lineage": "strategy games", + "tags": [ + "dependencies", + "branches", + "unlocks" + ] + }, + { + "id": "games-play-live-digital-system-modeled-on-a-technology-progress", + "form": "a live digital system modeled on a technology progression tree, with parallel branches, resource costs, and completed states", + "lineage": "strategy games; native digital", + "tags": [ + "dependencies", + "branches", + "unlocks" + ] + }, + { + "id": "games-play-shared-participatory-system-modeled-on-a-technology", + "form": "a shared participatory system modeled on a technology progression tree, with completed states, unlock nodes, and resource costs", + "lineage": "strategy games; participatory", + "tags": [ + "dependencies", + "branches", + "unlocks" + ] + }, + { + "id": "games-play-quest-log", + "form": "a quest log, with quest groups, ordered objectives, and location hints", + "lineage": "adventure games", + "tags": [ + "objectives", + "status", + "rewards" + ] + }, + { + "id": "games-play-live-digital-system-modeled-on-a-quest-log", + "form": "a live digital system modeled on a quest log, with location hints, completion states, and reward previews", + "lineage": "adventure games; native digital", + "tags": [ + "objectives", + "status", + "rewards" + ] + }, + { + "id": "games-play-shared-participatory-system-modeled-on-a-quest-log", + "form": "a shared participatory system modeled on a quest log, with reward previews, quest groups, and completion states", + "lineage": "adventure games; participatory", + "tags": [ + "objectives", + "status", + "rewards" + ] + }, + { + "id": "games-play-inventory-grid", + "form": "an inventory grid, with fixed item slots, stack counts, and equipment zones", + "lineage": "digital games", + "tags": [ + "slots", + "items", + "capacity" + ] + }, + { + "id": "games-play-live-digital-system-modeled-on-an-inventory-grid", + "form": "a live digital system modeled on an inventory grid, with equipment zones, weight capacity, and drag-and-drop moves", + "lineage": "digital games; native digital", + "tags": [ + "slots", + "items", + "capacity" + ] + }, + { + "id": "games-play-shared-participatory-system-modeled-on-an-inventory", + "form": "a shared participatory system modeled on an inventory grid, with drag-and-drop moves, fixed item slots, and weight capacity", + "lineage": "digital games; participatory", + "tags": [ + "slots", + "items", + "capacity" + ] + }, + { + "id": "games-play-role-playing-character-sheet", + "form": "a role-playing character sheet, with core attributes, derived statistics, and resource trackers", + "lineage": "role-playing games", + "tags": [ + "attributes", + "resources", + "history" + ] + }, + { + "id": "games-play-live-digital-system-modeled-on-a-role-playing-charac", + "form": "a live digital system modeled on a role-playing character sheet, with resource trackers, equipment lists, and advancement notes", + "lineage": "role-playing games; native digital", + "tags": [ + "attributes", + "resources", + "history" + ] + }, + { + "id": "games-play-shared-participatory-system-modeled-on-a-role-playin", + "form": "a shared participatory system modeled on a role-playing character sheet, with advancement notes, core attributes, and equipment lists", + "lineage": "role-playing games; participatory", + "tags": [ + "attributes", + "resources", + "history" + ] + }, + { + "id": "games-play-tabletop-turn-tracker", + "form": "a tabletop turn tracker, with ordered participant markers, round count, and timed effects", + "lineage": "tabletop games", + "tags": [ + "order", + "rounds", + "effects" + ] + }, + { + "id": "games-play-live-digital-system-modeled-on-a-tabletop-turn-track", + "form": "a live digital system modeled on a tabletop turn tracker, with timed effects, active-turn focus, and skip and delay states", + "lineage": "tabletop games; native digital", + "tags": [ + "order", + "rounds", + "effects" + ] + }, + { + "id": "games-play-shared-participatory-system-modeled-on-a-tabletop-tu", + "form": "a shared participatory system modeled on a tabletop turn tracker, with skip and delay states, ordered participant markers, and active-turn focus", + "lineage": "tabletop games; participatory", + "tags": [ + "order", + "rounds", + "effects" + ] + }, + { + "id": "games-play-hex-based-campaign-map", + "form": "a hex-based campaign map, with hexagonal cells, terrain codes, and movement ranges", + "lineage": "strategy games", + "tags": [ + "tiles", + "range", + "control" + ] + }, + { + "id": "games-play-live-digital-system-modeled-on-a-hex-based-campaign", + "form": "a live digital system modeled on a hex-based campaign map, with movement ranges, control markers, and hidden-information overlays", + "lineage": "strategy games; native digital", + "tags": [ + "tiles", + "range", + "control" + ] + }, + { + "id": "games-play-shared-participatory-system-modeled-on-a-hex-based-c", + "form": "a shared participatory system modeled on a hex-based campaign map, with hidden-information overlays, hexagonal cells, and control markers", + "lineage": "strategy games; participatory", + "tags": [ + "tiles", + "range", + "control" + ] + }, + { + "id": "games-play-score-attack-leaderboard", + "form": "a score-attack leaderboard, with ranked entries, score totals, and run metadata", + "lineage": "arcade games", + "tags": [ + "ranking", + "scores", + "runs" + ] + }, + { + "id": "games-play-live-digital-system-modeled-on-a-score-attack-leader", + "form": "a live digital system modeled on a score-attack leaderboard, with run metadata, personal-best highlight, and season reset", + "lineage": "arcade games; native digital", + "tags": [ + "ranking", + "scores", + "runs" + ] + }, + { + "id": "games-play-shared-participatory-system-modeled-on-a-score-attac", + "form": "a shared participatory system modeled on a score-attack leaderboard, with season reset, ranked entries, and personal-best highlight", + "lineage": "arcade games; participatory", + "tags": [ + "ranking", + "scores", + "runs" + ] + }, + { + "id": "games-play-rhythm-game-note-highway", + "form": "a rhythm-game note highway, with parallel input lanes, approaching note markers, and timing line", + "lineage": "music games", + "tags": [ + "lanes", + "timing", + "feedback" + ] + }, + { + "id": "games-play-live-digital-system-modeled-on-a-rhythm-game-note-hi", + "form": "a live digital system modeled on a rhythm-game note highway, with timing line, combo counter, and accuracy feedback", + "lineage": "music games; native digital", + "tags": [ + "lanes", + "timing", + "feedback" + ] + }, + { + "id": "games-play-shared-participatory-system-modeled-on-a-rhythm-game", + "form": "a shared participatory system modeled on a rhythm-game note highway, with accuracy feedback, parallel input lanes, and combo counter", + "lineage": "music games; participatory", + "tags": [ + "lanes", + "timing", + "feedback" + ] + }, + { + "id": "games-play-pinball-playfield", + "form": "a pinball playfield, with physical target zones, routed ball paths, and mode lights", + "lineage": "mechanical games", + "tags": [ + "zones", + "paths", + "multipliers" + ] + }, + { + "id": "games-play-live-digital-system-modeled-on-a-pinball-playfield", + "form": "a live digital system modeled on a pinball playfield, with mode lights, score multipliers, and drain recovery", + "lineage": "mechanical games; native digital", + "tags": [ + "zones", + "paths", + "multipliers" + ] + }, + { + "id": "games-play-shared-participatory-system-modeled-on-a-pinball-pla", + "form": "a shared participatory system modeled on a pinball playfield, with drain recovery, physical target zones, and score multipliers", + "lineage": "mechanical games; participatory", + "tags": [ + "zones", + "paths", + "multipliers" + ] + } + ] + }, + { + "id": "education-learning", + "label": "Education & learning", + "description": "Instruction and practice systems with scaffolding, demonstration, assessment, and reflection.", + "concepts": [ + { + "id": "education-learning-textbook-chapter", + "form": "a textbook chapter, with learning-objective opener, section hierarchy, and worked examples", + "lineage": "formal education", + "tags": [ + "hierarchy", + "examples", + "review" + ] + }, + { + "id": "education-learning-live-digital-system-modeled-on-a-textbook-chapter", + "form": "a live digital system modeled on a textbook chapter, with worked examples, margin definitions, and review questions", + "lineage": "formal education; native digital", + "tags": [ + "hierarchy", + "examples", + "review" + ] + }, + { + "id": "education-learning-shared-participatory-system-modeled-on-a-textbook-ch", + "form": "a shared participatory system modeled on a textbook chapter, with review questions, learning-objective opener, and margin definitions", + "lineage": "formal education; participatory", + "tags": [ + "hierarchy", + "examples", + "review" + ] + }, + { + "id": "education-learning-flashcard-deck", + "form": "a flashcard deck, with question fronts, answer backs, and difficulty ratings", + "lineage": "study tools", + "tags": [ + "prompt", + "recall", + "spaced" + ] + }, + { + "id": "education-learning-live-digital-system-modeled-on-a-flashcard-deck", + "form": "a live digital system modeled on a flashcard deck, with difficulty ratings, review intervals, and mastery piles", + "lineage": "study tools; native digital", + "tags": [ + "prompt", + "recall", + "spaced" + ] + }, + { + "id": "education-learning-shared-participatory-system-modeled-on-a-flashcard-d", + "form": "a shared participatory system modeled on a flashcard deck, with mastery piles, question fronts, and review intervals", + "lineage": "study tools; participatory", + "tags": [ + "prompt", + "recall", + "spaced" + ] + }, + { + "id": "education-learning-course-syllabus", + "form": "a course syllabus, with course objectives, weekly schedule, and assignment weights", + "lineage": "formal education", + "tags": [ + "schedule", + "expectations", + "assessment" + ] + }, + { + "id": "education-learning-live-digital-system-modeled-on-a-course-syllabus", + "form": "a live digital system modeled on a course syllabus, with assignment weights, policy sections, and resource list", + "lineage": "formal education; native digital", + "tags": [ + "schedule", + "expectations", + "assessment" + ] + }, + { + "id": "education-learning-shared-participatory-system-modeled-on-a-course-syll", + "form": "a shared participatory system modeled on a course syllabus, with resource list, course objectives, and policy sections", + "lineage": "formal education; participatory", + "tags": [ + "schedule", + "expectations", + "assessment" + ] + }, + { + "id": "education-learning-teacher-lesson-plan", + "form": "a teacher lesson plan, with lesson objectives, timed activity phases, and material requirements", + "lineage": "classroom practice", + "tags": [ + "sequence", + "timing", + "reflection" + ] + }, + { + "id": "education-learning-live-digital-system-modeled-on-a-teacher-lesson-plan", + "form": "a live digital system modeled on a teacher lesson plan, with material requirements, assessment checks, and reflection notes", + "lineage": "classroom practice; native digital", + "tags": [ + "sequence", + "timing", + "reflection" + ] + }, + { + "id": "education-learning-shared-participatory-system-modeled-on-a-teacher-les", + "form": "a shared participatory system modeled on a teacher lesson plan, with reflection notes, lesson objectives, and assessment checks", + "lineage": "classroom practice; participatory", + "tags": [ + "sequence", + "timing", + "reflection" + ] + }, + { + "id": "education-learning-classroom-blackboard", + "form": "a classroom blackboard, with zoned writing areas, progressive reveal, and worked steps", + "lineage": "classroom practice", + "tags": [ + "space", + "sequence", + "emphasis" + ] + }, + { + "id": "education-learning-live-digital-system-modeled-on-a-classroom-blackboar", + "form": "a live digital system modeled on a classroom blackboard, with worked steps, boxed takeaways, and erased history traces", + "lineage": "classroom practice; native digital", + "tags": [ + "space", + "sequence", + "emphasis" + ] + }, + { + "id": "education-learning-shared-participatory-system-modeled-on-a-classroom-b", + "form": "a shared participatory system modeled on a classroom blackboard, with erased history traces, zoned writing areas, and boxed takeaways", + "lineage": "classroom practice; participatory", + "tags": [ + "space", + "sequence", + "emphasis" + ] + }, + { + "id": "education-learning-laboratory-practical-sheet", + "form": "a laboratory practical sheet, with equipment inventory, ordered procedure, and observation fields", + "lineage": "science education", + "tags": [ + "procedure", + "observation", + "safety" + ] + }, + { + "id": "education-learning-live-digital-system-modeled-on-a-laboratory-practica", + "form": "a live digital system modeled on a laboratory practical sheet, with observation fields, safety cautions, and conclusion prompts", + "lineage": "science education; native digital", + "tags": [ + "procedure", + "observation", + "safety" + ] + }, + { + "id": "education-learning-shared-participatory-system-modeled-on-a-laboratory", + "form": "a shared participatory system modeled on a laboratory practical sheet, with conclusion prompts, equipment inventory, and safety cautions", + "lineage": "science education; participatory", + "tags": [ + "procedure", + "observation", + "safety" + ] + }, + { + "id": "education-learning-student-workbook", + "form": "a student workbook, with short instruction blocks, graduated exercises, and answer spaces", + "lineage": "formal education", + "tags": [ + "practice", + "progression", + "feedback" + ] + }, + { + "id": "education-learning-live-digital-system-modeled-on-a-student-workbook", + "form": "a live digital system modeled on a student workbook, with answer spaces, self-check boxes, and unit reviews", + "lineage": "formal education; native digital", + "tags": [ + "practice", + "progression", + "feedback" + ] + }, + { + "id": "education-learning-shared-participatory-system-modeled-on-a-student-wor", + "form": "a shared participatory system modeled on a student workbook, with unit reviews, short instruction blocks, and self-check boxes", + "lineage": "formal education; participatory", + "tags": [ + "practice", + "progression", + "feedback" + ] + }, + { + "id": "education-learning-seminar-reading-list", + "form": "a seminar reading list, with thematic groupings, ordered readings, and difficulty cues", + "lineage": "higher education", + "tags": [ + "sequence", + "themes", + "annotations" + ] + }, + { + "id": "education-learning-live-digital-system-modeled-on-a-seminar-reading-lis", + "form": "a live digital system modeled on a seminar reading list, with difficulty cues, discussion prompts, and optional extensions", + "lineage": "higher education; native digital", + "tags": [ + "sequence", + "themes", + "annotations" + ] + }, + { + "id": "education-learning-shared-participatory-system-modeled-on-a-seminar-rea", + "form": "a shared participatory system modeled on a seminar reading list, with optional extensions, thematic groupings, and discussion prompts", + "lineage": "higher education; participatory", + "tags": [ + "sequence", + "themes", + "annotations" + ] + }, + { + "id": "education-learning-concept-map", + "form": "a concept map, with concept nodes, labeled relationships, and hierarchical levels", + "lineage": "learning science", + "tags": [ + "nodes", + "relations", + "hierarchy" + ] + }, + { + "id": "education-learning-live-digital-system-modeled-on-a-concept-map", + "form": "a live digital system modeled on a concept map, with hierarchical levels, cross-links, and unresolved gaps", + "lineage": "learning science; native digital", + "tags": [ + "nodes", + "relations", + "hierarchy" + ] + }, + { + "id": "education-learning-shared-participatory-system-modeled-on-a-concept-map", + "form": "a shared participatory system modeled on a concept map, with unresolved gaps, concept nodes, and cross-links", + "lineage": "learning science; participatory", + "tags": [ + "nodes", + "relations", + "hierarchy" + ] + }, + { + "id": "education-learning-assessment-rubric", + "form": "an assessment rubric, with criterion rows, performance-level columns, and behavioral descriptors", + "lineage": "education assessment", + "tags": [ + "criteria", + "levels", + "evidence" + ] + }, + { + "id": "education-learning-live-digital-system-modeled-on-an-assessment-rubric", + "form": "a live digital system modeled on an assessment rubric, with behavioral descriptors, weighting fields, and feedback notes", + "lineage": "education assessment; native digital", + "tags": [ + "criteria", + "levels", + "evidence" + ] + }, + { + "id": "education-learning-shared-participatory-system-modeled-on-an-assessment", + "form": "a shared participatory system modeled on an assessment rubric, with feedback notes, criterion rows, and weighting fields", + "lineage": "education assessment; participatory", + "tags": [ + "criteria", + "levels", + "evidence" + ] + }, + { + "id": "education-learning-student-progress-report", + "form": "a student progress report, with subject sections, achievement levels, and trend indicators", + "lineage": "education reporting", + "tags": [ + "subjects", + "trends", + "actions" + ] + }, + { + "id": "education-learning-live-digital-system-modeled-on-a-student-progress-re", + "form": "a live digital system modeled on a student progress report, with trend indicators, teacher comments, and next-step actions", + "lineage": "education reporting; native digital", + "tags": [ + "subjects", + "trends", + "actions" + ] + }, + { + "id": "education-learning-shared-participatory-system-modeled-on-a-student-pro", + "form": "a shared participatory system modeled on a student progress report, with next-step actions, subject sections, and teacher comments", + "lineage": "education reporting; participatory", + "tags": [ + "subjects", + "trends", + "actions" + ] + }, + { + "id": "education-learning-library-call-number-slip", + "form": "a library call-number slip, with classification code, author marker, and shelf location", + "lineage": "library education", + "tags": [ + "classification", + "location", + "sequence" + ] + }, + { + "id": "education-learning-live-digital-system-modeled-on-a-library-call-number", + "form": "a live digital system modeled on a library call-number slip, with shelf location, edition details, and retrieval sequence", + "lineage": "library education; native digital", + "tags": [ + "classification", + "location", + "sequence" + ] + }, + { + "id": "education-learning-shared-participatory-system-modeled-on-a-library-cal", + "form": "a shared participatory system modeled on a library call-number slip, with retrieval sequence, classification code, and edition details", + "lineage": "library education; participatory", + "tags": [ + "classification", + "location", + "sequence" + ] + }, + { + "id": "education-learning-seminar-discussion-circle", + "form": "a seminar discussion circle, with shared prompt, speaking turns, and evidence references", + "lineage": "participatory education", + "tags": [ + "turns", + "prompts", + "synthesis" + ] + }, + { + "id": "education-learning-live-digital-system-modeled-on-a-seminar-discussion", + "form": "a live digital system modeled on a seminar discussion circle, with evidence references, challenge rounds, and closing synthesis", + "lineage": "participatory education; native digital", + "tags": [ + "turns", + "prompts", + "synthesis" + ] + }, + { + "id": "education-learning-shared-participatory-system-modeled-on-a-seminar-dis", + "form": "a shared participatory system modeled on a seminar discussion circle, with closing synthesis, shared prompt, and challenge rounds", + "lineage": "participatory education; participatory", + "tags": [ + "turns", + "prompts", + "synthesis" + ] + }, + { + "id": "education-learning-studio-critique-wall", + "form": "a studio critique wall, with displayed work sequence, author statements, and sticky-note feedback", + "lineage": "design education", + "tags": [ + "artifacts", + "feedback", + "iteration" + ] + }, + { + "id": "education-learning-live-digital-system-modeled-on-a-studio-critique-wal", + "form": "a live digital system modeled on a studio critique wall, with sticky-note feedback, theme clusters, and revision commitments", + "lineage": "design education; native digital", + "tags": [ + "artifacts", + "feedback", + "iteration" + ] + }, + { + "id": "education-learning-shared-participatory-system-modeled-on-a-studio-crit", + "form": "a shared participatory system modeled on a studio critique wall, with revision commitments, displayed work sequence, and theme clusters", + "lineage": "design education; participatory", + "tags": [ + "artifacts", + "feedback", + "iteration" + ] + }, + { + "id": "education-learning-workshop-station-rotation", + "form": "a workshop station rotation, with numbered activity stations, group assignments, and rotation timer", + "lineage": "hands-on education", + "tags": [ + "stations", + "timing", + "groups" + ] + }, + { + "id": "education-learning-live-digital-system-modeled-on-a-workshop-station-ro", + "form": "a live digital system modeled on a workshop station rotation, with rotation timer, material checklists, and completion stamps", + "lineage": "hands-on education; native digital", + "tags": [ + "stations", + "timing", + "groups" + ] + }, + { + "id": "education-learning-shared-participatory-system-modeled-on-a-workshop-st", + "form": "a shared participatory system modeled on a workshop station rotation, with completion stamps, numbered activity stations, and material checklists", + "lineage": "hands-on education; participatory", + "tags": [ + "stations", + "timing", + "groups" + ] + }, + { + "id": "education-learning-online-course-module-page", + "form": "an online course module page, with module sequence, completion indicators, and lesson resources", + "lineage": "digital education", + "tags": [ + "modules", + "progress", + "resources" + ] + }, + { + "id": "education-learning-live-digital-system-modeled-on-an-online-course-modu", + "form": "a live digital system modeled on an online course module page, with lesson resources, discussion links, and assessment gates", + "lineage": "digital education; native digital", + "tags": [ + "modules", + "progress", + "resources" + ] + }, + { + "id": "education-learning-shared-participatory-system-modeled-on-an-online-cou", + "form": "a shared participatory system modeled on an online course module page, with assessment gates, module sequence, and discussion links", + "lineage": "digital education; participatory", + "tags": [ + "modules", + "progress", + "resources" + ] + }, + { + "id": "education-learning-branching-quiz-flow", + "form": "a branching quiz flow, with one-question focus, answer branches, and immediate feedback", + "lineage": "digital assessment", + "tags": [ + "questions", + "branches", + "feedback" + ] + }, + { + "id": "education-learning-live-digital-system-modeled-on-a-branching-quiz-flow", + "form": "a live digital system modeled on a branching quiz flow, with immediate feedback, progress indicator, and review path", + "lineage": "digital assessment; native digital", + "tags": [ + "questions", + "branches", + "feedback" + ] + }, + { + "id": "education-learning-shared-participatory-system-modeled-on-a-branching-q", + "form": "a shared participatory system modeled on a branching quiz flow, with review path, one-question focus, and progress indicator", + "lineage": "digital assessment; participatory", + "tags": [ + "questions", + "branches", + "feedback" + ] + }, + { + "id": "education-learning-adaptive-practice-ladder", + "form": "an adaptive practice ladder, with skill levels, mastery thresholds, and difficulty adjustment", + "lineage": "learning software", + "tags": [ + "levels", + "mastery", + "remediation" + ] + }, + { + "id": "education-learning-live-digital-system-modeled-on-an-adaptive-practice", + "form": "a live digital system modeled on an adaptive practice ladder, with difficulty adjustment, remediation loops, and unlock conditions", + "lineage": "learning software; native digital", + "tags": [ + "levels", + "mastery", + "remediation" + ] + }, + { + "id": "education-learning-shared-participatory-system-modeled-on-an-adaptive-p", + "form": "a shared participatory system modeled on an adaptive practice ladder, with unlock conditions, skill levels, and remediation loops", + "lineage": "learning software; participatory", + "tags": [ + "levels", + "mastery", + "remediation" + ] + }, + { + "id": "education-learning-peer-review-worksheet", + "form": "a peer-review worksheet, with review criteria, evidence prompts, and strength observations", + "lineage": "collaborative learning", + "tags": [ + "criteria", + "evidence", + "revision" + ] + }, + { + "id": "education-learning-live-digital-system-modeled-on-a-peer-review-workshe", + "form": "a live digital system modeled on a peer-review worksheet, with strength observations, revision requests, and author response", + "lineage": "collaborative learning; native digital", + "tags": [ + "criteria", + "evidence", + "revision" + ] + }, + { + "id": "education-learning-shared-participatory-system-modeled-on-a-peer-review", + "form": "a shared participatory system modeled on a peer-review worksheet, with author response, review criteria, and revision requests", + "lineage": "collaborative learning; participatory", + "tags": [ + "criteria", + "evidence", + "revision" + ] + }, + { + "id": "education-learning-field-trip-itinerary", + "form": "a field-trip itinerary, with timed stops, route sequence, and observation tasks", + "lineage": "experiential learning", + "tags": [ + "schedule", + "places", + "tasks" + ] + }, + { + "id": "education-learning-live-digital-system-modeled-on-a-field-trip-itinerar", + "form": "a live digital system modeled on a field-trip itinerary, with observation tasks, meeting points, and contingency notes", + "lineage": "experiential learning; native digital", + "tags": [ + "schedule", + "places", + "tasks" + ] + }, + { + "id": "education-learning-shared-participatory-system-modeled-on-a-field-trip", + "form": "a shared participatory system modeled on a field-trip itinerary, with contingency notes, timed stops, and meeting points", + "lineage": "experiential learning; participatory", + "tags": [ + "schedule", + "places", + "tasks" + ] + }, + { + "id": "education-learning-museum-learning-trail", + "form": "a museum learning trail, with numbered exhibit stations, look-closely prompts, and choice routes", + "lineage": "informal education", + "tags": [ + "stations", + "prompts", + "reflection" + ] + }, + { + "id": "education-learning-live-digital-system-modeled-on-a-museum-learning-tra", + "form": "a live digital system modeled on a museum learning trail, with choice routes, collection links, and reflection checkpoints", + "lineage": "informal education; native digital", + "tags": [ + "stations", + "prompts", + "reflection" + ] + }, + { + "id": "education-learning-shared-participatory-system-modeled-on-a-museum-lear", + "form": "a shared participatory system modeled on a museum learning trail, with reflection checkpoints, numbered exhibit stations, and collection links", + "lineage": "informal education; participatory", + "tags": [ + "stations", + "prompts", + "reflection" + ] + }, + { + "id": "education-learning-apprenticeship-logbook", + "form": "an apprenticeship logbook, with skill categories, dated practice entries, and evidence attachments", + "lineage": "vocational learning", + "tags": [ + "tasks", + "evidence", + "signoff" + ] + } + ] + }, + { + "id": "exhibition-museum", + "label": "Exhibition & museum", + "description": "Curatorial systems that sequence objects, interpretation, spatial discovery, and multiple depths of reading.", + "concepts": [ + { + "id": "exhibition-museum-entrance-interpretive-panel", + "form": "an entrance interpretive panel, with numbered thresholds, a dominant orientation key, and progressive disclosure labels", + "lineage": "Builds from the legible conventions of the entrance interpretive panel.", + "tags": [ + "sequence", + "orientation", + "disclosure" + ] + }, + { + "id": "exhibition-museum-entrance-gallery-map", + "form": "an entrance gallery map, with room-coded bands, cross-reference markers, and a persistent location legend", + "lineage": "Builds from the legible conventions of the entrance gallery map.", + "tags": [ + "zoning", + "cross-reference", + "wayfinding" + ] + }, + { + "id": "exhibition-museum-entrance-label-rail", + "form": "an entrance label rail, with object clusters, short-to-long reading tiers, and adjacent comparison captions", + "lineage": "Builds from the legible conventions of the entrance label rail.", + "tags": [ + "clustering", + "hierarchy", + "comparison" + ] + }, + { + "id": "exhibition-museum-entrance-display-case", + "form": "an entrance display case, with a dated spine, branching event callouts, and period summary stops", + "lineage": "Builds from the legible conventions of the entrance display case.", + "tags": [ + "timeline", + "branching", + "summaries" + ] + }, + { + "id": "exhibition-museum-entrance-audio-guide-index", + "form": "an entrance audio-guide index, with paired viewing positions, shared attribute keys, and difference annotations", + "lineage": "Builds from the legible conventions of the entrance audio-guide index.", + "tags": [ + "pairing", + "attributes", + "annotation" + ] + }, + { + "id": "exhibition-museum-entrance-study-room-tray", + "form": "an entrance study-room tray, with movable specimen groupings, catalog identifiers, and handling-status cues", + "lineage": "Builds from the legible conventions of the entrance study-room tray.", + "tags": [ + "modularity", + "indexing", + "status" + ] + }, + { + "id": "exhibition-museum-orientation-interpretive-panel", + "form": "an orientation interpretive panel, with layered condition diagrams, before-and-after states, and treatment decision gates", + "lineage": "Builds from the legible conventions of the orientation interpretive panel.", + "tags": [ + "layers", + "states", + "gates" + ] + }, + { + "id": "exhibition-museum-orientation-gallery-map", + "form": "an orientation gallery map, with indexed drawers, provenance trails, and related-record links", + "lineage": "Builds from the legible conventions of the orientation gallery map.", + "tags": [ + "indexing", + "provenance", + "links" + ] + }, + { + "id": "exhibition-museum-orientation-label-rail", + "form": "an orientation label rail, with a focal artifact, radiating evidence nodes, and return-to-object anchors", + "lineage": "Builds from the legible conventions of the orientation label rail.", + "tags": [ + "focus", + "evidence", + "anchors" + ] + }, + { + "id": "exhibition-museum-orientation-display-case", + "form": "an orientation display case, with stepwise process stations, material samples, and tool-to-result mappings", + "lineage": "Builds from the legible conventions of the orientation display case.", + "tags": [ + "process", + "samples", + "mapping" + ] + }, + { + "id": "exhibition-museum-orientation-audio-guide-index", + "form": "an orientation audio-guide index, with prompted response slots, visible contribution totals, and curated synthesis panels", + "lineage": "Builds from the legible conventions of the orientation audio-guide index.", + "tags": [ + "participation", + "aggregation", + "synthesis" + ] + }, + { + "id": "exhibition-museum-orientation-study-room-tray", + "form": "an orientation study-room tray, with recap checkpoints, takeaway tokens, and a clear re-entry route", + "lineage": "Builds from the legible conventions of the orientation study-room tray.", + "tags": [ + "recap", + "takeaway", + "reentry" + ] + }, + { + "id": "exhibition-museum-thematic-interpretive-panel", + "form": "a thematic interpretive panel, with numbered thresholds, a dominant orientation key, and progressive disclosure labels", + "lineage": "Builds from the legible conventions of the thematic interpretive panel.", + "tags": [ + "sequence", + "orientation", + "disclosure" + ] + }, + { + "id": "exhibition-museum-thematic-gallery-map", + "form": "a thematic gallery map, with room-coded bands, cross-reference markers, and a persistent location legend", + "lineage": "Builds from the legible conventions of the thematic gallery map.", + "tags": [ + "zoning", + "cross-reference", + "wayfinding" + ] + }, + { + "id": "exhibition-museum-thematic-label-rail", + "form": "a thematic label rail, with object clusters, short-to-long reading tiers, and adjacent comparison captions", + "lineage": "Builds from the legible conventions of the thematic label rail.", + "tags": [ + "clustering", + "hierarchy", + "comparison" + ] + }, + { + "id": "exhibition-museum-thematic-display-case", + "form": "a thematic display case, with a dated spine, branching event callouts, and period summary stops", + "lineage": "Builds from the legible conventions of the thematic display case.", + "tags": [ + "timeline", + "branching", + "summaries" + ] + }, + { + "id": "exhibition-museum-thematic-audio-guide-index", + "form": "a thematic audio-guide index, with paired viewing positions, shared attribute keys, and difference annotations", + "lineage": "Builds from the legible conventions of the thematic audio-guide index.", + "tags": [ + "pairing", + "attributes", + "annotation" + ] + }, + { + "id": "exhibition-museum-thematic-study-room-tray", + "form": "a thematic study-room tray, with movable specimen groupings, catalog identifiers, and handling-status cues", + "lineage": "Builds from the legible conventions of the thematic study-room tray.", + "tags": [ + "modularity", + "indexing", + "status" + ] + }, + { + "id": "exhibition-museum-chronological-interpretive-panel", + "form": "a chronological interpretive panel, with layered condition diagrams, before-and-after states, and treatment decision gates", + "lineage": "Builds from the legible conventions of the chronological interpretive panel.", + "tags": [ + "layers", + "states", + "gates" + ] + }, + { + "id": "exhibition-museum-chronological-gallery-map", + "form": "a chronological gallery map, with indexed drawers, provenance trails, and related-record links", + "lineage": "Builds from the legible conventions of the chronological gallery map.", + "tags": [ + "indexing", + "provenance", + "links" + ] + }, + { + "id": "exhibition-museum-chronological-label-rail", + "form": "a chronological label rail, with a focal artifact, radiating evidence nodes, and return-to-object anchors", + "lineage": "Builds from the legible conventions of the chronological label rail.", + "tags": [ + "focus", + "evidence", + "anchors" + ] + }, + { + "id": "exhibition-museum-chronological-display-case", + "form": "a chronological display case, with stepwise process stations, material samples, and tool-to-result mappings", + "lineage": "Builds from the legible conventions of the chronological display case.", + "tags": [ + "process", + "samples", + "mapping" + ] + }, + { + "id": "exhibition-museum-chronological-audio-guide-index", + "form": "a chronological audio-guide index, with prompted response slots, visible contribution totals, and curated synthesis panels", + "lineage": "Builds from the legible conventions of the chronological audio-guide index.", + "tags": [ + "participation", + "aggregation", + "synthesis" + ] + }, + { + "id": "exhibition-museum-chronological-study-room-tray", + "form": "a chronological study-room tray, with recap checkpoints, takeaway tokens, and a clear re-entry route", + "lineage": "Builds from the legible conventions of the chronological study-room tray.", + "tags": [ + "recap", + "takeaway", + "reentry" + ] + }, + { + "id": "exhibition-museum-comparative-interpretive-panel", + "form": "a comparative interpretive panel, with numbered thresholds, a dominant orientation key, and progressive disclosure labels", + "lineage": "Builds from the legible conventions of the comparative interpretive panel.", + "tags": [ + "sequence", + "orientation", + "disclosure" + ] + }, + { + "id": "exhibition-museum-comparative-gallery-map", + "form": "a comparative gallery map, with room-coded bands, cross-reference markers, and a persistent location legend", + "lineage": "Builds from the legible conventions of the comparative gallery map.", + "tags": [ + "zoning", + "cross-reference", + "wayfinding" + ] + }, + { + "id": "exhibition-museum-comparative-label-rail", + "form": "a comparative label rail, with object clusters, short-to-long reading tiers, and adjacent comparison captions", + "lineage": "Builds from the legible conventions of the comparative label rail.", + "tags": [ + "clustering", + "hierarchy", + "comparison" + ] + }, + { + "id": "exhibition-museum-comparative-display-case", + "form": "a comparative display case, with a dated spine, branching event callouts, and period summary stops", + "lineage": "Builds from the legible conventions of the comparative display case.", + "tags": [ + "timeline", + "branching", + "summaries" + ] + }, + { + "id": "exhibition-museum-comparative-audio-guide-index", + "form": "a comparative audio-guide index, with paired viewing positions, shared attribute keys, and difference annotations", + "lineage": "Builds from the legible conventions of the comparative audio-guide index.", + "tags": [ + "pairing", + "attributes", + "annotation" + ] + }, + { + "id": "exhibition-museum-comparative-study-room-tray", + "form": "a comparative study-room tray, with movable specimen groupings, catalog identifiers, and handling-status cues", + "lineage": "Builds from the legible conventions of the comparative study-room tray.", + "tags": [ + "modularity", + "indexing", + "status" + ] + }, + { + "id": "exhibition-museum-collection-study-interpretive-panel", + "form": "a collection study interpretive panel, with layered condition diagrams, before-and-after states, and treatment decision gates", + "lineage": "Builds from the legible conventions of the collection study interpretive panel.", + "tags": [ + "layers", + "states", + "gates" + ] + }, + { + "id": "exhibition-museum-collection-study-gallery-map", + "form": "a collection study gallery map, with indexed drawers, provenance trails, and related-record links", + "lineage": "Builds from the legible conventions of the collection study gallery map.", + "tags": [ + "indexing", + "provenance", + "links" + ] + }, + { + "id": "exhibition-museum-collection-study-label-rail", + "form": "a collection study label rail, with a focal artifact, radiating evidence nodes, and return-to-object anchors", + "lineage": "Builds from the legible conventions of the collection study label rail.", + "tags": [ + "focus", + "evidence", + "anchors" + ] + }, + { + "id": "exhibition-museum-collection-study-display-case", + "form": "a collection study display case, with stepwise process stations, material samples, and tool-to-result mappings", + "lineage": "Builds from the legible conventions of the collection study display case.", + "tags": [ + "process", + "samples", + "mapping" + ] + }, + { + "id": "exhibition-museum-collection-study-audio-guide-index", + "form": "a collection study audio-guide index, with prompted response slots, visible contribution totals, and curated synthesis panels", + "lineage": "Builds from the legible conventions of the collection study audio-guide index.", + "tags": [ + "participation", + "aggregation", + "synthesis" + ] + }, + { + "id": "exhibition-museum-collection-study-study-room-tray", + "form": "a collection study study-room tray, with recap checkpoints, takeaway tokens, and a clear re-entry route", + "lineage": "Builds from the legible conventions of the collection study study-room tray.", + "tags": [ + "recap", + "takeaway", + "reentry" + ] + }, + { + "id": "exhibition-museum-conservation-interpretive-panel", + "form": "a conservation interpretive panel, with numbered thresholds, a dominant orientation key, and progressive disclosure labels", + "lineage": "Builds from the legible conventions of the conservation interpretive panel.", + "tags": [ + "sequence", + "orientation", + "disclosure" + ] + }, + { + "id": "exhibition-museum-conservation-gallery-map", + "form": "a conservation gallery map, with room-coded bands, cross-reference markers, and a persistent location legend", + "lineage": "Builds from the legible conventions of the conservation gallery map.", + "tags": [ + "zoning", + "cross-reference", + "wayfinding" + ] + }, + { + "id": "exhibition-museum-conservation-label-rail", + "form": "a conservation label rail, with object clusters, short-to-long reading tiers, and adjacent comparison captions", + "lineage": "Builds from the legible conventions of the conservation label rail.", + "tags": [ + "clustering", + "hierarchy", + "comparison" + ] + }, + { + "id": "exhibition-museum-conservation-display-case", + "form": "a conservation display case, with a dated spine, branching event callouts, and period summary stops", + "lineage": "Builds from the legible conventions of the conservation display case.", + "tags": [ + "timeline", + "branching", + "summaries" + ] + }, + { + "id": "exhibition-museum-conservation-audio-guide-index", + "form": "a conservation audio-guide index, with paired viewing positions, shared attribute keys, and difference annotations", + "lineage": "Builds from the legible conventions of the conservation audio-guide index.", + "tags": [ + "pairing", + "attributes", + "annotation" + ] + }, + { + "id": "exhibition-museum-conservation-study-room-tray", + "form": "a conservation study-room tray, with movable specimen groupings, catalog identifiers, and handling-status cues", + "lineage": "Builds from the legible conventions of the conservation study-room tray.", + "tags": [ + "modularity", + "indexing", + "status" + ] + }, + { + "id": "exhibition-museum-archive-interpretive-panel", + "form": "an archive interpretive panel, with layered condition diagrams, before-and-after states, and treatment decision gates", + "lineage": "Builds from the legible conventions of the archive interpretive panel.", + "tags": [ + "layers", + "states", + "gates" + ] + }, + { + "id": "exhibition-museum-archive-gallery-map", + "form": "an archive gallery map, with indexed drawers, provenance trails, and related-record links", + "lineage": "Builds from the legible conventions of the archive gallery map.", + "tags": [ + "indexing", + "provenance", + "links" + ] + }, + { + "id": "exhibition-museum-archive-label-rail", + "form": "an archive label rail, with a focal artifact, radiating evidence nodes, and return-to-object anchors", + "lineage": "Builds from the legible conventions of the archive label rail.", + "tags": [ + "focus", + "evidence", + "anchors" + ] + }, + { + "id": "exhibition-museum-archive-display-case", + "form": "an archive display case, with stepwise process stations, material samples, and tool-to-result mappings", + "lineage": "Builds from the legible conventions of the archive display case.", + "tags": [ + "process", + "samples", + "mapping" + ] + }, + { + "id": "exhibition-museum-archive-audio-guide-index", + "form": "an archive audio-guide index, with prompted response slots, visible contribution totals, and curated synthesis panels", + "lineage": "Builds from the legible conventions of the archive audio-guide index.", + "tags": [ + "participation", + "aggregation", + "synthesis" + ] + }, + { + "id": "exhibition-museum-archive-study-room-tray", + "form": "an archive study-room tray, with recap checkpoints, takeaway tokens, and a clear re-entry route", + "lineage": "Builds from the legible conventions of the archive study-room tray.", + "tags": [ + "recap", + "takeaway", + "reentry" + ] + }, + { + "id": "exhibition-museum-object-story-interpretive-panel", + "form": "an object story interpretive panel, with numbered thresholds, a dominant orientation key, and progressive disclosure labels", + "lineage": "Builds from the legible conventions of the object story interpretive panel.", + "tags": [ + "sequence", + "orientation", + "disclosure" + ] + }, + { + "id": "exhibition-museum-object-story-gallery-map", + "form": "an object story gallery map, with room-coded bands, cross-reference markers, and a persistent location legend", + "lineage": "Builds from the legible conventions of the object story gallery map.", + "tags": [ + "zoning", + "cross-reference", + "wayfinding" + ] + }, + { + "id": "exhibition-museum-object-story-label-rail", + "form": "an object story label rail, with object clusters, short-to-long reading tiers, and adjacent comparison captions", + "lineage": "Builds from the legible conventions of the object story label rail.", + "tags": [ + "clustering", + "hierarchy", + "comparison" + ] + }, + { + "id": "exhibition-museum-object-story-display-case", + "form": "an object story display case, with a dated spine, branching event callouts, and period summary stops", + "lineage": "Builds from the legible conventions of the object story display case.", + "tags": [ + "timeline", + "branching", + "summaries" + ] + }, + { + "id": "exhibition-museum-object-story-audio-guide-index", + "form": "an object story audio-guide index, with paired viewing positions, shared attribute keys, and difference annotations", + "lineage": "Builds from the legible conventions of the object story audio-guide index.", + "tags": [ + "pairing", + "attributes", + "annotation" + ] + }, + { + "id": "exhibition-museum-object-story-study-room-tray", + "form": "an object story study-room tray, with movable specimen groupings, catalog identifiers, and handling-status cues", + "lineage": "Builds from the legible conventions of the object story study-room tray.", + "tags": [ + "modularity", + "indexing", + "status" + ] + }, + { + "id": "exhibition-museum-material-process-interpretive-panel", + "form": "a material process interpretive panel, with layered condition diagrams, before-and-after states, and treatment decision gates", + "lineage": "Builds from the legible conventions of the material process interpretive panel.", + "tags": [ + "layers", + "states", + "gates" + ] + }, + { + "id": "exhibition-museum-material-process-gallery-map", + "form": "a material process gallery map, with indexed drawers, provenance trails, and related-record links", + "lineage": "Builds from the legible conventions of the material process gallery map.", + "tags": [ + "indexing", + "provenance", + "links" + ] + }, + { + "id": "exhibition-museum-material-process-label-rail", + "form": "a material process label rail, with a focal artifact, radiating evidence nodes, and return-to-object anchors", + "lineage": "Builds from the legible conventions of the material process label rail.", + "tags": [ + "focus", + "evidence", + "anchors" + ] + }, + { + "id": "exhibition-museum-material-process-display-case", + "form": "a material process display case, with stepwise process stations, material samples, and tool-to-result mappings", + "lineage": "Builds from the legible conventions of the material process display case.", + "tags": [ + "process", + "samples", + "mapping" + ] + }, + { + "id": "exhibition-museum-material-process-audio-guide-index", + "form": "a material process audio-guide index, with prompted response slots, visible contribution totals, and curated synthesis panels", + "lineage": "Builds from the legible conventions of the material process audio-guide index.", + "tags": [ + "participation", + "aggregation", + "synthesis" + ] + }, + { + "id": "exhibition-museum-material-process-study-room-tray", + "form": "a material process study-room tray, with recap checkpoints, takeaway tokens, and a clear re-entry route", + "lineage": "Builds from the legible conventions of the material process study-room tray.", + "tags": [ + "recap", + "takeaway", + "reentry" + ] + }, + { + "id": "exhibition-museum-visitor-response-interpretive-panel", + "form": "a visitor response interpretive panel, with numbered thresholds, a dominant orientation key, and progressive disclosure labels", + "lineage": "Builds from the legible conventions of the visitor response interpretive panel.", + "tags": [ + "sequence", + "orientation", + "disclosure" + ] + }, + { + "id": "exhibition-museum-visitor-response-gallery-map", + "form": "a visitor response gallery map, with room-coded bands, cross-reference markers, and a persistent location legend", + "lineage": "Builds from the legible conventions of the visitor response gallery map.", + "tags": [ + "zoning", + "cross-reference", + "wayfinding" + ] + }, + { + "id": "exhibition-museum-visitor-response-label-rail", + "form": "a visitor response label rail, with object clusters, short-to-long reading tiers, and adjacent comparison captions", + "lineage": "Builds from the legible conventions of the visitor response label rail.", + "tags": [ + "clustering", + "hierarchy", + "comparison" + ] + }, + { + "id": "exhibition-museum-visitor-response-display-case", + "form": "a visitor response display case, with a dated spine, branching event callouts, and period summary stops", + "lineage": "Builds from the legible conventions of the visitor response display case.", + "tags": [ + "timeline", + "branching", + "summaries" + ] + } + ] + }, + { + "id": "placemaking-memorial", + "label": "Placemaking & memorial", + "description": "Public spatial forms organizing arrival, identity, collective memory, pause, and orientation.", + "concepts": [ + { + "id": "placemaking-memorial-civic-history-commemorative-names-wall", + "form": "a civic history commemorative names wall, with an ordered name field, searchable grouping cues, and accessible reading intervals", + "lineage": "Builds from the legible conventions of the civic history commemorative names wall.", + "tags": [ + "names", + "grouping", + "intervals" + ] + }, + { + "id": "placemaking-memorial-civic-history-interpretive-walking-route", + "form": "a civic history interpretive walking route, with numbered route stops, distance markers, and return-path directions", + "lineage": "Builds from the legible conventions of the civic history interpretive walking route.", + "tags": [ + "route", + "distance", + "return" + ] + }, + { + "id": "placemaking-memorial-civic-history-timeline-pavement", + "form": "a civic history timeline pavement, with dated ground bands, event markers, and present-day comparison points", + "lineage": "Builds from the legible conventions of the civic history timeline pavement.", + "tags": [ + "chronology", + "markers", + "comparison" + ] + }, + { + "id": "placemaking-memorial-civic-history-public-story-kiosk", + "form": "a civic history public story kiosk, with a shared orientation face, layered local accounts, and nearby-site pointers", + "lineage": "Builds from the legible conventions of the civic history public story kiosk.", + "tags": [ + "orientation", + "voices", + "pointers" + ] + }, + { + "id": "placemaking-memorial-civic-history-orientation-plinth", + "form": "a civic history orientation plinth, with cardinal direction marks, site context diagrams, and view-corridor cues", + "lineage": "Builds from the legible conventions of the civic history orientation plinth.", + "tags": [ + "direction", + "context", + "views" + ] + }, + { + "id": "placemaking-memorial-civic-history-reflection-bench-sequence", + "form": "a civic history reflection bench sequence, with spaced pause points, short reflection prompts, and clear onward cues", + "lineage": "Builds from the legible conventions of the civic history reflection bench sequence.", + "tags": [ + "pause", + "prompt", + "continuity" + ] + }, + { + "id": "placemaking-memorial-neighborhood-memory-commemorative-names-wall", + "form": "a neighborhood memory commemorative names wall, with then-and-now overlays, change-rate indicators, and future commitment markers", + "lineage": "Builds from the legible conventions of the neighborhood memory commemorative names wall.", + "tags": [ + "change", + "rate", + "commitment" + ] + }, + { + "id": "placemaking-memorial-neighborhood-memory-interpretive-walking-route", + "form": "a neighborhood memory interpretive walking route, with material sample insets, maker-process captions, and repair-history traces", + "lineage": "Builds from the legible conventions of the neighborhood memory interpretive walking route.", + "tags": [ + "material", + "process", + "repair" + ] + }, + { + "id": "placemaking-memorial-neighborhood-memory-timeline-pavement", + "form": "a neighborhood memory timeline pavement, with mapped contribution points, period groupings, and public-record references", + "lineage": "Builds from the legible conventions of the neighborhood memory timeline pavement.", + "tags": [ + "mapping", + "periods", + "records" + ] + }, + { + "id": "placemaking-memorial-neighborhood-memory-public-story-kiosk", + "form": "a neighborhood memory public story kiosk, with route-line continuity, transfer-era milestones, and destination sightlines", + "lineage": "Builds from the legible conventions of the neighborhood memory public story kiosk.", + "tags": [ + "continuity", + "milestones", + "sightlines" + ] + }, + { + "id": "placemaking-memorial-neighborhood-memory-orientation-plinth", + "form": "a neighborhood memory orientation plinth, with water-level references, shoreline shift bands, and safe viewing boundaries", + "lineage": "Builds from the legible conventions of the neighborhood memory orientation plinth.", + "tags": [ + "levels", + "bands", + "boundaries" + ] + }, + { + "id": "placemaking-memorial-neighborhood-memory-reflection-bench-sequence", + "form": "a neighborhood memory reflection bench sequence, with annual milestone rings, community contribution slots, and next-year placeholders", + "lineage": "Builds from the legible conventions of the neighborhood memory reflection bench sequence.", + "tags": [ + "cycles", + "contribution", + "future" + ] + }, + { + "id": "placemaking-memorial-public-service-remembrance-commemorative-names-wall", + "form": "a public service remembrance commemorative names wall, with an ordered name field, searchable grouping cues, and accessible reading intervals", + "lineage": "Builds from the legible conventions of the public service remembrance commemorative names wall.", + "tags": [ + "names", + "grouping", + "intervals" + ] + }, + { + "id": "placemaking-memorial-public-service-remembrance-interpretive-walking-rout", + "form": "a public service remembrance interpretive walking route, with numbered route stops, distance markers, and return-path directions", + "lineage": "Builds from the legible conventions of the public service remembrance interpretive walking route.", + "tags": [ + "route", + "distance", + "return" + ] + }, + { + "id": "placemaking-memorial-public-service-remembrance-timeline-pavement", + "form": "a public service remembrance timeline pavement, with dated ground bands, event markers, and present-day comparison points", + "lineage": "Builds from the legible conventions of the public service remembrance timeline pavement.", + "tags": [ + "chronology", + "markers", + "comparison" + ] + }, + { + "id": "placemaking-memorial-public-service-remembrance-public-story-kiosk", + "form": "a public service remembrance public story kiosk, with a shared orientation face, layered local accounts, and nearby-site pointers", + "lineage": "Builds from the legible conventions of the public service remembrance public story kiosk.", + "tags": [ + "orientation", + "voices", + "pointers" + ] + }, + { + "id": "placemaking-memorial-public-service-remembrance-orientation-plinth", + "form": "a public service remembrance orientation plinth, with cardinal direction marks, site context diagrams, and view-corridor cues", + "lineage": "Builds from the legible conventions of the public service remembrance orientation plinth.", + "tags": [ + "direction", + "context", + "views" + ] + }, + { + "id": "placemaking-memorial-public-service-remembrance-reflection-bench-sequence", + "form": "a public service remembrance reflection bench sequence, with spaced pause points, short reflection prompts, and clear onward cues", + "lineage": "Builds from the legible conventions of the public service remembrance reflection bench sequence.", + "tags": [ + "pause", + "prompt", + "continuity" + ] + }, + { + "id": "placemaking-memorial-labor-history-commemorative-names-wall", + "form": "a labor history commemorative names wall, with then-and-now overlays, change-rate indicators, and future commitment markers", + "lineage": "Builds from the legible conventions of the labor history commemorative names wall.", + "tags": [ + "change", + "rate", + "commitment" + ] + }, + { + "id": "placemaking-memorial-labor-history-interpretive-walking-route", + "form": "a labor history interpretive walking route, with material sample insets, maker-process captions, and repair-history traces", + "lineage": "Builds from the legible conventions of the labor history interpretive walking route.", + "tags": [ + "material", + "process", + "repair" + ] + }, + { + "id": "placemaking-memorial-labor-history-timeline-pavement", + "form": "a labor history timeline pavement, with mapped contribution points, period groupings, and public-record references", + "lineage": "Builds from the legible conventions of the labor history timeline pavement.", + "tags": [ + "mapping", + "periods", + "records" + ] + }, + { + "id": "placemaking-memorial-labor-history-public-story-kiosk", + "form": "a labor history public story kiosk, with route-line continuity, transfer-era milestones, and destination sightlines", + "lineage": "Builds from the legible conventions of the labor history public story kiosk.", + "tags": [ + "continuity", + "milestones", + "sightlines" + ] + }, + { + "id": "placemaking-memorial-labor-history-orientation-plinth", + "form": "a labor history orientation plinth, with water-level references, shoreline shift bands, and safe viewing boundaries", + "lineage": "Builds from the legible conventions of the labor history orientation plinth.", + "tags": [ + "levels", + "bands", + "boundaries" + ] + }, + { + "id": "placemaking-memorial-labor-history-reflection-bench-sequence", + "form": "a labor history reflection bench sequence, with annual milestone rings, community contribution slots, and next-year placeholders", + "lineage": "Builds from the legible conventions of the labor history reflection bench sequence.", + "tags": [ + "cycles", + "contribution", + "future" + ] + }, + { + "id": "placemaking-memorial-migration-record-commemorative-names-wall", + "form": "a migration record commemorative names wall, with an ordered name field, searchable grouping cues, and accessible reading intervals", + "lineage": "Builds from the legible conventions of the migration record commemorative names wall.", + "tags": [ + "names", + "grouping", + "intervals" + ] + }, + { + "id": "placemaking-memorial-migration-record-interpretive-walking-route", + "form": "a migration record interpretive walking route, with numbered route stops, distance markers, and return-path directions", + "lineage": "Builds from the legible conventions of the migration record interpretive walking route.", + "tags": [ + "route", + "distance", + "return" + ] + }, + { + "id": "placemaking-memorial-migration-record-timeline-pavement", + "form": "a migration record timeline pavement, with dated ground bands, event markers, and present-day comparison points", + "lineage": "Builds from the legible conventions of the migration record timeline pavement.", + "tags": [ + "chronology", + "markers", + "comparison" + ] + }, + { + "id": "placemaking-memorial-migration-record-public-story-kiosk", + "form": "a migration record public story kiosk, with a shared orientation face, layered local accounts, and nearby-site pointers", + "lineage": "Builds from the legible conventions of the migration record public story kiosk.", + "tags": [ + "orientation", + "voices", + "pointers" + ] + }, + { + "id": "placemaking-memorial-migration-record-orientation-plinth", + "form": "a migration record orientation plinth, with cardinal direction marks, site context diagrams, and view-corridor cues", + "lineage": "Builds from the legible conventions of the migration record orientation plinth.", + "tags": [ + "direction", + "context", + "views" + ] + }, + { + "id": "placemaking-memorial-migration-record-reflection-bench-sequence", + "form": "a migration record reflection bench sequence, with spaced pause points, short reflection prompts, and clear onward cues", + "lineage": "Builds from the legible conventions of the migration record reflection bench sequence.", + "tags": [ + "pause", + "prompt", + "continuity" + ] + }, + { + "id": "placemaking-memorial-disaster-recovery-commemorative-names-wall", + "form": "a disaster recovery commemorative names wall, with then-and-now overlays, change-rate indicators, and future commitment markers", + "lineage": "Builds from the legible conventions of the disaster recovery commemorative names wall.", + "tags": [ + "change", + "rate", + "commitment" + ] + }, + { + "id": "placemaking-memorial-disaster-recovery-interpretive-walking-route", + "form": "a disaster recovery interpretive walking route, with material sample insets, maker-process captions, and repair-history traces", + "lineage": "Builds from the legible conventions of the disaster recovery interpretive walking route.", + "tags": [ + "material", + "process", + "repair" + ] + }, + { + "id": "placemaking-memorial-disaster-recovery-timeline-pavement", + "form": "a disaster recovery timeline pavement, with mapped contribution points, period groupings, and public-record references", + "lineage": "Builds from the legible conventions of the disaster recovery timeline pavement.", + "tags": [ + "mapping", + "periods", + "records" + ] + }, + { + "id": "placemaking-memorial-disaster-recovery-public-story-kiosk", + "form": "a disaster recovery public story kiosk, with route-line continuity, transfer-era milestones, and destination sightlines", + "lineage": "Builds from the legible conventions of the disaster recovery public story kiosk.", + "tags": [ + "continuity", + "milestones", + "sightlines" + ] + }, + { + "id": "placemaking-memorial-disaster-recovery-orientation-plinth", + "form": "a disaster recovery orientation plinth, with water-level references, shoreline shift bands, and safe viewing boundaries", + "lineage": "Builds from the legible conventions of the disaster recovery orientation plinth.", + "tags": [ + "levels", + "bands", + "boundaries" + ] + }, + { + "id": "placemaking-memorial-disaster-recovery-reflection-bench-sequence", + "form": "a disaster recovery reflection bench sequence, with annual milestone rings, community contribution slots, and next-year placeholders", + "lineage": "Builds from the legible conventions of the disaster recovery reflection bench sequence.", + "tags": [ + "cycles", + "contribution", + "future" + ] + }, + { + "id": "placemaking-memorial-ecological-change-commemorative-names-wall", + "form": "an ecological change commemorative names wall, with an ordered name field, searchable grouping cues, and accessible reading intervals", + "lineage": "Builds from the legible conventions of the ecological change commemorative names wall.", + "tags": [ + "names", + "grouping", + "intervals" + ] + }, + { + "id": "placemaking-memorial-ecological-change-interpretive-walking-route", + "form": "an ecological change interpretive walking route, with numbered route stops, distance markers, and return-path directions", + "lineage": "Builds from the legible conventions of the ecological change interpretive walking route.", + "tags": [ + "route", + "distance", + "return" + ] + }, + { + "id": "placemaking-memorial-ecological-change-timeline-pavement", + "form": "an ecological change timeline pavement, with dated ground bands, event markers, and present-day comparison points", + "lineage": "Builds from the legible conventions of the ecological change timeline pavement.", + "tags": [ + "chronology", + "markers", + "comparison" + ] + }, + { + "id": "placemaking-memorial-ecological-change-public-story-kiosk", + "form": "an ecological change public story kiosk, with a shared orientation face, layered local accounts, and nearby-site pointers", + "lineage": "Builds from the legible conventions of the ecological change public story kiosk.", + "tags": [ + "orientation", + "voices", + "pointers" + ] + }, + { + "id": "placemaking-memorial-ecological-change-orientation-plinth", + "form": "an ecological change orientation plinth, with cardinal direction marks, site context diagrams, and view-corridor cues", + "lineage": "Builds from the legible conventions of the ecological change orientation plinth.", + "tags": [ + "direction", + "context", + "views" + ] + }, + { + "id": "placemaking-memorial-ecological-change-reflection-bench-sequence", + "form": "an ecological change reflection bench sequence, with spaced pause points, short reflection prompts, and clear onward cues", + "lineage": "Builds from the legible conventions of the ecological change reflection bench sequence.", + "tags": [ + "pause", + "prompt", + "continuity" + ] + }, + { + "id": "placemaking-memorial-local-craft-commemorative-names-wall", + "form": "a local craft commemorative names wall, with then-and-now overlays, change-rate indicators, and future commitment markers", + "lineage": "Builds from the legible conventions of the local craft commemorative names wall.", + "tags": [ + "change", + "rate", + "commitment" + ] + }, + { + "id": "placemaking-memorial-local-craft-interpretive-walking-route", + "form": "a local craft interpretive walking route, with material sample insets, maker-process captions, and repair-history traces", + "lineage": "Builds from the legible conventions of the local craft interpretive walking route.", + "tags": [ + "material", + "process", + "repair" + ] + }, + { + "id": "placemaking-memorial-local-craft-timeline-pavement", + "form": "a local craft timeline pavement, with mapped contribution points, period groupings, and public-record references", + "lineage": "Builds from the legible conventions of the local craft timeline pavement.", + "tags": [ + "mapping", + "periods", + "records" + ] + }, + { + "id": "placemaking-memorial-local-craft-public-story-kiosk", + "form": "a local craft public story kiosk, with route-line continuity, transfer-era milestones, and destination sightlines", + "lineage": "Builds from the legible conventions of the local craft public story kiosk.", + "tags": [ + "continuity", + "milestones", + "sightlines" + ] + }, + { + "id": "placemaking-memorial-local-craft-orientation-plinth", + "form": "a local craft orientation plinth, with water-level references, shoreline shift bands, and safe viewing boundaries", + "lineage": "Builds from the legible conventions of the local craft orientation plinth.", + "tags": [ + "levels", + "bands", + "boundaries" + ] + }, + { + "id": "placemaking-memorial-local-craft-reflection-bench-sequence", + "form": "a local craft reflection bench sequence, with annual milestone rings, community contribution slots, and next-year placeholders", + "lineage": "Builds from the legible conventions of the local craft reflection bench sequence.", + "tags": [ + "cycles", + "contribution", + "future" + ] + }, + { + "id": "placemaking-memorial-market-history-commemorative-names-wall", + "form": "a market history commemorative names wall, with an ordered name field, searchable grouping cues, and accessible reading intervals", + "lineage": "Builds from the legible conventions of the market history commemorative names wall.", + "tags": [ + "names", + "grouping", + "intervals" + ] + }, + { + "id": "placemaking-memorial-market-history-interpretive-walking-route", + "form": "a market history interpretive walking route, with numbered route stops, distance markers, and return-path directions", + "lineage": "Builds from the legible conventions of the market history interpretive walking route.", + "tags": [ + "route", + "distance", + "return" + ] + }, + { + "id": "placemaking-memorial-market-history-timeline-pavement", + "form": "a market history timeline pavement, with dated ground bands, event markers, and present-day comparison points", + "lineage": "Builds from the legible conventions of the market history timeline pavement.", + "tags": [ + "chronology", + "markers", + "comparison" + ] + }, + { + "id": "placemaking-memorial-market-history-public-story-kiosk", + "form": "a market history public story kiosk, with a shared orientation face, layered local accounts, and nearby-site pointers", + "lineage": "Builds from the legible conventions of the market history public story kiosk.", + "tags": [ + "orientation", + "voices", + "pointers" + ] + }, + { + "id": "placemaking-memorial-market-history-orientation-plinth", + "form": "a market history orientation plinth, with cardinal direction marks, site context diagrams, and view-corridor cues", + "lineage": "Builds from the legible conventions of the market history orientation plinth.", + "tags": [ + "direction", + "context", + "views" + ] + }, + { + "id": "placemaking-memorial-market-history-reflection-bench-sequence", + "form": "a market history reflection bench sequence, with spaced pause points, short reflection prompts, and clear onward cues", + "lineage": "Builds from the legible conventions of the market history reflection bench sequence.", + "tags": [ + "pause", + "prompt", + "continuity" + ] + }, + { + "id": "placemaking-memorial-transit-heritage-commemorative-names-wall", + "form": "a transit heritage commemorative names wall, with then-and-now overlays, change-rate indicators, and future commitment markers", + "lineage": "Builds from the legible conventions of the transit heritage commemorative names wall.", + "tags": [ + "change", + "rate", + "commitment" + ] + }, + { + "id": "placemaking-memorial-transit-heritage-interpretive-walking-route", + "form": "a transit heritage interpretive walking route, with material sample insets, maker-process captions, and repair-history traces", + "lineage": "Builds from the legible conventions of the transit heritage interpretive walking route.", + "tags": [ + "material", + "process", + "repair" + ] + }, + { + "id": "placemaking-memorial-transit-heritage-timeline-pavement", + "form": "a transit heritage timeline pavement, with mapped contribution points, period groupings, and public-record references", + "lineage": "Builds from the legible conventions of the transit heritage timeline pavement.", + "tags": [ + "mapping", + "periods", + "records" + ] + }, + { + "id": "placemaking-memorial-transit-heritage-public-story-kiosk", + "form": "a transit heritage public story kiosk, with route-line continuity, transfer-era milestones, and destination sightlines", + "lineage": "Builds from the legible conventions of the transit heritage public story kiosk.", + "tags": [ + "continuity", + "milestones", + "sightlines" + ] + }, + { + "id": "placemaking-memorial-transit-heritage-orientation-plinth", + "form": "a transit heritage orientation plinth, with water-level references, shoreline shift bands, and safe viewing boundaries", + "lineage": "Builds from the legible conventions of the transit heritage orientation plinth.", + "tags": [ + "levels", + "bands", + "boundaries" + ] + }, + { + "id": "placemaking-memorial-transit-heritage-reflection-bench-sequence", + "form": "a transit heritage reflection bench sequence, with annual milestone rings, community contribution slots, and next-year placeholders", + "lineage": "Builds from the legible conventions of the transit heritage reflection bench sequence.", + "tags": [ + "cycles", + "contribution", + "future" + ] + }, + { + "id": "placemaking-memorial-waterfront-change-commemorative-names-wall", + "form": "a waterfront change commemorative names wall, with an ordered name field, searchable grouping cues, and accessible reading intervals", + "lineage": "Builds from the legible conventions of the waterfront change commemorative names wall.", + "tags": [ + "names", + "grouping", + "intervals" + ] + }, + { + "id": "placemaking-memorial-waterfront-change-interpretive-walking-route", + "form": "a waterfront change interpretive walking route, with numbered route stops, distance markers, and return-path directions", + "lineage": "Builds from the legible conventions of the waterfront change interpretive walking route.", + "tags": [ + "route", + "distance", + "return" + ] + }, + { + "id": "placemaking-memorial-waterfront-change-timeline-pavement", + "form": "a waterfront change timeline pavement, with dated ground bands, event markers, and present-day comparison points", + "lineage": "Builds from the legible conventions of the waterfront change timeline pavement.", + "tags": [ + "chronology", + "markers", + "comparison" + ] + }, + { + "id": "placemaking-memorial-waterfront-change-public-story-kiosk", + "form": "a waterfront change public story kiosk, with a shared orientation face, layered local accounts, and nearby-site pointers", + "lineage": "Builds from the legible conventions of the waterfront change public story kiosk.", + "tags": [ + "orientation", + "voices", + "pointers" + ] + } + ] + }, + { + "id": "landscape-ecology-time", + "label": "Landscape, ecology & time", + "description": "Living and temporal systems with cycles, layers, stewardship, succession, and change.", + "concepts": [ + { + "id": "landscape-ecology-time-watershed-field-station-board", + "form": "a watershed field-station board, with upstream-to-downstream bands, flow-direction arrows, and sample-point readings", + "lineage": "Builds from the legible conventions of the watershed field-station board.", + "tags": [ + "flow", + "direction", + "sampling" + ] + }, + { + "id": "landscape-ecology-time-watershed-transect-map", + "form": "a watershed transect map, with month-by-month sectors, peak-period markers, and off-season gaps", + "lineage": "Builds from the legible conventions of the watershed transect map.", + "tags": [ + "cycles", + "peaks", + "gaps" + ] + }, + { + "id": "landscape-ecology-time-watershed-seasonal-wheel", + "form": "a watershed seasonal wheel, with event-date rows, first-and-last sightings, and year-over-year comparisons", + "lineage": "Builds from the legible conventions of the watershed seasonal wheel.", + "tags": [ + "events", + "range", + "comparison" + ] + }, + { + "id": "landscape-ecology-time-watershed-observation-ledger", + "form": "a watershed observation ledger, with layered depth profiles, sample identifiers, and moisture-change traces", + "lineage": "Builds from the legible conventions of the watershed observation ledger.", + "tags": [ + "layers", + "samples", + "change" + ] + }, + { + "id": "landscape-ecology-time-watershed-species-index", + "form": "a watershed species index, with height strata, light-exposure bands, and habitat connection lines", + "lineage": "Builds from the legible conventions of the watershed species index.", + "tags": [ + "strata", + "exposure", + "connections" + ] + }, + { + "id": "landscape-ecology-time-watershed-change-over-time-marker", + "form": "a watershed change-over-time marker, with species-to-resource links, activity windows, and corridor checkpoints", + "lineage": "Builds from the legible conventions of the watershed change-over-time marker.", + "tags": [ + "network", + "windows", + "corridors" + ] + }, + { + "id": "landscape-ecology-time-seasonal-field-station-board", + "form": "a seasonal field-station board, with tidal-height scales, erosion boundaries, and safe-access windows", + "lineage": "Builds from the legible conventions of the seasonal field-station board.", + "tags": [ + "scale", + "boundaries", + "access" + ] + }, + { + "id": "landscape-ecology-time-seasonal-transect-map", + "form": "a seasonal transect map, with channel branches, gauge stations, and flood-stage thresholds", + "lineage": "Builds from the legible conventions of the seasonal transect map.", + "tags": [ + "branching", + "stations", + "thresholds" + ] + }, + { + "id": "landscape-ecology-time-seasonal-seasonal-wheel", + "form": "a seasonal seasonal wheel, with time-stamped readings, trend sparklines, and alert-condition bands", + "lineage": "Builds from the legible conventions of the seasonal seasonal wheel.", + "tags": [ + "timestamps", + "trends", + "alerts" + ] + }, + { + "id": "landscape-ecology-time-seasonal-observation-ledger", + "form": "a seasonal observation ledger, with baseline plots, intervention milestones, and recovery indicators", + "lineage": "Builds from the legible conventions of the seasonal observation ledger.", + "tags": [ + "baseline", + "milestones", + "recovery" + ] + }, + { + "id": "landscape-ecology-time-seasonal-species-index", + "form": "a seasonal species index, with rotation sequences, input-output tallies, and seasonal decision gates", + "lineage": "Builds from the legible conventions of the seasonal species index.", + "tags": [ + "rotation", + "tallies", + "gates" + ] + }, + { + "id": "landscape-ecology-time-seasonal-change-over-time-marker", + "form": "a seasonal change-over-time marker, with sky-position grids, darkness windows, and repeat-observation prompts", + "lineage": "Builds from the legible conventions of the seasonal change-over-time marker.", + "tags": [ + "grid", + "windows", + "repeat" + ] + }, + { + "id": "landscape-ecology-time-phenology-field-station-board", + "form": "a phenology field-station board, with upstream-to-downstream bands, flow-direction arrows, and sample-point readings", + "lineage": "Builds from the legible conventions of the phenology field-station board.", + "tags": [ + "flow", + "direction", + "sampling" + ] + }, + { + "id": "landscape-ecology-time-phenology-transect-map", + "form": "a phenology transect map, with month-by-month sectors, peak-period markers, and off-season gaps", + "lineage": "Builds from the legible conventions of the phenology transect map.", + "tags": [ + "cycles", + "peaks", + "gaps" + ] + }, + { + "id": "landscape-ecology-time-phenology-seasonal-wheel", + "form": "a phenology seasonal wheel, with event-date rows, first-and-last sightings, and year-over-year comparisons", + "lineage": "Builds from the legible conventions of the phenology seasonal wheel.", + "tags": [ + "events", + "range", + "comparison" + ] + }, + { + "id": "landscape-ecology-time-phenology-observation-ledger", + "form": "a phenology observation ledger, with layered depth profiles, sample identifiers, and moisture-change traces", + "lineage": "Builds from the legible conventions of the phenology observation ledger.", + "tags": [ + "layers", + "samples", + "change" + ] + }, + { + "id": "landscape-ecology-time-phenology-species-index", + "form": "a phenology species index, with height strata, light-exposure bands, and habitat connection lines", + "lineage": "Builds from the legible conventions of the phenology species index.", + "tags": [ + "strata", + "exposure", + "connections" + ] + }, + { + "id": "landscape-ecology-time-phenology-change-over-time-marker", + "form": "a phenology change-over-time marker, with species-to-resource links, activity windows, and corridor checkpoints", + "lineage": "Builds from the legible conventions of the phenology change-over-time marker.", + "tags": [ + "network", + "windows", + "corridors" + ] + }, + { + "id": "landscape-ecology-time-soil-field-station-board", + "form": "a soil field-station board, with tidal-height scales, erosion boundaries, and safe-access windows", + "lineage": "Builds from the legible conventions of the soil field-station board.", + "tags": [ + "scale", + "boundaries", + "access" + ] + }, + { + "id": "landscape-ecology-time-soil-transect-map", + "form": "a soil transect map, with channel branches, gauge stations, and flood-stage thresholds", + "lineage": "Builds from the legible conventions of the soil transect map.", + "tags": [ + "branching", + "stations", + "thresholds" + ] + }, + { + "id": "landscape-ecology-time-soil-seasonal-wheel", + "form": "a soil seasonal wheel, with time-stamped readings, trend sparklines, and alert-condition bands", + "lineage": "Builds from the legible conventions of the soil seasonal wheel.", + "tags": [ + "timestamps", + "trends", + "alerts" + ] + }, + { + "id": "landscape-ecology-time-soil-observation-ledger", + "form": "a soil observation ledger, with baseline plots, intervention milestones, and recovery indicators", + "lineage": "Builds from the legible conventions of the soil observation ledger.", + "tags": [ + "baseline", + "milestones", + "recovery" + ] + }, + { + "id": "landscape-ecology-time-soil-species-index", + "form": "a soil species index, with rotation sequences, input-output tallies, and seasonal decision gates", + "lineage": "Builds from the legible conventions of the soil species index.", + "tags": [ + "rotation", + "tallies", + "gates" + ] + }, + { + "id": "landscape-ecology-time-soil-change-over-time-marker", + "form": "a soil change-over-time marker, with sky-position grids, darkness windows, and repeat-observation prompts", + "lineage": "Builds from the legible conventions of the soil change-over-time marker.", + "tags": [ + "grid", + "windows", + "repeat" + ] + }, + { + "id": "landscape-ecology-time-canopy-field-station-board", + "form": "a canopy field-station board, with upstream-to-downstream bands, flow-direction arrows, and sample-point readings", + "lineage": "Builds from the legible conventions of the canopy field-station board.", + "tags": [ + "flow", + "direction", + "sampling" + ] + }, + { + "id": "landscape-ecology-time-canopy-transect-map", + "form": "a canopy transect map, with month-by-month sectors, peak-period markers, and off-season gaps", + "lineage": "Builds from the legible conventions of the canopy transect map.", + "tags": [ + "cycles", + "peaks", + "gaps" + ] + }, + { + "id": "landscape-ecology-time-canopy-seasonal-wheel", + "form": "a canopy seasonal wheel, with event-date rows, first-and-last sightings, and year-over-year comparisons", + "lineage": "Builds from the legible conventions of the canopy seasonal wheel.", + "tags": [ + "events", + "range", + "comparison" + ] + }, + { + "id": "landscape-ecology-time-canopy-observation-ledger", + "form": "a canopy observation ledger, with layered depth profiles, sample identifiers, and moisture-change traces", + "lineage": "Builds from the legible conventions of the canopy observation ledger.", + "tags": [ + "layers", + "samples", + "change" + ] + }, + { + "id": "landscape-ecology-time-canopy-species-index", + "form": "a canopy species index, with height strata, light-exposure bands, and habitat connection lines", + "lineage": "Builds from the legible conventions of the canopy species index.", + "tags": [ + "strata", + "exposure", + "connections" + ] + }, + { + "id": "landscape-ecology-time-canopy-change-over-time-marker", + "form": "a canopy change-over-time marker, with species-to-resource links, activity windows, and corridor checkpoints", + "lineage": "Builds from the legible conventions of the canopy change-over-time marker.", + "tags": [ + "network", + "windows", + "corridors" + ] + }, + { + "id": "landscape-ecology-time-pollinator-field-station-board", + "form": "a pollinator field-station board, with tidal-height scales, erosion boundaries, and safe-access windows", + "lineage": "Builds from the legible conventions of the pollinator field-station board.", + "tags": [ + "scale", + "boundaries", + "access" + ] + }, + { + "id": "landscape-ecology-time-pollinator-transect-map", + "form": "a pollinator transect map, with channel branches, gauge stations, and flood-stage thresholds", + "lineage": "Builds from the legible conventions of the pollinator transect map.", + "tags": [ + "branching", + "stations", + "thresholds" + ] + }, + { + "id": "landscape-ecology-time-pollinator-seasonal-wheel", + "form": "a pollinator seasonal wheel, with time-stamped readings, trend sparklines, and alert-condition bands", + "lineage": "Builds from the legible conventions of the pollinator seasonal wheel.", + "tags": [ + "timestamps", + "trends", + "alerts" + ] + }, + { + "id": "landscape-ecology-time-pollinator-observation-ledger", + "form": "a pollinator observation ledger, with baseline plots, intervention milestones, and recovery indicators", + "lineage": "Builds from the legible conventions of the pollinator observation ledger.", + "tags": [ + "baseline", + "milestones", + "recovery" + ] + }, + { + "id": "landscape-ecology-time-pollinator-species-index", + "form": "a pollinator species index, with rotation sequences, input-output tallies, and seasonal decision gates", + "lineage": "Builds from the legible conventions of the pollinator species index.", + "tags": [ + "rotation", + "tallies", + "gates" + ] + }, + { + "id": "landscape-ecology-time-pollinator-change-over-time-marker", + "form": "a pollinator change-over-time marker, with sky-position grids, darkness windows, and repeat-observation prompts", + "lineage": "Builds from the legible conventions of the pollinator change-over-time marker.", + "tags": [ + "grid", + "windows", + "repeat" + ] + }, + { + "id": "landscape-ecology-time-coastal-field-station-board", + "form": "a coastal field-station board, with upstream-to-downstream bands, flow-direction arrows, and sample-point readings", + "lineage": "Builds from the legible conventions of the coastal field-station board.", + "tags": [ + "flow", + "direction", + "sampling" + ] + }, + { + "id": "landscape-ecology-time-coastal-transect-map", + "form": "a coastal transect map, with month-by-month sectors, peak-period markers, and off-season gaps", + "lineage": "Builds from the legible conventions of the coastal transect map.", + "tags": [ + "cycles", + "peaks", + "gaps" + ] + }, + { + "id": "landscape-ecology-time-coastal-seasonal-wheel", + "form": "a coastal seasonal wheel, with event-date rows, first-and-last sightings, and year-over-year comparisons", + "lineage": "Builds from the legible conventions of the coastal seasonal wheel.", + "tags": [ + "events", + "range", + "comparison" + ] + }, + { + "id": "landscape-ecology-time-coastal-observation-ledger", + "form": "a coastal observation ledger, with layered depth profiles, sample identifiers, and moisture-change traces", + "lineage": "Builds from the legible conventions of the coastal observation ledger.", + "tags": [ + "layers", + "samples", + "change" + ] + }, + { + "id": "landscape-ecology-time-coastal-species-index", + "form": "a coastal species index, with height strata, light-exposure bands, and habitat connection lines", + "lineage": "Builds from the legible conventions of the coastal species index.", + "tags": [ + "strata", + "exposure", + "connections" + ] + }, + { + "id": "landscape-ecology-time-coastal-change-over-time-marker", + "form": "a coastal change-over-time marker, with species-to-resource links, activity windows, and corridor checkpoints", + "lineage": "Builds from the legible conventions of the coastal change-over-time marker.", + "tags": [ + "network", + "windows", + "corridors" + ] + }, + { + "id": "landscape-ecology-time-river-field-station-board", + "form": "a river field-station board, with tidal-height scales, erosion boundaries, and safe-access windows", + "lineage": "Builds from the legible conventions of the river field-station board.", + "tags": [ + "scale", + "boundaries", + "access" + ] + }, + { + "id": "landscape-ecology-time-river-transect-map", + "form": "a river transect map, with channel branches, gauge stations, and flood-stage thresholds", + "lineage": "Builds from the legible conventions of the river transect map.", + "tags": [ + "branching", + "stations", + "thresholds" + ] + }, + { + "id": "landscape-ecology-time-river-seasonal-wheel", + "form": "a river seasonal wheel, with time-stamped readings, trend sparklines, and alert-condition bands", + "lineage": "Builds from the legible conventions of the river seasonal wheel.", + "tags": [ + "timestamps", + "trends", + "alerts" + ] + }, + { + "id": "landscape-ecology-time-river-observation-ledger", + "form": "a river observation ledger, with baseline plots, intervention milestones, and recovery indicators", + "lineage": "Builds from the legible conventions of the river observation ledger.", + "tags": [ + "baseline", + "milestones", + "recovery" + ] + }, + { + "id": "landscape-ecology-time-river-species-index", + "form": "a river species index, with rotation sequences, input-output tallies, and seasonal decision gates", + "lineage": "Builds from the legible conventions of the river species index.", + "tags": [ + "rotation", + "tallies", + "gates" + ] + }, + { + "id": "landscape-ecology-time-river-change-over-time-marker", + "form": "a river change-over-time marker, with sky-position grids, darkness windows, and repeat-observation prompts", + "lineage": "Builds from the legible conventions of the river change-over-time marker.", + "tags": [ + "grid", + "windows", + "repeat" + ] + }, + { + "id": "landscape-ecology-time-weather-field-station-board", + "form": "a weather field-station board, with upstream-to-downstream bands, flow-direction arrows, and sample-point readings", + "lineage": "Builds from the legible conventions of the weather field-station board.", + "tags": [ + "flow", + "direction", + "sampling" + ] + }, + { + "id": "landscape-ecology-time-weather-transect-map", + "form": "a weather transect map, with month-by-month sectors, peak-period markers, and off-season gaps", + "lineage": "Builds from the legible conventions of the weather transect map.", + "tags": [ + "cycles", + "peaks", + "gaps" + ] + }, + { + "id": "landscape-ecology-time-weather-seasonal-wheel", + "form": "a weather seasonal wheel, with event-date rows, first-and-last sightings, and year-over-year comparisons", + "lineage": "Builds from the legible conventions of the weather seasonal wheel.", + "tags": [ + "events", + "range", + "comparison" + ] + }, + { + "id": "landscape-ecology-time-weather-observation-ledger", + "form": "a weather observation ledger, with layered depth profiles, sample identifiers, and moisture-change traces", + "lineage": "Builds from the legible conventions of the weather observation ledger.", + "tags": [ + "layers", + "samples", + "change" + ] + }, + { + "id": "landscape-ecology-time-weather-species-index", + "form": "a weather species index, with height strata, light-exposure bands, and habitat connection lines", + "lineage": "Builds from the legible conventions of the weather species index.", + "tags": [ + "strata", + "exposure", + "connections" + ] + }, + { + "id": "landscape-ecology-time-weather-change-over-time-marker", + "form": "a weather change-over-time marker, with species-to-resource links, activity windows, and corridor checkpoints", + "lineage": "Builds from the legible conventions of the weather change-over-time marker.", + "tags": [ + "network", + "windows", + "corridors" + ] + }, + { + "id": "landscape-ecology-time-restoration-field-station-board", + "form": "a restoration field-station board, with tidal-height scales, erosion boundaries, and safe-access windows", + "lineage": "Builds from the legible conventions of the restoration field-station board.", + "tags": [ + "scale", + "boundaries", + "access" + ] + }, + { + "id": "landscape-ecology-time-restoration-transect-map", + "form": "a restoration transect map, with channel branches, gauge stations, and flood-stage thresholds", + "lineage": "Builds from the legible conventions of the restoration transect map.", + "tags": [ + "branching", + "stations", + "thresholds" + ] + }, + { + "id": "landscape-ecology-time-restoration-seasonal-wheel", + "form": "a restoration seasonal wheel, with time-stamped readings, trend sparklines, and alert-condition bands", + "lineage": "Builds from the legible conventions of the restoration seasonal wheel.", + "tags": [ + "timestamps", + "trends", + "alerts" + ] + }, + { + "id": "landscape-ecology-time-restoration-observation-ledger", + "form": "a restoration observation ledger, with baseline plots, intervention milestones, and recovery indicators", + "lineage": "Builds from the legible conventions of the restoration observation ledger.", + "tags": [ + "baseline", + "milestones", + "recovery" + ] + }, + { + "id": "landscape-ecology-time-restoration-species-index", + "form": "a restoration species index, with rotation sequences, input-output tallies, and seasonal decision gates", + "lineage": "Builds from the legible conventions of the restoration species index.", + "tags": [ + "rotation", + "tallies", + "gates" + ] + }, + { + "id": "landscape-ecology-time-restoration-change-over-time-marker", + "form": "a restoration change-over-time marker, with sky-position grids, darkness windows, and repeat-observation prompts", + "lineage": "Builds from the legible conventions of the restoration change-over-time marker.", + "tags": [ + "grid", + "windows", + "repeat" + ] + }, + { + "id": "landscape-ecology-time-agricultural-field-station-board", + "form": "an agricultural field-station board, with upstream-to-downstream bands, flow-direction arrows, and sample-point readings", + "lineage": "Builds from the legible conventions of the agricultural field-station board.", + "tags": [ + "flow", + "direction", + "sampling" + ] + }, + { + "id": "landscape-ecology-time-agricultural-transect-map", + "form": "an agricultural transect map, with month-by-month sectors, peak-period markers, and off-season gaps", + "lineage": "Builds from the legible conventions of the agricultural transect map.", + "tags": [ + "cycles", + "peaks", + "gaps" + ] + }, + { + "id": "landscape-ecology-time-agricultural-seasonal-wheel", + "form": "an agricultural seasonal wheel, with event-date rows, first-and-last sightings, and year-over-year comparisons", + "lineage": "Builds from the legible conventions of the agricultural seasonal wheel.", + "tags": [ + "events", + "range", + "comparison" + ] + }, + { + "id": "landscape-ecology-time-agricultural-observation-ledger", + "form": "an agricultural observation ledger, with layered depth profiles, sample identifiers, and moisture-change traces", + "lineage": "Builds from the legible conventions of the agricultural observation ledger.", + "tags": [ + "layers", + "samples", + "change" + ] + } + ] + }, + { + "id": "fashion-textile-material", + "label": "Fashion, textile & material", + "description": "Material systems built from pattern, assembly, transformation, tactility, fit, and variation.", + "concepts": [ + { + "id": "fashion-textile-material-atelier-swatch-book", + "form": "an atelier swatch book, with ordered material tabs, touch-safe sample windows, and fiber-content keys", + "lineage": "Builds from the legible conventions of the atelier swatch book.", + "tags": [ + "tabs", + "samples", + "keys" + ] + }, + { + "id": "fashion-textile-material-atelier-pattern-board", + "form": "an atelier pattern board, with nested pattern pieces, grain-direction arrows, and assembly notches", + "lineage": "Builds from the legible conventions of the atelier pattern board.", + "tags": [ + "nesting", + "direction", + "assembly" + ] + }, + { + "id": "fashion-textile-material-atelier-garment-rail", + "form": "an atelier garment rail, with look-number markers, silhouette groupings, and outfit-sequence dividers", + "lineage": "Builds from the legible conventions of the atelier garment rail.", + "tags": [ + "numbering", + "grouping", + "sequence" + ] + }, + { + "id": "fashion-textile-material-atelier-specification-sheet", + "form": "an atelier specification sheet, with dimension columns, tolerance notes, and construction callouts", + "lineage": "Builds from the legible conventions of the atelier specification sheet.", + "tags": [ + "dimensions", + "tolerance", + "callouts" + ] + }, + { + "id": "fashion-textile-material-atelier-sample-card", + "form": "an atelier sample card, with color-lot fields, finish comparisons, and approval-status stamps", + "lineage": "Builds from the legible conventions of the atelier sample card.", + "tags": [ + "color", + "comparison", + "status" + ] + }, + { + "id": "fashion-textile-material-atelier-process-wall", + "form": "an atelier process wall, with step-by-step stations, tool pairings, and result checkpoints", + "lineage": "Builds from the legible conventions of the atelier process wall.", + "tags": [ + "steps", + "tools", + "checkpoints" + ] + }, + { + "id": "fashion-textile-material-cutting-room-swatch-book", + "form": "a cutting room swatch book, with before-and-after panels, repair-method labels, and durability notes", + "lineage": "Builds from the legible conventions of the cutting room swatch book.", + "tags": [ + "states", + "methods", + "durability" + ] + }, + { + "id": "fashion-textile-material-cutting-room-pattern-board", + "form": "a cutting room pattern board, with occasion zones, layering suggestions, and availability markers", + "lineage": "Builds from the legible conventions of the cutting room pattern board.", + "tags": [ + "zoning", + "layering", + "availability" + ] + }, + { + "id": "fashion-textile-material-cutting-room-garment-rail", + "form": "a cutting room garment rail, with entrance-to-finale order, changeover cues, and focal-look pauses", + "lineage": "Builds from the legible conventions of the cutting room garment rail.", + "tags": [ + "progression", + "cues", + "pauses" + ] + }, + { + "id": "fashion-textile-material-cutting-room-specification-sheet", + "form": "a cutting room specification sheet, with measurement landmarks, adjustment pins, and fit-decision notes", + "lineage": "Builds from the legible conventions of the cutting room specification sheet.", + "tags": [ + "measurement", + "adjustment", + "decisions" + ] + }, + { + "id": "fashion-textile-material-cutting-room-sample-card", + "form": "a cutting room sample card, with repeat-unit grids, size grading bands, and cut-count summaries", + "lineage": "Builds from the legible conventions of the cutting room sample card.", + "tags": [ + "grid", + "grading", + "summary" + ] + }, + { + "id": "fashion-textile-material-cutting-room-process-wall", + "form": "a cutting room process wall, with surface-treatment samples, quality checkpoints, and care-instruction links", + "lineage": "Builds from the legible conventions of the cutting room process wall.", + "tags": [ + "finish", + "quality", + "care" + ] + }, + { + "id": "fashion-textile-material-material-library-swatch-book", + "form": "a material library swatch book, with ordered material tabs, touch-safe sample windows, and fiber-content keys", + "lineage": "Builds from the legible conventions of the material library swatch book.", + "tags": [ + "tabs", + "samples", + "keys" + ] + }, + { + "id": "fashion-textile-material-material-library-pattern-board", + "form": "a material library pattern board, with nested pattern pieces, grain-direction arrows, and assembly notches", + "lineage": "Builds from the legible conventions of the material library pattern board.", + "tags": [ + "nesting", + "direction", + "assembly" + ] + }, + { + "id": "fashion-textile-material-material-library-garment-rail", + "form": "a material library garment rail, with look-number markers, silhouette groupings, and outfit-sequence dividers", + "lineage": "Builds from the legible conventions of the material library garment rail.", + "tags": [ + "numbering", + "grouping", + "sequence" + ] + }, + { + "id": "fashion-textile-material-material-library-specification-sheet", + "form": "a material library specification sheet, with dimension columns, tolerance notes, and construction callouts", + "lineage": "Builds from the legible conventions of the material library specification sheet.", + "tags": [ + "dimensions", + "tolerance", + "callouts" + ] + }, + { + "id": "fashion-textile-material-material-library-sample-card", + "form": "a material library sample card, with color-lot fields, finish comparisons, and approval-status stamps", + "lineage": "Builds from the legible conventions of the material library sample card.", + "tags": [ + "color", + "comparison", + "status" + ] + }, + { + "id": "fashion-textile-material-material-library-process-wall", + "form": "a material library process wall, with step-by-step stations, tool pairings, and result checkpoints", + "lineage": "Builds from the legible conventions of the material library process wall.", + "tags": [ + "steps", + "tools", + "checkpoints" + ] + }, + { + "id": "fashion-textile-material-dye-lab-swatch-book", + "form": "a dye lab swatch book, with before-and-after panels, repair-method labels, and durability notes", + "lineage": "Builds from the legible conventions of the dye lab swatch book.", + "tags": [ + "states", + "methods", + "durability" + ] + }, + { + "id": "fashion-textile-material-dye-lab-pattern-board", + "form": "a dye lab pattern board, with occasion zones, layering suggestions, and availability markers", + "lineage": "Builds from the legible conventions of the dye lab pattern board.", + "tags": [ + "zoning", + "layering", + "availability" + ] + }, + { + "id": "fashion-textile-material-dye-lab-garment-rail", + "form": "a dye lab garment rail, with entrance-to-finale order, changeover cues, and focal-look pauses", + "lineage": "Builds from the legible conventions of the dye lab garment rail.", + "tags": [ + "progression", + "cues", + "pauses" + ] + }, + { + "id": "fashion-textile-material-dye-lab-specification-sheet", + "form": "a dye lab specification sheet, with measurement landmarks, adjustment pins, and fit-decision notes", + "lineage": "Builds from the legible conventions of the dye lab specification sheet.", + "tags": [ + "measurement", + "adjustment", + "decisions" + ] + }, + { + "id": "fashion-textile-material-dye-lab-sample-card", + "form": "a dye lab sample card, with repeat-unit grids, size grading bands, and cut-count summaries", + "lineage": "Builds from the legible conventions of the dye lab sample card.", + "tags": [ + "grid", + "grading", + "summary" + ] + }, + { + "id": "fashion-textile-material-dye-lab-process-wall", + "form": "a dye lab process wall, with surface-treatment samples, quality checkpoints, and care-instruction links", + "lineage": "Builds from the legible conventions of the dye lab process wall.", + "tags": [ + "finish", + "quality", + "care" + ] + }, + { + "id": "fashion-textile-material-weaving-swatch-book", + "form": "a weaving swatch book, with ordered material tabs, touch-safe sample windows, and fiber-content keys", + "lineage": "Builds from the legible conventions of the weaving swatch book.", + "tags": [ + "tabs", + "samples", + "keys" + ] + }, + { + "id": "fashion-textile-material-weaving-pattern-board", + "form": "a weaving pattern board, with nested pattern pieces, grain-direction arrows, and assembly notches", + "lineage": "Builds from the legible conventions of the weaving pattern board.", + "tags": [ + "nesting", + "direction", + "assembly" + ] + }, + { + "id": "fashion-textile-material-weaving-garment-rail", + "form": "a weaving garment rail, with look-number markers, silhouette groupings, and outfit-sequence dividers", + "lineage": "Builds from the legible conventions of the weaving garment rail.", + "tags": [ + "numbering", + "grouping", + "sequence" + ] + }, + { + "id": "fashion-textile-material-weaving-specification-sheet", + "form": "a weaving specification sheet, with dimension columns, tolerance notes, and construction callouts", + "lineage": "Builds from the legible conventions of the weaving specification sheet.", + "tags": [ + "dimensions", + "tolerance", + "callouts" + ] + }, + { + "id": "fashion-textile-material-weaving-sample-card", + "form": "a weaving sample card, with color-lot fields, finish comparisons, and approval-status stamps", + "lineage": "Builds from the legible conventions of the weaving sample card.", + "tags": [ + "color", + "comparison", + "status" + ] + }, + { + "id": "fashion-textile-material-weaving-process-wall", + "form": "a weaving process wall, with step-by-step stations, tool pairings, and result checkpoints", + "lineage": "Builds from the legible conventions of the weaving process wall.", + "tags": [ + "steps", + "tools", + "checkpoints" + ] + }, + { + "id": "fashion-textile-material-knit-swatch-book", + "form": "a knit swatch book, with before-and-after panels, repair-method labels, and durability notes", + "lineage": "Builds from the legible conventions of the knit swatch book.", + "tags": [ + "states", + "methods", + "durability" + ] + }, + { + "id": "fashion-textile-material-knit-pattern-board", + "form": "a knit pattern board, with occasion zones, layering suggestions, and availability markers", + "lineage": "Builds from the legible conventions of the knit pattern board.", + "tags": [ + "zoning", + "layering", + "availability" + ] + }, + { + "id": "fashion-textile-material-knit-garment-rail", + "form": "a knit garment rail, with entrance-to-finale order, changeover cues, and focal-look pauses", + "lineage": "Builds from the legible conventions of the knit garment rail.", + "tags": [ + "progression", + "cues", + "pauses" + ] + }, + { + "id": "fashion-textile-material-knit-specification-sheet", + "form": "a knit specification sheet, with measurement landmarks, adjustment pins, and fit-decision notes", + "lineage": "Builds from the legible conventions of the knit specification sheet.", + "tags": [ + "measurement", + "adjustment", + "decisions" + ] + }, + { + "id": "fashion-textile-material-knit-sample-card", + "form": "a knit sample card, with repeat-unit grids, size grading bands, and cut-count summaries", + "lineage": "Builds from the legible conventions of the knit sample card.", + "tags": [ + "grid", + "grading", + "summary" + ] + }, + { + "id": "fashion-textile-material-knit-process-wall", + "form": "a knit process wall, with surface-treatment samples, quality checkpoints, and care-instruction links", + "lineage": "Builds from the legible conventions of the knit process wall.", + "tags": [ + "finish", + "quality", + "care" + ] + }, + { + "id": "fashion-textile-material-repair-swatch-book", + "form": "a repair swatch book, with ordered material tabs, touch-safe sample windows, and fiber-content keys", + "lineage": "Builds from the legible conventions of the repair swatch book.", + "tags": [ + "tabs", + "samples", + "keys" + ] + }, + { + "id": "fashion-textile-material-repair-pattern-board", + "form": "a repair pattern board, with nested pattern pieces, grain-direction arrows, and assembly notches", + "lineage": "Builds from the legible conventions of the repair pattern board.", + "tags": [ + "nesting", + "direction", + "assembly" + ] + }, + { + "id": "fashion-textile-material-repair-garment-rail", + "form": "a repair garment rail, with look-number markers, silhouette groupings, and outfit-sequence dividers", + "lineage": "Builds from the legible conventions of the repair garment rail.", + "tags": [ + "numbering", + "grouping", + "sequence" + ] + }, + { + "id": "fashion-textile-material-repair-specification-sheet", + "form": "a repair specification sheet, with dimension columns, tolerance notes, and construction callouts", + "lineage": "Builds from the legible conventions of the repair specification sheet.", + "tags": [ + "dimensions", + "tolerance", + "callouts" + ] + }, + { + "id": "fashion-textile-material-repair-sample-card", + "form": "a repair sample card, with color-lot fields, finish comparisons, and approval-status stamps", + "lineage": "Builds from the legible conventions of the repair sample card.", + "tags": [ + "color", + "comparison", + "status" + ] + }, + { + "id": "fashion-textile-material-repair-process-wall", + "form": "a repair process wall, with step-by-step stations, tool pairings, and result checkpoints", + "lineage": "Builds from the legible conventions of the repair process wall.", + "tags": [ + "steps", + "tools", + "checkpoints" + ] + }, + { + "id": "fashion-textile-material-wardrobe-swatch-book", + "form": "a wardrobe swatch book, with before-and-after panels, repair-method labels, and durability notes", + "lineage": "Builds from the legible conventions of the wardrobe swatch book.", + "tags": [ + "states", + "methods", + "durability" + ] + }, + { + "id": "fashion-textile-material-wardrobe-pattern-board", + "form": "a wardrobe pattern board, with occasion zones, layering suggestions, and availability markers", + "lineage": "Builds from the legible conventions of the wardrobe pattern board.", + "tags": [ + "zoning", + "layering", + "availability" + ] + }, + { + "id": "fashion-textile-material-wardrobe-garment-rail", + "form": "a wardrobe garment rail, with entrance-to-finale order, changeover cues, and focal-look pauses", + "lineage": "Builds from the legible conventions of the wardrobe garment rail.", + "tags": [ + "progression", + "cues", + "pauses" + ] + }, + { + "id": "fashion-textile-material-wardrobe-specification-sheet", + "form": "a wardrobe specification sheet, with measurement landmarks, adjustment pins, and fit-decision notes", + "lineage": "Builds from the legible conventions of the wardrobe specification sheet.", + "tags": [ + "measurement", + "adjustment", + "decisions" + ] + }, + { + "id": "fashion-textile-material-wardrobe-sample-card", + "form": "a wardrobe sample card, with repeat-unit grids, size grading bands, and cut-count summaries", + "lineage": "Builds from the legible conventions of the wardrobe sample card.", + "tags": [ + "grid", + "grading", + "summary" + ] + }, + { + "id": "fashion-textile-material-wardrobe-process-wall", + "form": "a wardrobe process wall, with surface-treatment samples, quality checkpoints, and care-instruction links", + "lineage": "Builds from the legible conventions of the wardrobe process wall.", + "tags": [ + "finish", + "quality", + "care" + ] + }, + { + "id": "fashion-textile-material-runway-swatch-book", + "form": "a runway swatch book, with ordered material tabs, touch-safe sample windows, and fiber-content keys", + "lineage": "Builds from the legible conventions of the runway swatch book.", + "tags": [ + "tabs", + "samples", + "keys" + ] + }, + { + "id": "fashion-textile-material-runway-pattern-board", + "form": "a runway pattern board, with nested pattern pieces, grain-direction arrows, and assembly notches", + "lineage": "Builds from the legible conventions of the runway pattern board.", + "tags": [ + "nesting", + "direction", + "assembly" + ] + }, + { + "id": "fashion-textile-material-runway-garment-rail", + "form": "a runway garment rail, with look-number markers, silhouette groupings, and outfit-sequence dividers", + "lineage": "Builds from the legible conventions of the runway garment rail.", + "tags": [ + "numbering", + "grouping", + "sequence" + ] + }, + { + "id": "fashion-textile-material-runway-specification-sheet", + "form": "a runway specification sheet, with dimension columns, tolerance notes, and construction callouts", + "lineage": "Builds from the legible conventions of the runway specification sheet.", + "tags": [ + "dimensions", + "tolerance", + "callouts" + ] + }, + { + "id": "fashion-textile-material-runway-sample-card", + "form": "a runway sample card, with color-lot fields, finish comparisons, and approval-status stamps", + "lineage": "Builds from the legible conventions of the runway sample card.", + "tags": [ + "color", + "comparison", + "status" + ] + }, + { + "id": "fashion-textile-material-runway-process-wall", + "form": "a runway process wall, with step-by-step stations, tool pairings, and result checkpoints", + "lineage": "Builds from the legible conventions of the runway process wall.", + "tags": [ + "steps", + "tools", + "checkpoints" + ] + }, + { + "id": "fashion-textile-material-fitting-swatch-book", + "form": "a fitting swatch book, with before-and-after panels, repair-method labels, and durability notes", + "lineage": "Builds from the legible conventions of the fitting swatch book.", + "tags": [ + "states", + "methods", + "durability" + ] + }, + { + "id": "fashion-textile-material-fitting-pattern-board", + "form": "a fitting pattern board, with occasion zones, layering suggestions, and availability markers", + "lineage": "Builds from the legible conventions of the fitting pattern board.", + "tags": [ + "zoning", + "layering", + "availability" + ] + }, + { + "id": "fashion-textile-material-fitting-garment-rail", + "form": "a fitting garment rail, with entrance-to-finale order, changeover cues, and focal-look pauses", + "lineage": "Builds from the legible conventions of the fitting garment rail.", + "tags": [ + "progression", + "cues", + "pauses" + ] + }, + { + "id": "fashion-textile-material-fitting-specification-sheet", + "form": "a fitting specification sheet, with measurement landmarks, adjustment pins, and fit-decision notes", + "lineage": "Builds from the legible conventions of the fitting specification sheet.", + "tags": [ + "measurement", + "adjustment", + "decisions" + ] + }, + { + "id": "fashion-textile-material-fitting-sample-card", + "form": "a fitting sample card, with repeat-unit grids, size grading bands, and cut-count summaries", + "lineage": "Builds from the legible conventions of the fitting sample card.", + "tags": [ + "grid", + "grading", + "summary" + ] + }, + { + "id": "fashion-textile-material-fitting-process-wall", + "form": "a fitting process wall, with surface-treatment samples, quality checkpoints, and care-instruction links", + "lineage": "Builds from the legible conventions of the fitting process wall.", + "tags": [ + "finish", + "quality", + "care" + ] + }, + { + "id": "fashion-textile-material-pattern-swatch-book", + "form": "a pattern swatch book, with ordered material tabs, touch-safe sample windows, and fiber-content keys", + "lineage": "Builds from the legible conventions of the pattern swatch book.", + "tags": [ + "tabs", + "samples", + "keys" + ] + }, + { + "id": "fashion-textile-material-pattern-pattern-board", + "form": "a pattern pattern board, with nested pattern pieces, grain-direction arrows, and assembly notches", + "lineage": "Builds from the legible conventions of the pattern pattern board.", + "tags": [ + "nesting", + "direction", + "assembly" + ] + }, + { + "id": "fashion-textile-material-pattern-garment-rail", + "form": "a pattern garment rail, with look-number markers, silhouette groupings, and outfit-sequence dividers", + "lineage": "Builds from the legible conventions of the pattern garment rail.", + "tags": [ + "numbering", + "grouping", + "sequence" + ] + }, + { + "id": "fashion-textile-material-pattern-specification-sheet", + "form": "a pattern specification sheet, with dimension columns, tolerance notes, and construction callouts", + "lineage": "Builds from the legible conventions of the pattern specification sheet.", + "tags": [ + "dimensions", + "tolerance", + "callouts" + ] + } + ] + }, + { + "id": "domestic-everyday", + "label": "Domestic & everyday", + "description": "Familiar household and neighborhood systems whose affordances make organization immediately legible.", + "concepts": [ + { + "id": "domestic-everyday-kitchen-prep-task-board", + "form": "a kitchen prep task board, with ordered task rows, owner markers, and done-state flips", + "lineage": "Builds from the legible conventions of the kitchen prep task board.", + "tags": [ + "tasks", + "ownership", + "completion" + ] + }, + { + "id": "domestic-everyday-kitchen-prep-inventory-card", + "form": "a kitchen prep inventory card, with quantity fields, restock thresholds, and last-checked dates", + "lineage": "Builds from the legible conventions of the kitchen prep inventory card.", + "tags": [ + "quantity", + "threshold", + "recency" + ] + }, + { + "id": "domestic-everyday-kitchen-prep-labeled-shelf", + "form": "a kitchen prep labeled shelf, with category zones, front-facing labels, and empty-space restock cues", + "lineage": "Builds from the legible conventions of the kitchen prep labeled shelf.", + "tags": [ + "zoning", + "labels", + "restock" + ] + }, + { + "id": "domestic-everyday-kitchen-prep-instruction-strip", + "form": "a kitchen prep instruction strip, with picture-led steps, safety warnings, and completion checks", + "lineage": "Builds from the legible conventions of the kitchen prep instruction strip.", + "tags": [ + "steps", + "safety", + "checks" + ] + }, + { + "id": "domestic-everyday-kitchen-prep-timing-dial", + "form": "a kitchen prep timing dial, with elapsed-time sectors, next-action pointers, and pause-and-resume marks", + "lineage": "Builds from the legible conventions of the kitchen prep timing dial.", + "tags": [ + "timing", + "action", + "resume" + ] + }, + { + "id": "domestic-everyday-kitchen-prep-handoff-checklist", + "form": "a kitchen prep handoff checklist, with giver-and-receiver fields, exception notes, and sign-off boxes", + "lineage": "Builds from the legible conventions of the kitchen prep handoff checklist.", + "tags": [ + "handoff", + "exceptions", + "signoff" + ] + }, + { + "id": "domestic-everyday-pantry-stock-task-board", + "form": "a pantry stock task board, with prepare-use-clean phases, tool placement outlines, and reset-state examples", + "lineage": "Builds from the legible conventions of the pantry stock task board.", + "tags": [ + "phases", + "placement", + "reset" + ] + }, + { + "id": "domestic-everyday-pantry-stock-inventory-card", + "form": "a pantry stock inventory card, with keep-pack-donate columns, room destination codes, and box-count totals", + "lineage": "Builds from the legible conventions of the pantry stock inventory card.", + "tags": [ + "sorting", + "destination", + "totals" + ] + }, + { + "id": "domestic-everyday-pantry-stock-labeled-shelf", + "form": "a pantry stock labeled shelf, with arrival sequence cues, shared-space boundaries, and departure reminders", + "lineage": "Builds from the legible conventions of the pantry stock labeled shelf.", + "tags": [ + "arrival", + "boundaries", + "reminders" + ] + }, + { + "id": "domestic-everyday-pantry-stock-instruction-strip", + "form": "a pantry stock instruction strip, with day-by-day slots, ingredient reuse links, and prep-time bands", + "lineage": "Builds from the legible conventions of the pantry stock instruction strip.", + "tags": [ + "calendar", + "reuse", + "timing" + ] + }, + { + "id": "domestic-everyday-pantry-stock-timing-dial", + "form": "a pantry stock timing dial, with material sorting lanes, contamination examples, and collection-day markers", + "lineage": "Builds from the legible conventions of the pantry stock timing dial.", + "tags": [ + "sorting", + "examples", + "schedule" + ] + }, + { + "id": "domestic-everyday-pantry-stock-handoff-checklist", + "form": "a pantry stock handoff checklist, with fixed-variable groupings, running-balance lines, and review-date checkpoints", + "lineage": "Builds from the legible conventions of the pantry stock handoff checklist.", + "tags": [ + "grouping", + "balance", + "review" + ] + }, + { + "id": "domestic-everyday-laundry-cycle-task-board", + "form": "a laundry cycle task board, with ordered task rows, owner markers, and done-state flips", + "lineage": "Builds from the legible conventions of the laundry cycle task board.", + "tags": [ + "tasks", + "ownership", + "completion" + ] + }, + { + "id": "domestic-everyday-laundry-cycle-inventory-card", + "form": "a laundry cycle inventory card, with quantity fields, restock thresholds, and last-checked dates", + "lineage": "Builds from the legible conventions of the laundry cycle inventory card.", + "tags": [ + "quantity", + "threshold", + "recency" + ] + }, + { + "id": "domestic-everyday-laundry-cycle-labeled-shelf", + "form": "a laundry cycle labeled shelf, with category zones, front-facing labels, and empty-space restock cues", + "lineage": "Builds from the legible conventions of the laundry cycle labeled shelf.", + "tags": [ + "zoning", + "labels", + "restock" + ] + }, + { + "id": "domestic-everyday-laundry-cycle-instruction-strip", + "form": "a laundry cycle instruction strip, with picture-led steps, safety warnings, and completion checks", + "lineage": "Builds from the legible conventions of the laundry cycle instruction strip.", + "tags": [ + "steps", + "safety", + "checks" + ] + }, + { + "id": "domestic-everyday-laundry-cycle-timing-dial", + "form": "a laundry cycle timing dial, with elapsed-time sectors, next-action pointers, and pause-and-resume marks", + "lineage": "Builds from the legible conventions of the laundry cycle timing dial.", + "tags": [ + "timing", + "action", + "resume" + ] + }, + { + "id": "domestic-everyday-laundry-cycle-handoff-checklist", + "form": "a laundry cycle handoff checklist, with giver-and-receiver fields, exception notes, and sign-off boxes", + "lineage": "Builds from the legible conventions of the laundry cycle handoff checklist.", + "tags": [ + "handoff", + "exceptions", + "signoff" + ] + }, + { + "id": "domestic-everyday-home-repair-task-board", + "form": "a home repair task board, with prepare-use-clean phases, tool placement outlines, and reset-state examples", + "lineage": "Builds from the legible conventions of the home repair task board.", + "tags": [ + "phases", + "placement", + "reset" + ] + }, + { + "id": "domestic-everyday-home-repair-inventory-card", + "form": "a home repair inventory card, with keep-pack-donate columns, room destination codes, and box-count totals", + "lineage": "Builds from the legible conventions of the home repair inventory card.", + "tags": [ + "sorting", + "destination", + "totals" + ] + }, + { + "id": "domestic-everyday-home-repair-labeled-shelf", + "form": "a home repair labeled shelf, with arrival sequence cues, shared-space boundaries, and departure reminders", + "lineage": "Builds from the legible conventions of the home repair labeled shelf.", + "tags": [ + "arrival", + "boundaries", + "reminders" + ] + }, + { + "id": "domestic-everyday-home-repair-instruction-strip", + "form": "a home repair instruction strip, with day-by-day slots, ingredient reuse links, and prep-time bands", + "lineage": "Builds from the legible conventions of the home repair instruction strip.", + "tags": [ + "calendar", + "reuse", + "timing" + ] + }, + { + "id": "domestic-everyday-home-repair-timing-dial", + "form": "a home repair timing dial, with material sorting lanes, contamination examples, and collection-day markers", + "lineage": "Builds from the legible conventions of the home repair timing dial.", + "tags": [ + "sorting", + "examples", + "schedule" + ] + }, + { + "id": "domestic-everyday-home-repair-handoff-checklist", + "form": "a home repair handoff checklist, with fixed-variable groupings, running-balance lines, and review-date checkpoints", + "lineage": "Builds from the legible conventions of the home repair handoff checklist.", + "tags": [ + "grouping", + "balance", + "review" + ] + }, + { + "id": "domestic-everyday-tool-storage-task-board", + "form": "a tool storage task board, with ordered task rows, owner markers, and done-state flips", + "lineage": "Builds from the legible conventions of the tool storage task board.", + "tags": [ + "tasks", + "ownership", + "completion" + ] + }, + { + "id": "domestic-everyday-tool-storage-inventory-card", + "form": "a tool storage inventory card, with quantity fields, restock thresholds, and last-checked dates", + "lineage": "Builds from the legible conventions of the tool storage inventory card.", + "tags": [ + "quantity", + "threshold", + "recency" + ] + }, + { + "id": "domestic-everyday-tool-storage-labeled-shelf", + "form": "a tool storage labeled shelf, with category zones, front-facing labels, and empty-space restock cues", + "lineage": "Builds from the legible conventions of the tool storage labeled shelf.", + "tags": [ + "zoning", + "labels", + "restock" + ] + }, + { + "id": "domestic-everyday-tool-storage-instruction-strip", + "form": "a tool storage instruction strip, with picture-led steps, safety warnings, and completion checks", + "lineage": "Builds from the legible conventions of the tool storage instruction strip.", + "tags": [ + "steps", + "safety", + "checks" + ] + }, + { + "id": "domestic-everyday-tool-storage-timing-dial", + "form": "a tool storage timing dial, with elapsed-time sectors, next-action pointers, and pause-and-resume marks", + "lineage": "Builds from the legible conventions of the tool storage timing dial.", + "tags": [ + "timing", + "action", + "resume" + ] + }, + { + "id": "domestic-everyday-tool-storage-handoff-checklist", + "form": "a tool storage handoff checklist, with giver-and-receiver fields, exception notes, and sign-off boxes", + "lineage": "Builds from the legible conventions of the tool storage handoff checklist.", + "tags": [ + "handoff", + "exceptions", + "signoff" + ] + }, + { + "id": "domestic-everyday-shared-chores-task-board", + "form": "a shared chores task board, with prepare-use-clean phases, tool placement outlines, and reset-state examples", + "lineage": "Builds from the legible conventions of the shared chores task board.", + "tags": [ + "phases", + "placement", + "reset" + ] + }, + { + "id": "domestic-everyday-shared-chores-inventory-card", + "form": "a shared chores inventory card, with keep-pack-donate columns, room destination codes, and box-count totals", + "lineage": "Builds from the legible conventions of the shared chores inventory card.", + "tags": [ + "sorting", + "destination", + "totals" + ] + }, + { + "id": "domestic-everyday-shared-chores-labeled-shelf", + "form": "a shared chores labeled shelf, with arrival sequence cues, shared-space boundaries, and departure reminders", + "lineage": "Builds from the legible conventions of the shared chores labeled shelf.", + "tags": [ + "arrival", + "boundaries", + "reminders" + ] + }, + { + "id": "domestic-everyday-shared-chores-instruction-strip", + "form": "a shared chores instruction strip, with day-by-day slots, ingredient reuse links, and prep-time bands", + "lineage": "Builds from the legible conventions of the shared chores instruction strip.", + "tags": [ + "calendar", + "reuse", + "timing" + ] + }, + { + "id": "domestic-everyday-shared-chores-timing-dial", + "form": "a shared chores timing dial, with material sorting lanes, contamination examples, and collection-day markers", + "lineage": "Builds from the legible conventions of the shared chores timing dial.", + "tags": [ + "sorting", + "examples", + "schedule" + ] + }, + { + "id": "domestic-everyday-shared-chores-handoff-checklist", + "form": "a shared chores handoff checklist, with fixed-variable groupings, running-balance lines, and review-date checkpoints", + "lineage": "Builds from the legible conventions of the shared chores handoff checklist.", + "tags": [ + "grouping", + "balance", + "review" + ] + }, + { + "id": "domestic-everyday-seasonal-maintenance-task-board", + "form": "a seasonal maintenance task board, with ordered task rows, owner markers, and done-state flips", + "lineage": "Builds from the legible conventions of the seasonal maintenance task board.", + "tags": [ + "tasks", + "ownership", + "completion" + ] + }, + { + "id": "domestic-everyday-seasonal-maintenance-inventory-card", + "form": "a seasonal maintenance inventory card, with quantity fields, restock thresholds, and last-checked dates", + "lineage": "Builds from the legible conventions of the seasonal maintenance inventory card.", + "tags": [ + "quantity", + "threshold", + "recency" + ] + }, + { + "id": "domestic-everyday-seasonal-maintenance-labeled-shelf", + "form": "a seasonal maintenance labeled shelf, with category zones, front-facing labels, and empty-space restock cues", + "lineage": "Builds from the legible conventions of the seasonal maintenance labeled shelf.", + "tags": [ + "zoning", + "labels", + "restock" + ] + }, + { + "id": "domestic-everyday-seasonal-maintenance-instruction-strip", + "form": "a seasonal maintenance instruction strip, with picture-led steps, safety warnings, and completion checks", + "lineage": "Builds from the legible conventions of the seasonal maintenance instruction strip.", + "tags": [ + "steps", + "safety", + "checks" + ] + }, + { + "id": "domestic-everyday-seasonal-maintenance-timing-dial", + "form": "a seasonal maintenance timing dial, with elapsed-time sectors, next-action pointers, and pause-and-resume marks", + "lineage": "Builds from the legible conventions of the seasonal maintenance timing dial.", + "tags": [ + "timing", + "action", + "resume" + ] + }, + { + "id": "domestic-everyday-seasonal-maintenance-handoff-checklist", + "form": "a seasonal maintenance handoff checklist, with giver-and-receiver fields, exception notes, and sign-off boxes", + "lineage": "Builds from the legible conventions of the seasonal maintenance handoff checklist.", + "tags": [ + "handoff", + "exceptions", + "signoff" + ] + }, + { + "id": "domestic-everyday-moving-day-task-board", + "form": "a moving day task board, with prepare-use-clean phases, tool placement outlines, and reset-state examples", + "lineage": "Builds from the legible conventions of the moving day task board.", + "tags": [ + "phases", + "placement", + "reset" + ] + }, + { + "id": "domestic-everyday-moving-day-inventory-card", + "form": "a moving day inventory card, with keep-pack-donate columns, room destination codes, and box-count totals", + "lineage": "Builds from the legible conventions of the moving day inventory card.", + "tags": [ + "sorting", + "destination", + "totals" + ] + }, + { + "id": "domestic-everyday-moving-day-labeled-shelf", + "form": "a moving day labeled shelf, with arrival sequence cues, shared-space boundaries, and departure reminders", + "lineage": "Builds from the legible conventions of the moving day labeled shelf.", + "tags": [ + "arrival", + "boundaries", + "reminders" + ] + }, + { + "id": "domestic-everyday-moving-day-instruction-strip", + "form": "a moving day instruction strip, with day-by-day slots, ingredient reuse links, and prep-time bands", + "lineage": "Builds from the legible conventions of the moving day instruction strip.", + "tags": [ + "calendar", + "reuse", + "timing" + ] + }, + { + "id": "domestic-everyday-moving-day-timing-dial", + "form": "a moving day timing dial, with material sorting lanes, contamination examples, and collection-day markers", + "lineage": "Builds from the legible conventions of the moving day timing dial.", + "tags": [ + "sorting", + "examples", + "schedule" + ] + }, + { + "id": "domestic-everyday-moving-day-handoff-checklist", + "form": "a moving day handoff checklist, with fixed-variable groupings, running-balance lines, and review-date checkpoints", + "lineage": "Builds from the legible conventions of the moving day handoff checklist.", + "tags": [ + "grouping", + "balance", + "review" + ] + }, + { + "id": "domestic-everyday-guest-arrival-task-board", + "form": "a guest arrival task board, with ordered task rows, owner markers, and done-state flips", + "lineage": "Builds from the legible conventions of the guest arrival task board.", + "tags": [ + "tasks", + "ownership", + "completion" + ] + }, + { + "id": "domestic-everyday-guest-arrival-inventory-card", + "form": "a guest arrival inventory card, with quantity fields, restock thresholds, and last-checked dates", + "lineage": "Builds from the legible conventions of the guest arrival inventory card.", + "tags": [ + "quantity", + "threshold", + "recency" + ] + }, + { + "id": "domestic-everyday-guest-arrival-labeled-shelf", + "form": "a guest arrival labeled shelf, with category zones, front-facing labels, and empty-space restock cues", + "lineage": "Builds from the legible conventions of the guest arrival labeled shelf.", + "tags": [ + "zoning", + "labels", + "restock" + ] + }, + { + "id": "domestic-everyday-guest-arrival-instruction-strip", + "form": "a guest arrival instruction strip, with picture-led steps, safety warnings, and completion checks", + "lineage": "Builds from the legible conventions of the guest arrival instruction strip.", + "tags": [ + "steps", + "safety", + "checks" + ] + }, + { + "id": "domestic-everyday-guest-arrival-timing-dial", + "form": "a guest arrival timing dial, with elapsed-time sectors, next-action pointers, and pause-and-resume marks", + "lineage": "Builds from the legible conventions of the guest arrival timing dial.", + "tags": [ + "timing", + "action", + "resume" + ] + }, + { + "id": "domestic-everyday-guest-arrival-handoff-checklist", + "form": "a guest arrival handoff checklist, with giver-and-receiver fields, exception notes, and sign-off boxes", + "lineage": "Builds from the legible conventions of the guest arrival handoff checklist.", + "tags": [ + "handoff", + "exceptions", + "signoff" + ] + }, + { + "id": "domestic-everyday-meal-planning-task-board", + "form": "a meal planning task board, with prepare-use-clean phases, tool placement outlines, and reset-state examples", + "lineage": "Builds from the legible conventions of the meal planning task board.", + "tags": [ + "phases", + "placement", + "reset" + ] + }, + { + "id": "domestic-everyday-meal-planning-inventory-card", + "form": "a meal planning inventory card, with keep-pack-donate columns, room destination codes, and box-count totals", + "lineage": "Builds from the legible conventions of the meal planning inventory card.", + "tags": [ + "sorting", + "destination", + "totals" + ] + }, + { + "id": "domestic-everyday-meal-planning-labeled-shelf", + "form": "a meal planning labeled shelf, with arrival sequence cues, shared-space boundaries, and departure reminders", + "lineage": "Builds from the legible conventions of the meal planning labeled shelf.", + "tags": [ + "arrival", + "boundaries", + "reminders" + ] + }, + { + "id": "domestic-everyday-meal-planning-instruction-strip", + "form": "a meal planning instruction strip, with day-by-day slots, ingredient reuse links, and prep-time bands", + "lineage": "Builds from the legible conventions of the meal planning instruction strip.", + "tags": [ + "calendar", + "reuse", + "timing" + ] + }, + { + "id": "domestic-everyday-meal-planning-timing-dial", + "form": "a meal planning timing dial, with material sorting lanes, contamination examples, and collection-day markers", + "lineage": "Builds from the legible conventions of the meal planning timing dial.", + "tags": [ + "sorting", + "examples", + "schedule" + ] + }, + { + "id": "domestic-everyday-meal-planning-handoff-checklist", + "form": "a meal planning handoff checklist, with fixed-variable groupings, running-balance lines, and review-date checkpoints", + "lineage": "Builds from the legible conventions of the meal planning handoff checklist.", + "tags": [ + "grouping", + "balance", + "review" + ] + }, + { + "id": "domestic-everyday-recycling-task-board", + "form": "a recycling task board, with ordered task rows, owner markers, and done-state flips", + "lineage": "Builds from the legible conventions of the recycling task board.", + "tags": [ + "tasks", + "ownership", + "completion" + ] + }, + { + "id": "domestic-everyday-recycling-inventory-card", + "form": "a recycling inventory card, with quantity fields, restock thresholds, and last-checked dates", + "lineage": "Builds from the legible conventions of the recycling inventory card.", + "tags": [ + "quantity", + "threshold", + "recency" + ] + }, + { + "id": "domestic-everyday-recycling-labeled-shelf", + "form": "a recycling labeled shelf, with category zones, front-facing labels, and empty-space restock cues", + "lineage": "Builds from the legible conventions of the recycling labeled shelf.", + "tags": [ + "zoning", + "labels", + "restock" + ] + }, + { + "id": "domestic-everyday-recycling-instruction-strip", + "form": "a recycling instruction strip, with picture-led steps, safety warnings, and completion checks", + "lineage": "Builds from the legible conventions of the recycling instruction strip.", + "tags": [ + "steps", + "safety", + "checks" + ] + } + ] + }, + { + "id": "ritual-ceremony", + "label": "Ritual & ceremony", + "description": "Ordinary public and secular coordination forms with phases, roles, repetition, and acknowledgment.", + "concepts": [ + { + "id": "ritual-ceremony-graduation-arrival-sequence", + "form": "a graduation arrival sequence, with timed arrival windows, role-specific entry points, and visible readiness checks", + "lineage": "Builds from the legible conventions of the graduation arrival sequence.", + "tags": [ + "timing", + "roles", + "readiness" + ] + }, + { + "id": "ritual-ceremony-graduation-program-card", + "form": "a graduation program card, with ordered program blocks, speaker-or-activity labels, and duration markers", + "lineage": "Builds from the legible conventions of the graduation program card.", + "tags": [ + "order", + "labels", + "duration" + ] + }, + { + "id": "ritual-ceremony-graduation-seating-map", + "form": "a graduation seating map, with zone-coded sections, accessible route lines, and reserved-place symbols", + "lineage": "Builds from the legible conventions of the graduation seating map.", + "tags": [ + "zones", + "access", + "reservation" + ] + }, + { + "id": "ritual-ceremony-graduation-stage-cue-sheet", + "form": "a graduation stage cue sheet, with cue numbers, responsible-role columns, and go-hold-complete states", + "lineage": "Builds from the legible conventions of the graduation stage cue sheet.", + "tags": [ + "cues", + "responsibility", + "states" + ] + }, + { + "id": "ritual-ceremony-graduation-role-marker-system", + "form": "a graduation role marker system, with color-and-shape identifiers, plain-language role names, and handoff permissions", + "lineage": "Builds from the legible conventions of the graduation role marker system.", + "tags": [ + "identity", + "language", + "permissions" + ] + }, + { + "id": "ritual-ceremony-graduation-closing-handoff", + "form": "a graduation closing handoff, with recap prompts, next-owner fields, and formal completion marks", + "lineage": "Builds from the legible conventions of the graduation closing handoff.", + "tags": [ + "recap", + "ownership", + "closure" + ] + }, + { + "id": "ritual-ceremony-awards-arrival-sequence", + "form": "an awards arrival sequence, with gathering points, progression checkpoints, and late-arrival rejoin cues", + "lineage": "Builds from the legible conventions of the awards arrival sequence.", + "tags": [ + "gathering", + "progression", + "rejoin" + ] + }, + { + "id": "ritual-ceremony-awards-program-card", + "form": "an awards program card, with attention signals, shared response windows, and quiet reset intervals", + "lineage": "Builds from the legible conventions of the awards program card.", + "tags": [ + "attention", + "response", + "reset" + ] + }, + { + "id": "ritual-ceremony-awards-seating-map", + "form": "an awards seating map, with participation order, visible turn indicators, and skip-without-penalty options", + "lineage": "Builds from the legible conventions of the awards seating map.", + "tags": [ + "turns", + "visibility", + "choice" + ] + }, + { + "id": "ritual-ceremony-awards-stage-cue-sheet", + "form": "an awards stage cue sheet, with service flow lanes, dietary information keys, and table reset signals", + "lineage": "Builds from the legible conventions of the awards stage cue sheet.", + "tags": [ + "flow", + "information", + "reset" + ] + }, + { + "id": "ritual-ceremony-awards-role-marker-system", + "form": "an awards role marker system, with team assignment bands, supply pickup points, and end-of-shift returns", + "lineage": "Builds from the legible conventions of the awards role marker system.", + "tags": [ + "teams", + "supplies", + "return" + ] + }, + { + "id": "ritual-ceremony-awards-closing-handoff", + "form": "an awards closing handoff, with synchronized time marks, large shared numerals, and post-zero transition cues", + "lineage": "Builds from the legible conventions of the awards closing handoff.", + "tags": [ + "synchrony", + "numerals", + "transition" + ] + }, + { + "id": "ritual-ceremony-civic-welcome-arrival-sequence", + "form": "a civic welcome arrival sequence, with timed arrival windows, role-specific entry points, and visible readiness checks", + "lineage": "Builds from the legible conventions of the civic welcome arrival sequence.", + "tags": [ + "timing", + "roles", + "readiness" + ] + }, + { + "id": "ritual-ceremony-civic-welcome-program-card", + "form": "a civic welcome program card, with ordered program blocks, speaker-or-activity labels, and duration markers", + "lineage": "Builds from the legible conventions of the civic welcome program card.", + "tags": [ + "order", + "labels", + "duration" + ] + }, + { + "id": "ritual-ceremony-civic-welcome-seating-map", + "form": "a civic welcome seating map, with zone-coded sections, accessible route lines, and reserved-place symbols", + "lineage": "Builds from the legible conventions of the civic welcome seating map.", + "tags": [ + "zones", + "access", + "reservation" + ] + }, + { + "id": "ritual-ceremony-civic-welcome-stage-cue-sheet", + "form": "a civic welcome stage cue sheet, with cue numbers, responsible-role columns, and go-hold-complete states", + "lineage": "Builds from the legible conventions of the civic welcome stage cue sheet.", + "tags": [ + "cues", + "responsibility", + "states" + ] + }, + { + "id": "ritual-ceremony-civic-welcome-role-marker-system", + "form": "a civic welcome role marker system, with color-and-shape identifiers, plain-language role names, and handoff permissions", + "lineage": "Builds from the legible conventions of the civic welcome role marker system.", + "tags": [ + "identity", + "language", + "permissions" + ] + }, + { + "id": "ritual-ceremony-civic-welcome-closing-handoff", + "form": "a civic welcome closing handoff, with recap prompts, next-owner fields, and formal completion marks", + "lineage": "Builds from the legible conventions of the civic welcome closing handoff.", + "tags": [ + "recap", + "ownership", + "closure" + ] + }, + { + "id": "ritual-ceremony-public-meeting-arrival-sequence", + "form": "a public meeting arrival sequence, with gathering points, progression checkpoints, and late-arrival rejoin cues", + "lineage": "Builds from the legible conventions of the public meeting arrival sequence.", + "tags": [ + "gathering", + "progression", + "rejoin" + ] + }, + { + "id": "ritual-ceremony-public-meeting-program-card", + "form": "a public meeting program card, with attention signals, shared response windows, and quiet reset intervals", + "lineage": "Builds from the legible conventions of the public meeting program card.", + "tags": [ + "attention", + "response", + "reset" + ] + }, + { + "id": "ritual-ceremony-public-meeting-seating-map", + "form": "a public meeting seating map, with participation order, visible turn indicators, and skip-without-penalty options", + "lineage": "Builds from the legible conventions of the public meeting seating map.", + "tags": [ + "turns", + "visibility", + "choice" + ] + }, + { + "id": "ritual-ceremony-public-meeting-stage-cue-sheet", + "form": "a public meeting stage cue sheet, with service flow lanes, dietary information keys, and table reset signals", + "lineage": "Builds from the legible conventions of the public meeting stage cue sheet.", + "tags": [ + "flow", + "information", + "reset" + ] + }, + { + "id": "ritual-ceremony-public-meeting-role-marker-system", + "form": "a public meeting role marker system, with team assignment bands, supply pickup points, and end-of-shift returns", + "lineage": "Builds from the legible conventions of the public meeting role marker system.", + "tags": [ + "teams", + "supplies", + "return" + ] + }, + { + "id": "ritual-ceremony-public-meeting-closing-handoff", + "form": "a public meeting closing handoff, with synchronized time marks, large shared numerals, and post-zero transition cues", + "lineage": "Builds from the legible conventions of the public meeting closing handoff.", + "tags": [ + "synchrony", + "numerals", + "transition" + ] + }, + { + "id": "ritual-ceremony-town-hall-arrival-sequence", + "form": "a town hall arrival sequence, with timed arrival windows, role-specific entry points, and visible readiness checks", + "lineage": "Builds from the legible conventions of the town hall arrival sequence.", + "tags": [ + "timing", + "roles", + "readiness" + ] + }, + { + "id": "ritual-ceremony-town-hall-program-card", + "form": "a town hall program card, with ordered program blocks, speaker-or-activity labels, and duration markers", + "lineage": "Builds from the legible conventions of the town hall program card.", + "tags": [ + "order", + "labels", + "duration" + ] + }, + { + "id": "ritual-ceremony-town-hall-seating-map", + "form": "a town hall seating map, with zone-coded sections, accessible route lines, and reserved-place symbols", + "lineage": "Builds from the legible conventions of the town hall seating map.", + "tags": [ + "zones", + "access", + "reservation" + ] + }, + { + "id": "ritual-ceremony-town-hall-stage-cue-sheet", + "form": "a town hall stage cue sheet, with cue numbers, responsible-role columns, and go-hold-complete states", + "lineage": "Builds from the legible conventions of the town hall stage cue sheet.", + "tags": [ + "cues", + "responsibility", + "states" + ] + }, + { + "id": "ritual-ceremony-town-hall-role-marker-system", + "form": "a town hall role marker system, with color-and-shape identifiers, plain-language role names, and handoff permissions", + "lineage": "Builds from the legible conventions of the town hall role marker system.", + "tags": [ + "identity", + "language", + "permissions" + ] + }, + { + "id": "ritual-ceremony-town-hall-closing-handoff", + "form": "a town hall closing handoff, with recap prompts, next-owner fields, and formal completion marks", + "lineage": "Builds from the legible conventions of the town hall closing handoff.", + "tags": [ + "recap", + "ownership", + "closure" + ] + }, + { + "id": "ritual-ceremony-parade-arrival-sequence", + "form": "a parade arrival sequence, with gathering points, progression checkpoints, and late-arrival rejoin cues", + "lineage": "Builds from the legible conventions of the parade arrival sequence.", + "tags": [ + "gathering", + "progression", + "rejoin" + ] + }, + { + "id": "ritual-ceremony-parade-program-card", + "form": "a parade program card, with attention signals, shared response windows, and quiet reset intervals", + "lineage": "Builds from the legible conventions of the parade program card.", + "tags": [ + "attention", + "response", + "reset" + ] + }, + { + "id": "ritual-ceremony-parade-seating-map", + "form": "a parade seating map, with participation order, visible turn indicators, and skip-without-penalty options", + "lineage": "Builds from the legible conventions of the parade seating map.", + "tags": [ + "turns", + "visibility", + "choice" + ] + }, + { + "id": "ritual-ceremony-parade-stage-cue-sheet", + "form": "a parade stage cue sheet, with service flow lanes, dietary information keys, and table reset signals", + "lineage": "Builds from the legible conventions of the parade stage cue sheet.", + "tags": [ + "flow", + "information", + "reset" + ] + }, + { + "id": "ritual-ceremony-parade-role-marker-system", + "form": "a parade role marker system, with team assignment bands, supply pickup points, and end-of-shift returns", + "lineage": "Builds from the legible conventions of the parade role marker system.", + "tags": [ + "teams", + "supplies", + "return" + ] + }, + { + "id": "ritual-ceremony-parade-closing-handoff", + "form": "a parade closing handoff, with synchronized time marks, large shared numerals, and post-zero transition cues", + "lineage": "Builds from the legible conventions of the parade closing handoff.", + "tags": [ + "synchrony", + "numerals", + "transition" + ] + }, + { + "id": "ritual-ceremony-festival-entry-arrival-sequence", + "form": "a festival entry arrival sequence, with timed arrival windows, role-specific entry points, and visible readiness checks", + "lineage": "Builds from the legible conventions of the festival entry arrival sequence.", + "tags": [ + "timing", + "roles", + "readiness" + ] + }, + { + "id": "ritual-ceremony-festival-entry-program-card", + "form": "a festival entry program card, with ordered program blocks, speaker-or-activity labels, and duration markers", + "lineage": "Builds from the legible conventions of the festival entry program card.", + "tags": [ + "order", + "labels", + "duration" + ] + }, + { + "id": "ritual-ceremony-festival-entry-seating-map", + "form": "a festival entry seating map, with zone-coded sections, accessible route lines, and reserved-place symbols", + "lineage": "Builds from the legible conventions of the festival entry seating map.", + "tags": [ + "zones", + "access", + "reservation" + ] + }, + { + "id": "ritual-ceremony-festival-entry-stage-cue-sheet", + "form": "a festival entry stage cue sheet, with cue numbers, responsible-role columns, and go-hold-complete states", + "lineage": "Builds from the legible conventions of the festival entry stage cue sheet.", + "tags": [ + "cues", + "responsibility", + "states" + ] + }, + { + "id": "ritual-ceremony-festival-entry-role-marker-system", + "form": "a festival entry role marker system, with color-and-shape identifiers, plain-language role names, and handoff permissions", + "lineage": "Builds from the legible conventions of the festival entry role marker system.", + "tags": [ + "identity", + "language", + "permissions" + ] + }, + { + "id": "ritual-ceremony-festival-entry-closing-handoff", + "form": "a festival entry closing handoff, with recap prompts, next-owner fields, and formal completion marks", + "lineage": "Builds from the legible conventions of the festival entry closing handoff.", + "tags": [ + "recap", + "ownership", + "closure" + ] + }, + { + "id": "ritual-ceremony-conference-opening-arrival-sequence", + "form": "a conference opening arrival sequence, with gathering points, progression checkpoints, and late-arrival rejoin cues", + "lineage": "Builds from the legible conventions of the conference opening arrival sequence.", + "tags": [ + "gathering", + "progression", + "rejoin" + ] + }, + { + "id": "ritual-ceremony-conference-opening-program-card", + "form": "a conference opening program card, with attention signals, shared response windows, and quiet reset intervals", + "lineage": "Builds from the legible conventions of the conference opening program card.", + "tags": [ + "attention", + "response", + "reset" + ] + }, + { + "id": "ritual-ceremony-conference-opening-seating-map", + "form": "a conference opening seating map, with participation order, visible turn indicators, and skip-without-penalty options", + "lineage": "Builds from the legible conventions of the conference opening seating map.", + "tags": [ + "turns", + "visibility", + "choice" + ] + }, + { + "id": "ritual-ceremony-conference-opening-stage-cue-sheet", + "form": "a conference opening stage cue sheet, with service flow lanes, dietary information keys, and table reset signals", + "lineage": "Builds from the legible conventions of the conference opening stage cue sheet.", + "tags": [ + "flow", + "information", + "reset" + ] + }, + { + "id": "ritual-ceremony-conference-opening-role-marker-system", + "form": "a conference opening role marker system, with team assignment bands, supply pickup points, and end-of-shift returns", + "lineage": "Builds from the legible conventions of the conference opening role marker system.", + "tags": [ + "teams", + "supplies", + "return" + ] + }, + { + "id": "ritual-ceremony-conference-opening-closing-handoff", + "form": "a conference opening closing handoff, with synchronized time marks, large shared numerals, and post-zero transition cues", + "lineage": "Builds from the legible conventions of the conference opening closing handoff.", + "tags": [ + "synchrony", + "numerals", + "transition" + ] + }, + { + "id": "ritual-ceremony-exhibition-opening-arrival-sequence", + "form": "an exhibition opening arrival sequence, with timed arrival windows, role-specific entry points, and visible readiness checks", + "lineage": "Builds from the legible conventions of the exhibition opening arrival sequence.", + "tags": [ + "timing", + "roles", + "readiness" + ] + }, + { + "id": "ritual-ceremony-exhibition-opening-program-card", + "form": "an exhibition opening program card, with ordered program blocks, speaker-or-activity labels, and duration markers", + "lineage": "Builds from the legible conventions of the exhibition opening program card.", + "tags": [ + "order", + "labels", + "duration" + ] + }, + { + "id": "ritual-ceremony-exhibition-opening-seating-map", + "form": "an exhibition opening seating map, with zone-coded sections, accessible route lines, and reserved-place symbols", + "lineage": "Builds from the legible conventions of the exhibition opening seating map.", + "tags": [ + "zones", + "access", + "reservation" + ] + }, + { + "id": "ritual-ceremony-exhibition-opening-stage-cue-sheet", + "form": "an exhibition opening stage cue sheet, with cue numbers, responsible-role columns, and go-hold-complete states", + "lineage": "Builds from the legible conventions of the exhibition opening stage cue sheet.", + "tags": [ + "cues", + "responsibility", + "states" + ] + }, + { + "id": "ritual-ceremony-exhibition-opening-role-marker-system", + "form": "an exhibition opening role marker system, with color-and-shape identifiers, plain-language role names, and handoff permissions", + "lineage": "Builds from the legible conventions of the exhibition opening role marker system.", + "tags": [ + "identity", + "language", + "permissions" + ] + }, + { + "id": "ritual-ceremony-exhibition-opening-closing-handoff", + "form": "an exhibition opening closing handoff, with recap prompts, next-owner fields, and formal completion marks", + "lineage": "Builds from the legible conventions of the exhibition opening closing handoff.", + "tags": [ + "recap", + "ownership", + "closure" + ] + }, + { + "id": "ritual-ceremony-community-meal-arrival-sequence", + "form": "a community meal arrival sequence, with gathering points, progression checkpoints, and late-arrival rejoin cues", + "lineage": "Builds from the legible conventions of the community meal arrival sequence.", + "tags": [ + "gathering", + "progression", + "rejoin" + ] + }, + { + "id": "ritual-ceremony-community-meal-program-card", + "form": "a community meal program card, with attention signals, shared response windows, and quiet reset intervals", + "lineage": "Builds from the legible conventions of the community meal program card.", + "tags": [ + "attention", + "response", + "reset" + ] + }, + { + "id": "ritual-ceremony-community-meal-seating-map", + "form": "a community meal seating map, with participation order, visible turn indicators, and skip-without-penalty options", + "lineage": "Builds from the legible conventions of the community meal seating map.", + "tags": [ + "turns", + "visibility", + "choice" + ] + }, + { + "id": "ritual-ceremony-community-meal-stage-cue-sheet", + "form": "a community meal stage cue sheet, with service flow lanes, dietary information keys, and table reset signals", + "lineage": "Builds from the legible conventions of the community meal stage cue sheet.", + "tags": [ + "flow", + "information", + "reset" + ] + }, + { + "id": "ritual-ceremony-community-meal-role-marker-system", + "form": "a community meal role marker system, with team assignment bands, supply pickup points, and end-of-shift returns", + "lineage": "Builds from the legible conventions of the community meal role marker system.", + "tags": [ + "teams", + "supplies", + "return" + ] + }, + { + "id": "ritual-ceremony-community-meal-closing-handoff", + "form": "a community meal closing handoff, with synchronized time marks, large shared numerals, and post-zero transition cues", + "lineage": "Builds from the legible conventions of the community meal closing handoff.", + "tags": [ + "synchrony", + "numerals", + "transition" + ] + }, + { + "id": "ritual-ceremony-volunteer-day-arrival-sequence", + "form": "a volunteer day arrival sequence, with timed arrival windows, role-specific entry points, and visible readiness checks", + "lineage": "Builds from the legible conventions of the volunteer day arrival sequence.", + "tags": [ + "timing", + "roles", + "readiness" + ] + }, + { + "id": "ritual-ceremony-volunteer-day-program-card", + "form": "a volunteer day program card, with ordered program blocks, speaker-or-activity labels, and duration markers", + "lineage": "Builds from the legible conventions of the volunteer day program card.", + "tags": [ + "order", + "labels", + "duration" + ] + }, + { + "id": "ritual-ceremony-volunteer-day-seating-map", + "form": "a volunteer day seating map, with zone-coded sections, accessible route lines, and reserved-place symbols", + "lineage": "Builds from the legible conventions of the volunteer day seating map.", + "tags": [ + "zones", + "access", + "reservation" + ] + }, + { + "id": "ritual-ceremony-volunteer-day-stage-cue-sheet", + "form": "a volunteer day stage cue sheet, with cue numbers, responsible-role columns, and go-hold-complete states", + "lineage": "Builds from the legible conventions of the volunteer day stage cue sheet.", + "tags": [ + "cues", + "responsibility", + "states" + ] + } + ] + }, + { + "id": "global-manuscripts-knowledge", + "label": "Global manuscripts & knowledge", + "description": "Public knowledge formats from many writing traditions, translated through topology rather than ornament.", + "concepts": [ + { + "id": "global-manuscripts-knowledge-east-asian-handscroll", + "form": "an East Asian handscroll, with continuous lateral progression, scene breaks, and a terminal colophon", + "lineage": "Global manuscripts & knowledge", + "tags": [ + "reading-path", + "register-layer", + "section-marker" + ] + }, + { + "id": "global-manuscripts-knowledge-chinese-accordion-fold-book", + "form": "a Chinese accordion-fold book, with panel-by-panel progression and paired image-text registers", + "lineage": "Global manuscripts & knowledge", + "tags": [ + "reading-path", + "register-layer", + "section-marker" + ] + }, + { + "id": "global-manuscripts-knowledge-japanese-bento-box-partition", + "form": "a Japanese bento box partition, with distinct compartments and a central main focus", + "lineage": "Global manuscripts & knowledge", + "tags": [ + "reading-path", + "register-layer", + "section-marker" + ] + }, + { + "id": "global-manuscripts-knowledge-korean-folding-screen", + "form": "a Korean folding screen, with modular vertical panels, panoramic continuity, and a center axis", + "lineage": "Global manuscripts & knowledge", + "tags": [ + "reading-path", + "register-layer", + "section-marker" + ] + }, + { + "id": "global-manuscripts-knowledge-indian-palm-leaf-manuscript", + "form": "an Indian palm-leaf manuscript, with long horizontal folios, line bands around a binding axis, and leaf numbers", + "lineage": "Global manuscripts & knowledge", + "tags": [ + "reading-path", + "register-layer", + "section-marker" + ] + }, + { + "id": "global-manuscripts-knowledge-persian-manuscript-page", + "form": "a Persian manuscript page, with a central narrative panel, nested marginal commentary, and illuminated thresholds", + "lineage": "Global manuscripts & knowledge", + "tags": [ + "reading-path", + "register-layer", + "section-marker" + ] + }, + { + "id": "global-manuscripts-knowledge-annotated-manuscript-folio", + "form": "an annotated manuscript folio, with a central text block, marginal glosses, and tiered section markers", + "lineage": "Global manuscripts & knowledge", + "tags": [ + "reading-path", + "register-layer", + "section-marker" + ] + }, + { + "id": "global-manuscripts-knowledge-ethiopian-codex-spread", + "form": "an Ethiopian codex spread, with facing text columns, color-coded voices, and ornamental dividers", + "lineage": "Global manuscripts & knowledge", + "tags": [ + "reading-path", + "register-layer", + "section-marker" + ] + }, + { + "id": "global-manuscripts-knowledge-brazilian-cordel-chapbook", + "form": "a Brazilian cordel chapbook, with a declarative cover, short sequential sections, and illustrated breaks", + "lineage": "Global manuscripts & knowledge", + "tags": [ + "reading-path", + "register-layer", + "section-marker" + ] + }, + { + "id": "global-manuscripts-knowledge-aztec-tribute-codex", + "form": "an Aztec tribute codex, with pictorial quantity symbols, item icons, and origin-town signs", + "lineage": "Global manuscripts & knowledge", + "tags": [ + "reading-path", + "register-layer", + "section-marker" + ] + }, + { + "id": "global-manuscripts-knowledge-islamic-geometric-tile-system", + "form": "an Islamic geometric tile system, with interlocking symmetry lines, border courses, and calligraphic bands", + "lineage": "Global manuscripts & knowledge", + "tags": [ + "reading-path", + "register-layer", + "section-marker" + ] + }, + { + "id": "global-manuscripts-knowledge-african-block-print-textile-grid", + "form": "an African block-print textile grid, with repeating symbolic patterns, border frames, and color blocks", + "lineage": "Global manuscripts & knowledge", + "tags": [ + "reading-path", + "register-layer", + "section-marker" + ] + }, + { + "id": "global-manuscripts-knowledge-vintage-postcard-back", + "form": "a vintage postcard back, with a split-half line, message field, and stamp-and-address boxes", + "lineage": "Global manuscripts & knowledge", + "tags": [ + "reading-path", + "register-layer", + "section-marker" + ] + }, + { + "id": "global-manuscripts-knowledge-stamp-collector-s-album", + "form": "a stamp collector's album, with mounted specimens and perforation notes", + "lineage": "Global manuscripts & knowledge", + "tags": [ + "reading-path", + "register-layer", + "section-marker" + ] + }, + { + "id": "global-manuscripts-knowledge-sheet-music", + "form": "sheet music, with synchronized staves, movement markers, and recurring motifs", + "lineage": "Global manuscripts & knowledge", + "tags": [ + "reading-path", + "register-layer", + "section-marker" + ] + }, + { + "id": "global-manuscripts-knowledge-multilingual-classroom-accordion-fold-reference-book", + "form": "a multilingual classroom accordion-fold reference book, with panel-by-panel progression, continuous cross-panel diagrams, and a terminal summary", + "lineage": "Builds from the legible conventions of the multilingual classroom accordion-fold reference book.", + "tags": [ + "panels", + "continuity", + "summary" + ] + }, + { + "id": "global-manuscripts-knowledge-multilingual-classroom-side-stitched-workbook", + "form": "a multilingual classroom side-stitched workbook, with page-edge binding cues, numbered practice rows, and teacher-or-peer check marks", + "lineage": "Builds from the legible conventions of the multilingual classroom side-stitched workbook.", + "tags": [ + "binding", + "practice", + "verification" + ] + }, + { + "id": "global-manuscripts-knowledge-multilingual-classroom-parallel-text-primer", + "form": "a multilingual classroom parallel-text primer, with aligned language columns, shared image anchors, and line-by-line correspondence", + "lineage": "Builds from the legible conventions of the multilingual classroom parallel-text primer.", + "tags": [ + "parallel", + "anchors", + "alignment" + ] + }, + { + "id": "global-manuscripts-knowledge-multilingual-classroom-illustrated-mnemonic-chart", + "form": "a multilingual classroom illustrated mnemonic chart, with picture-led memory groups, sequence arrows, and compact recall captions", + "lineage": "Builds from the legible conventions of the multilingual classroom illustrated mnemonic chart.", + "tags": [ + "mnemonic", + "sequence", + "captions" + ] + }, + { + "id": "global-manuscripts-knowledge-multilingual-classroom-tabbed-field-manual", + "form": "a multilingual classroom tabbed field manual, with edge tabs, problem-to-procedure sections, and field-note blanks", + "lineage": "Builds from the legible conventions of the multilingual classroom tabbed field manual.", + "tags": [ + "tabs", + "procedure", + "notes" + ] + }, + { + "id": "global-manuscripts-knowledge-multilingual-classroom-portable-card-index", + "form": "a multilingual classroom portable card index, with standardized record fields, alphabetic dividers, and related-card references", + "lineage": "Builds from the legible conventions of the multilingual classroom portable card index.", + "tags": [ + "records", + "alphabetic", + "links" + ] + }, + { + "id": "global-manuscripts-knowledge-agricultural-extension-accordion-fold-reference-book", + "form": "an agricultural extension accordion-fold reference book, with seasonal action bands, local observation fields, and next-visit prompts", + "lineage": "Builds from the legible conventions of the agricultural extension accordion-fold reference book.", + "tags": [ + "season", + "observation", + "followup" + ] + }, + { + "id": "global-manuscripts-knowledge-agricultural-extension-side-stitched-workbook", + "form": "an agricultural extension side-stitched workbook, with demonstration stages, tool-and-material keys, and mastery checkpoints", + "lineage": "Builds from the legible conventions of the agricultural extension side-stitched workbook.", + "tags": [ + "stages", + "keys", + "mastery" + ] + }, + { + "id": "global-manuscripts-knowledge-agricultural-extension-parallel-text-primer", + "form": "an agricultural extension parallel-text primer, with counting columns, running totals, and transaction verification marks", + "lineage": "Builds from the legible conventions of the agricultural extension parallel-text primer.", + "tags": [ + "counting", + "totals", + "verification" + ] + }, + { + "id": "global-manuscripts-knowledge-agricultural-extension-illustrated-mnemonic-chart", + "form": "an agricultural extension illustrated mnemonic chart, with landmark sequences, distance intervals, and branch-choice diagrams", + "lineage": "Builds from the legible conventions of the agricultural extension illustrated mnemonic chart.", + "tags": [ + "landmarks", + "distance", + "branching" + ] + }, + { + "id": "global-manuscripts-knowledge-agricultural-extension-tabbed-field-manual", + "form": "an agricultural extension tabbed field manual, with dispatch-receipt fields, route stamps, and exception annotations", + "lineage": "Builds from the legible conventions of the agricultural extension tabbed field manual.", + "tags": [ + "dispatch", + "route", + "exceptions" + ] + }, + { + "id": "global-manuscripts-knowledge-agricultural-extension-portable-card-index", + "form": "an agricultural extension portable card index, with date-indexed entries, responsible-party fields, and amendment trails", + "lineage": "Builds from the legible conventions of the agricultural extension portable card index.", + "tags": [ + "dates", + "responsibility", + "amendment" + ] + }, + { + "id": "global-manuscripts-knowledge-public-health-accordion-fold-reference-book", + "form": "a public health accordion-fold reference book, with panel-by-panel progression, continuous cross-panel diagrams, and a terminal summary", + "lineage": "Builds from the legible conventions of the public health accordion-fold reference book.", + "tags": [ + "panels", + "continuity", + "summary" + ] + }, + { + "id": "global-manuscripts-knowledge-public-health-side-stitched-workbook", + "form": "a public health side-stitched workbook, with page-edge binding cues, numbered practice rows, and teacher-or-peer check marks", + "lineage": "Builds from the legible conventions of the public health side-stitched workbook.", + "tags": [ + "binding", + "practice", + "verification" + ] + }, + { + "id": "global-manuscripts-knowledge-public-health-parallel-text-primer", + "form": "a public health parallel-text primer, with aligned language columns, shared image anchors, and line-by-line correspondence", + "lineage": "Builds from the legible conventions of the public health parallel-text primer.", + "tags": [ + "parallel", + "anchors", + "alignment" + ] + }, + { + "id": "global-manuscripts-knowledge-public-health-illustrated-mnemonic-chart", + "form": "a public health illustrated mnemonic chart, with picture-led memory groups, sequence arrows, and compact recall captions", + "lineage": "Builds from the legible conventions of the public health illustrated mnemonic chart.", + "tags": [ + "mnemonic", + "sequence", + "captions" + ] + }, + { + "id": "global-manuscripts-knowledge-public-health-tabbed-field-manual", + "form": "a public health tabbed field manual, with edge tabs, problem-to-procedure sections, and field-note blanks", + "lineage": "Builds from the legible conventions of the public health tabbed field manual.", + "tags": [ + "tabs", + "procedure", + "notes" + ] + }, + { + "id": "global-manuscripts-knowledge-public-health-portable-card-index", + "form": "a public health portable card index, with standardized record fields, alphabetic dividers, and related-card references", + "lineage": "Builds from the legible conventions of the public health portable card index.", + "tags": [ + "records", + "alphabetic", + "links" + ] + }, + { + "id": "global-manuscripts-knowledge-craft-apprenticeship-accordion-fold-reference-book", + "form": "a craft apprenticeship accordion-fold reference book, with seasonal action bands, local observation fields, and next-visit prompts", + "lineage": "Builds from the legible conventions of the craft apprenticeship accordion-fold reference book.", + "tags": [ + "season", + "observation", + "followup" + ] + }, + { + "id": "global-manuscripts-knowledge-craft-apprenticeship-side-stitched-workbook", + "form": "a craft apprenticeship side-stitched workbook, with demonstration stages, tool-and-material keys, and mastery checkpoints", + "lineage": "Builds from the legible conventions of the craft apprenticeship side-stitched workbook.", + "tags": [ + "stages", + "keys", + "mastery" + ] + }, + { + "id": "global-manuscripts-knowledge-craft-apprenticeship-parallel-text-primer", + "form": "a craft apprenticeship parallel-text primer, with counting columns, running totals, and transaction verification marks", + "lineage": "Builds from the legible conventions of the craft apprenticeship parallel-text primer.", + "tags": [ + "counting", + "totals", + "verification" + ] + }, + { + "id": "global-manuscripts-knowledge-craft-apprenticeship-illustrated-mnemonic-chart", + "form": "a craft apprenticeship illustrated mnemonic chart, with landmark sequences, distance intervals, and branch-choice diagrams", + "lineage": "Builds from the legible conventions of the craft apprenticeship illustrated mnemonic chart.", + "tags": [ + "landmarks", + "distance", + "branching" + ] + }, + { + "id": "global-manuscripts-knowledge-craft-apprenticeship-tabbed-field-manual", + "form": "a craft apprenticeship tabbed field manual, with dispatch-receipt fields, route stamps, and exception annotations", + "lineage": "Builds from the legible conventions of the craft apprenticeship tabbed field manual.", + "tags": [ + "dispatch", + "route", + "exceptions" + ] + }, + { + "id": "global-manuscripts-knowledge-craft-apprenticeship-portable-card-index", + "form": "a craft apprenticeship portable card index, with date-indexed entries, responsible-party fields, and amendment trails", + "lineage": "Builds from the legible conventions of the craft apprenticeship portable card index.", + "tags": [ + "dates", + "responsibility", + "amendment" + ] + }, + { + "id": "global-manuscripts-knowledge-market-accounting-accordion-fold-reference-book", + "form": "a market accounting accordion-fold reference book, with panel-by-panel progression, continuous cross-panel diagrams, and a terminal summary", + "lineage": "Builds from the legible conventions of the market accounting accordion-fold reference book.", + "tags": [ + "panels", + "continuity", + "summary" + ] + }, + { + "id": "global-manuscripts-knowledge-market-accounting-side-stitched-workbook", + "form": "a market accounting side-stitched workbook, with page-edge binding cues, numbered practice rows, and teacher-or-peer check marks", + "lineage": "Builds from the legible conventions of the market accounting side-stitched workbook.", + "tags": [ + "binding", + "practice", + "verification" + ] + }, + { + "id": "global-manuscripts-knowledge-market-accounting-parallel-text-primer", + "form": "a market accounting parallel-text primer, with aligned language columns, shared image anchors, and line-by-line correspondence", + "lineage": "Builds from the legible conventions of the market accounting parallel-text primer.", + "tags": [ + "parallel", + "anchors", + "alignment" + ] + }, + { + "id": "global-manuscripts-knowledge-market-accounting-illustrated-mnemonic-chart", + "form": "a market accounting illustrated mnemonic chart, with picture-led memory groups, sequence arrows, and compact recall captions", + "lineage": "Builds from the legible conventions of the market accounting illustrated mnemonic chart.", + "tags": [ + "mnemonic", + "sequence", + "captions" + ] + }, + { + "id": "global-manuscripts-knowledge-market-accounting-tabbed-field-manual", + "form": "a market accounting tabbed field manual, with edge tabs, problem-to-procedure sections, and field-note blanks", + "lineage": "Builds from the legible conventions of the market accounting tabbed field manual.", + "tags": [ + "tabs", + "procedure", + "notes" + ] + }, + { + "id": "global-manuscripts-knowledge-market-accounting-portable-card-index", + "form": "a market accounting portable card index, with standardized record fields, alphabetic dividers, and related-card references", + "lineage": "Builds from the legible conventions of the market accounting portable card index.", + "tags": [ + "records", + "alphabetic", + "links" + ] + }, + { + "id": "global-manuscripts-knowledge-route-navigation-accordion-fold-reference-book", + "form": "a route navigation accordion-fold reference book, with seasonal action bands, local observation fields, and next-visit prompts", + "lineage": "Builds from the legible conventions of the route navigation accordion-fold reference book.", + "tags": [ + "season", + "observation", + "followup" + ] + }, + { + "id": "global-manuscripts-knowledge-route-navigation-side-stitched-workbook", + "form": "a route navigation side-stitched workbook, with demonstration stages, tool-and-material keys, and mastery checkpoints", + "lineage": "Builds from the legible conventions of the route navigation side-stitched workbook.", + "tags": [ + "stages", + "keys", + "mastery" + ] + }, + { + "id": "global-manuscripts-knowledge-route-navigation-parallel-text-primer", + "form": "a route navigation parallel-text primer, with counting columns, running totals, and transaction verification marks", + "lineage": "Builds from the legible conventions of the route navigation parallel-text primer.", + "tags": [ + "counting", + "totals", + "verification" + ] + }, + { + "id": "global-manuscripts-knowledge-route-navigation-illustrated-mnemonic-chart", + "form": "a route navigation illustrated mnemonic chart, with landmark sequences, distance intervals, and branch-choice diagrams", + "lineage": "Builds from the legible conventions of the route navigation illustrated mnemonic chart.", + "tags": [ + "landmarks", + "distance", + "branching" + ] + }, + { + "id": "global-manuscripts-knowledge-route-navigation-tabbed-field-manual", + "form": "a route navigation tabbed field manual, with dispatch-receipt fields, route stamps, and exception annotations", + "lineage": "Builds from the legible conventions of the route navigation tabbed field manual.", + "tags": [ + "dispatch", + "route", + "exceptions" + ] + }, + { + "id": "global-manuscripts-knowledge-route-navigation-portable-card-index", + "form": "a route navigation portable card index, with date-indexed entries, responsible-party fields, and amendment trails", + "lineage": "Builds from the legible conventions of the route navigation portable card index.", + "tags": [ + "dates", + "responsibility", + "amendment" + ] + }, + { + "id": "global-manuscripts-knowledge-postal-service-accordion-fold-reference-book", + "form": "a postal service accordion-fold reference book, with panel-by-panel progression, continuous cross-panel diagrams, and a terminal summary", + "lineage": "Builds from the legible conventions of the postal service accordion-fold reference book.", + "tags": [ + "panels", + "continuity", + "summary" + ] + }, + { + "id": "global-manuscripts-knowledge-postal-service-side-stitched-workbook", + "form": "a postal service side-stitched workbook, with page-edge binding cues, numbered practice rows, and teacher-or-peer check marks", + "lineage": "Builds from the legible conventions of the postal service side-stitched workbook.", + "tags": [ + "binding", + "practice", + "verification" + ] + }, + { + "id": "global-manuscripts-knowledge-postal-service-parallel-text-primer", + "form": "a postal service parallel-text primer, with aligned language columns, shared image anchors, and line-by-line correspondence", + "lineage": "Builds from the legible conventions of the postal service parallel-text primer.", + "tags": [ + "parallel", + "anchors", + "alignment" + ] + }, + { + "id": "global-manuscripts-knowledge-postal-service-illustrated-mnemonic-chart", + "form": "a postal service illustrated mnemonic chart, with picture-led memory groups, sequence arrows, and compact recall captions", + "lineage": "Builds from the legible conventions of the postal service illustrated mnemonic chart.", + "tags": [ + "mnemonic", + "sequence", + "captions" + ] + }, + { + "id": "global-manuscripts-knowledge-postal-service-tabbed-field-manual", + "form": "a postal service tabbed field manual, with edge tabs, problem-to-procedure sections, and field-note blanks", + "lineage": "Builds from the legible conventions of the postal service tabbed field manual.", + "tags": [ + "tabs", + "procedure", + "notes" + ] + }, + { + "id": "global-manuscripts-knowledge-postal-service-portable-card-index", + "form": "a postal service portable card index, with standardized record fields, alphabetic dividers, and related-card references", + "lineage": "Builds from the legible conventions of the postal service portable card index.", + "tags": [ + "records", + "alphabetic", + "links" + ] + }, + { + "id": "global-manuscripts-knowledge-civic-records-accordion-fold-reference-book", + "form": "a civic records accordion-fold reference book, with seasonal action bands, local observation fields, and next-visit prompts", + "lineage": "Builds from the legible conventions of the civic records accordion-fold reference book.", + "tags": [ + "season", + "observation", + "followup" + ] + }, + { + "id": "global-manuscripts-knowledge-civic-records-side-stitched-workbook", + "form": "a civic records side-stitched workbook, with demonstration stages, tool-and-material keys, and mastery checkpoints", + "lineage": "Builds from the legible conventions of the civic records side-stitched workbook.", + "tags": [ + "stages", + "keys", + "mastery" + ] + }, + { + "id": "global-manuscripts-knowledge-civic-records-parallel-text-primer", + "form": "a civic records parallel-text primer, with counting columns, running totals, and transaction verification marks", + "lineage": "Builds from the legible conventions of the civic records parallel-text primer.", + "tags": [ + "counting", + "totals", + "verification" + ] + }, + { + "id": "global-manuscripts-knowledge-civic-records-illustrated-mnemonic-chart", + "form": "a civic records illustrated mnemonic chart, with landmark sequences, distance intervals, and branch-choice diagrams", + "lineage": "Builds from the legible conventions of the civic records illustrated mnemonic chart.", + "tags": [ + "landmarks", + "distance", + "branching" + ] + }, + { + "id": "global-manuscripts-knowledge-civic-records-tabbed-field-manual", + "form": "a civic records tabbed field manual, with dispatch-receipt fields, route stamps, and exception annotations", + "lineage": "Builds from the legible conventions of the civic records tabbed field manual.", + "tags": [ + "dispatch", + "route", + "exceptions" + ] + }, + { + "id": "global-manuscripts-knowledge-civic-records-portable-card-index", + "form": "a civic records portable card index, with date-indexed entries, responsible-party fields, and amendment trails", + "lineage": "Builds from the legible conventions of the civic records portable card index.", + "tags": [ + "dates", + "responsibility", + "amendment" + ] + }, + { + "id": "global-manuscripts-knowledge-weather-observation-accordion-fold-reference-book", + "form": "a weather observation accordion-fold reference book, with panel-by-panel progression, continuous cross-panel diagrams, and a terminal summary", + "lineage": "Builds from the legible conventions of the weather observation accordion-fold reference book.", + "tags": [ + "panels", + "continuity", + "summary" + ] + } + ] + }, + { + "id": "activism-campaign", + "label": "Activism & campaign", + "description": "Collective-action systems with demands, participation, distributed evidence, escalation, and momentum.", + "concepts": [ + { + "id": "activism-campaign-petition-drive-field-packet", + "form": "a petition drive field packet, with objective-first sections, talking-point tiers, and field-note capture", + "lineage": "Builds from the legible conventions of the petition drive field packet.", + "tags": [ + "objective", + "messaging", + "notes" + ] + }, + { + "id": "activism-campaign-petition-drive-message-ladder", + "form": "a petition drive message ladder, with core-message steps, supporting-proof branches, and audience-specific adaptations", + "lineage": "Builds from the legible conventions of the petition drive message ladder.", + "tags": [ + "message", + "proof", + "adaptation" + ] + }, + { + "id": "activism-campaign-petition-drive-route-map", + "form": "a petition drive route map, with territory zones, assigned path segments, and completion markers", + "lineage": "Builds from the legible conventions of the petition drive route map.", + "tags": [ + "territory", + "assignment", + "completion" + ] + }, + { + "id": "activism-campaign-petition-drive-action-calendar", + "form": "a petition drive action calendar, with dated action windows, dependency links, and deadline emphasis", + "lineage": "Builds from the legible conventions of the petition drive action calendar.", + "tags": [ + "calendar", + "dependencies", + "deadlines" + ] + }, + { + "id": "activism-campaign-petition-drive-pledge-card-wall", + "form": "a petition drive pledge card wall, with individual commitment slots, public total counters, and follow-up status marks", + "lineage": "Builds from the legible conventions of the petition drive pledge card wall.", + "tags": [ + "commitment", + "totals", + "followup" + ] + }, + { + "id": "activism-campaign-petition-drive-progress-board", + "form": "a petition drive progress board, with goal-versus-actual bars, milestone thresholds, and blocker annotations", + "lineage": "Builds from the legible conventions of the petition drive progress board.", + "tags": [ + "goals", + "milestones", + "blockers" + ] + }, + { + "id": "activism-campaign-public-information-field-packet", + "form": "a public information field packet, with claim-evidence pairs, plain-language definitions, and decision-point summaries", + "lineage": "Builds from the legible conventions of the public information field packet.", + "tags": [ + "claims", + "definitions", + "decisions" + ] + }, + { + "id": "activism-campaign-public-information-message-ladder", + "form": "a public information message ladder, with question-led sections, myth-and-fact comparisons, and source trails", + "lineage": "Builds from the legible conventions of the public information message ladder.", + "tags": [ + "questions", + "comparison", + "sources" + ] + }, + { + "id": "activism-campaign-public-information-route-map", + "form": "a public information route map, with alert levels, role assignment rows, and time-boxed response steps", + "lineage": "Builds from the legible conventions of the public information route map.", + "tags": [ + "alerts", + "roles", + "response" + ] + }, + { + "id": "activism-campaign-public-information-action-calendar", + "form": "a public information action calendar, with member lanes, shared-principle overlaps, and decision handoff points", + "lineage": "Builds from the legible conventions of the public information action calendar.", + "tags": [ + "coalition", + "overlap", + "handoff" + ] + }, + { + "id": "activism-campaign-public-information-pledge-card-wall", + "form": "a public information pledge card wall, with arrival channels, participation options, and post-event next steps", + "lineage": "Builds from the legible conventions of the public information pledge card wall.", + "tags": [ + "arrival", + "participation", + "nextsteps" + ] + }, + { + "id": "activism-campaign-public-information-progress-board", + "form": "a public information progress board, with promise rows, dated evidence links, and open-or-closed status", + "lineage": "Builds from the legible conventions of the public information progress board.", + "tags": [ + "promises", + "evidence", + "status" + ] + }, + { + "id": "activism-campaign-neighborhood-canvass-field-packet", + "form": "a neighborhood canvass field packet, with objective-first sections, talking-point tiers, and field-note capture", + "lineage": "Builds from the legible conventions of the neighborhood canvass field packet.", + "tags": [ + "objective", + "messaging", + "notes" + ] + }, + { + "id": "activism-campaign-neighborhood-canvass-message-ladder", + "form": "a neighborhood canvass message ladder, with core-message steps, supporting-proof branches, and audience-specific adaptations", + "lineage": "Builds from the legible conventions of the neighborhood canvass message ladder.", + "tags": [ + "message", + "proof", + "adaptation" + ] + }, + { + "id": "activism-campaign-neighborhood-canvass-route-map", + "form": "a neighborhood canvass route map, with territory zones, assigned path segments, and completion markers", + "lineage": "Builds from the legible conventions of the neighborhood canvass route map.", + "tags": [ + "territory", + "assignment", + "completion" + ] + }, + { + "id": "activism-campaign-neighborhood-canvass-action-calendar", + "form": "a neighborhood canvass action calendar, with dated action windows, dependency links, and deadline emphasis", + "lineage": "Builds from the legible conventions of the neighborhood canvass action calendar.", + "tags": [ + "calendar", + "dependencies", + "deadlines" + ] + }, + { + "id": "activism-campaign-neighborhood-canvass-pledge-card-wall", + "form": "a neighborhood canvass pledge card wall, with individual commitment slots, public total counters, and follow-up status marks", + "lineage": "Builds from the legible conventions of the neighborhood canvass pledge card wall.", + "tags": [ + "commitment", + "totals", + "followup" + ] + }, + { + "id": "activism-campaign-neighborhood-canvass-progress-board", + "form": "a neighborhood canvass progress board, with goal-versus-actual bars, milestone thresholds, and blocker annotations", + "lineage": "Builds from the legible conventions of the neighborhood canvass progress board.", + "tags": [ + "goals", + "milestones", + "blockers" + ] + }, + { + "id": "activism-campaign-rally-coordination-field-packet", + "form": "a rally coordination field packet, with claim-evidence pairs, plain-language definitions, and decision-point summaries", + "lineage": "Builds from the legible conventions of the rally coordination field packet.", + "tags": [ + "claims", + "definitions", + "decisions" + ] + }, + { + "id": "activism-campaign-rally-coordination-message-ladder", + "form": "a rally coordination message ladder, with question-led sections, myth-and-fact comparisons, and source trails", + "lineage": "Builds from the legible conventions of the rally coordination message ladder.", + "tags": [ + "questions", + "comparison", + "sources" + ] + }, + { + "id": "activism-campaign-rally-coordination-route-map", + "form": "a rally coordination route map, with alert levels, role assignment rows, and time-boxed response steps", + "lineage": "Builds from the legible conventions of the rally coordination route map.", + "tags": [ + "alerts", + "roles", + "response" + ] + }, + { + "id": "activism-campaign-rally-coordination-action-calendar", + "form": "a rally coordination action calendar, with member lanes, shared-principle overlaps, and decision handoff points", + "lineage": "Builds from the legible conventions of the rally coordination action calendar.", + "tags": [ + "coalition", + "overlap", + "handoff" + ] + }, + { + "id": "activism-campaign-rally-coordination-pledge-card-wall", + "form": "a rally coordination pledge card wall, with arrival channels, participation options, and post-event next steps", + "lineage": "Builds from the legible conventions of the rally coordination pledge card wall.", + "tags": [ + "arrival", + "participation", + "nextsteps" + ] + }, + { + "id": "activism-campaign-rally-coordination-progress-board", + "form": "a rally coordination progress board, with promise rows, dated evidence links, and open-or-closed status", + "lineage": "Builds from the legible conventions of the rally coordination progress board.", + "tags": [ + "promises", + "evidence", + "status" + ] + }, + { + "id": "activism-campaign-volunteer-recruitment-field-packet", + "form": "a volunteer recruitment field packet, with objective-first sections, talking-point tiers, and field-note capture", + "lineage": "Builds from the legible conventions of the volunteer recruitment field packet.", + "tags": [ + "objective", + "messaging", + "notes" + ] + }, + { + "id": "activism-campaign-volunteer-recruitment-message-ladder", + "form": "a volunteer recruitment message ladder, with core-message steps, supporting-proof branches, and audience-specific adaptations", + "lineage": "Builds from the legible conventions of the volunteer recruitment message ladder.", + "tags": [ + "message", + "proof", + "adaptation" + ] + }, + { + "id": "activism-campaign-volunteer-recruitment-route-map", + "form": "a volunteer recruitment route map, with territory zones, assigned path segments, and completion markers", + "lineage": "Builds from the legible conventions of the volunteer recruitment route map.", + "tags": [ + "territory", + "assignment", + "completion" + ] + }, + { + "id": "activism-campaign-volunteer-recruitment-action-calendar", + "form": "a volunteer recruitment action calendar, with dated action windows, dependency links, and deadline emphasis", + "lineage": "Builds from the legible conventions of the volunteer recruitment action calendar.", + "tags": [ + "calendar", + "dependencies", + "deadlines" + ] + }, + { + "id": "activism-campaign-volunteer-recruitment-pledge-card-wall", + "form": "a volunteer recruitment pledge card wall, with individual commitment slots, public total counters, and follow-up status marks", + "lineage": "Builds from the legible conventions of the volunteer recruitment pledge card wall.", + "tags": [ + "commitment", + "totals", + "followup" + ] + }, + { + "id": "activism-campaign-volunteer-recruitment-progress-board", + "form": "a volunteer recruitment progress board, with goal-versus-actual bars, milestone thresholds, and blocker annotations", + "lineage": "Builds from the legible conventions of the volunteer recruitment progress board.", + "tags": [ + "goals", + "milestones", + "blockers" + ] + }, + { + "id": "activism-campaign-donation-drive-field-packet", + "form": "a donation drive field packet, with claim-evidence pairs, plain-language definitions, and decision-point summaries", + "lineage": "Builds from the legible conventions of the donation drive field packet.", + "tags": [ + "claims", + "definitions", + "decisions" + ] + }, + { + "id": "activism-campaign-donation-drive-message-ladder", + "form": "a donation drive message ladder, with question-led sections, myth-and-fact comparisons, and source trails", + "lineage": "Builds from the legible conventions of the donation drive message ladder.", + "tags": [ + "questions", + "comparison", + "sources" + ] + }, + { + "id": "activism-campaign-donation-drive-route-map", + "form": "a donation drive route map, with alert levels, role assignment rows, and time-boxed response steps", + "lineage": "Builds from the legible conventions of the donation drive route map.", + "tags": [ + "alerts", + "roles", + "response" + ] + }, + { + "id": "activism-campaign-donation-drive-action-calendar", + "form": "a donation drive action calendar, with member lanes, shared-principle overlaps, and decision handoff points", + "lineage": "Builds from the legible conventions of the donation drive action calendar.", + "tags": [ + "coalition", + "overlap", + "handoff" + ] + }, + { + "id": "activism-campaign-donation-drive-pledge-card-wall", + "form": "a donation drive pledge card wall, with arrival channels, participation options, and post-event next steps", + "lineage": "Builds from the legible conventions of the donation drive pledge card wall.", + "tags": [ + "arrival", + "participation", + "nextsteps" + ] + }, + { + "id": "activism-campaign-donation-drive-progress-board", + "form": "a donation drive progress board, with promise rows, dated evidence links, and open-or-closed status", + "lineage": "Builds from the legible conventions of the donation drive progress board.", + "tags": [ + "promises", + "evidence", + "status" + ] + }, + { + "id": "activism-campaign-policy-explainer-field-packet", + "form": "a policy explainer field packet, with objective-first sections, talking-point tiers, and field-note capture", + "lineage": "Builds from the legible conventions of the policy explainer field packet.", + "tags": [ + "objective", + "messaging", + "notes" + ] + }, + { + "id": "activism-campaign-policy-explainer-message-ladder", + "form": "a policy explainer message ladder, with core-message steps, supporting-proof branches, and audience-specific adaptations", + "lineage": "Builds from the legible conventions of the policy explainer message ladder.", + "tags": [ + "message", + "proof", + "adaptation" + ] + }, + { + "id": "activism-campaign-policy-explainer-route-map", + "form": "a policy explainer route map, with territory zones, assigned path segments, and completion markers", + "lineage": "Builds from the legible conventions of the policy explainer route map.", + "tags": [ + "territory", + "assignment", + "completion" + ] + }, + { + "id": "activism-campaign-policy-explainer-action-calendar", + "form": "a policy explainer action calendar, with dated action windows, dependency links, and deadline emphasis", + "lineage": "Builds from the legible conventions of the policy explainer action calendar.", + "tags": [ + "calendar", + "dependencies", + "deadlines" + ] + }, + { + "id": "activism-campaign-policy-explainer-pledge-card-wall", + "form": "a policy explainer pledge card wall, with individual commitment slots, public total counters, and follow-up status marks", + "lineage": "Builds from the legible conventions of the policy explainer pledge card wall.", + "tags": [ + "commitment", + "totals", + "followup" + ] + }, + { + "id": "activism-campaign-policy-explainer-progress-board", + "form": "a policy explainer progress board, with goal-versus-actual bars, milestone thresholds, and blocker annotations", + "lineage": "Builds from the legible conventions of the policy explainer progress board.", + "tags": [ + "goals", + "milestones", + "blockers" + ] + }, + { + "id": "activism-campaign-issue-education-field-packet", + "form": "an issue education field packet, with claim-evidence pairs, plain-language definitions, and decision-point summaries", + "lineage": "Builds from the legible conventions of the issue education field packet.", + "tags": [ + "claims", + "definitions", + "decisions" + ] + }, + { + "id": "activism-campaign-issue-education-message-ladder", + "form": "an issue education message ladder, with question-led sections, myth-and-fact comparisons, and source trails", + "lineage": "Builds from the legible conventions of the issue education message ladder.", + "tags": [ + "questions", + "comparison", + "sources" + ] + }, + { + "id": "activism-campaign-issue-education-route-map", + "form": "an issue education route map, with alert levels, role assignment rows, and time-boxed response steps", + "lineage": "Builds from the legible conventions of the issue education route map.", + "tags": [ + "alerts", + "roles", + "response" + ] + }, + { + "id": "activism-campaign-issue-education-action-calendar", + "form": "an issue education action calendar, with member lanes, shared-principle overlaps, and decision handoff points", + "lineage": "Builds from the legible conventions of the issue education action calendar.", + "tags": [ + "coalition", + "overlap", + "handoff" + ] + }, + { + "id": "activism-campaign-issue-education-pledge-card-wall", + "form": "an issue education pledge card wall, with arrival channels, participation options, and post-event next steps", + "lineage": "Builds from the legible conventions of the issue education pledge card wall.", + "tags": [ + "arrival", + "participation", + "nextsteps" + ] + }, + { + "id": "activism-campaign-issue-education-progress-board", + "form": "an issue education progress board, with promise rows, dated evidence links, and open-or-closed status", + "lineage": "Builds from the legible conventions of the issue education progress board.", + "tags": [ + "promises", + "evidence", + "status" + ] + }, + { + "id": "activism-campaign-rapid-response-field-packet", + "form": "a rapid response field packet, with objective-first sections, talking-point tiers, and field-note capture", + "lineage": "Builds from the legible conventions of the rapid response field packet.", + "tags": [ + "objective", + "messaging", + "notes" + ] + }, + { + "id": "activism-campaign-rapid-response-message-ladder", + "form": "a rapid response message ladder, with core-message steps, supporting-proof branches, and audience-specific adaptations", + "lineage": "Builds from the legible conventions of the rapid response message ladder.", + "tags": [ + "message", + "proof", + "adaptation" + ] + }, + { + "id": "activism-campaign-rapid-response-route-map", + "form": "a rapid response route map, with territory zones, assigned path segments, and completion markers", + "lineage": "Builds from the legible conventions of the rapid response route map.", + "tags": [ + "territory", + "assignment", + "completion" + ] + }, + { + "id": "activism-campaign-rapid-response-action-calendar", + "form": "a rapid response action calendar, with dated action windows, dependency links, and deadline emphasis", + "lineage": "Builds from the legible conventions of the rapid response action calendar.", + "tags": [ + "calendar", + "dependencies", + "deadlines" + ] + }, + { + "id": "activism-campaign-rapid-response-pledge-card-wall", + "form": "a rapid response pledge card wall, with individual commitment slots, public total counters, and follow-up status marks", + "lineage": "Builds from the legible conventions of the rapid response pledge card wall.", + "tags": [ + "commitment", + "totals", + "followup" + ] + }, + { + "id": "activism-campaign-rapid-response-progress-board", + "form": "a rapid response progress board, with goal-versus-actual bars, milestone thresholds, and blocker annotations", + "lineage": "Builds from the legible conventions of the rapid response progress board.", + "tags": [ + "goals", + "milestones", + "blockers" + ] + }, + { + "id": "activism-campaign-coalition-coordination-field-packet", + "form": "a coalition coordination field packet, with claim-evidence pairs, plain-language definitions, and decision-point summaries", + "lineage": "Builds from the legible conventions of the coalition coordination field packet.", + "tags": [ + "claims", + "definitions", + "decisions" + ] + }, + { + "id": "activism-campaign-coalition-coordination-message-ladder", + "form": "a coalition coordination message ladder, with question-led sections, myth-and-fact comparisons, and source trails", + "lineage": "Builds from the legible conventions of the coalition coordination message ladder.", + "tags": [ + "questions", + "comparison", + "sources" + ] + }, + { + "id": "activism-campaign-coalition-coordination-route-map", + "form": "a coalition coordination route map, with alert levels, role assignment rows, and time-boxed response steps", + "lineage": "Builds from the legible conventions of the coalition coordination route map.", + "tags": [ + "alerts", + "roles", + "response" + ] + }, + { + "id": "activism-campaign-coalition-coordination-action-calendar", + "form": "a coalition coordination action calendar, with member lanes, shared-principle overlaps, and decision handoff points", + "lineage": "Builds from the legible conventions of the coalition coordination action calendar.", + "tags": [ + "coalition", + "overlap", + "handoff" + ] + }, + { + "id": "activism-campaign-coalition-coordination-pledge-card-wall", + "form": "a coalition coordination pledge card wall, with arrival channels, participation options, and post-event next steps", + "lineage": "Builds from the legible conventions of the coalition coordination pledge card wall.", + "tags": [ + "arrival", + "participation", + "nextsteps" + ] + }, + { + "id": "activism-campaign-coalition-coordination-progress-board", + "form": "a coalition coordination progress board, with promise rows, dated evidence links, and open-or-closed status", + "lineage": "Builds from the legible conventions of the coalition coordination progress board.", + "tags": [ + "promises", + "evidence", + "status" + ] + }, + { + "id": "activism-campaign-event-mobilization-field-packet", + "form": "an event mobilization field packet, with objective-first sections, talking-point tiers, and field-note capture", + "lineage": "Builds from the legible conventions of the event mobilization field packet.", + "tags": [ + "objective", + "messaging", + "notes" + ] + }, + { + "id": "activism-campaign-event-mobilization-message-ladder", + "form": "an event mobilization message ladder, with core-message steps, supporting-proof branches, and audience-specific adaptations", + "lineage": "Builds from the legible conventions of the event mobilization message ladder.", + "tags": [ + "message", + "proof", + "adaptation" + ] + }, + { + "id": "activism-campaign-event-mobilization-route-map", + "form": "an event mobilization route map, with territory zones, assigned path segments, and completion markers", + "lineage": "Builds from the legible conventions of the event mobilization route map.", + "tags": [ + "territory", + "assignment", + "completion" + ] + }, + { + "id": "activism-campaign-event-mobilization-action-calendar", + "form": "an event mobilization action calendar, with dated action windows, dependency links, and deadline emphasis", + "lineage": "Builds from the legible conventions of the event mobilization action calendar.", + "tags": [ + "calendar", + "dependencies", + "deadlines" + ] + } + ] + }, + { + "id": "accessibility-assistive", + "label": "Accessibility & assistive", + "description": "Inclusive interaction systems built around alternatives, focus, confirmation, pacing, and adaptation.", + "concepts": [ + { + "id": "accessibility-assistive-low-vision-navigation-orientation-map", + "form": "a low vision navigation orientation map, with high-contrast landmarks, magnification-safe labels, and position-confirmation cues", + "lineage": "Builds from the legible conventions of the low vision navigation orientation map.", + "tags": [ + "contrast", + "scale", + "position" + ] + }, + { + "id": "accessibility-assistive-low-vision-navigation-instruction-sequence", + "form": "a low vision navigation instruction sequence, with raised route lines, distinct texture keys, and start-and-end anchors", + "lineage": "Builds from the legible conventions of the low vision navigation instruction sequence.", + "tags": [ + "tactile", + "texture", + "anchors" + ] + }, + { + "id": "accessibility-assistive-low-vision-navigation-control-legend", + "form": "a low vision navigation control legend, with speaker labels, sound-event captions, and synchronized transcript markers", + "lineage": "Builds from the legible conventions of the low vision navigation control legend.", + "tags": [ + "captions", + "events", + "synchrony" + ] + }, + { + "id": "accessibility-assistive-low-vision-navigation-preference-card", + "form": "a low vision navigation preference card, with logical focus order, shortcut groupings, and visible current-control state", + "lineage": "Builds from the legible conventions of the low vision navigation preference card.", + "tags": [ + "focus", + "shortcuts", + "state" + ] + }, + { + "id": "accessibility-assistive-low-vision-navigation-feedback-panel", + "form": "a low vision navigation feedback panel, with one-step chunks, plain-language summaries, and repeat-without-penalty controls", + "lineage": "Builds from the legible conventions of the low vision navigation feedback panel.", + "tags": [ + "chunking", + "language", + "repeat" + ] + }, + { + "id": "accessibility-assistive-low-vision-navigation-handoff-record", + "form": "a low vision navigation handoff record, with channel status lights, volume-or-mode indicators, and confirmation feedback", + "lineage": "Builds from the legible conventions of the low vision navigation handoff record.", + "tags": [ + "channel", + "indicators", + "feedback" + ] + }, + { + "id": "accessibility-assistive-tactile-orientation-orientation-map", + "form": "a tactile orientation orientation map, with slope-and-width data, rest-point intervals, and obstacle alternatives", + "lineage": "Builds from the legible conventions of the tactile orientation orientation map.", + "tags": [ + "dimensions", + "rest", + "alternatives" + ] + }, + { + "id": "accessibility-assistive-tactile-orientation-instruction-sequence", + "form": "a tactile orientation instruction sequence, with command examples, listening-state indicators, and correction-and-confirmation loops", + "lineage": "Builds from the legible conventions of the tactile orientation instruction sequence.", + "tags": [ + "commands", + "listening", + "correction" + ] + }, + { + "id": "accessibility-assistive-tactile-orientation-control-legend", + "form": "a tactile orientation control legend, with aligned translations, language switch landmarks, and untranslated-content warnings", + "lineage": "Builds from the legible conventions of the tactile orientation control legend.", + "tags": [ + "translation", + "switching", + "warnings" + ] + }, + { + "id": "accessibility-assistive-tactile-orientation-preference-card", + "form": "a tactile orientation preference card, with intensity settings, quiet-route choices, and pause-and-resume controls", + "lineage": "Builds from the legible conventions of the tactile orientation preference card.", + "tags": [ + "intensity", + "choice", + "pause" + ] + }, + { + "id": "accessibility-assistive-tactile-orientation-feedback-panel", + "form": "a tactile orientation feedback panel, with large action priorities, redundant visual-audible-tactile alerts, and assisted-exit routes", + "lineage": "Builds from the legible conventions of the tactile orientation feedback panel.", + "tags": [ + "priority", + "redundancy", + "exit" + ] + }, + { + "id": "accessibility-assistive-tactile-orientation-handoff-record", + "form": "a tactile orientation handoff record, with current-needs summary, change-since-last-time fields, and receiver confirmation", + "lineage": "Builds from the legible conventions of the tactile orientation handoff record.", + "tags": [ + "needs", + "change", + "confirmation" + ] + }, + { + "id": "accessibility-assistive-captioned-media-orientation-map", + "form": "a captioned media orientation map, with high-contrast landmarks, magnification-safe labels, and position-confirmation cues", + "lineage": "Builds from the legible conventions of the captioned media orientation map.", + "tags": [ + "contrast", + "scale", + "position" + ] + }, + { + "id": "accessibility-assistive-captioned-media-instruction-sequence", + "form": "a captioned media instruction sequence, with raised route lines, distinct texture keys, and start-and-end anchors", + "lineage": "Builds from the legible conventions of the captioned media instruction sequence.", + "tags": [ + "tactile", + "texture", + "anchors" + ] + }, + { + "id": "accessibility-assistive-captioned-media-control-legend", + "form": "a captioned media control legend, with speaker labels, sound-event captions, and synchronized transcript markers", + "lineage": "Builds from the legible conventions of the captioned media control legend.", + "tags": [ + "captions", + "events", + "synchrony" + ] + }, + { + "id": "accessibility-assistive-captioned-media-preference-card", + "form": "a captioned media preference card, with logical focus order, shortcut groupings, and visible current-control state", + "lineage": "Builds from the legible conventions of the captioned media preference card.", + "tags": [ + "focus", + "shortcuts", + "state" + ] + }, + { + "id": "accessibility-assistive-captioned-media-feedback-panel", + "form": "a captioned media feedback panel, with one-step chunks, plain-language summaries, and repeat-without-penalty controls", + "lineage": "Builds from the legible conventions of the captioned media feedback panel.", + "tags": [ + "chunking", + "language", + "repeat" + ] + }, + { + "id": "accessibility-assistive-captioned-media-handoff-record", + "form": "a captioned media handoff record, with channel status lights, volume-or-mode indicators, and confirmation feedback", + "lineage": "Builds from the legible conventions of the captioned media handoff record.", + "tags": [ + "channel", + "indicators", + "feedback" + ] + }, + { + "id": "accessibility-assistive-keyboard-operation-orientation-map", + "form": "a keyboard operation orientation map, with slope-and-width data, rest-point intervals, and obstacle alternatives", + "lineage": "Builds from the legible conventions of the keyboard operation orientation map.", + "tags": [ + "dimensions", + "rest", + "alternatives" + ] + }, + { + "id": "accessibility-assistive-keyboard-operation-instruction-sequence", + "form": "a keyboard operation instruction sequence, with command examples, listening-state indicators, and correction-and-confirmation loops", + "lineage": "Builds from the legible conventions of the keyboard operation instruction sequence.", + "tags": [ + "commands", + "listening", + "correction" + ] + }, + { + "id": "accessibility-assistive-keyboard-operation-control-legend", + "form": "a keyboard operation control legend, with aligned translations, language switch landmarks, and untranslated-content warnings", + "lineage": "Builds from the legible conventions of the keyboard operation control legend.", + "tags": [ + "translation", + "switching", + "warnings" + ] + }, + { + "id": "accessibility-assistive-keyboard-operation-preference-card", + "form": "a keyboard operation preference card, with intensity settings, quiet-route choices, and pause-and-resume controls", + "lineage": "Builds from the legible conventions of the keyboard operation preference card.", + "tags": [ + "intensity", + "choice", + "pause" + ] + }, + { + "id": "accessibility-assistive-keyboard-operation-feedback-panel", + "form": "a keyboard operation feedback panel, with large action priorities, redundant visual-audible-tactile alerts, and assisted-exit routes", + "lineage": "Builds from the legible conventions of the keyboard operation feedback panel.", + "tags": [ + "priority", + "redundancy", + "exit" + ] + }, + { + "id": "accessibility-assistive-keyboard-operation-handoff-record", + "form": "a keyboard operation handoff record, with current-needs summary, change-since-last-time fields, and receiver confirmation", + "lineage": "Builds from the legible conventions of the keyboard operation handoff record.", + "tags": [ + "needs", + "change", + "confirmation" + ] + }, + { + "id": "accessibility-assistive-cognitive-support-orientation-map", + "form": "a cognitive support orientation map, with high-contrast landmarks, magnification-safe labels, and position-confirmation cues", + "lineage": "Builds from the legible conventions of the cognitive support orientation map.", + "tags": [ + "contrast", + "scale", + "position" + ] + }, + { + "id": "accessibility-assistive-cognitive-support-instruction-sequence", + "form": "a cognitive support instruction sequence, with raised route lines, distinct texture keys, and start-and-end anchors", + "lineage": "Builds from the legible conventions of the cognitive support instruction sequence.", + "tags": [ + "tactile", + "texture", + "anchors" + ] + }, + { + "id": "accessibility-assistive-cognitive-support-control-legend", + "form": "a cognitive support control legend, with speaker labels, sound-event captions, and synchronized transcript markers", + "lineage": "Builds from the legible conventions of the cognitive support control legend.", + "tags": [ + "captions", + "events", + "synchrony" + ] + }, + { + "id": "accessibility-assistive-cognitive-support-preference-card", + "form": "a cognitive support preference card, with logical focus order, shortcut groupings, and visible current-control state", + "lineage": "Builds from the legible conventions of the cognitive support preference card.", + "tags": [ + "focus", + "shortcuts", + "state" + ] + }, + { + "id": "accessibility-assistive-cognitive-support-feedback-panel", + "form": "a cognitive support feedback panel, with one-step chunks, plain-language summaries, and repeat-without-penalty controls", + "lineage": "Builds from the legible conventions of the cognitive support feedback panel.", + "tags": [ + "chunking", + "language", + "repeat" + ] + }, + { + "id": "accessibility-assistive-cognitive-support-handoff-record", + "form": "a cognitive support handoff record, with channel status lights, volume-or-mode indicators, and confirmation feedback", + "lineage": "Builds from the legible conventions of the cognitive support handoff record.", + "tags": [ + "channel", + "indicators", + "feedback" + ] + }, + { + "id": "accessibility-assistive-hearing-assistance-orientation-map", + "form": "a hearing assistance orientation map, with slope-and-width data, rest-point intervals, and obstacle alternatives", + "lineage": "Builds from the legible conventions of the hearing assistance orientation map.", + "tags": [ + "dimensions", + "rest", + "alternatives" + ] + }, + { + "id": "accessibility-assistive-hearing-assistance-instruction-sequence", + "form": "a hearing assistance instruction sequence, with command examples, listening-state indicators, and correction-and-confirmation loops", + "lineage": "Builds from the legible conventions of the hearing assistance instruction sequence.", + "tags": [ + "commands", + "listening", + "correction" + ] + }, + { + "id": "accessibility-assistive-hearing-assistance-control-legend", + "form": "a hearing assistance control legend, with aligned translations, language switch landmarks, and untranslated-content warnings", + "lineage": "Builds from the legible conventions of the hearing assistance control legend.", + "tags": [ + "translation", + "switching", + "warnings" + ] + }, + { + "id": "accessibility-assistive-hearing-assistance-preference-card", + "form": "a hearing assistance preference card, with intensity settings, quiet-route choices, and pause-and-resume controls", + "lineage": "Builds from the legible conventions of the hearing assistance preference card.", + "tags": [ + "intensity", + "choice", + "pause" + ] + }, + { + "id": "accessibility-assistive-hearing-assistance-feedback-panel", + "form": "a hearing assistance feedback panel, with large action priorities, redundant visual-audible-tactile alerts, and assisted-exit routes", + "lineage": "Builds from the legible conventions of the hearing assistance feedback panel.", + "tags": [ + "priority", + "redundancy", + "exit" + ] + }, + { + "id": "accessibility-assistive-hearing-assistance-handoff-record", + "form": "a hearing assistance handoff record, with current-needs summary, change-since-last-time fields, and receiver confirmation", + "lineage": "Builds from the legible conventions of the hearing assistance handoff record.", + "tags": [ + "needs", + "change", + "confirmation" + ] + }, + { + "id": "accessibility-assistive-mobility-planning-orientation-map", + "form": "a mobility planning orientation map, with high-contrast landmarks, magnification-safe labels, and position-confirmation cues", + "lineage": "Builds from the legible conventions of the mobility planning orientation map.", + "tags": [ + "contrast", + "scale", + "position" + ] + }, + { + "id": "accessibility-assistive-mobility-planning-instruction-sequence", + "form": "a mobility planning instruction sequence, with raised route lines, distinct texture keys, and start-and-end anchors", + "lineage": "Builds from the legible conventions of the mobility planning instruction sequence.", + "tags": [ + "tactile", + "texture", + "anchors" + ] + }, + { + "id": "accessibility-assistive-mobility-planning-control-legend", + "form": "a mobility planning control legend, with speaker labels, sound-event captions, and synchronized transcript markers", + "lineage": "Builds from the legible conventions of the mobility planning control legend.", + "tags": [ + "captions", + "events", + "synchrony" + ] + }, + { + "id": "accessibility-assistive-mobility-planning-preference-card", + "form": "a mobility planning preference card, with logical focus order, shortcut groupings, and visible current-control state", + "lineage": "Builds from the legible conventions of the mobility planning preference card.", + "tags": [ + "focus", + "shortcuts", + "state" + ] + }, + { + "id": "accessibility-assistive-mobility-planning-feedback-panel", + "form": "a mobility planning feedback panel, with one-step chunks, plain-language summaries, and repeat-without-penalty controls", + "lineage": "Builds from the legible conventions of the mobility planning feedback panel.", + "tags": [ + "chunking", + "language", + "repeat" + ] + }, + { + "id": "accessibility-assistive-mobility-planning-handoff-record", + "form": "a mobility planning handoff record, with channel status lights, volume-or-mode indicators, and confirmation feedback", + "lineage": "Builds from the legible conventions of the mobility planning handoff record.", + "tags": [ + "channel", + "indicators", + "feedback" + ] + }, + { + "id": "accessibility-assistive-speech-input-orientation-map", + "form": "a speech input orientation map, with slope-and-width data, rest-point intervals, and obstacle alternatives", + "lineage": "Builds from the legible conventions of the speech input orientation map.", + "tags": [ + "dimensions", + "rest", + "alternatives" + ] + }, + { + "id": "accessibility-assistive-speech-input-instruction-sequence", + "form": "a speech input instruction sequence, with command examples, listening-state indicators, and correction-and-confirmation loops", + "lineage": "Builds from the legible conventions of the speech input instruction sequence.", + "tags": [ + "commands", + "listening", + "correction" + ] + }, + { + "id": "accessibility-assistive-speech-input-control-legend", + "form": "a speech input control legend, with aligned translations, language switch landmarks, and untranslated-content warnings", + "lineage": "Builds from the legible conventions of the speech input control legend.", + "tags": [ + "translation", + "switching", + "warnings" + ] + }, + { + "id": "accessibility-assistive-speech-input-preference-card", + "form": "a speech input preference card, with intensity settings, quiet-route choices, and pause-and-resume controls", + "lineage": "Builds from the legible conventions of the speech input preference card.", + "tags": [ + "intensity", + "choice", + "pause" + ] + }, + { + "id": "accessibility-assistive-speech-input-feedback-panel", + "form": "a speech input feedback panel, with large action priorities, redundant visual-audible-tactile alerts, and assisted-exit routes", + "lineage": "Builds from the legible conventions of the speech input feedback panel.", + "tags": [ + "priority", + "redundancy", + "exit" + ] + }, + { + "id": "accessibility-assistive-speech-input-handoff-record", + "form": "a speech input handoff record, with current-needs summary, change-since-last-time fields, and receiver confirmation", + "lineage": "Builds from the legible conventions of the speech input handoff record.", + "tags": [ + "needs", + "change", + "confirmation" + ] + }, + { + "id": "accessibility-assistive-multilingual-access-orientation-map", + "form": "a multilingual access orientation map, with high-contrast landmarks, magnification-safe labels, and position-confirmation cues", + "lineage": "Builds from the legible conventions of the multilingual access orientation map.", + "tags": [ + "contrast", + "scale", + "position" + ] + }, + { + "id": "accessibility-assistive-multilingual-access-instruction-sequence", + "form": "a multilingual access instruction sequence, with raised route lines, distinct texture keys, and start-and-end anchors", + "lineage": "Builds from the legible conventions of the multilingual access instruction sequence.", + "tags": [ + "tactile", + "texture", + "anchors" + ] + }, + { + "id": "accessibility-assistive-multilingual-access-control-legend", + "form": "a multilingual access control legend, with speaker labels, sound-event captions, and synchronized transcript markers", + "lineage": "Builds from the legible conventions of the multilingual access control legend.", + "tags": [ + "captions", + "events", + "synchrony" + ] + }, + { + "id": "accessibility-assistive-multilingual-access-preference-card", + "form": "a multilingual access preference card, with logical focus order, shortcut groupings, and visible current-control state", + "lineage": "Builds from the legible conventions of the multilingual access preference card.", + "tags": [ + "focus", + "shortcuts", + "state" + ] + }, + { + "id": "accessibility-assistive-multilingual-access-feedback-panel", + "form": "a multilingual access feedback panel, with one-step chunks, plain-language summaries, and repeat-without-penalty controls", + "lineage": "Builds from the legible conventions of the multilingual access feedback panel.", + "tags": [ + "chunking", + "language", + "repeat" + ] + }, + { + "id": "accessibility-assistive-multilingual-access-handoff-record", + "form": "a multilingual access handoff record, with channel status lights, volume-or-mode indicators, and confirmation feedback", + "lineage": "Builds from the legible conventions of the multilingual access handoff record.", + "tags": [ + "channel", + "indicators", + "feedback" + ] + }, + { + "id": "accessibility-assistive-sensory-regulation-orientation-map", + "form": "a sensory regulation orientation map, with slope-and-width data, rest-point intervals, and obstacle alternatives", + "lineage": "Builds from the legible conventions of the sensory regulation orientation map.", + "tags": [ + "dimensions", + "rest", + "alternatives" + ] + }, + { + "id": "accessibility-assistive-sensory-regulation-instruction-sequence", + "form": "a sensory regulation instruction sequence, with command examples, listening-state indicators, and correction-and-confirmation loops", + "lineage": "Builds from the legible conventions of the sensory regulation instruction sequence.", + "tags": [ + "commands", + "listening", + "correction" + ] + }, + { + "id": "accessibility-assistive-sensory-regulation-control-legend", + "form": "a sensory regulation control legend, with aligned translations, language switch landmarks, and untranslated-content warnings", + "lineage": "Builds from the legible conventions of the sensory regulation control legend.", + "tags": [ + "translation", + "switching", + "warnings" + ] + }, + { + "id": "accessibility-assistive-sensory-regulation-preference-card", + "form": "a sensory regulation preference card, with intensity settings, quiet-route choices, and pause-and-resume controls", + "lineage": "Builds from the legible conventions of the sensory regulation preference card.", + "tags": [ + "intensity", + "choice", + "pause" + ] + }, + { + "id": "accessibility-assistive-sensory-regulation-feedback-panel", + "form": "a sensory regulation feedback panel, with large action priorities, redundant visual-audible-tactile alerts, and assisted-exit routes", + "lineage": "Builds from the legible conventions of the sensory regulation feedback panel.", + "tags": [ + "priority", + "redundancy", + "exit" + ] + }, + { + "id": "accessibility-assistive-sensory-regulation-handoff-record", + "form": "a sensory regulation handoff record, with current-needs summary, change-since-last-time fields, and receiver confirmation", + "lineage": "Builds from the legible conventions of the sensory regulation handoff record.", + "tags": [ + "needs", + "change", + "confirmation" + ] + }, + { + "id": "accessibility-assistive-emergency-access-orientation-map", + "form": "an emergency access orientation map, with high-contrast landmarks, magnification-safe labels, and position-confirmation cues", + "lineage": "Builds from the legible conventions of the emergency access orientation map.", + "tags": [ + "contrast", + "scale", + "position" + ] + }, + { + "id": "accessibility-assistive-emergency-access-instruction-sequence", + "form": "an emergency access instruction sequence, with raised route lines, distinct texture keys, and start-and-end anchors", + "lineage": "Builds from the legible conventions of the emergency access instruction sequence.", + "tags": [ + "tactile", + "texture", + "anchors" + ] + }, + { + "id": "accessibility-assistive-emergency-access-control-legend", + "form": "an emergency access control legend, with speaker labels, sound-event captions, and synchronized transcript markers", + "lineage": "Builds from the legible conventions of the emergency access control legend.", + "tags": [ + "captions", + "events", + "synchrony" + ] + }, + { + "id": "accessibility-assistive-emergency-access-preference-card", + "form": "an emergency access preference card, with logical focus order, shortcut groupings, and visible current-control state", + "lineage": "Builds from the legible conventions of the emergency access preference card.", + "tags": [ + "focus", + "shortcuts", + "state" + ] + } + ] + }, + { + "id": "speculative-critical", + "label": "Speculative & critical", + "description": "Inquiry systems that expose assumptions through scenarios, evidence, consequences, and debate.", + "concepts": [ + { + "id": "speculative-critical-future-household-scenario-card-deck", + "form": "a future household scenario card deck, with assumption cards, wildcard interruptions, and end-state comparisons", + "lineage": "Builds from the legible conventions of the future household scenario card deck.", + "tags": [ + "assumptions", + "wildcards", + "outcomes" + ] + }, + { + "id": "speculative-critical-future-household-service-receipt", + "form": "a future household service receipt, with itemized hidden costs, data-or-resource line items, and terms-of-use footnotes", + "lineage": "Builds from the legible conventions of the future household service receipt.", + "tags": [ + "costs", + "resources", + "terms" + ] + }, + { + "id": "speculative-critical-future-household-policy-prototype", + "form": "a future household policy prototype, with eligibility clauses, decision pathways, and appeal-or-exit routes", + "lineage": "Builds from the legible conventions of the future household policy prototype.", + "tags": [ + "rules", + "pathways", + "appeal" + ] + }, + { + "id": "speculative-critical-future-household-object-catalog", + "form": "a future household object catalog, with numbered future objects, use-case captions, and unintended-effect notes", + "lineage": "Builds from the legible conventions of the future household object catalog.", + "tags": [ + "objects", + "usecases", + "effects" + ] + }, + { + "id": "speculative-critical-future-household-timeline-wall", + "form": "a future household timeline wall, with near-mid-far horizons, trigger events, and branching possibility bands", + "lineage": "Builds from the legible conventions of the future household timeline wall.", + "tags": [ + "horizons", + "triggers", + "branches" + ] + }, + { + "id": "speculative-critical-future-household-consequence-map", + "form": "a future household consequence map, with first-order effects, downstream ripple paths, and affected-party markers", + "lineage": "Builds from the legible conventions of the future household consequence map.", + "tags": [ + "effects", + "ripples", + "stakeholders" + ] + }, + { + "id": "speculative-critical-future-transit-scenario-card-deck", + "form": "a future transit scenario card deck, with baseline-present comparisons, changed norm indicators, and residual tension notes", + "lineage": "Builds from the legible conventions of the future transit scenario card deck.", + "tags": [ + "baseline", + "norms", + "tension" + ] + }, + { + "id": "speculative-critical-future-transit-service-receipt", + "form": "a future transit service receipt, with choice points, visible tradeoff scales, and reversible-or-locked states", + "lineage": "Builds from the legible conventions of the future transit service receipt.", + "tags": [ + "choices", + "tradeoffs", + "reversibility" + ] + }, + { + "id": "speculative-critical-future-transit-policy-prototype", + "form": "a future transit policy prototype, with institutional voice panels, personal account panels, and conflict annotations", + "lineage": "Builds from the legible conventions of the future transit policy prototype.", + "tags": [ + "voices", + "accounts", + "conflict" + ] + }, + { + "id": "speculative-critical-future-transit-object-catalog", + "form": "a future transit object catalog, with resource-flow arrows, gain-and-loss zones, and externality callouts", + "lineage": "Builds from the legible conventions of the future transit object catalog.", + "tags": [ + "flow", + "distribution", + "externality" + ] + }, + { + "id": "speculative-critical-future-transit-timeline-wall", + "form": "a future transit timeline wall, with participation boundaries, access-condition labels, and contested-use overlays", + "lineage": "Builds from the legible conventions of the future transit timeline wall.", + "tags": [ + "participation", + "access", + "contestation" + ] + }, + { + "id": "speculative-critical-future-transit-consequence-map", + "form": "a future transit consequence map, with credential layers, disclosure controls, and misclassification warnings", + "lineage": "Builds from the legible conventions of the future transit consequence map.", + "tags": [ + "credentials", + "disclosure", + "warnings" + ] + }, + { + "id": "speculative-critical-future-food-scenario-card-deck", + "form": "a future food scenario card deck, with assumption cards, wildcard interruptions, and end-state comparisons", + "lineage": "Builds from the legible conventions of the future food scenario card deck.", + "tags": [ + "assumptions", + "wildcards", + "outcomes" + ] + }, + { + "id": "speculative-critical-future-food-service-receipt", + "form": "a future food service receipt, with itemized hidden costs, data-or-resource line items, and terms-of-use footnotes", + "lineage": "Builds from the legible conventions of the future food service receipt.", + "tags": [ + "costs", + "resources", + "terms" + ] + }, + { + "id": "speculative-critical-future-food-policy-prototype", + "form": "a future food policy prototype, with eligibility clauses, decision pathways, and appeal-or-exit routes", + "lineage": "Builds from the legible conventions of the future food policy prototype.", + "tags": [ + "rules", + "pathways", + "appeal" + ] + }, + { + "id": "speculative-critical-future-food-object-catalog", + "form": "a future food object catalog, with numbered future objects, use-case captions, and unintended-effect notes", + "lineage": "Builds from the legible conventions of the future food object catalog.", + "tags": [ + "objects", + "usecases", + "effects" + ] + }, + { + "id": "speculative-critical-future-food-timeline-wall", + "form": "a future food timeline wall, with near-mid-far horizons, trigger events, and branching possibility bands", + "lineage": "Builds from the legible conventions of the future food timeline wall.", + "tags": [ + "horizons", + "triggers", + "branches" + ] + }, + { + "id": "speculative-critical-future-food-consequence-map", + "form": "a future food consequence map, with first-order effects, downstream ripple paths, and affected-party markers", + "lineage": "Builds from the legible conventions of the future food consequence map.", + "tags": [ + "effects", + "ripples", + "stakeholders" + ] + }, + { + "id": "speculative-critical-future-labor-scenario-card-deck", + "form": "a future labor scenario card deck, with baseline-present comparisons, changed norm indicators, and residual tension notes", + "lineage": "Builds from the legible conventions of the future labor scenario card deck.", + "tags": [ + "baseline", + "norms", + "tension" + ] + }, + { + "id": "speculative-critical-future-labor-service-receipt", + "form": "a future labor service receipt, with choice points, visible tradeoff scales, and reversible-or-locked states", + "lineage": "Builds from the legible conventions of the future labor service receipt.", + "tags": [ + "choices", + "tradeoffs", + "reversibility" + ] + }, + { + "id": "speculative-critical-future-labor-policy-prototype", + "form": "a future labor policy prototype, with institutional voice panels, personal account panels, and conflict annotations", + "lineage": "Builds from the legible conventions of the future labor policy prototype.", + "tags": [ + "voices", + "accounts", + "conflict" + ] + }, + { + "id": "speculative-critical-future-labor-object-catalog", + "form": "a future labor object catalog, with resource-flow arrows, gain-and-loss zones, and externality callouts", + "lineage": "Builds from the legible conventions of the future labor object catalog.", + "tags": [ + "flow", + "distribution", + "externality" + ] + }, + { + "id": "speculative-critical-future-labor-timeline-wall", + "form": "a future labor timeline wall, with participation boundaries, access-condition labels, and contested-use overlays", + "lineage": "Builds from the legible conventions of the future labor timeline wall.", + "tags": [ + "participation", + "access", + "contestation" + ] + }, + { + "id": "speculative-critical-future-labor-consequence-map", + "form": "a future labor consequence map, with credential layers, disclosure controls, and misclassification warnings", + "lineage": "Builds from the legible conventions of the future labor consequence map.", + "tags": [ + "credentials", + "disclosure", + "warnings" + ] + }, + { + "id": "speculative-critical-future-climate-scenario-card-deck", + "form": "a future climate scenario card deck, with assumption cards, wildcard interruptions, and end-state comparisons", + "lineage": "Builds from the legible conventions of the future climate scenario card deck.", + "tags": [ + "assumptions", + "wildcards", + "outcomes" + ] + }, + { + "id": "speculative-critical-future-climate-service-receipt", + "form": "a future climate service receipt, with itemized hidden costs, data-or-resource line items, and terms-of-use footnotes", + "lineage": "Builds from the legible conventions of the future climate service receipt.", + "tags": [ + "costs", + "resources", + "terms" + ] + }, + { + "id": "speculative-critical-future-climate-policy-prototype", + "form": "a future climate policy prototype, with eligibility clauses, decision pathways, and appeal-or-exit routes", + "lineage": "Builds from the legible conventions of the future climate policy prototype.", + "tags": [ + "rules", + "pathways", + "appeal" + ] + }, + { + "id": "speculative-critical-future-climate-object-catalog", + "form": "a future climate object catalog, with numbered future objects, use-case captions, and unintended-effect notes", + "lineage": "Builds from the legible conventions of the future climate object catalog.", + "tags": [ + "objects", + "usecases", + "effects" + ] + }, + { + "id": "speculative-critical-future-climate-timeline-wall", + "form": "a future climate timeline wall, with near-mid-far horizons, trigger events, and branching possibility bands", + "lineage": "Builds from the legible conventions of the future climate timeline wall.", + "tags": [ + "horizons", + "triggers", + "branches" + ] + }, + { + "id": "speculative-critical-future-climate-consequence-map", + "form": "a future climate consequence map, with first-order effects, downstream ripple paths, and affected-party markers", + "lineage": "Builds from the legible conventions of the future climate consequence map.", + "tags": [ + "effects", + "ripples", + "stakeholders" + ] + }, + { + "id": "speculative-critical-future-governance-scenario-card-deck", + "form": "a future governance scenario card deck, with baseline-present comparisons, changed norm indicators, and residual tension notes", + "lineage": "Builds from the legible conventions of the future governance scenario card deck.", + "tags": [ + "baseline", + "norms", + "tension" + ] + }, + { + "id": "speculative-critical-future-governance-service-receipt", + "form": "a future governance service receipt, with choice points, visible tradeoff scales, and reversible-or-locked states", + "lineage": "Builds from the legible conventions of the future governance service receipt.", + "tags": [ + "choices", + "tradeoffs", + "reversibility" + ] + }, + { + "id": "speculative-critical-future-governance-policy-prototype", + "form": "a future governance policy prototype, with institutional voice panels, personal account panels, and conflict annotations", + "lineage": "Builds from the legible conventions of the future governance policy prototype.", + "tags": [ + "voices", + "accounts", + "conflict" + ] + }, + { + "id": "speculative-critical-future-governance-object-catalog", + "form": "a future governance object catalog, with resource-flow arrows, gain-and-loss zones, and externality callouts", + "lineage": "Builds from the legible conventions of the future governance object catalog.", + "tags": [ + "flow", + "distribution", + "externality" + ] + }, + { + "id": "speculative-critical-future-governance-timeline-wall", + "form": "a future governance timeline wall, with participation boundaries, access-condition labels, and contested-use overlays", + "lineage": "Builds from the legible conventions of the future governance timeline wall.", + "tags": [ + "participation", + "access", + "contestation" + ] + }, + { + "id": "speculative-critical-future-governance-consequence-map", + "form": "a future governance consequence map, with credential layers, disclosure controls, and misclassification warnings", + "lineage": "Builds from the legible conventions of the future governance consequence map.", + "tags": [ + "credentials", + "disclosure", + "warnings" + ] + }, + { + "id": "speculative-critical-future-health-scenario-card-deck", + "form": "a future health scenario card deck, with assumption cards, wildcard interruptions, and end-state comparisons", + "lineage": "Builds from the legible conventions of the future health scenario card deck.", + "tags": [ + "assumptions", + "wildcards", + "outcomes" + ] + }, + { + "id": "speculative-critical-future-health-service-receipt", + "form": "a future health service receipt, with itemized hidden costs, data-or-resource line items, and terms-of-use footnotes", + "lineage": "Builds from the legible conventions of the future health service receipt.", + "tags": [ + "costs", + "resources", + "terms" + ] + }, + { + "id": "speculative-critical-future-health-policy-prototype", + "form": "a future health policy prototype, with eligibility clauses, decision pathways, and appeal-or-exit routes", + "lineage": "Builds from the legible conventions of the future health policy prototype.", + "tags": [ + "rules", + "pathways", + "appeal" + ] + }, + { + "id": "speculative-critical-future-health-object-catalog", + "form": "a future health object catalog, with numbered future objects, use-case captions, and unintended-effect notes", + "lineage": "Builds from the legible conventions of the future health object catalog.", + "tags": [ + "objects", + "usecases", + "effects" + ] + }, + { + "id": "speculative-critical-future-health-timeline-wall", + "form": "a future health timeline wall, with near-mid-far horizons, trigger events, and branching possibility bands", + "lineage": "Builds from the legible conventions of the future health timeline wall.", + "tags": [ + "horizons", + "triggers", + "branches" + ] + }, + { + "id": "speculative-critical-future-health-consequence-map", + "form": "a future health consequence map, with first-order effects, downstream ripple paths, and affected-party markers", + "lineage": "Builds from the legible conventions of the future health consequence map.", + "tags": [ + "effects", + "ripples", + "stakeholders" + ] + }, + { + "id": "speculative-critical-future-education-scenario-card-deck", + "form": "a future education scenario card deck, with baseline-present comparisons, changed norm indicators, and residual tension notes", + "lineage": "Builds from the legible conventions of the future education scenario card deck.", + "tags": [ + "baseline", + "norms", + "tension" + ] + }, + { + "id": "speculative-critical-future-education-service-receipt", + "form": "a future education service receipt, with choice points, visible tradeoff scales, and reversible-or-locked states", + "lineage": "Builds from the legible conventions of the future education service receipt.", + "tags": [ + "choices", + "tradeoffs", + "reversibility" + ] + }, + { + "id": "speculative-critical-future-education-policy-prototype", + "form": "a future education policy prototype, with institutional voice panels, personal account panels, and conflict annotations", + "lineage": "Builds from the legible conventions of the future education policy prototype.", + "tags": [ + "voices", + "accounts", + "conflict" + ] + }, + { + "id": "speculative-critical-future-education-object-catalog", + "form": "a future education object catalog, with resource-flow arrows, gain-and-loss zones, and externality callouts", + "lineage": "Builds from the legible conventions of the future education object catalog.", + "tags": [ + "flow", + "distribution", + "externality" + ] + }, + { + "id": "speculative-critical-future-education-timeline-wall", + "form": "a future education timeline wall, with participation boundaries, access-condition labels, and contested-use overlays", + "lineage": "Builds from the legible conventions of the future education timeline wall.", + "tags": [ + "participation", + "access", + "contestation" + ] + }, + { + "id": "speculative-critical-future-education-consequence-map", + "form": "a future education consequence map, with credential layers, disclosure controls, and misclassification warnings", + "lineage": "Builds from the legible conventions of the future education consequence map.", + "tags": [ + "credentials", + "disclosure", + "warnings" + ] + }, + { + "id": "speculative-critical-future-media-scenario-card-deck", + "form": "a future media scenario card deck, with assumption cards, wildcard interruptions, and end-state comparisons", + "lineage": "Builds from the legible conventions of the future media scenario card deck.", + "tags": [ + "assumptions", + "wildcards", + "outcomes" + ] + }, + { + "id": "speculative-critical-future-media-service-receipt", + "form": "a future media service receipt, with itemized hidden costs, data-or-resource line items, and terms-of-use footnotes", + "lineage": "Builds from the legible conventions of the future media service receipt.", + "tags": [ + "costs", + "resources", + "terms" + ] + }, + { + "id": "speculative-critical-future-media-policy-prototype", + "form": "a future media policy prototype, with eligibility clauses, decision pathways, and appeal-or-exit routes", + "lineage": "Builds from the legible conventions of the future media policy prototype.", + "tags": [ + "rules", + "pathways", + "appeal" + ] + }, + { + "id": "speculative-critical-future-media-object-catalog", + "form": "a future media object catalog, with numbered future objects, use-case captions, and unintended-effect notes", + "lineage": "Builds from the legible conventions of the future media object catalog.", + "tags": [ + "objects", + "usecases", + "effects" + ] + }, + { + "id": "speculative-critical-future-media-timeline-wall", + "form": "a future media timeline wall, with near-mid-far horizons, trigger events, and branching possibility bands", + "lineage": "Builds from the legible conventions of the future media timeline wall.", + "tags": [ + "horizons", + "triggers", + "branches" + ] + }, + { + "id": "speculative-critical-future-media-consequence-map", + "form": "a future media consequence map, with first-order effects, downstream ripple paths, and affected-party markers", + "lineage": "Builds from the legible conventions of the future media consequence map.", + "tags": [ + "effects", + "ripples", + "stakeholders" + ] + }, + { + "id": "speculative-critical-future-commerce-scenario-card-deck", + "form": "a future commerce scenario card deck, with baseline-present comparisons, changed norm indicators, and residual tension notes", + "lineage": "Builds from the legible conventions of the future commerce scenario card deck.", + "tags": [ + "baseline", + "norms", + "tension" + ] + }, + { + "id": "speculative-critical-future-commerce-service-receipt", + "form": "a future commerce service receipt, with choice points, visible tradeoff scales, and reversible-or-locked states", + "lineage": "Builds from the legible conventions of the future commerce service receipt.", + "tags": [ + "choices", + "tradeoffs", + "reversibility" + ] + }, + { + "id": "speculative-critical-future-commerce-policy-prototype", + "form": "a future commerce policy prototype, with institutional voice panels, personal account panels, and conflict annotations", + "lineage": "Builds from the legible conventions of the future commerce policy prototype.", + "tags": [ + "voices", + "accounts", + "conflict" + ] + }, + { + "id": "speculative-critical-future-commerce-object-catalog", + "form": "a future commerce object catalog, with resource-flow arrows, gain-and-loss zones, and externality callouts", + "lineage": "Builds from the legible conventions of the future commerce object catalog.", + "tags": [ + "flow", + "distribution", + "externality" + ] + }, + { + "id": "speculative-critical-future-commerce-timeline-wall", + "form": "a future commerce timeline wall, with participation boundaries, access-condition labels, and contested-use overlays", + "lineage": "Builds from the legible conventions of the future commerce timeline wall.", + "tags": [ + "participation", + "access", + "contestation" + ] + }, + { + "id": "speculative-critical-future-commerce-consequence-map", + "form": "a future commerce consequence map, with credential layers, disclosure controls, and misclassification warnings", + "lineage": "Builds from the legible conventions of the future commerce consequence map.", + "tags": [ + "credentials", + "disclosure", + "warnings" + ] + }, + { + "id": "speculative-critical-future-public-space-scenario-card-deck", + "form": "a future public space scenario card deck, with assumption cards, wildcard interruptions, and end-state comparisons", + "lineage": "Builds from the legible conventions of the future public space scenario card deck.", + "tags": [ + "assumptions", + "wildcards", + "outcomes" + ] + }, + { + "id": "speculative-critical-future-public-space-service-receipt", + "form": "a future public space service receipt, with itemized hidden costs, data-or-resource line items, and terms-of-use footnotes", + "lineage": "Builds from the legible conventions of the future public space service receipt.", + "tags": [ + "costs", + "resources", + "terms" + ] + }, + { + "id": "speculative-critical-future-public-space-policy-prototype", + "form": "a future public space policy prototype, with eligibility clauses, decision pathways, and appeal-or-exit routes", + "lineage": "Builds from the legible conventions of the future public space policy prototype.", + "tags": [ + "rules", + "pathways", + "appeal" + ] + }, + { + "id": "speculative-critical-future-public-space-object-catalog", + "form": "a future public space object catalog, with numbered future objects, use-case captions, and unintended-effect notes", + "lineage": "Builds from the legible conventions of the future public space object catalog.", + "tags": [ + "objects", + "usecases", + "effects" + ] + } + ] + }, + { + "id": "identity-sign-systems", + "label": "Identity & sign systems", + "description": "Repeatable naming, symbol, wayfinding, and identification grammars that work across contexts.", + "concepts": [ + { + "id": "identity-sign-systems-transit-directional-sign-family", + "form": "a transit directional sign family, with destination-first labels, arrow-aligned choices, and distance-or-time cues", + "lineage": "Builds from the legible conventions of the transit directional sign family.", + "tags": [ + "destination", + "direction", + "distance" + ] + }, + { + "id": "identity-sign-systems-transit-pictogram-set", + "form": "a transit pictogram set, with consistent silhouette rules, text-label pairing, and small-size recognition tests", + "lineage": "Builds from the legible conventions of the transit pictogram set.", + "tags": [ + "pictogram", + "label", + "recognition" + ] + }, + { + "id": "identity-sign-systems-transit-route-marker-system", + "form": "a transit route marker system, with continuous color-shape codes, decision-point confirmations, and arrival markers", + "lineage": "Builds from the legible conventions of the transit route marker system.", + "tags": [ + "continuity", + "decision", + "arrival" + ] + }, + { + "id": "identity-sign-systems-transit-identification-band", + "form": "a transit identification band, with role-or-area categories, redundant text-and-symbol coding, and permission cues", + "lineage": "Builds from the legible conventions of the transit identification band.", + "tags": [ + "category", + "redundancy", + "permission" + ] + }, + { + "id": "identity-sign-systems-transit-zone-map", + "form": "a transit zone map, with bounded areas, you-are-here anchors, and cross-zone connector lines", + "lineage": "Builds from the legible conventions of the transit zone map.", + "tags": [ + "zones", + "position", + "connectors" + ] + }, + { + "id": "identity-sign-systems-transit-numbering-scheme", + "form": "a transit numbering scheme, with hierarchical identifiers, predictable reading order, and duplicate-prevention rules", + "lineage": "Builds from the legible conventions of the transit numbering scheme.", + "tags": [ + "hierarchy", + "order", + "uniqueness" + ] + }, + { + "id": "identity-sign-systems-campus-directional-sign-family", + "form": "a campus directional sign family, with primary-secondary destination tiers, consistent mounting positions, and obstruction-safe repeats", + "lineage": "Builds from the legible conventions of the campus directional sign family.", + "tags": [ + "tiers", + "placement", + "repeat" + ] + }, + { + "id": "identity-sign-systems-campus-pictogram-set", + "form": "a campus pictogram set, with entry-exit distinctions, one-way flow cues, and return-route reassurance", + "lineage": "Builds from the legible conventions of the campus pictogram set.", + "tags": [ + "entry", + "flow", + "return" + ] + }, + { + "id": "identity-sign-systems-campus-route-marker-system", + "form": "a campus route marker system, with temporary-change overlays, effective-date labels, and superseded-route masking", + "lineage": "Builds from the legible conventions of the campus route marker system.", + "tags": [ + "temporary", + "dates", + "override" + ] + }, + { + "id": "identity-sign-systems-campus-identification-band", + "form": "a campus identification band, with aisle-bay-position codes, large-range landmarks, and pick-and-return confirmation", + "lineage": "Builds from the legible conventions of the campus identification band.", + "tags": [ + "coordinates", + "landmarks", + "confirmation" + ] + }, + { + "id": "identity-sign-systems-campus-zone-map", + "form": "a campus zone map, with program-area symbols, time-window strips, and crowd-capacity notices", + "lineage": "Builds from the legible conventions of the campus zone map.", + "tags": [ + "program", + "time", + "capacity" + ] + }, + { + "id": "identity-sign-systems-campus-numbering-scheme", + "form": "a campus numbering scheme, with priority action verbs, multiple alert channels, and safe-area confirmation", + "lineage": "Builds from the legible conventions of the campus numbering scheme.", + "tags": [ + "priority", + "alerts", + "safety" + ] + }, + { + "id": "identity-sign-systems-hospital-directional-sign-family", + "form": "a hospital directional sign family, with destination-first labels, arrow-aligned choices, and distance-or-time cues", + "lineage": "Builds from the legible conventions of the hospital directional sign family.", + "tags": [ + "destination", + "direction", + "distance" + ] + }, + { + "id": "identity-sign-systems-hospital-pictogram-set", + "form": "a hospital pictogram set, with consistent silhouette rules, text-label pairing, and small-size recognition tests", + "lineage": "Builds from the legible conventions of the hospital pictogram set.", + "tags": [ + "pictogram", + "label", + "recognition" + ] + }, + { + "id": "identity-sign-systems-hospital-route-marker-system", + "form": "a hospital route marker system, with continuous color-shape codes, decision-point confirmations, and arrival markers", + "lineage": "Builds from the legible conventions of the hospital route marker system.", + "tags": [ + "continuity", + "decision", + "arrival" + ] + }, + { + "id": "identity-sign-systems-hospital-identification-band", + "form": "a hospital identification band, with role-or-area categories, redundant text-and-symbol coding, and permission cues", + "lineage": "Builds from the legible conventions of the hospital identification band.", + "tags": [ + "category", + "redundancy", + "permission" + ] + }, + { + "id": "identity-sign-systems-hospital-zone-map", + "form": "a hospital zone map, with bounded areas, you-are-here anchors, and cross-zone connector lines", + "lineage": "Builds from the legible conventions of the hospital zone map.", + "tags": [ + "zones", + "position", + "connectors" + ] + }, + { + "id": "identity-sign-systems-hospital-numbering-scheme", + "form": "a hospital numbering scheme, with hierarchical identifiers, predictable reading order, and duplicate-prevention rules", + "lineage": "Builds from the legible conventions of the hospital numbering scheme.", + "tags": [ + "hierarchy", + "order", + "uniqueness" + ] + }, + { + "id": "identity-sign-systems-library-directional-sign-family", + "form": "a library directional sign family, with primary-secondary destination tiers, consistent mounting positions, and obstruction-safe repeats", + "lineage": "Builds from the legible conventions of the library directional sign family.", + "tags": [ + "tiers", + "placement", + "repeat" + ] + }, + { + "id": "identity-sign-systems-library-pictogram-set", + "form": "a library pictogram set, with entry-exit distinctions, one-way flow cues, and return-route reassurance", + "lineage": "Builds from the legible conventions of the library pictogram set.", + "tags": [ + "entry", + "flow", + "return" + ] + }, + { + "id": "identity-sign-systems-library-route-marker-system", + "form": "a library route marker system, with temporary-change overlays, effective-date labels, and superseded-route masking", + "lineage": "Builds from the legible conventions of the library route marker system.", + "tags": [ + "temporary", + "dates", + "override" + ] + }, + { + "id": "identity-sign-systems-library-identification-band", + "form": "a library identification band, with aisle-bay-position codes, large-range landmarks, and pick-and-return confirmation", + "lineage": "Builds from the legible conventions of the library identification band.", + "tags": [ + "coordinates", + "landmarks", + "confirmation" + ] + }, + { + "id": "identity-sign-systems-library-zone-map", + "form": "a library zone map, with program-area symbols, time-window strips, and crowd-capacity notices", + "lineage": "Builds from the legible conventions of the library zone map.", + "tags": [ + "program", + "time", + "capacity" + ] + }, + { + "id": "identity-sign-systems-library-numbering-scheme", + "form": "a library numbering scheme, with priority action verbs, multiple alert channels, and safe-area confirmation", + "lineage": "Builds from the legible conventions of the library numbering scheme.", + "tags": [ + "priority", + "alerts", + "safety" + ] + }, + { + "id": "identity-sign-systems-sports-venue-directional-sign-family", + "form": "a sports venue directional sign family, with destination-first labels, arrow-aligned choices, and distance-or-time cues", + "lineage": "Builds from the legible conventions of the sports venue directional sign family.", + "tags": [ + "destination", + "direction", + "distance" + ] + }, + { + "id": "identity-sign-systems-sports-venue-pictogram-set", + "form": "a sports venue pictogram set, with consistent silhouette rules, text-label pairing, and small-size recognition tests", + "lineage": "Builds from the legible conventions of the sports venue pictogram set.", + "tags": [ + "pictogram", + "label", + "recognition" + ] + }, + { + "id": "identity-sign-systems-sports-venue-route-marker-system", + "form": "a sports venue route marker system, with continuous color-shape codes, decision-point confirmations, and arrival markers", + "lineage": "Builds from the legible conventions of the sports venue route marker system.", + "tags": [ + "continuity", + "decision", + "arrival" + ] + }, + { + "id": "identity-sign-systems-sports-venue-identification-band", + "form": "a sports venue identification band, with role-or-area categories, redundant text-and-symbol coding, and permission cues", + "lineage": "Builds from the legible conventions of the sports venue identification band.", + "tags": [ + "category", + "redundancy", + "permission" + ] + }, + { + "id": "identity-sign-systems-sports-venue-zone-map", + "form": "a sports venue zone map, with bounded areas, you-are-here anchors, and cross-zone connector lines", + "lineage": "Builds from the legible conventions of the sports venue zone map.", + "tags": [ + "zones", + "position", + "connectors" + ] + }, + { + "id": "identity-sign-systems-sports-venue-numbering-scheme", + "form": "a sports venue numbering scheme, with hierarchical identifiers, predictable reading order, and duplicate-prevention rules", + "lineage": "Builds from the legible conventions of the sports venue numbering scheme.", + "tags": [ + "hierarchy", + "order", + "uniqueness" + ] + }, + { + "id": "identity-sign-systems-civic-service-directional-sign-family", + "form": "a civic service directional sign family, with primary-secondary destination tiers, consistent mounting positions, and obstruction-safe repeats", + "lineage": "Builds from the legible conventions of the civic service directional sign family.", + "tags": [ + "tiers", + "placement", + "repeat" + ] + }, + { + "id": "identity-sign-systems-civic-service-pictogram-set", + "form": "a civic service pictogram set, with entry-exit distinctions, one-way flow cues, and return-route reassurance", + "lineage": "Builds from the legible conventions of the civic service pictogram set.", + "tags": [ + "entry", + "flow", + "return" + ] + }, + { + "id": "identity-sign-systems-civic-service-route-marker-system", + "form": "a civic service route marker system, with temporary-change overlays, effective-date labels, and superseded-route masking", + "lineage": "Builds from the legible conventions of the civic service route marker system.", + "tags": [ + "temporary", + "dates", + "override" + ] + }, + { + "id": "identity-sign-systems-civic-service-identification-band", + "form": "a civic service identification band, with aisle-bay-position codes, large-range landmarks, and pick-and-return confirmation", + "lineage": "Builds from the legible conventions of the civic service identification band.", + "tags": [ + "coordinates", + "landmarks", + "confirmation" + ] + }, + { + "id": "identity-sign-systems-civic-service-zone-map", + "form": "a civic service zone map, with program-area symbols, time-window strips, and crowd-capacity notices", + "lineage": "Builds from the legible conventions of the civic service zone map.", + "tags": [ + "program", + "time", + "capacity" + ] + }, + { + "id": "identity-sign-systems-civic-service-numbering-scheme", + "form": "a civic service numbering scheme, with priority action verbs, multiple alert channels, and safe-area confirmation", + "lineage": "Builds from the legible conventions of the civic service numbering scheme.", + "tags": [ + "priority", + "alerts", + "safety" + ] + }, + { + "id": "identity-sign-systems-market-hall-directional-sign-family", + "form": "a market hall directional sign family, with destination-first labels, arrow-aligned choices, and distance-or-time cues", + "lineage": "Builds from the legible conventions of the market hall directional sign family.", + "tags": [ + "destination", + "direction", + "distance" + ] + }, + { + "id": "identity-sign-systems-market-hall-pictogram-set", + "form": "a market hall pictogram set, with consistent silhouette rules, text-label pairing, and small-size recognition tests", + "lineage": "Builds from the legible conventions of the market hall pictogram set.", + "tags": [ + "pictogram", + "label", + "recognition" + ] + }, + { + "id": "identity-sign-systems-market-hall-route-marker-system", + "form": "a market hall route marker system, with continuous color-shape codes, decision-point confirmations, and arrival markers", + "lineage": "Builds from the legible conventions of the market hall route marker system.", + "tags": [ + "continuity", + "decision", + "arrival" + ] + }, + { + "id": "identity-sign-systems-market-hall-identification-band", + "form": "a market hall identification band, with role-or-area categories, redundant text-and-symbol coding, and permission cues", + "lineage": "Builds from the legible conventions of the market hall identification band.", + "tags": [ + "category", + "redundancy", + "permission" + ] + }, + { + "id": "identity-sign-systems-market-hall-zone-map", + "form": "a market hall zone map, with bounded areas, you-are-here anchors, and cross-zone connector lines", + "lineage": "Builds from the legible conventions of the market hall zone map.", + "tags": [ + "zones", + "position", + "connectors" + ] + }, + { + "id": "identity-sign-systems-market-hall-numbering-scheme", + "form": "a market hall numbering scheme, with hierarchical identifiers, predictable reading order, and duplicate-prevention rules", + "lineage": "Builds from the legible conventions of the market hall numbering scheme.", + "tags": [ + "hierarchy", + "order", + "uniqueness" + ] + }, + { + "id": "identity-sign-systems-park-directional-sign-family", + "form": "a park directional sign family, with primary-secondary destination tiers, consistent mounting positions, and obstruction-safe repeats", + "lineage": "Builds from the legible conventions of the park directional sign family.", + "tags": [ + "tiers", + "placement", + "repeat" + ] + }, + { + "id": "identity-sign-systems-park-pictogram-set", + "form": "a park pictogram set, with entry-exit distinctions, one-way flow cues, and return-route reassurance", + "lineage": "Builds from the legible conventions of the park pictogram set.", + "tags": [ + "entry", + "flow", + "return" + ] + }, + { + "id": "identity-sign-systems-park-route-marker-system", + "form": "a park route marker system, with temporary-change overlays, effective-date labels, and superseded-route masking", + "lineage": "Builds from the legible conventions of the park route marker system.", + "tags": [ + "temporary", + "dates", + "override" + ] + }, + { + "id": "identity-sign-systems-park-identification-band", + "form": "a park identification band, with aisle-bay-position codes, large-range landmarks, and pick-and-return confirmation", + "lineage": "Builds from the legible conventions of the park identification band.", + "tags": [ + "coordinates", + "landmarks", + "confirmation" + ] + }, + { + "id": "identity-sign-systems-park-zone-map", + "form": "a park zone map, with program-area symbols, time-window strips, and crowd-capacity notices", + "lineage": "Builds from the legible conventions of the park zone map.", + "tags": [ + "program", + "time", + "capacity" + ] + }, + { + "id": "identity-sign-systems-park-numbering-scheme", + "form": "a park numbering scheme, with priority action verbs, multiple alert channels, and safe-area confirmation", + "lineage": "Builds from the legible conventions of the park numbering scheme.", + "tags": [ + "priority", + "alerts", + "safety" + ] + }, + { + "id": "identity-sign-systems-construction-site-directional-sign-family", + "form": "a construction site directional sign family, with destination-first labels, arrow-aligned choices, and distance-or-time cues", + "lineage": "Builds from the legible conventions of the construction site directional sign family.", + "tags": [ + "destination", + "direction", + "distance" + ] + }, + { + "id": "identity-sign-systems-construction-site-pictogram-set", + "form": "a construction site pictogram set, with consistent silhouette rules, text-label pairing, and small-size recognition tests", + "lineage": "Builds from the legible conventions of the construction site pictogram set.", + "tags": [ + "pictogram", + "label", + "recognition" + ] + }, + { + "id": "identity-sign-systems-construction-site-route-marker-system", + "form": "a construction site route marker system, with continuous color-shape codes, decision-point confirmations, and arrival markers", + "lineage": "Builds from the legible conventions of the construction site route marker system.", + "tags": [ + "continuity", + "decision", + "arrival" + ] + }, + { + "id": "identity-sign-systems-construction-site-identification-band", + "form": "a construction site identification band, with role-or-area categories, redundant text-and-symbol coding, and permission cues", + "lineage": "Builds from the legible conventions of the construction site identification band.", + "tags": [ + "category", + "redundancy", + "permission" + ] + }, + { + "id": "identity-sign-systems-construction-site-zone-map", + "form": "a construction site zone map, with bounded areas, you-are-here anchors, and cross-zone connector lines", + "lineage": "Builds from the legible conventions of the construction site zone map.", + "tags": [ + "zones", + "position", + "connectors" + ] + }, + { + "id": "identity-sign-systems-construction-site-numbering-scheme", + "form": "a construction site numbering scheme, with hierarchical identifiers, predictable reading order, and duplicate-prevention rules", + "lineage": "Builds from the legible conventions of the construction site numbering scheme.", + "tags": [ + "hierarchy", + "order", + "uniqueness" + ] + }, + { + "id": "identity-sign-systems-warehouse-directional-sign-family", + "form": "a warehouse directional sign family, with primary-secondary destination tiers, consistent mounting positions, and obstruction-safe repeats", + "lineage": "Builds from the legible conventions of the warehouse directional sign family.", + "tags": [ + "tiers", + "placement", + "repeat" + ] + }, + { + "id": "identity-sign-systems-warehouse-pictogram-set", + "form": "a warehouse pictogram set, with entry-exit distinctions, one-way flow cues, and return-route reassurance", + "lineage": "Builds from the legible conventions of the warehouse pictogram set.", + "tags": [ + "entry", + "flow", + "return" + ] + }, + { + "id": "identity-sign-systems-warehouse-route-marker-system", + "form": "a warehouse route marker system, with temporary-change overlays, effective-date labels, and superseded-route masking", + "lineage": "Builds from the legible conventions of the warehouse route marker system.", + "tags": [ + "temporary", + "dates", + "override" + ] + }, + { + "id": "identity-sign-systems-warehouse-identification-band", + "form": "a warehouse identification band, with aisle-bay-position codes, large-range landmarks, and pick-and-return confirmation", + "lineage": "Builds from the legible conventions of the warehouse identification band.", + "tags": [ + "coordinates", + "landmarks", + "confirmation" + ] + }, + { + "id": "identity-sign-systems-warehouse-zone-map", + "form": "a warehouse zone map, with program-area symbols, time-window strips, and crowd-capacity notices", + "lineage": "Builds from the legible conventions of the warehouse zone map.", + "tags": [ + "program", + "time", + "capacity" + ] + }, + { + "id": "identity-sign-systems-warehouse-numbering-scheme", + "form": "a warehouse numbering scheme, with priority action verbs, multiple alert channels, and safe-area confirmation", + "lineage": "Builds from the legible conventions of the warehouse numbering scheme.", + "tags": [ + "priority", + "alerts", + "safety" + ] + }, + { + "id": "identity-sign-systems-festival-directional-sign-family", + "form": "a festival directional sign family, with destination-first labels, arrow-aligned choices, and distance-or-time cues", + "lineage": "Builds from the legible conventions of the festival directional sign family.", + "tags": [ + "destination", + "direction", + "distance" + ] + }, + { + "id": "identity-sign-systems-festival-pictogram-set", + "form": "a festival pictogram set, with consistent silhouette rules, text-label pairing, and small-size recognition tests", + "lineage": "Builds from the legible conventions of the festival pictogram set.", + "tags": [ + "pictogram", + "label", + "recognition" + ] + }, + { + "id": "identity-sign-systems-festival-route-marker-system", + "form": "a festival route marker system, with continuous color-shape codes, decision-point confirmations, and arrival markers", + "lineage": "Builds from the legible conventions of the festival route marker system.", + "tags": [ + "continuity", + "decision", + "arrival" + ] + }, + { + "id": "identity-sign-systems-festival-identification-band", + "form": "a festival identification band, with role-or-area categories, redundant text-and-symbol coding, and permission cues", + "lineage": "Builds from the legible conventions of the festival identification band.", + "tags": [ + "category", + "redundancy", + "permission" + ] + } + ] + }, + { + "id": "spatial-embodied-interaction", + "label": "Spatial & embodied interaction", + "description": "Physical and mixed-reality systems organized around position, gesture, proximity, feedback, and shared space.", + "concepts": [ + { + "id": "spatial-embodied-interaction-arrival-floor-cue-system", + "form": "an arrival floor cue system, with approach-direction arrows, pause footprints, and next-step markers", + "lineage": "Builds from the legible conventions of the arrival floor cue system.", + "tags": [ + "approach", + "pause", + "nextstep" + ] + }, + { + "id": "spatial-embodied-interaction-arrival-movable-station-set", + "form": "an arrival movable station set, with reconfigurable modules, clear occupied states, and reset positions", + "lineage": "Builds from the legible conventions of the arrival movable station set.", + "tags": [ + "modular", + "occupancy", + "reset" + ] + }, + { + "id": "spatial-embodied-interaction-arrival-proximity-marker", + "form": "an arrival proximity marker, with near-mid-far distance bands, activation boundaries, and opt-out paths", + "lineage": "Builds from the legible conventions of the arrival proximity marker.", + "tags": [ + "distance", + "activation", + "optout" + ] + }, + { + "id": "spatial-embodied-interaction-arrival-handoff-zone", + "form": "an arrival handoff zone, with giver-and-receiver positions, object transfer surface, and completion confirmation", + "lineage": "Builds from the legible conventions of the arrival handoff zone.", + "tags": [ + "roles", + "transfer", + "confirmation" + ] + }, + { + "id": "spatial-embodied-interaction-arrival-participation-grid", + "form": "an arrival participation grid, with individual standing cells, shared center area, and turn-order indicators", + "lineage": "Builds from the legible conventions of the arrival participation grid.", + "tags": [ + "cells", + "sharedspace", + "turns" + ] + }, + { + "id": "spatial-embodied-interaction-arrival-feedback-wall", + "form": "an arrival feedback wall, with immediate response zones, aggregate result fields, and reflection prompts", + "lineage": "Builds from the legible conventions of the arrival feedback wall.", + "tags": [ + "response", + "aggregation", + "reflection" + ] + }, + { + "id": "spatial-embodied-interaction-queue-floor-cue-system", + "form": "a queue floor cue system, with entry orientation point, choice branches, and rejoin landmarks", + "lineage": "Builds from the legible conventions of the queue floor cue system.", + "tags": [ + "orientation", + "choice", + "rejoin" + ] + }, + { + "id": "spatial-embodied-interaction-queue-movable-station-set", + "form": "a queue movable station set, with clockwise work sequence, tool-return outlines, and safety clearance bands", + "lineage": "Builds from the legible conventions of the queue movable station set.", + "tags": [ + "sequence", + "tools", + "clearance" + ] + }, + { + "id": "spatial-embodied-interaction-queue-proximity-marker", + "form": "a queue proximity marker, with sample-and-return points, comparison bays, and selection hold areas", + "lineage": "Builds from the legible conventions of the queue proximity marker.", + "tags": [ + "sampling", + "comparison", + "holding" + ] + }, + { + "id": "spatial-embodied-interaction-queue-handoff-zone", + "form": "a queue handoff zone, with performer-audience boundaries, attention focus lines, and quiet exit routes", + "lineage": "Builds from the legible conventions of the queue handoff zone.", + "tags": [ + "boundary", + "focus", + "exit" + ] + }, + { + "id": "spatial-embodied-interaction-queue-participation-grid", + "form": "a queue participation grid, with rest-duration cues, personal-space intervals, and low-stimulation routes", + "lineage": "Builds from the legible conventions of the queue participation grid.", + "tags": [ + "rest", + "spacing", + "calm" + ] + }, + { + "id": "spatial-embodied-interaction-queue-feedback-wall", + "form": "a queue feedback wall, with completion checkpoint, belongings reminder zone, and onward destination signs", + "lineage": "Builds from the legible conventions of the queue feedback wall.", + "tags": [ + "completion", + "reminder", + "onward" + ] + }, + { + "id": "spatial-embodied-interaction-threshold-floor-cue-system", + "form": "a threshold floor cue system, with approach-direction arrows, pause footprints, and next-step markers", + "lineage": "Builds from the legible conventions of the threshold floor cue system.", + "tags": [ + "approach", + "pause", + "nextstep" + ] + }, + { + "id": "spatial-embodied-interaction-threshold-movable-station-set", + "form": "a threshold movable station set, with reconfigurable modules, clear occupied states, and reset positions", + "lineage": "Builds from the legible conventions of the threshold movable station set.", + "tags": [ + "modular", + "occupancy", + "reset" + ] + }, + { + "id": "spatial-embodied-interaction-threshold-proximity-marker", + "form": "a threshold proximity marker, with near-mid-far distance bands, activation boundaries, and opt-out paths", + "lineage": "Builds from the legible conventions of the threshold proximity marker.", + "tags": [ + "distance", + "activation", + "optout" + ] + }, + { + "id": "spatial-embodied-interaction-threshold-handoff-zone", + "form": "a threshold handoff zone, with giver-and-receiver positions, object transfer surface, and completion confirmation", + "lineage": "Builds from the legible conventions of the threshold handoff zone.", + "tags": [ + "roles", + "transfer", + "confirmation" + ] + }, + { + "id": "spatial-embodied-interaction-threshold-participation-grid", + "form": "a threshold participation grid, with individual standing cells, shared center area, and turn-order indicators", + "lineage": "Builds from the legible conventions of the threshold participation grid.", + "tags": [ + "cells", + "sharedspace", + "turns" + ] + }, + { + "id": "spatial-embodied-interaction-threshold-feedback-wall", + "form": "a threshold feedback wall, with immediate response zones, aggregate result fields, and reflection prompts", + "lineage": "Builds from the legible conventions of the threshold feedback wall.", + "tags": [ + "response", + "aggregation", + "reflection" + ] + }, + { + "id": "spatial-embodied-interaction-circulation-floor-cue-system", + "form": "a circulation floor cue system, with entry orientation point, choice branches, and rejoin landmarks", + "lineage": "Builds from the legible conventions of the circulation floor cue system.", + "tags": [ + "orientation", + "choice", + "rejoin" + ] + }, + { + "id": "spatial-embodied-interaction-circulation-movable-station-set", + "form": "a circulation movable station set, with clockwise work sequence, tool-return outlines, and safety clearance bands", + "lineage": "Builds from the legible conventions of the circulation movable station set.", + "tags": [ + "sequence", + "tools", + "clearance" + ] + }, + { + "id": "spatial-embodied-interaction-circulation-proximity-marker", + "form": "a circulation proximity marker, with sample-and-return points, comparison bays, and selection hold areas", + "lineage": "Builds from the legible conventions of the circulation proximity marker.", + "tags": [ + "sampling", + "comparison", + "holding" + ] + }, + { + "id": "spatial-embodied-interaction-circulation-handoff-zone", + "form": "a circulation handoff zone, with performer-audience boundaries, attention focus lines, and quiet exit routes", + "lineage": "Builds from the legible conventions of the circulation handoff zone.", + "tags": [ + "boundary", + "focus", + "exit" + ] + }, + { + "id": "spatial-embodied-interaction-circulation-participation-grid", + "form": "a circulation participation grid, with rest-duration cues, personal-space intervals, and low-stimulation routes", + "lineage": "Builds from the legible conventions of the circulation participation grid.", + "tags": [ + "rest", + "spacing", + "calm" + ] + }, + { + "id": "spatial-embodied-interaction-circulation-feedback-wall", + "form": "a circulation feedback wall, with completion checkpoint, belongings reminder zone, and onward destination signs", + "lineage": "Builds from the legible conventions of the circulation feedback wall.", + "tags": [ + "completion", + "reminder", + "onward" + ] + }, + { + "id": "spatial-embodied-interaction-gathering-floor-cue-system", + "form": "a gathering floor cue system, with approach-direction arrows, pause footprints, and next-step markers", + "lineage": "Builds from the legible conventions of the gathering floor cue system.", + "tags": [ + "approach", + "pause", + "nextstep" + ] + }, + { + "id": "spatial-embodied-interaction-gathering-movable-station-set", + "form": "a gathering movable station set, with reconfigurable modules, clear occupied states, and reset positions", + "lineage": "Builds from the legible conventions of the gathering movable station set.", + "tags": [ + "modular", + "occupancy", + "reset" + ] + }, + { + "id": "spatial-embodied-interaction-gathering-proximity-marker", + "form": "a gathering proximity marker, with near-mid-far distance bands, activation boundaries, and opt-out paths", + "lineage": "Builds from the legible conventions of the gathering proximity marker.", + "tags": [ + "distance", + "activation", + "optout" + ] + }, + { + "id": "spatial-embodied-interaction-gathering-handoff-zone", + "form": "a gathering handoff zone, with giver-and-receiver positions, object transfer surface, and completion confirmation", + "lineage": "Builds from the legible conventions of the gathering handoff zone.", + "tags": [ + "roles", + "transfer", + "confirmation" + ] + }, + { + "id": "spatial-embodied-interaction-gathering-participation-grid", + "form": "a gathering participation grid, with individual standing cells, shared center area, and turn-order indicators", + "lineage": "Builds from the legible conventions of the gathering participation grid.", + "tags": [ + "cells", + "sharedspace", + "turns" + ] + }, + { + "id": "spatial-embodied-interaction-gathering-feedback-wall", + "form": "a gathering feedback wall, with immediate response zones, aggregate result fields, and reflection prompts", + "lineage": "Builds from the legible conventions of the gathering feedback wall.", + "tags": [ + "response", + "aggregation", + "reflection" + ] + }, + { + "id": "spatial-embodied-interaction-collaboration-floor-cue-system", + "form": "a collaboration floor cue system, with entry orientation point, choice branches, and rejoin landmarks", + "lineage": "Builds from the legible conventions of the collaboration floor cue system.", + "tags": [ + "orientation", + "choice", + "rejoin" + ] + }, + { + "id": "spatial-embodied-interaction-collaboration-movable-station-set", + "form": "a collaboration movable station set, with clockwise work sequence, tool-return outlines, and safety clearance bands", + "lineage": "Builds from the legible conventions of the collaboration movable station set.", + "tags": [ + "sequence", + "tools", + "clearance" + ] + }, + { + "id": "spatial-embodied-interaction-collaboration-proximity-marker", + "form": "a collaboration proximity marker, with sample-and-return points, comparison bays, and selection hold areas", + "lineage": "Builds from the legible conventions of the collaboration proximity marker.", + "tags": [ + "sampling", + "comparison", + "holding" + ] + }, + { + "id": "spatial-embodied-interaction-collaboration-handoff-zone", + "form": "a collaboration handoff zone, with performer-audience boundaries, attention focus lines, and quiet exit routes", + "lineage": "Builds from the legible conventions of the collaboration handoff zone.", + "tags": [ + "boundary", + "focus", + "exit" + ] + }, + { + "id": "spatial-embodied-interaction-collaboration-participation-grid", + "form": "a collaboration participation grid, with rest-duration cues, personal-space intervals, and low-stimulation routes", + "lineage": "Builds from the legible conventions of the collaboration participation grid.", + "tags": [ + "rest", + "spacing", + "calm" + ] + }, + { + "id": "spatial-embodied-interaction-collaboration-feedback-wall", + "form": "a collaboration feedback wall, with completion checkpoint, belongings reminder zone, and onward destination signs", + "lineage": "Builds from the legible conventions of the collaboration feedback wall.", + "tags": [ + "completion", + "reminder", + "onward" + ] + }, + { + "id": "spatial-embodied-interaction-learning-floor-cue-system", + "form": "a learning floor cue system, with approach-direction arrows, pause footprints, and next-step markers", + "lineage": "Builds from the legible conventions of the learning floor cue system.", + "tags": [ + "approach", + "pause", + "nextstep" + ] + }, + { + "id": "spatial-embodied-interaction-learning-movable-station-set", + "form": "a learning movable station set, with reconfigurable modules, clear occupied states, and reset positions", + "lineage": "Builds from the legible conventions of the learning movable station set.", + "tags": [ + "modular", + "occupancy", + "reset" + ] + }, + { + "id": "spatial-embodied-interaction-learning-proximity-marker", + "form": "a learning proximity marker, with near-mid-far distance bands, activation boundaries, and opt-out paths", + "lineage": "Builds from the legible conventions of the learning proximity marker.", + "tags": [ + "distance", + "activation", + "optout" + ] + }, + { + "id": "spatial-embodied-interaction-learning-handoff-zone", + "form": "a learning handoff zone, with giver-and-receiver positions, object transfer surface, and completion confirmation", + "lineage": "Builds from the legible conventions of the learning handoff zone.", + "tags": [ + "roles", + "transfer", + "confirmation" + ] + }, + { + "id": "spatial-embodied-interaction-learning-participation-grid", + "form": "a learning participation grid, with individual standing cells, shared center area, and turn-order indicators", + "lineage": "Builds from the legible conventions of the learning participation grid.", + "tags": [ + "cells", + "sharedspace", + "turns" + ] + }, + { + "id": "spatial-embodied-interaction-learning-feedback-wall", + "form": "a learning feedback wall, with immediate response zones, aggregate result fields, and reflection prompts", + "lineage": "Builds from the legible conventions of the learning feedback wall.", + "tags": [ + "response", + "aggregation", + "reflection" + ] + }, + { + "id": "spatial-embodied-interaction-making-floor-cue-system", + "form": "a making floor cue system, with entry orientation point, choice branches, and rejoin landmarks", + "lineage": "Builds from the legible conventions of the making floor cue system.", + "tags": [ + "orientation", + "choice", + "rejoin" + ] + }, + { + "id": "spatial-embodied-interaction-making-movable-station-set", + "form": "a making movable station set, with clockwise work sequence, tool-return outlines, and safety clearance bands", + "lineage": "Builds from the legible conventions of the making movable station set.", + "tags": [ + "sequence", + "tools", + "clearance" + ] + }, + { + "id": "spatial-embodied-interaction-making-proximity-marker", + "form": "a making proximity marker, with sample-and-return points, comparison bays, and selection hold areas", + "lineage": "Builds from the legible conventions of the making proximity marker.", + "tags": [ + "sampling", + "comparison", + "holding" + ] + }, + { + "id": "spatial-embodied-interaction-making-handoff-zone", + "form": "a making handoff zone, with performer-audience boundaries, attention focus lines, and quiet exit routes", + "lineage": "Builds from the legible conventions of the making handoff zone.", + "tags": [ + "boundary", + "focus", + "exit" + ] + }, + { + "id": "spatial-embodied-interaction-making-participation-grid", + "form": "a making participation grid, with rest-duration cues, personal-space intervals, and low-stimulation routes", + "lineage": "Builds from the legible conventions of the making participation grid.", + "tags": [ + "rest", + "spacing", + "calm" + ] + }, + { + "id": "spatial-embodied-interaction-making-feedback-wall", + "form": "a making feedback wall, with completion checkpoint, belongings reminder zone, and onward destination signs", + "lineage": "Builds from the legible conventions of the making feedback wall.", + "tags": [ + "completion", + "reminder", + "onward" + ] + }, + { + "id": "spatial-embodied-interaction-browsing-floor-cue-system", + "form": "a browsing floor cue system, with approach-direction arrows, pause footprints, and next-step markers", + "lineage": "Builds from the legible conventions of the browsing floor cue system.", + "tags": [ + "approach", + "pause", + "nextstep" + ] + }, + { + "id": "spatial-embodied-interaction-browsing-movable-station-set", + "form": "a browsing movable station set, with reconfigurable modules, clear occupied states, and reset positions", + "lineage": "Builds from the legible conventions of the browsing movable station set.", + "tags": [ + "modular", + "occupancy", + "reset" + ] + }, + { + "id": "spatial-embodied-interaction-browsing-proximity-marker", + "form": "a browsing proximity marker, with near-mid-far distance bands, activation boundaries, and opt-out paths", + "lineage": "Builds from the legible conventions of the browsing proximity marker.", + "tags": [ + "distance", + "activation", + "optout" + ] + }, + { + "id": "spatial-embodied-interaction-browsing-handoff-zone", + "form": "a browsing handoff zone, with giver-and-receiver positions, object transfer surface, and completion confirmation", + "lineage": "Builds from the legible conventions of the browsing handoff zone.", + "tags": [ + "roles", + "transfer", + "confirmation" + ] + }, + { + "id": "spatial-embodied-interaction-browsing-participation-grid", + "form": "a browsing participation grid, with individual standing cells, shared center area, and turn-order indicators", + "lineage": "Builds from the legible conventions of the browsing participation grid.", + "tags": [ + "cells", + "sharedspace", + "turns" + ] + }, + { + "id": "spatial-embodied-interaction-browsing-feedback-wall", + "form": "a browsing feedback wall, with immediate response zones, aggregate result fields, and reflection prompts", + "lineage": "Builds from the legible conventions of the browsing feedback wall.", + "tags": [ + "response", + "aggregation", + "reflection" + ] + }, + { + "id": "spatial-embodied-interaction-performance-floor-cue-system", + "form": "a performance floor cue system, with entry orientation point, choice branches, and rejoin landmarks", + "lineage": "Builds from the legible conventions of the performance floor cue system.", + "tags": [ + "orientation", + "choice", + "rejoin" + ] + }, + { + "id": "spatial-embodied-interaction-performance-movable-station-set", + "form": "a performance movable station set, with clockwise work sequence, tool-return outlines, and safety clearance bands", + "lineage": "Builds from the legible conventions of the performance movable station set.", + "tags": [ + "sequence", + "tools", + "clearance" + ] + }, + { + "id": "spatial-embodied-interaction-performance-proximity-marker", + "form": "a performance proximity marker, with sample-and-return points, comparison bays, and selection hold areas", + "lineage": "Builds from the legible conventions of the performance proximity marker.", + "tags": [ + "sampling", + "comparison", + "holding" + ] + }, + { + "id": "spatial-embodied-interaction-performance-handoff-zone", + "form": "a performance handoff zone, with performer-audience boundaries, attention focus lines, and quiet exit routes", + "lineage": "Builds from the legible conventions of the performance handoff zone.", + "tags": [ + "boundary", + "focus", + "exit" + ] + }, + { + "id": "spatial-embodied-interaction-performance-participation-grid", + "form": "a performance participation grid, with rest-duration cues, personal-space intervals, and low-stimulation routes", + "lineage": "Builds from the legible conventions of the performance participation grid.", + "tags": [ + "rest", + "spacing", + "calm" + ] + }, + { + "id": "spatial-embodied-interaction-performance-feedback-wall", + "form": "a performance feedback wall, with completion checkpoint, belongings reminder zone, and onward destination signs", + "lineage": "Builds from the legible conventions of the performance feedback wall.", + "tags": [ + "completion", + "reminder", + "onward" + ] + }, + { + "id": "spatial-embodied-interaction-rest-floor-cue-system", + "form": "a rest floor cue system, with approach-direction arrows, pause footprints, and next-step markers", + "lineage": "Builds from the legible conventions of the rest floor cue system.", + "tags": [ + "approach", + "pause", + "nextstep" + ] + }, + { + "id": "spatial-embodied-interaction-rest-movable-station-set", + "form": "a rest movable station set, with reconfigurable modules, clear occupied states, and reset positions", + "lineage": "Builds from the legible conventions of the rest movable station set.", + "tags": [ + "modular", + "occupancy", + "reset" + ] + }, + { + "id": "spatial-embodied-interaction-rest-proximity-marker", + "form": "a rest proximity marker, with near-mid-far distance bands, activation boundaries, and opt-out paths", + "lineage": "Builds from the legible conventions of the rest proximity marker.", + "tags": [ + "distance", + "activation", + "optout" + ] + }, + { + "id": "spatial-embodied-interaction-rest-handoff-zone", + "form": "a rest handoff zone, with giver-and-receiver positions, object transfer surface, and completion confirmation", + "lineage": "Builds from the legible conventions of the rest handoff zone.", + "tags": [ + "roles", + "transfer", + "confirmation" + ] + } + ] + } ] } diff --git a/skill/scripts/concept-reviews.json b/skill/scripts/concept-reviews.json new file mode 100644 index 000000000..28cc9888c --- /dev/null +++ b/skill/scripts/concept-reviews.json @@ -0,0 +1,571 @@ +{ + "schemaVersion": 1, + "comment": "Human review decisions keyed by stable concept ID. Missing entries are pending and ineligible for challenger sampling.", + "reviews": { + "broadcast-programming-cinema-s-projection-booth-reel-change-cue-sheets": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "broadcast-programming-cinema-ticket-stub": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "broadcast-programming-photographic-slide-carousel-and-its-typed-index-card": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "broadcast-programming-printed-tv-programme-guide": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "broadcast-programming-radio-station-s-program-log-and-request-line-cards": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "broadcast-programming-shortwave-listener-s-qsl-card-collection-and-frequen": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "broadcast-programming-teletext-service": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "broadcast-programming-video-rental-shop": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "broadcast-programming-vintage-radio-receiver-plate": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "broadcast-programming-vinyl-double-album-gatefold": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "civic-legal-governance-ballot-paper": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "civic-legal-governance-bank-passbook": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "civic-legal-governance-bank-statement": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "civic-legal-governance-botanist-s-herbarium-sheet": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "civic-legal-governance-cadastral-survey-map": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "civic-legal-governance-census-questionnaire": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "civic-legal-governance-court-transcript": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "civic-legal-governance-customs-declaration-form": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "civic-legal-governance-double-entry-ledger": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "civic-legal-governance-expedition-s-field-notebook": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "civic-legal-governance-industrial-standards-sheet": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "civic-legal-governance-laboratory-notebook": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "civic-legal-governance-legal-case-file": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "civic-legal-governance-library-card-catalog": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "civic-legal-governance-passport-booklet": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "civic-legal-governance-patent-folio": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "civic-legal-governance-repair-manual-s-exploded-parts-diagrams": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "civic-legal-governance-ship-s-log": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "civic-legal-governance-shipping-manifest": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "civic-legal-governance-telephone-directory": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "civic-legal-governance-title-deed-and-property-survey": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "civic-legal-governance-utility-bill": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "editorial-news-serial-atlas-plate": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "editorial-news-serial-broadsheet-newspaper-s-sports-section": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "editorial-news-serial-classified-ads-section": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "editorial-news-serial-comic-book-page": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "editorial-news-serial-dictionary-spread": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "editorial-news-serial-encyclopedia-double-spread": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "editorial-news-serial-farmer-s-almanac": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "editorial-news-serial-fashion-lookbook": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "editorial-news-serial-literary-journal-s-table-of-contents": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "editorial-news-serial-mail-order-catalog": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "editorial-news-serial-naturalist-s-field-guide": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "editorial-news-serial-paperback-s-back-cover-and-front-matter": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "editorial-news-serial-pharmacopoeia-monograph": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "editorial-news-serial-storyboard-sheet": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "editorial-news-serial-theater-playbill": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "editorial-news-serial-zine": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "games-play-bingo-hall-s-number-board-and-dabbed-cards": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "games-play-board-game-s-rulebook": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "games-play-chess-annotation-sheet": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "games-play-crossword-page": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "games-play-kanban-board": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "games-play-loteri-a-board": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "games-play-perpetual-calendar": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "games-play-scorekeeper-s-baseball-scorecard": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "games-play-tarot-spread": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "games-play-tournament-bracket": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "global-manuscripts-knowledge-african-block-print-textile-grid": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "global-manuscripts-knowledge-annotated-manuscript-folio": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "global-manuscripts-knowledge-aztec-tribute-codex": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "global-manuscripts-knowledge-brazilian-cordel-chapbook": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "global-manuscripts-knowledge-chinese-accordion-fold-book": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "global-manuscripts-knowledge-east-asian-handscroll": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "global-manuscripts-knowledge-ethiopian-codex-spread": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "global-manuscripts-knowledge-indian-palm-leaf-manuscript": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "global-manuscripts-knowledge-islamic-geometric-tile-system": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "global-manuscripts-knowledge-japanese-bento-box-partition": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "global-manuscripts-knowledge-korean-folding-screen": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "global-manuscripts-knowledge-persian-manuscript-page": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "global-manuscripts-knowledge-sheet-music": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "global-manuscripts-knowledge-stamp-collector-s-album": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "global-manuscripts-knowledge-vintage-postcard-back": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "industrial-control-aircraft-preflight-checklist": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "industrial-control-camera-viewfinder-overlay": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "industrial-control-darkroom-contact-sheet": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "industrial-control-hospital-patient-chart": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "industrial-control-medical-triage-chart": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "industrial-control-mission-control-status-wall": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "industrial-control-oscilloscope-faceplate": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "industrial-control-patch-bay-diagram": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "industrial-control-periodic-table": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "industrial-control-recording-studio-mixing-console": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "industrial-control-seismograph-station-s-drum-recorders-and-event-logs": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "industrial-control-ship-s-bridge-instrument-panel": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "industrial-control-sonar-sweep-display": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "industrial-control-weather-station-s-synoptic-chart": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "mapping-navigation-airport-departures-board": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "mapping-navigation-botanical-garden-trail-map": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "mapping-navigation-emergency-evacuation-plan": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "mapping-navigation-harbor-s-tide-table-board-and-small-craft-advisories": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "mapping-navigation-metro-system-s-map-and-station-signage": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "mapping-navigation-motorway-sign-gantry": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "mapping-navigation-museum-gallery-directory": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "mapping-navigation-national-park-trailhead-kiosk": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "mapping-navigation-parking-garage-level-guide": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "mapping-navigation-pilgrimage-route-map": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "mapping-navigation-race-circuit-s-pit-wall-timing-screens": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "mapping-navigation-rail-platform-sign-system": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "mapping-navigation-supermarket-planogram": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "mapping-navigation-theater-s-lobby-cards-and-marquee": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "retail-packaging-service-auction-house-catalog": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "retail-packaging-service-coupon-sheet": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "retail-packaging-service-hardware-store-s-parts-drawers": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "retail-packaging-service-mail-order-seed-catalog": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "retail-packaging-service-market-price-board": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "retail-packaging-service-matchbook-and-cigar-band-graphics": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "retail-packaging-service-pharmacy-prescription-label-and-patient-information": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "retail-packaging-service-produce-market-s-chalkboard-price-signs-and-crate-si": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "retail-packaging-service-recipe-card": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "retail-packaging-service-restaurant-order-rail": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "retail-packaging-service-restaurant-order-ticket": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + }, + "retail-packaging-service-seed-packet-s-front-and-back-panels": { + "status": "approved", + "reviewedBy": "pbakaus", + "reviewedAt": "2026-07-18T00:00:00.000Z" + } + } +} diff --git a/skill/scripts/concept-seed.mjs b/skill/scripts/concept-seed.mjs index 6a5e580aa..e72b920ff 100644 --- a/skill/scripts/concept-seed.mjs +++ b/skill/scripts/concept-seed.mjs @@ -1,7 +1,7 @@ #!/usr/bin/env node /** - * External concept seed: the dice half of new-work's world and surface - * selection procedures. + * External concept seed: the dice half of new-work's coupled-direction and + * established-world surface procedures. * * The model derives a grounded shortlist of candidate FORMS from the * audience's world and the subject's cultural home (see @@ -23,94 +23,128 @@ * material and win over thin categories, which is the intended shape. * * Usage: - * node scripts/concept-seed.mjs --scope surface - * node scripts/concept-seed.mjs --scope world --from + * node scripts/concept-seed.mjs --scope direction + * node scripts/concept-seed.mjs --scope surface --from * * Env vars: * IMPECCABLE_CONCEPT_SEED — same as --from; for reproducible eval runs. */ import crypto from 'node:crypto'; -import { readFileSync } from 'node:fs'; -import { dirname, join } from 'node:path'; +import { dirname, join, resolve } from 'node:path'; import { fileURLToPath } from 'node:url'; +import { + approvedPoolRevision, + deterministicRank, + readConceptCatalog, +} from './lib/concept-catalog.mjs'; const here = dirname(fileURLToPath(import.meta.url)); -const pool = JSON.parse(readFileSync(join(here, 'concept-ingredients.json'), 'utf8')); +const { concepts } = readConceptCatalog( + join(here, 'concept-ingredients.json'), + join(here, 'concept-reviews.json') +); -const args = process.argv.slice(2); -const fromIdx = args.indexOf('--from'); -const scopeIdx = args.indexOf('--scope'); -const scope = scopeIdx !== -1 ? args[scopeIdx + 1] : 'surface'; -if (scope !== 'surface' && scope !== 'world') { - process.stderr.write('concept-seed: --scope must be world or surface\n'); - process.exit(1); -} -// When no key is supplied, generate one and print it: a user reporting a -// bad outcome can hand us the key and we replay the exact roll. -const key = fromIdx !== -1 - ? args[fromIdx + 1] - : (process.env.IMPECCABLE_CONCEPT_SEED || crypto.randomBytes(4).toString('hex')); - -function hashUnit(k, salt) { - const h = crypto.createHash('sha256').update(`${scope}:${salt}:${k}`).digest(); - return h.readUInt32BE(0) / 0xffffffff; -} -const unit = (salt) => hashUnit(key, salt); - -const buildIndex = 3 + Math.floor(unit('index') * 5); // 3..7 - -const entries = Object.entries(pool) - .filter(([k]) => !k.startsWith('_')) - .flatMap(([, list]) => list); -const picks = []; -const taken = new Set(); -for (let i = 0; picks.length < 3 && i < 60; i++) { - const idx = Math.floor(unit(`challenger-${i}`) * entries.length) % entries.length; - if (!taken.has(idx)) { - taken.add(idx); - picks.push(entries[idx]); +export function selectApprovedChallengers({ scope, key, sourceConcepts = concepts }) { + const approved = sourceConcepts.filter(concept => concept.status === 'approved'); + const approvedByFamily = new Map(); + for (const concept of approved) { + const family = approvedByFamily.get(concept.familyId) || []; + family.push(concept); + approvedByFamily.set(concept.familyId, family); } + if (approvedByFamily.size < 3) { + throw new Error('concept-seed: at least three families need approved concepts'); + } + const familyIds = deterministicRank( + [...approvedByFamily.keys()].map(id => ({ id })), + `${scope}:${key}:families` + ).slice(0, 3).map(item => item.id); + const picks = familyIds.map((familyId, index) => deterministicRank( + approvedByFamily.get(familyId), + `${scope}:${key}:challenger-${index}` + )[0]); + return { + approved, + picks, + poolRevision: approvedPoolRevision(sourceConcepts), + }; } -const promotedInstruction = scope === 'world' - ? `After ordering the grounded visual-world candidates by product fit, promote - candidate ${buildIndex} into the serious shortlist. Present it beside the - strongest materially different candidates and let the user select or revise - the durable world. It must survive navigation, quiet and dense content, - interaction and state, and a surface unlike the current request.` +export function renderConceptSeed({ + scope = 'surface', + key = process.env.IMPECCABLE_CONCEPT_SEED || crypto.randomBytes(4).toString('hex'), +} = {}) { + if (scope !== 'surface' && scope !== 'direction') { + throw new Error('concept-seed: --scope must be direction or surface'); + } + const unit = (salt) => { + const h = crypto.createHash('sha256').update(`${scope}:${salt}:${key}`).digest(); + return h.readUInt32BE(0) / 0xffffffff; + }; + const buildIndex = 3 + Math.floor(unit('index') * 5); // 3..7 + const { approved, picks, poolRevision } = selectApprovedChallengers({ scope, key }); + + const promotedInstruction = scope === 'direction' + ? `After ordering the grounded coupled directions by product fit, promote + candidate ${buildIndex} into the serious shortlist. Each candidate must join + a durable visual system to a concrete expression for the requested first + surface; select or revise that pair as one decision. It must survive the + current task plus navigation, quiet and dense content, interaction and state, + and a substantially different future surface.` : `After ordering the task's grounded structural candidates by resonance, promote candidate ${buildIndex} into the serious shortlist. In an attended run, present it beside the strongest materially different candidates and let the user select or revise the surface concept. In a truly unattended run, use it when it survives audience identification and product clarity.`; -const challengerInstruction = scope === 'world' - ? `A challenger enters the world shortlist only when its structure can become - reusable identity grammar across the product, not a one-page costume. Weigh - product identification, product clarity, and cross-surface system breadth.` + const challengerInstruction = scope === 'direction' + ? `Translate each challenger's organizing logic into reusable identity grammar + and a strong first-surface structure before judging it. Noticeable form is + allowed when the product stays clear. Compare only audience identification + and product clarity.` : `A challenger wins only when it beats the grounded list on both audience identification and product clarity. It may change task topology or interaction, but never the committed visual identity.`; -const authorityInstruction = scope === 'world' - ? `PRODUCT.md and explicit incumbent brand commitments constrain every world. -The seed never chooses exact colors, fonts, tokens, or a user preference.` + const authorityInstruction = scope === 'direction' + ? `PRODUCT.md and explicit incumbent brand commitments constrain every coupled +direction. The seed never chooses exact colors, fonts, tokens, or a user +preference, and it never permits the world and first surface to be selected +independently.` : `PRODUCT.md and DESIGN.md constrain every surface candidate's identity vocabulary; they do not cancel task-level composition. The seed never authorizes a new palette, type system, material world, or unfamiliar control behavior.`; -process.stdout.write(`${scope.toUpperCase()} CONCEPT SEED (key: ${key}; rerun with --scope ${scope} --from ${key} to reproduce this roll) + return `${scope.toUpperCase()} CONCEPT SEED (key: ${key}; approved pool: ${poolRevision}; ${approved.length}/${concepts.length} human-approved; rerun with --scope ${scope} --from ${key} to reproduce this roll against this catalog revision) PROMOTED INDEX: ${buildIndex} ${promotedInstruction} The promotion exists to refuse the model's ranking rut, not to outrank the - user or the brief. + user or the brief. Never expose promotion metadata in choice labels or order. CHALLENGERS: - 1. ${picks[0]} - 2. ${picks[1]} - 3. ${picks[2]} + 1. ${picks[0].form} + 2. ${picks[1].form} + 3. ${picks[2].form} ${challengerInstruction} ${authorityInstruction} A user- or brief-pinned decision beats the roll, always. -`); +`; +} + +if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) { + const args = process.argv.slice(2); + const fromIdx = args.indexOf('--from'); + const scopeIdx = args.indexOf('--scope'); + try { + process.stdout.write(renderConceptSeed({ + scope: scopeIdx !== -1 ? args[scopeIdx + 1] : 'surface', + key: fromIdx !== -1 + ? args[fromIdx + 1] + : (process.env.IMPECCABLE_CONCEPT_SEED || crypto.randomBytes(4).toString('hex')), + })); + } catch (error) { + process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`); + process.exitCode = 1; + } +} diff --git a/skill/scripts/context.mjs b/skill/scripts/context.mjs index aa657b863..080e7ee3a 100644 --- a/skill/scripts/context.mjs +++ b/skill/scripts/context.mjs @@ -25,7 +25,8 @@ import os from 'node:os'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { parseTargetOptions } from './lib/target-args.mjs'; -import { IMPECCABLE_COMMAND } from './lib/provider.mjs'; +import { IMPECCABLE_COMMAND, IMPECCABLE_PROVIDER_ID } from './lib/provider.mjs'; +import { renderLlmOnlySlopReview } from './lib/slop-review.mjs'; import { resolveSurfaceBrief } from './lib/surface-briefs.mjs'; const PRODUCT_NAMES = ['PRODUCT.md', 'Product.md', 'product.md']; @@ -1051,6 +1052,7 @@ async function cli() { ]; appendSurfaceBriefContext(parts, ctx); parts.push(buildResolvedContextDirective(ctx, cliOptions, { targetExists })); + appendHookFallback(parts, ctx); if (shouldWarnMissingTarget(ctx, targetProvided, targetExists)) { parts.push(buildMissingTargetDirective()); } @@ -1064,6 +1066,7 @@ async function cli() { } appendSurfaceBriefContext(parts, ctx); parts.push(buildResolvedContextDirective(ctx, cliOptions, { targetExists })); + appendHookFallback(parts, ctx); if (shouldWarnMissingTarget(ctx, targetProvided, targetExists)) { parts.push(buildMissingTargetDirective()); } @@ -1111,6 +1114,72 @@ function pathExistsForTarget(cwd, targetPath) { return fs.existsSync(abs); } +const HOOK_MANIFESTS_BY_PROVIDER = Object.freeze({ + 'claude-code': ['.claude/settings.local.json', '.claude/settings.json'], + codex: ['.codex/hooks.json'], + agents: ['.codex/hooks.json'], + cursor: ['.cursor/hooks.json'], + github: ['.github/hooks/impeccable.json'], +}); + +function truthyEnv(value) { + return typeof value === 'string' && /^(1|true|yes|on)$/i.test(value.trim()); +} + +function valueHasHookMarker(value) { + if (typeof value === 'string') { + return value.includes('skills/impeccable/scripts/hook.mjs') + || value.includes('skills/impeccable/scripts/hook-before-edit.mjs'); + } + if (Array.isArray(value)) return value.some(valueHasHookMarker); + if (value && typeof value === 'object') return Object.values(value).some(valueHasHookMarker); + return false; +} + +function hookEnabledAt(root) { + if (truthyEnv(process.env.IMPECCABLE_HOOK_DISABLED)) return false; + let enabled = true; + for (const name of ['.impeccable/config.json', '.impeccable/config.local.json']) { + const raw = readJson(path.join(root, name)); + if (raw?.hook && Object.prototype.hasOwnProperty.call(raw.hook, 'enabled')) { + enabled = raw.hook.enabled !== false; + } + } + return enabled; +} + +const STOP_REVIEW_PROVIDERS = new Set(['claude-code', 'codex', 'agents']); + +function automaticHookMode(ctx) { + if (ctx.platform === 'ios' || ctx.platform === 'android' || ctx.platform === 'adaptive') { + return 'none'; + } + const activeRoot = path.resolve(ctx.projectRoot || process.cwd()); + if (!hookEnabledAt(activeRoot)) return 'none'; + const manifests = HOOK_MANIFESTS_BY_PROVIDER[IMPECCABLE_PROVIDER_ID] || []; + const roots = [...new Set([process.cwd(), ctx.projectRoot, ctx.repoRoot].filter(Boolean).map((root) => path.resolve(root)))]; + for (const root of roots) { + for (const rel of manifests) { + const raw = readJson(path.join(root, rel)); + if (raw?.hooks && valueHasHookMarker(raw.hooks)) { + return STOP_REVIEW_PROVIDERS.has(IMPECCABLE_PROVIDER_ID) ? 'stop' : 'per-edit'; + } + } + } + return 'none'; +} + +function appendHookFallback(parts, ctx) { + const hookMode = automaticHookMode(ctx); + if (hookMode === 'stop') return; + const native = ctx.platform === 'ios' || ctx.platform === 'android' || ctx.platform === 'adaptive'; + parts.push(renderLlmOnlySlopReview({ + automaticDetector: hookMode === 'per-edit', + manualDetector: hookMode === 'none' && !native, + scriptsPath: path.dirname(fileURLToPath(import.meta.url)), + })); +} + function buildResolvedContextDirective(ctx, options, { targetExists = null } = {}) { const targetPath = hasTargetOption(options) ? options.targetPath : null; return `RESOLVED_CONTEXT:\n${JSON.stringify({ diff --git a/skill/scripts/hook-lib.mjs b/skill/scripts/hook-lib.mjs index 2c6bf240c..50aa2f8fb 100644 --- a/skill/scripts/hook-lib.mjs +++ b/skill/scripts/hook-lib.mjs @@ -46,6 +46,7 @@ import path from 'node:path'; import { pathToFileURL, fileURLToPath } from 'node:url'; import { extractPlatform, loadContext } from './context.mjs'; import { IMPECCABLE_COMMAND } from './lib/provider.mjs'; +import { renderStopSlopReview } from './lib/slop-review.mjs'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); @@ -2008,9 +2009,22 @@ export async function runStopHook({ stdinJson, env = {}, cwd = process.cwd(), no return result({ skipped: 'native-platform', platform, durationMs: Date.now() - started }); } + const session = ensureSession(cache, sessionId); + const needsSlopReview = session.llmSlopReviewed !== true; + const det = detector || await loadDetector(); if (!det || typeof det.detectText !== 'function') { - return result({ skipped: 'detector-missing', durationMs: Date.now() - started }); + if (!needsSlopReview) return result({ skipped: 'detector-missing', durationMs: Date.now() - started }); + session.llmSlopReviewed = true; + session.updatedAt = Date.now(); + persistCache(projectCwd, cache); + const text = renderStopSlopReview(); + return { + exitCode: 0, + stdout: payload(text, 'Stop', harness), + emission: { kind: 'stop-llm-slop-review', llmSlopReview: true }, + audit: { ...audit, emitted: true, detectorMissing: true, llmSlopReview: true, chars: text.length, durationMs: Date.now() - started }, + }; } const scanOptions = designSystemOptions(config, det, projectCwd); @@ -2071,10 +2085,15 @@ export async function runStopHook({ stdinJson, env = {}, cwd = process.cwd(), no } audit.scannedFiles = scanned; - if (freshGroups.length === 0 && contractEntries.length === 0) { + if (freshGroups.length === 0 && contractEntries.length === 0 && !needsSlopReview) { return result({ emitted: false, skipped: 'stop-clean', durationMs: Date.now() - started }); } + if (needsSlopReview) { + session.llmSlopReviewed = true; + session.updatedAt = Date.now(); + } + // Fresh findings and first-time contract audits earn the cache write; // both mark this batch as surfaced so the next Stop fire is silent // unless new issues appear. @@ -2089,6 +2108,9 @@ export async function runStopHook({ stdinJson, env = {}, cwd = process.cwd(), no if (contractEntries.length > 0) { parts.push(renderContractAudit(contractEntries, { cwd: projectCwd })); } + if (needsSlopReview) { + parts.push(renderStopSlopReview()); + } const text = appendDesignSystemNote(parts.join('\n\n'), scanOptions); return { exitCode: 0, @@ -2099,6 +2121,7 @@ export async function runStopHook({ stdinJson, env = {}, cwd = process.cwd(), no ...(contractEntries.length > 0 ? { contractFiles: contractEntries.map((entry) => entry.filePath) } : {}), + ...(needsSlopReview ? { llmSlopReview: true } : {}), }, audit: { ...audit, @@ -2106,6 +2129,7 @@ export async function runStopHook({ stdinJson, env = {}, cwd = process.cwd(), no freshFiles: freshGroups.length, freshFindings: freshGroups.reduce((sum, group) => sum + group.findings.length, 0), ...(contractEntries.length > 0 ? { contractAudits: contractEntries.length } : {}), + ...(needsSlopReview ? { llmSlopReview: true } : {}), chars: text.length, durationMs: Date.now() - started, }, diff --git a/skill/scripts/lib/concept-catalog.mjs b/skill/scripts/lib/concept-catalog.mjs new file mode 100644 index 000000000..f5150b307 --- /dev/null +++ b/skill/scripts/lib/concept-catalog.mjs @@ -0,0 +1,159 @@ +import crypto from 'node:crypto'; +import { readFileSync } from 'node:fs'; + +export const CONCEPT_STATUSES = new Set(['approved', 'rejected']); + +export function normalizeConceptForm(value) { + return String(value || '') + .normalize('NFKD') + .toLowerCase() + .replace(/[’‘]/g, "'") + .replace(/[^a-z0-9]+/g, ' ') + .trim(); +} + +export function readConceptCatalog(catalogPath, reviewsPath) { + const catalog = JSON.parse(readFileSync(catalogPath, 'utf8')); + const reviewData = JSON.parse(readFileSync(reviewsPath, 'utf8')); + const reviews = reviewData.reviews || {}; + const concepts = []; + + for (const family of catalog.families || []) { + for (const concept of family.concepts || []) { + concepts.push({ + ...concept, + familyId: family.id, + familyLabel: family.label, + status: reviews[concept.id]?.status || 'pending', + review: reviews[concept.id] || null, + }); + } + } + + return { catalog, reviewData, reviews, concepts }; +} + +export function validateConceptCatalog(catalog, reviewData, { expectedTotal, minimumTotal } = {}) { + const errors = []; + const warnings = []; + const familyIds = new Set(); + const conceptIds = new Set(); + const normalizedForms = new Map(); + const concepts = []; + + if (!Number.isInteger(catalog?.schemaVersion) || catalog.schemaVersion < 1) { + errors.push('catalog.schemaVersion must be a positive integer'); + } + if (typeof catalog?.catalogVersion !== 'string' || !catalog.catalogVersion.trim()) { + errors.push('catalog.catalogVersion must be a non-empty string'); + } + if (!Array.isArray(catalog?.families) || catalog.families.length < 3) { + errors.push('catalog.families must contain at least three families'); + } + + for (const family of catalog?.families || []) { + if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(family.id || '')) { + errors.push(`invalid family id: ${String(family.id)}`); + } else if (familyIds.has(family.id)) { + errors.push(`duplicate family id: ${family.id}`); + } + familyIds.add(family.id); + if (typeof family.label !== 'string' || !family.label.trim()) { + errors.push(`family ${family.id || '(unknown)'} needs a label`); + } + if (!Array.isArray(family.concepts) || family.concepts.length === 0) { + errors.push(`family ${family.id || '(unknown)'} has no concepts`); + continue; + } + + for (const concept of family.concepts) { + concepts.push(concept); + if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(concept.id || '')) { + errors.push(`invalid concept id: ${String(concept.id)}`); + } else if (conceptIds.has(concept.id)) { + errors.push(`duplicate concept id: ${concept.id}`); + } + conceptIds.add(concept.id); + + const normalized = normalizeConceptForm(concept.form); + if (!normalized) { + errors.push(`concept ${concept.id || '(unknown)'} needs a form`); + } else if (normalizedForms.has(normalized)) { + errors.push(`duplicate concept form: ${concept.id} and ${normalizedForms.get(normalized)}`); + } + normalizedForms.set(normalized, concept.id); + + if (typeof concept.form !== 'string' || !concept.form.includes(',')) { + errors.push(`concept ${concept.id || '(unknown)'} must name a form and inherited structure after a comma`); + } + if (typeof concept.lineage !== 'string' || !concept.lineage.trim()) { + errors.push(`concept ${concept.id || '(unknown)'} needs lineage metadata`); + } + if (!Array.isArray(concept.tags) || concept.tags.length !== 3 || concept.tags.some(tag => typeof tag !== 'string' || !tag.trim())) { + errors.push(`concept ${concept.id || '(unknown)'} must have exactly three structural tags`); + } + if (/\b(?:in the style of|styled like|copy of)\b/i.test(concept.form)) { + errors.push(`concept ${concept.id || '(unknown)'} contains imitation language`); + } + } + } + + if (expectedTotal !== undefined && concepts.length !== expectedTotal) { + errors.push(`expected ${expectedTotal} concepts, found ${concepts.length}`); + } + if (minimumTotal !== undefined && concepts.length < minimumTotal) { + errors.push(`expected at least ${minimumTotal} concepts, found ${concepts.length}`); + } + + if (!Number.isInteger(reviewData?.schemaVersion) || reviewData.schemaVersion < 1) { + errors.push('reviews.schemaVersion must be a positive integer'); + } + for (const [id, review] of Object.entries(reviewData?.reviews || {})) { + if (!conceptIds.has(id)) errors.push(`review references missing concept: ${id}`); + if (!CONCEPT_STATUSES.has(review?.status)) errors.push(`invalid review status for ${id}: ${String(review?.status)}`); + if (typeof review?.reviewedBy !== 'string' || !review.reviewedBy.trim()) { + errors.push(`review ${id} needs reviewedBy`); + } + if (typeof review?.reviewedAt !== 'string' || Number.isNaN(Date.parse(review.reviewedAt))) { + errors.push(`review ${id} needs an ISO reviewedAt timestamp`); + } + } + + const approved = concepts.filter(concept => reviewData?.reviews?.[concept.id]?.status === 'approved'); + const approvedFamilies = new Set( + (catalog?.families || []) + .filter(family => family.concepts?.some(concept => reviewData?.reviews?.[concept.id]?.status === 'approved')) + .map(family => family.id) + ); + if (approved.length < 3) errors.push('at least three concepts must be approved'); + if (approvedFamilies.size < 3) errors.push('approved concepts must span at least three families'); + + return { + errors, + warnings, + stats: { + families: familyIds.size, + concepts: concepts.length, + approved: approved.length, + pending: concepts.length - Object.keys(reviewData?.reviews || {}).length, + rejected: Object.values(reviewData?.reviews || {}).filter(review => review?.status === 'rejected').length, + }, + }; +} + +export function approvedPoolRevision(concepts) { + const payload = concepts + .filter(concept => concept.status === 'approved') + .map(concept => `${concept.familyId}:${concept.id}:${concept.form}`) + .sort() + .join('\n'); + return crypto.createHash('sha256').update(payload).digest('hex').slice(0, 12); +} + +export function deterministicRank(items, input, idFor = item => item.id) { + return [...items].sort((a, b) => { + const scoreA = crypto.createHash('sha256').update(`${input}:${idFor(a)}`).digest('hex'); + const scoreB = crypto.createHash('sha256').update(`${input}:${idFor(b)}`).digest('hex'); + return scoreB.localeCompare(scoreA) || idFor(a).localeCompare(idFor(b)); + }); +} diff --git a/skill/scripts/lib/provider.mjs b/skill/scripts/lib/provider.mjs index 7fd7951f5..f3dad0a66 100644 --- a/skill/scripts/lib/provider.mjs +++ b/skill/scripts/lib/provider.mjs @@ -1,4 +1,5 @@ // Source scripts default to slash commands. The provider build replaces only // this exact declaration, avoiding heuristic rewrites across executable code. export const IMPECCABLE_COMMAND_PREFIX = '/'; // @impeccable-provider-command-prefix +export const IMPECCABLE_PROVIDER_ID = 'source'; // @impeccable-provider-id export const IMPECCABLE_COMMAND = `${IMPECCABLE_COMMAND_PREFIX}impeccable`; diff --git a/skill/scripts/lib/slop-review.mjs b/skill/scripts/lib/slop-review.mjs new file mode 100644 index 000000000..30f03e34d --- /dev/null +++ b/skill/scripts/lib/slop-review.mjs @@ -0,0 +1,30 @@ +export const LLM_ONLY_SLOP_ITEMS = Object.freeze([ + 'Monospace used merely to signal “technical” or “developer.”', + 'Light or dark mode chosen by category habit rather than the actual use scene.', + 'Everything wrapped in cards, or identical icon-heading-text cards repeated as the page structure.', + 'Hero-metric scaffolds: one oversized number, a small label, supporting stats, and an accent treatment.', + 'Decorative glassmorphism, meaningless sparklines, or generic rounded rectangles with drop shadows.', + 'A modal chosen by reflex when the task does not require interruption or protected focus.', +]); + +export function renderLlmOnlySlopReview({ manualDetector = false, automaticDetector = false, scriptsPath = null } = {}) { + const lines = [ + automaticDetector + ? 'AI_SLOP_REVIEW_REQUIRED: The automatic detector covers mechanical rules, but this harness has no reliable late review. Before finishing changed UI, inspect the authored result for detector-blind model reflexes:' + : 'AI_SLOP_REVIEW_REQUIRED: The automatic Impeccable design hook is not available for this session. Before finishing changed UI, inspect the authored result for detector-blind model reflexes:', + ...LLM_ONLY_SLOP_ITEMS.map((item) => `- ${item}`), + 'Fix reflexes, not intentional choices required by the brief or established visual authority.', + ]; + if (manualDetector && scriptsPath) { + lines.push(`Then run the mechanical detector once over the changed web UI: \`node ${scriptsPath}/detect.mjs --json \`. Do not run it earlier during concept selection.`); + } + return lines.join('\n'); +} + +export function renderStopSlopReview() { + return [ + '[impeccable@1] Detector-blind AI-slop review. The mechanical pass is complete; now inspect the rendered result for model reflexes it cannot reliably detect:', + ...LLM_ONLY_SLOP_ITEMS.map((item) => `- ${item}`), + 'Fix reflexes, not intentional choices required by the brief or established visual authority. Do not rerun the detector; this is the authored judgment layer.', + ].join('\n'); +} diff --git a/skill/scripts/validate-concept-catalog.mjs b/skill/scripts/validate-concept-catalog.mjs new file mode 100644 index 000000000..f6c17812d --- /dev/null +++ b/skill/scripts/validate-concept-catalog.mjs @@ -0,0 +1,22 @@ +#!/usr/bin/env node + +import { dirname, join } from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { readConceptCatalog, validateConceptCatalog } from './lib/concept-catalog.mjs'; + +const here = dirname(fileURLToPath(import.meta.url)); +const { catalog, reviewData } = readConceptCatalog( + join(here, 'concept-ingredients.json'), + join(here, 'concept-reviews.json') +); +const result = validateConceptCatalog(catalog, reviewData, { minimumTotal: 2304 }); + +if (result.errors.length > 0) { + for (const error of result.errors) process.stderr.write(`concept-catalog: ${error}\n`); + process.exitCode = 1; +} else { + process.stdout.write( + `concept-catalog: ${result.stats.concepts} concepts across ${result.stats.families} families; ` + + `${result.stats.approved} approved, ${result.stats.pending} pending, ${result.stats.rejected} rejected\n` + ); +} diff --git a/tests/concept-seed.test.mjs b/tests/concept-seed.test.mjs index 6d1779984..23b49f124 100644 --- a/tests/concept-seed.test.mjs +++ b/tests/concept-seed.test.mjs @@ -1,8 +1,11 @@ import { describe, it } from 'node:test'; import assert from 'node:assert/strict'; import { spawnSync } from 'node:child_process'; +import fs from 'node:fs'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; +import { readConceptCatalog, validateConceptCatalog } from '../skill/scripts/lib/concept-catalog.mjs'; +import { selectApprovedChallengers } from '../skill/scripts/concept-seed.mjs'; const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..'); const SCRIPT = path.join(ROOT, 'skill', 'scripts', 'concept-seed.mjs'); @@ -15,15 +18,17 @@ function run(scope) { } describe('concept seed scopes', () => { - it('keeps world and surface rolls reproducible but independent', () => { - const worldA = run('world'); - const worldB = run('world'); + it('keeps coupled-direction and established-world surface rolls reproducible but independent', () => { + const directionA = run('direction'); + const directionB = run('direction'); const surface = run('surface'); - assert.equal(worldA.status, 0); - assert.equal(worldA.stdout, worldB.stdout); - assert.notEqual(worldA.stdout, surface.stdout); - assert.match(worldA.stdout, /WORLD CONCEPT SEED/); - assert.match(worldA.stdout, /cross-surface system breadth/); + assert.equal(directionA.status, 0); + assert.equal(directionA.stdout, directionB.stdout); + assert.notEqual(directionA.stdout, surface.stdout); + assert.match(directionA.stdout, /DIRECTION CONCEPT SEED/); + assert.match(directionA.stdout, /selected\s+independently/); + assert.match(directionA.stdout, /substantially different future surface/); + assert.match(directionA.stdout, /Never expose promotion metadata/); assert.match(surface.stdout, /SURFACE CONCEPT SEED/); assert.match(surface.stdout, /committed visual identity/); }); @@ -31,6 +36,33 @@ describe('concept seed scopes', () => { it('rejects unknown scopes', () => { const result = run('unknown'); assert.notEqual(result.status, 0); - assert.match(result.stderr, /world or surface/); + assert.match(result.stderr, /direction or surface/); + }); + + it('validates the expanded catalog and human review gate', () => { + const catalogPath = path.join(ROOT, 'skill', 'scripts', 'concept-ingredients.json'); + const reviewsPath = path.join(ROOT, 'skill', 'scripts', 'concept-reviews.json'); + const { catalog, reviewData, concepts } = readConceptCatalog(catalogPath, reviewsPath); + const result = validateConceptCatalog(catalog, reviewData, { minimumTotal: 2304 }); + + assert.deepEqual(result.errors, []); + assert.equal(result.stats.families, 36); + assert.equal(result.stats.concepts >= 2304, true); + assert.equal(result.stats.approved >= 3, true); + assert.equal(result.stats.pending + result.stats.approved + result.stats.rejected, result.stats.concepts); + assert.equal(fs.statSync(catalogPath).size > 500_000, true); + assert.equal( + concepts.filter(concept => concept.status === 'approved').length, + result.stats.approved + ); + }); + + it('selects three approved challengers from distinct families', () => { + for (let index = 0; index < 200; index += 1) { + const { picks } = selectApprovedChallengers({ scope: 'surface', key: `coverage-${index}` }); + assert.equal(picks.length, 3); + assert.equal(new Set(picks.map(pick => pick.familyId)).size, 3); + assert.equal(picks.every(pick => pick.status === 'approved'), true); + } }); }); diff --git a/tests/context.test.mjs b/tests/context.test.mjs index ac4949b32..d9b9679a4 100644 --- a/tests/context.test.mjs +++ b/tests/context.test.mjs @@ -802,6 +802,78 @@ describe('context.mjs CLI', () => { // routes to new-work rather than back through product init. assert.match(res.stdout, /\n---\n\n/); assert.match(res.stdout, /WORLD_DISCOVERY_REQUIRED: PRODUCT\.md exists but no DESIGN\.md/); + assert.match(res.stdout, /AI_SLOP_REVIEW_REQUIRED:/); + assert.match(res.stdout, /Monospace used merely/); + assert.match(res.stdout, /detect\.mjs --json /); + }); + + it('keeps LLM-only fallback guidance out of early context when the current provider hook is active', () => { + const scripts = path.join(scratch, 'bundle', 'skills', 'impeccable', 'scripts'); + const lib = path.join(scripts, 'lib'); + fs.mkdirSync(lib, { recursive: true }); + fs.copyFileSync(SCRIPT_PATH, path.join(scripts, 'context.mjs')); + for (const helper of ['target-args.mjs', 'surface-briefs.mjs', 'target-slug.mjs', 'slop-review.mjs']) { + fs.copyFileSync(path.join(path.dirname(SCRIPT_PATH), 'lib', helper), path.join(lib, helper)); + } + const provider = fs.readFileSync(path.join(path.dirname(SCRIPT_PATH), 'lib', 'provider.mjs'), 'utf8') + .replace("IMPECCABLE_PROVIDER_ID = 'source'", "IMPECCABLE_PROVIDER_ID = 'codex'"); + fs.writeFileSync(path.join(lib, 'provider.mjs'), provider); + + const project = path.join(scratch, 'project'); + fs.mkdirSync(path.join(project, '.codex'), { recursive: true }); + fs.writeFileSync(path.join(project, 'PRODUCT.md'), '# Acme\n'); + fs.writeFileSync(path.join(project, '.codex', 'hooks.json'), JSON.stringify({ + hooks: { Stop: [{ hooks: [{ command: 'node .agents/skills/impeccable/scripts/hook.mjs' }] }] }, + })); + + const res = spawnSync(process.execPath, [path.join(scripts, 'context.mjs')], { + cwd: project, + encoding: 'utf8', + env: { ...process.env, IMPECCABLE_NO_UPDATE_CHECK: '1' }, + }); + assert.equal(res.status, 0, res.stderr); + assert.doesNotMatch(res.stdout, /AI_SLOP_REVIEW_REQUIRED:/); + + fs.mkdirSync(path.join(project, '.impeccable'), { recursive: true }); + fs.writeFileSync(path.join(project, '.impeccable', 'config.json'), JSON.stringify({ hook: { enabled: false } })); + const disabled = spawnSync(process.execPath, [path.join(scripts, 'context.mjs')], { + cwd: project, + encoding: 'utf8', + env: { ...process.env, IMPECCABLE_NO_UPDATE_CHECK: '1' }, + }); + assert.equal(disabled.status, 0, disabled.stderr); + assert.match(disabled.stdout, /AI_SLOP_REVIEW_REQUIRED:/); + assert.match(disabled.stdout, /detect\.mjs --json /); + }); + + it('injects only detector-blind guidance when a per-edit-only hook is active', () => { + const scripts = path.join(scratch, 'bundle', 'skills', 'impeccable', 'scripts'); + const lib = path.join(scripts, 'lib'); + fs.mkdirSync(lib, { recursive: true }); + fs.copyFileSync(SCRIPT_PATH, path.join(scripts, 'context.mjs')); + for (const helper of ['target-args.mjs', 'surface-briefs.mjs', 'target-slug.mjs', 'slop-review.mjs']) { + fs.copyFileSync(path.join(path.dirname(SCRIPT_PATH), 'lib', helper), path.join(lib, helper)); + } + const provider = fs.readFileSync(path.join(path.dirname(SCRIPT_PATH), 'lib', 'provider.mjs'), 'utf8') + .replace("IMPECCABLE_PROVIDER_ID = 'source'", "IMPECCABLE_PROVIDER_ID = 'cursor'"); + fs.writeFileSync(path.join(lib, 'provider.mjs'), provider); + + const project = path.join(scratch, 'project'); + fs.mkdirSync(path.join(project, '.cursor'), { recursive: true }); + fs.writeFileSync(path.join(project, 'PRODUCT.md'), '# Acme\n'); + fs.writeFileSync(path.join(project, '.cursor', 'hooks.json'), JSON.stringify({ + hooks: { preToolUse: [{ command: 'node .cursor/skills/impeccable/scripts/hook-before-edit.mjs' }] }, + })); + + const res = spawnSync(process.execPath, [path.join(scripts, 'context.mjs')], { + cwd: project, + encoding: 'utf8', + env: { ...process.env, IMPECCABLE_NO_UPDATE_CHECK: '1' }, + }); + assert.equal(res.status, 0, res.stderr); + assert.match(res.stdout, /AI_SLOP_REVIEW_REQUIRED:/); + assert.match(res.stdout, /automatic detector covers mechanical rules/); + assert.doesNotMatch(res.stdout, /detect\.mjs --json /); }); it('treats tokenized code as incumbent design authority when DESIGN.md is missing', () => { @@ -1049,7 +1121,7 @@ describe('context.mjs update check', () => { const providerSrc = path.join(path.dirname(SCRIPT_PATH), 'lib', 'provider.mjs'); const providerDest = path.join(path.dirname(skillScript), 'lib', 'provider.mjs'); fs.copyFileSync(providerSrc, providerDest); - for (const helper of ['surface-briefs.mjs', 'target-slug.mjs']) { + for (const helper of ['surface-briefs.mjs', 'target-slug.mjs', 'slop-review.mjs']) { fs.copyFileSync( path.join(path.dirname(SCRIPT_PATH), 'lib', helper), path.join(path.dirname(skillScript), 'lib', helper), diff --git a/tests/docs-integrity.test.js b/tests/docs-integrity.test.js index 0a8b60421..406dff299 100644 --- a/tests/docs-integrity.test.js +++ b/tests/docs-integrity.test.js @@ -98,21 +98,27 @@ describe('docs integrity', () => { const document = fs.readFileSync(path.join(ROOT, 'skill/reference/document.md'), 'utf8'); const codex = fs.readFileSync(path.join(ROOT, 'skill/reference/codex.md'), 'utf8'); const typeset = fs.readFileSync(path.join(ROOT, 'skill/reference/typeset.md'), 'utf8'); + const context = fs.readFileSync(path.join(ROOT, 'skill/scripts/context.mjs'), 'utf8'); expect(init).toContain('durable product truth'); expect(init).toContain('does not invent a visual world'); expect(init).toContain('does not write DESIGN.md'); + expect(init).toContain('If the file is absent, init is incomplete'); expect(init).not.toContain('## Audience World'); expect(newWork).toContain('Missing DESIGN.md does not route back to init'); - expect(newWork).toContain('Establish or replace the visual world'); - expect(newWork).toContain('concept-seed.mjs --scope world'); + expect(newWork).toContain('Shape or select the direction'); + expect(newWork).toContain('concept-seed.mjs --scope direction'); expect(newWork).toContain('concept-seed.mjs --scope surface'); + expect(newWork).toContain('Local extension inside stable authority: shape, do not seed'); + expect(newWork).toContain('Never run it for a local extension'); + expect(newWork).toContain('Do not select a new world and its first surface concept in separate tournaments'); + expect(newWork).toContain('do not reopen either half independently'); expect(newWork).toContain('.impeccable/surfaces/.md'); expect(newWork).toContain('DIRECTION CONTRACT'); expect(newWork).toContain('[codex.md](codex.md)'); expect(newWork).toContain('Commit before correcting'); - expect(newWork).toContain('Judge the shortlist skin-blind'); + expect(newWork).toContain('Judge candidates skin-blind'); expect(newWork).not.toContain('palette.mjs'); expect(shape).toContain('follow [new-work.md](new-work.md)'); @@ -123,10 +129,15 @@ describe('docs integrity', () => { expect(skill).toContain('Premium motion is not transform/opacity alone'); expect(newWork).toContain('The old DESIGN.md and implementation are not authority'); expect(newWork).toContain('Do not offer replacement worlds unless the user asked for a redesign'); - expect(document).toContain('run **Establish or replace the visual world**'); + expect(document).toContain('Run **Select one direction**'); + expect(document).toContain('requires a concrete first surface'); expect(codex).toContain('this file must not reopen it'); expect(codex).toContain('Do not generate a palette artifact'); - expect(typeset).toContain('return to [new-work.md](new-work.md)'); + expect(typeset).toContain('route through [new-work.md](new-work.md)'); + expect(skill).not.toContain('reference/finish.md'); + expect(skill).toContain('manual scan only when no automatic detector is active'); + expect(context).toContain('renderLlmOnlySlopReview'); + expect(context).toContain('automaticHookMode'); }); test('internal docs links point at canonical local routes', () => { diff --git a/tests/hook.test.mjs b/tests/hook.test.mjs index 4463b6ebb..6e6b5bbc3 100644 --- a/tests/hook.test.mjs +++ b/tests/hook.test.mjs @@ -2725,6 +2725,8 @@ describe('runStopHook()', () => { assert.match(out.hookSpecificOutput.additionalContext, /em-dash-overuse/); assert.match(out.hookSpecificOutput.additionalContext, /side-tab/); assert.doesNotMatch(out.hookSpecificOutput.additionalContext, /dark-glow/); + assert.match(out.hookSpecificOutput.additionalContext, /Detector-blind AI-slop review/); + assert.match(out.hookSpecificOutput.additionalContext, /Monospace used merely/); assert.equal(stop.emission.kind, 'stop-deep-pass'); }); @@ -2749,7 +2751,7 @@ describe('runStopHook()', () => { assert.equal(second.audit.skipped, 'stop-clean'); }); - it('respects detector.ignoreRules in the deep pass', async () => { + it('respects detector.ignoreRules while still delivering the one-time LLM-only review', async () => { const sid = 'stop-ignored'; fs.mkdirSync(path.join(cwd, '.impeccable'), { recursive: true }); fs.writeFileSync(getConfigPath(cwd), JSON.stringify({ @@ -2760,8 +2762,12 @@ describe('runStopHook()', () => { await runHook({ stdinJson: JSON.stringify(editEvent(file, sid)), env: {}, cwd, detector: det }); const stop = await runStopHook({ stdinJson: JSON.stringify(stopEvent(sid)), env: {}, cwd, detector: det }); - assert.equal(stop.stdout, ''); - assert.equal(stop.audit.skipped, 'stop-clean'); + const text = JSON.parse(stop.stdout).hookSpecificOutput.additionalContext; + assert.doesNotMatch(text, /em-dash-overuse/); + assert.match(text, /Detector-blind AI-slop review/); + const second = await runStopHook({ stdinJson: JSON.stringify(stopEvent(sid)), env: {}, cwd, detector: det }); + assert.equal(second.stdout, ''); + assert.equal(second.audit.skipped, 'stop-clean'); }); it('honors kill switches and the re-entrancy guard', async () => { @@ -3024,14 +3030,15 @@ describe('runStopHook() — direction-contract audit', () => { assert.doesNotMatch(text, /Direction-contract audit/); }); - it('stays silent for a malformed (unclosed) contract comment on a clean pass', async () => { + it('does not audit a malformed contract but still emits the one-time LLM-only review', async () => { const sid = 'contract-malformed'; const file = write('index.html', '