refactor(bmad-ux): adopt DESIGN.md spec, split into two-file spine, align prd/brief

DESIGN.md (visual identity per the Google Labs spec) and EXPERIENCE.md
(behavior, flow, IA) replace the single design.md spine. EXPERIENCE.md
cross-references DESIGN.md tokens via the spec's {path.to.token} syntax.

Example suite restructure
- 3 DESIGN.md examples: editorial (Stitch source / Linen & Logic), calm
  native mobile (Quill), shadcn-on-Tailwind web SaaS (Drift)
- 2 paired EXPERIENCE.md examples (Quill, Drift); Linen & Logic unpaired
  to model the Stitch handoff scenario
- Replaces the prior 2-example combined spine set

Discovery additions (outcome-driven, one line each)
- Source scan: glob {planning_artifacts}/ for candidates, parent never reads
- Form-factor: resolve before IA closes; journeys often derive it
- Surface closure: every stated need has a surface, every surface a journey
- Named-protagonist journeys (Mary, not "the user")
- Design handoff working mode (extensible producer registry, default: Stitch)

PRD and brief alignment with same insights
- bmad-prd: dropped standalone Primary Persona section from template;
  renamed "Personas + Journeys" entry to "Journey-led"; named-protagonist
  rule on UJs; form-factor probe; validation checklist updated
- bmad-product-brief: form-factor surfaced in Discovery topics

Quality scan fixes
- Added ## Overview heading; renamed ## Activation to ## On Activation
- Replaced ../ paths in example assets with {planning_artifacts}/
- Sources section compressed (abstract delta-only rule)
- Working mode aligned to "Fast path" / "Coaching path" BMad-wide convention

New
- references/design-md-spec.md: working summary of the spec for the LLM
- customize.toml: design_md_examples, experience_md_examples,
  design_handoffs registries
- .prettierignore: ignore .analysis/ quality-scan artifacts repo-wide
This commit is contained in:
Brian Madison 2026-05-21 22:29:58 -05:00
parent 1040f5d3cd
commit f5e320cd04
20 changed files with 679 additions and 255 deletions

View File

@ -10,3 +10,8 @@ _bmad*/
# IDE integration folders (user-specific, not in repo)
.junie/
# Quality scan artifacts produced by bmad-workflow-builder
# (per-skill .analysis/ folders contain JSON/HTML reports that should
# not block commits with formatting checks)
**/.analysis/

View File

@ -61,7 +61,7 @@ Omit keys for artifacts that were not produced.
## Discovery
Conversationally surface what the user brings, why this brief exists, and the domain — echo back how each shapes your approach. Open with space for the full picture: invite a brain dump and ask up front for any source material they already have (memo, deck, transcript, prior brief, slack thread). Read what exists first; ask only what is missing. After the dump, a simple "anything else?" often surfaces what they almost forgot. Drill into specifics only after the broad shape is on the table; premature granular questions interrupt the dump and miss the room. Get a read on stakes early (passion project, internal pitch, investor input, public launch), and let that calibrate how hard you push. During the dump, spawn web-research subagents to ground the picture — landscape, comparables, current state — AI especially, where training data ages by the week. Subagent searches; parent gets a digest. Deep work (full market sizing, exhaustive teardowns) → suggest `bmad-market-research` or `bmad-domain-research`.
Conversationally surface what the user brings, why this brief exists, the domain, and the form-factor (mobile / web / desktop / multi-surface / hardware / API — what *is* this thing) — echo back how each shapes your approach. Open with space for the full picture: invite a brain dump and ask up front for any source material they already have (memo, deck, transcript, prior brief, slack thread). Read what exists first; ask only what is missing. After the dump, a simple "anything else?" often surfaces what they almost forgot. Drill into specifics only after the broad shape is on the table; premature granular questions interrupt the dump and miss the room. Get a read on stakes early (passion project, internal pitch, investor input, public launch), and let that calibrate how hard you push. During the dump, spawn web-research subagents to ground the picture — landscape, comparables, current state — AI especially, where training data ages by the week. Subagent searches; parent gets a digest. Deep work (full market sizing, exhaustive teardowns) → suggest `bmad-market-research` or `bmad-domain-research`.
Once stakes are read and the dump is captured, offer the working mode in the user's language:

View File

@ -45,13 +45,15 @@ Order: **Brain dump → Stakes calibration → Working mode → mode-scoped work
**Working mode.** Offer the choice in the user's language:
- **Fast path** — I batch remaining gaps into one or two consolidated questions, then draft the full PRD with `[ASSUMPTION]` tags where I inferred. You review and we iterate. The initial quality depends on how much you gave me upfront.
- **Coaching path** — we walk PM-thinking sections together. Once chosen, I ask which entry point fits: **Vision + Features** (capability-first — for enterprise, dev products, internal tools, anyone who thinks in features), **Personas + Journeys** (user-first — for consumer, UX-heavy, multi-stakeholder products), or *let me suggest* based on what I heard. The chosen entry sets the section order.
- **Coaching path** — we walk PM-thinking sections together. Once chosen, I ask which entry point fits: **Vision + Features** (capability-first — for enterprise, dev products, internal tools, anyone who thinks in features), **Journey-led** (user-first — for consumer, UX-heavy, multi-stakeholder products; journeys with named protagonists carry persona context inline, no standalone persona section), or *let me suggest* based on what I heard. The chosen entry sets the section order.
The workspace persists; stop and resume freely.
**Concern scan.** As you read what the user gave you, name the concerns this product actually carries — compliance, integration density, operational SLAs, hardware constraints, public-API contracts, monetization, data governance, whatever applies. The list is open; recognize what's there, do not classify into a fixed shape. These concerns drive which template sections to pull in from the Adapt-In Menu and which to invent when no cluster names them.
**User Journeys are captured, not authored.** When UJs are warranted (consumer / multi-stakeholder B2B / meaningful UX — drop or downscale for internal tooling with a single operator role, regulatory-only updates, hobby/solo, pure technical PRDs), prompt the user to narrate a real session — what the person does, in what order, where it lands — then structure the answer into UJ-N form and confirm.
**Form-factor.** If not stated in sources, probe — mobile / web / desktop / multi-surface / hardware / API.
**User Journeys are captured, not authored.** When UJs are warranted (consumer / multi-stakeholder B2B / meaningful UX — drop or downscale for internal tooling with a single operator role, regulatory-only updates, hobby/solo, pure technical PRDs), prompt the user to narrate a real session with a named protagonist (Mary, mom of three — not "the user") — what the person does, in what order, where it lands — then structure the answer into UJ-N form and confirm. Persona context lives inline at the moments that matter; no standalone persona section.
## PRD Discipline

View File

@ -20,16 +20,13 @@ updated: {YYYY-MM-DD}
## 2. Target User
### 2.1 Primary Persona
[Vivid but tight. Who they are, how this product fits their context.]
### 2.1 Jobs To Be Done
[Bulleted. Emotional, social, functional, contextual — whichever apply. Even "this is for me as the builder" is a valid framing for a hobby project.]
### 2.2 Jobs To Be Done
[Bulleted. Emotional, social, functional, contextual — whichever apply. Even "this is for me as the builder" is a valid persona for a hobby project.]
### 2.3 Non-Users (v1) *(add when the audience boundary is non-obvious)*
### 2.2 Non-Users (v1) *(add when the audience boundary is non-obvious)*
[Who this is explicitly not for in v1.]
### 2.4 Key User Journeys
### 2.3 Key User Journeys
*Named-persona narratives the product enables. Numbered globally as UJ-1 through UJ-N. FRs reference journeys by ID inline ("realizes UJ-3"); SMs may also cross-reference. If a UX doc already exists, mirror its UJ IDs here and point to the source.*
**Default shape:** a named scene with entry state, path, climax, and resolution. Each beat forces specificity the team would otherwise leave implicit — auth assumptions, screen order, what tells the user value landed. Read together as a short narrative; the example below shows the form.

View File

@ -107,7 +107,7 @@ Look for:
- Glossary present; every domain noun used identically across FRs, UJs, SM definitions.
- FR / UJ / SM IDs contiguous, unique, and cross-references that resolve.
- Each section makes sense pulled out alone — cross-references via Glossary terms, not "see above."
- UJs each name a persona from §2 by exact label; no floating UJs.
- UJs each have a named protagonist; no floating UJs.
For standalone PRDs (no downstream), this dimension matters less — say so.
@ -115,14 +115,14 @@ For standalone PRDs (no downstream), this dimension matters less — say so.
Has the PRD been forced into a shape that doesn't match the product?
- Consumer product / multi-stakeholder B2B / meaningful UX → UJs and personas are load-bearing.
- Consumer product / multi-stakeholder B2B / meaningful UX → UJs with named protagonists are load-bearing.
- Internal tool, single-operator role → capability spec shape; UJs may be overhead; SMs may be operational rather than user-facing.
- Regulatory or compliance update → constraint traceability is non-negotiable; UJs may be irrelevant.
- Hobby / solo → rigor light, substance bar still applies.
- Brownfield → existing-code references must be accurate; new UJs and existing UJs must be distinguished.
- Chain-top (feeds UX → architecture → stories) → downstream usability matters more; standalone PRDs can be lighter on traceability.
Flag PRDs that are over-formalized (UJ density for a single-operator tool) or under-formalized (consumer product with no personas or UJs).
Flag PRDs that are over-formalized (UJ density for a single-operator tool) or under-formalized (consumer product with no UJs).
## Mechanical notes
@ -131,5 +131,5 @@ Cover these as a tail section, not a primary dimension. They matter for downstre
- Glossary drift (case, plural, synonyms across the PRD).
- ID continuity (gaps, duplicates, unresolved cross-references).
- Assumptions Index roundtrip (every inline `[ASSUMPTION]` indexed; index entries all appear inline).
- UJ persona linkage (each UJ names a defined persona by exact label).
- UJ protagonist naming (each UJ has a named protagonist carrying context inline).
- Required sections present for the agreed stakes and product type.

View File

@ -18,11 +18,11 @@ When ambiguous, default to interactive.
The caller passes inputs in their first message (free-form structured payload; no fixed schema, but every field below should be present when applicable):
- `intent``"create"`, `"update"`, or `"validate"`. If absent, infer from the artifact set.
- For **Create**: a brief or product spec the LLM works from (plain text, file path, or URL), plus any persona/scope notes; `doc_workspace` if a specific run folder is required (otherwise the workflow binds the default).
- For **Create**: a brief or product spec the LLM works from (plain text, file path, or URL), plus any user/scope notes; `doc_workspace` if a specific run folder is required (otherwise the workflow binds the default).
- For **Update**: the existing `prd.md` path (or a workspace path that contains one), and a change signal (the request: what to change and why).
- For **Validate**: the existing `prd.md` path (or workspace path), and optionally a checklist override path. Workspace defaults to the PRD's containing directory.
Anything the caller does not provide is either inferred from inputs/workspace or recorded as `assumptions[]` / `open_questions[]` in the JSON status. Do not invent persona detail, success metrics, or scope decisions to fill gaps — record them.
Anything the caller does not provide is either inferred from inputs/workspace or recorded as `assumptions[]` / `open_questions[]` in the JSON status. Do not invent user detail, success metrics, or scope decisions to fill gaps — record them.
## General

View File

@ -4,23 +4,31 @@ description: Plan UX patterns and design specifications. Use when the user says
---
# BMad UX
## Overview
You are a master UX facilitator. **Elicit and capture** the user's vision — never impose yours. Probe like a senior practitioner; never volunteer colors, patterns, or directions. Render options via creative tools when seeing helps; the picks are the user's.
Produce `design.md` — a **lean design spine**: the contract architecture, story-dev, and AI implementers build against. Every line a committed decision. Bloat compounds as drift downstream. Spine wins on conflict with any mock, wireframe, or import.
Produce two peer contracts: **`DESIGN.md`** (visual identity per the [Google Labs spec](https://github.com/google-labs-code/design.md) — owns *how it looks*) and **`EXPERIENCE.md`** (information architecture, behavior, states, interactions, accessibility, journeys — owns *how it works*). EXPERIENCE.md cross-references DESIGN.md tokens by name using `{path.to.token}` syntax. Both spines win on conflict with any mock, wireframe, or import.
## The spine
## The DESIGN.md spine
Always: **Information Architecture** · **Voice and Tone** · **Design Tokens** (values for user picks — color hex with light/dark pairs, custom type ramp, custom radii; platform conventions stay semantic; *spine is the spec, code mirrors it*) · **Component Patterns** · **State Patterns** · **Interaction Primitives** · **Accessibility Floor** · **Key Flows** (numbered steps + a climax beat).
Per the [Google Labs spec](https://github.com/google-labs-code/design.md). YAML frontmatter tokens (**colors** · **typography** · **rounded** · **spacing** · **components**) + markdown body in canonical order: **Brand & Style** · **Colors** · **Typography** · **Layout & Spacing** · **Elevation & Depth** · **Shapes** · **Components** · **Do's and Don'ts**. Sections omittable; order locked when present. Spec rules: `references/design-md-spec.md`. Shape: read every entry in `{workflow.design_md_examples}`.
When triggered: **Inspiration & Anti-patterns** (Discovery surfaced reference products / rejects) · **Responsive & Platform** (multi-surface or breakpoints).
## The EXPERIENCE.md spine
Invent sections for product-specific concerns. Drop defaults only when truly inapplicable. Shape: read every entry in `{workflow.spine_examples}`.
Always: **Foundation** (form-factor, UI system when present; DESIGN.md is the visual identity reference) · **Information Architecture** · **Voice and Tone** (microcopy — brand voice lives in DESIGN.md.Brand & Style) · **Component Patterns** (behavioral — visual specs live in DESIGN.md.Components) · **State Patterns** · **Interaction Primitives** · **Accessibility Floor** (behavioral — visual contrast lives in DESIGN.md) · **Key Flows** (named-protagonist journeys with a climax beat).
When triggered: **Inspiration & Anti-patterns** · **Responsive & Platform**.
Invent sections for product-specific concerns. Shape: read every entry in `{workflow.experience_md_examples}`.
When Foundation names a UI system (shadcn, MUI, native UIKit, Compose, internal design system), both spines inherit from it; DESIGN.md tokens reference or extend the system's defaults, EXPERIENCE.md specifies only the behavioral delta.
## Sources
Inputs vary — PRD, brief, design-thinking output, requirements list, brainstorm notes, prior UX, brand deck. UX may lead, follow, or stand alone. Frontmatter `sources: [...]` lists what the spine inherits from. Inherit by reference; never restate scope, personas, or FRs.
UX may lead, follow, or stand alone. Inherit `sources:` by reference; the spines hold design and experience decisions, not duplicates of upstream product content.
## Activation
## On Activation
Resolve customization: `python3 {project-root}/_bmad/scripts/resolve_customization.py --skill {skill-root} --key workflow` (fallback: read `customize.toml`). Run `{workflow.activation_steps_prepend}`. Load `{workflow.persistent_facts}` and `{project-root}/_bmad/bmm/config.yaml` (+ `config.user.yaml`).
@ -30,25 +38,35 @@ Detect intent — **Create**, **Update**, **Validate**. Create scans `{workflow.
## Modes
**Create.** Bind `{doc_workspace}` to `{workflow.ux_output_path}/{workflow.run_folder_pattern}/`. Create `.working/`, `imports/`, `.decision-log.md`, and `design.md` (frontmatter only). Run Discovery → Finalize.
**Create.** Bind `{doc_workspace}` to `{workflow.ux_output_path}/{workflow.run_folder_pattern}/`. Create `.working/`, `imports/`, `.decision-log.md`, `DESIGN.md` (frontmatter only), and `EXPERIENCE.md` (frontmatter only). Run Discovery → Finalize.
**Update.** Read spine + log + sources. Create the log if missing — this update is entry one. Surface conflicts with prior decisions. Run Finalize.
**Update.** Read spines + log + sources. Create the log if missing — this update is entry one. Surface conflicts with prior decisions. Run Finalize.
**Validate.** See `references/validate.md`.
## Discovery
**Capture; do not author.** The spine is distilled at Finalize. Decisions → `.decision-log.md` (canonical). Creative-tool artifacts → `.working/`. User-supplied visuals (Figma, sketches, brand decks, image folders) → `imports/`, one log line per item. Spine wins on conflict.
**Capture; do not author.** The spines are distilled at Finalize. Decisions → `.decision-log.md` (canonical). Creative-tool artifacts → `.working/`. User-supplied visuals (Figma, sketches, brand decks, image folders) → `imports/`, one log line per item. Spines win on conflict.
**Source scan.** Glob `{planning_artifacts}/` for candidate input paths; surface paths only — never read content in the parent. User confirms which apply or adds others; subagent-extracts on confirm.
Brain dump first — even when the user opens with paragraphs (that's intake). Subagent-extract big docs. One "anything else?" probe. Stakes: hobby / internal / consumer / regulated.
Working mode: **Fast** (batch gaps, draft with `[ASSUMPTION]` tags, skip creative tools) or **Coaching** (walk decisions; creative tools woven in).
Working mode:
- **Fast path** — batch gaps, draft both spines with `[ASSUMPTION]` tags, skip creative tools.
- **Coaching path** — walk decisions; creative tools woven in.
- **Design handoff** — assemble captured Discovery into a producer-shaped prompt; user runs the external tool and saves outputs to `{doc_workspace}` in whatever format the tool emits. Producer registry: `{workflow.design_handoffs}` (default: Google Stitch). EXPERIENCE.md can follow via Update mode when ready.
Creative tools — scan `{workflow.creative_tools}`, invoke when seeing helps. Defaults: HTML color themes, design directions, Excalidraw wireframes; key-screen HTML mocks at Finalize. See `references/creative-tools.md`. Research subagents on demand; consult `{workflow.external_sources}` when entries match.
Concern scan — name what the UX carries: accessibility, platforms, brand, regulated language, motion, i18n, dark mode, offline, content density, input modalities, notifications. Open list; drives invented sections.
Journeys: user narrates a real session; structure into numbered steps with a climax beat. Mirror source-spec names verbatim when defined.
Journeys: user narrates a real session with a named protagonist (Mary, mom of three, kids asleep — not "the user"); structure into numbered steps with a climax beat. Mirror source-spec names verbatim when defined.
Form-factor: mobile / web / desktop / multi-surface must resolve before IA closes. Named-protagonist journeys often derive it (Pary on iPad implies an iPad surface; Skeeter on Android adds a multi-surface need); when journeys don't disambiguate, probe.
Surface closure: stated needs become screens through journeys. IA closes when every stated need has a surface that delivers it, and every surface has a journey that lands there. When closure fails, probe — never invent the missing piece.
## Reviewer Gate
@ -58,11 +76,11 @@ Used by Validate and Finalize. Menu: rubric walker (`references/validate.md`) +
Outcomes, in order:
- **Spine distilled.** Subagent reads `.decision-log.md`, `.working/`, `imports/`, sources; produces `design.md` against `## The spine` and `{workflow.spine_examples}`. Runs the rubric walker's Pass 1 coverage checks proactively (see `references/validate.md`) — flow / token / component / state / visual-reference / conditional-sections. Surface gaps; never invent.
- **Spines distilled.** Subagent reads `.decision-log.md`, `.working/`, `imports/`, sources; produces `DESIGN.md` against `## The DESIGN.md spine` + `{workflow.design_md_examples}` and `EXPERIENCE.md` against `## The EXPERIENCE.md spine` + `{workflow.experience_md_examples}`. Runs the rubric walker's Pass 1 coverage checks proactively (see `references/validate.md`). Surface gaps; never invent.
- **Inputs reconciled.** Subagent per user-supplied input → `reconcile-{slug}.md`. Surface dropped qualitative ideas.
- **Reviewer Gate passed.** Resolve before polish.
- **Open items triaged.** Open Questions, `[ASSUMPTION]`, `[NOTE FOR UX]`. Phase-blockers one at a time; non-blockers → log.
- **Key-screen mocks rendered.** Key-screens tool → `.working/` for surfaces where layout drives behavior or anchors visual language.
- **Mock coverage confirmed.** Walk every IA surface; classify *mocked* vs *spine-only*. Ask: *"These will be built from spine tables alone — any need a visual reference?"* Render more if named; log spine-only choices.
- **Layout extracted, artifacts promoted.** Distill subagent re-reads each `.working/` and `imports/` artifact; lifts layout / component / state decisions into spine tables. Promote `.working/` keepers to `mockups/` (HTML) or `wireframes/` (Excalidraw); imports stay. Inline relative links at relevant spine sections; state spine-wins-on-conflict once.
- **Polished, handed off, closed.** Apply `{workflow.doc_standards}` in order. Execute `{workflow.external_handoffs}`; surface URLs. Set `status: final`, `updated: {date}`. Log finalization. Share paths. Common next: `bmad-create-architecture`, `bmad-create-epics-and-stories`, `bmad-dev-story`. Run `{workflow.on_complete}`.
- **Layout extracted, artifacts promoted.** Distill subagent re-reads each `.working/` and `imports/` artifact; lifts visual decisions into DESIGN.md and behavioral decisions into EXPERIENCE.md. Promote `.working/` keepers to `mockups/` (HTML) or `wireframes/` (Excalidraw); imports stay. Inline relative links at relevant spine sections; state spines-win-on-conflict once.
- **Polished, handed off, closed.** Apply `{workflow.doc_standards}` in order. Execute `{workflow.external_handoffs}`; surface URLs. Set both files' `status: final`, `updated: {date}`. Log finalization. Share paths. Common next: `bmad-create-architecture`, `bmad-create-epics-and-stories`, `bmad-dev-story`. Run `{workflow.on_complete}`.

View File

@ -0,0 +1,158 @@
---
name: Linen & Logic
colors:
surface: '#fbf9f4'
surface-dim: '#dbdad5'
surface-bright: '#fbf9f4'
surface-container-lowest: '#ffffff'
surface-container-low: '#f5f3ee'
surface-container: '#f0eee9'
surface-container-high: '#eae8e3'
surface-container-highest: '#e4e2dd'
on-surface: '#1b1c19'
on-surface-variant: '#4e453d'
inverse-surface: '#30312e'
inverse-on-surface: '#f2f1ec'
outline: '#80756b'
outline-variant: '#d1c4b9'
surface-tint: '#715a3f'
primary: '#59452b'
on-primary: '#ffffff'
primary-container: '#735c41'
on-primary-container: '#f5d6b4'
inverse-primary: '#e0c1a1'
secondary: '#a43b2c'
on-secondary: '#ffffff'
secondary-container: '#fd7d69'
on-secondary-container: '#71160b'
tertiary: '#374a5f'
on-tertiary: '#ffffff'
tertiary-container: '#4f6278'
on-tertiary-container: '#caddf8'
error: '#ba1a1a'
on-error: '#ffffff'
error-container: '#ffdad6'
on-error-container: '#93000a'
primary-fixed: '#fdddbb'
primary-fixed-dim: '#e0c1a1'
on-primary-fixed: '#281804'
on-primary-fixed-variant: '#58432a'
secondary-fixed: '#ffdad4'
secondary-fixed-dim: '#ffb4a7'
on-secondary-fixed: '#400200'
on-secondary-fixed-variant: '#842417'
tertiary-fixed: '#d0e4ff'
tertiary-fixed-dim: '#b4c8e2'
on-tertiary-fixed: '#071d30'
on-tertiary-fixed-variant: '#35485d'
background: '#fbf9f4'
on-background: '#1b1c19'
surface-variant: '#e4e2dd'
typography:
display-lg:
fontFamily: Libre Caslon Text
fontSize: 48px
fontWeight: '400'
lineHeight: '1.1'
letterSpacing: -0.02em
display-lg-mobile:
fontFamily: Libre Caslon Text
fontSize: 36px
fontWeight: '400'
lineHeight: '1.1'
headline-md:
fontFamily: Libre Caslon Text
fontSize: 32px
fontWeight: '400'
lineHeight: '1.2'
headline-sm:
fontFamily: Libre Caslon Text
fontSize: 24px
fontWeight: '400'
lineHeight: '1.3'
body-lg:
fontFamily: DM Sans
fontSize: 18px
fontWeight: '400'
lineHeight: '1.6'
letterSpacing: 0.01em
body-md:
fontFamily: DM Sans
fontSize: 16px
fontWeight: '400'
lineHeight: '1.6'
label-caps:
fontFamily: DM Sans
fontSize: 12px
fontWeight: '500'
lineHeight: '1.4'
letterSpacing: 0.1em
caption:
fontFamily: DM Sans
fontSize: 13px
fontWeight: '400'
lineHeight: '1.4'
rounded:
sm: 0.125rem
DEFAULT: 0.25rem
md: 0.375rem
lg: 0.5rem
xl: 0.75rem
full: 9999px
spacing:
unit: 8px
gutter: 24px
margin-mobile: 20px
margin-desktop: 64px
editorial-gap: 80px
---
## Brand & Style
The design system is rooted in the philosophy of "Slow Design"—an intentional departure from the frantic pace of fast fashion. It evokes a tactile, "linen-weight" sensation through high-end editorial layouts and a restrained aesthetic. The target audience values provenance over presence, seeking a reflective and sophisticated discovery experience that feels as much like a boutique magazine as a digital marketplace.
The style is **Editorial Minimalism** with **Tactile** accents. It prioritizes breathable white space, asymmetrical layouts that mimic printed lookbooks, and a soft, sun-faded palette. Every interaction is designed to be deliberate and "anti-hype," eschewing aggressive animations for subtle transitions and quiet confidence.
## Colors
The palette is inspired by natural fibers and weathered landscapes.
- **Warm White (#F9F7F2)** serves as the primary canvas, providing a soft, non-clinical background that reduces eye strain.
- **Bone (#E3DED1)** and **Dust (#C2B9A7)** are used for structural depth, subtle dividers, and secondary surfaces.
- **Tobacco (#735C41)** is the primary ink color, used for high-contrast typography and essential UI elements.
- **Sun-faded Red (#B84A39)** and **Wool Blanket Blue (#4A5D73)** are used sparingly as "organic accents"—highlighting editorial picks or signifying subtle state changes without disrupting the tranquil atmosphere.
## Typography
Typography is the primary vehicle for the brands sophisticated voice.
- **Libre Caslon Text** is the voice of the curator. Its classic proportions and elegant serifs provide the editorial weight required for discovery and storytelling.
- **DM Sans** provides a quiet, functional counterpoint. It is used for body copy and navigational elements, ensuring clarity without competing with the headlines.
Large display titles should often use "optical sizing" logic—tighter leading and slightly negative letter spacing to create a cohesive visual block. Labels are always tracked out (0.1em) to maintain a sense of airy premiumness.
## Layout & Spacing
This design system employs a **Fluid Editorial Grid**. While it follows a 12-column structure on desktop, it encourages "asymmetrical breathing room"—intentionally leaving columns empty to direct focus toward high-quality imagery.
Spacing is generous. The `editorial-gap` (80px+) should be used between major content sections to allow the user to pause and reflect. Mobile layouts should maintain a minimum of 20px side margins to ensure the content feels framed like a page, rather than bleeding to the edges of the device. Elements should lean toward vertical stacks to mimic the scroll of a digital journal.
## Elevation & Depth
Depth is communicated through **Tonal Layering** and **Ambient Shadows** rather than sharp borders.
- **Surfaces:** Use the "Bone" color to define containers against the "Warm White" base.
- **Shadows:** Shadows are highly diffused and tinted with the "Tobacco" hue (`rgba(115, 92, 65, 0.08)`). They should feel like a soft glow of light hitting fabric, with large blur radii (20px+) and very low opacity.
- **Borders:** When borders are necessary, they are 1px thick and rendered in "Dust," creating a "ghost" outline that barely separates elements from the background.
## Shapes
The shape language is **Soft (0.25rem)**. While a sharp edge feels too aggressive and a pill-shape feels too digital/tech-heavy, a subtle rounding of corners mimics the natural softening of woven textiles over time.
Larger containers (Cards, Modals) may use `rounded-lg` (0.5rem) to emphasize their tactile, object-like quality. Imagery should always follow these corner radii to maintain a cohesive, "framed" appearance.
## Components
- **Buttons:** Primary buttons use a solid "Tobacco" fill with "Warm White" text. Secondary buttons are "Bone" with "Tobacco" text or simply "Tobacco" text with a 1px "Dust" border. Padding is generous horizontally to create an elegant, elongated silhouette.
- **Cards:** Editorial cards feature large imagery, a "headline-sm" title, and a "caption" subline. Shadows are only applied on hover to simulate a gentle lift.
- **Inputs:** Minimalist underlines in "Dust" that transition to "Tobacco" on focus. Label text remains in "label-caps" above the field.
- **Chips/Tags:** Used for material types (e.g., "100% Linen"). These are rendered in "Bone" backgrounds with "Tobacco" text, using the "Soft" corner radius.
- **Icons:** Must be "Hand-drawn" or "Fine-line" style. Lines should have slight imperfections and vary in weight to reinforce the tactile, artisanal nature of the fashion being discovered.
- **Navigation:** A simple, centered bottom bar or a top-weighted "Ghost" header that disappears on scroll to maximize the editorial viewport.

View File

@ -0,0 +1,93 @@
---
name: Quill
description: Daily writing companion. Calm, intentional, dark-mode-by-default. No streaks, no gamification.
colors:
surface-base: '#FAF9F7'
surface-raised: '#FFFFFF'
ink-primary: '#1A1B1F'
ink-secondary: '#6B655A'
ink-disabled: '#B5AFA5'
accent: '#A87434'
border-hairline: '#E8E4DD'
surface-base-dark: '#1A1B1F'
surface-raised-dark: '#23252B'
ink-primary-dark: '#F0EDE8'
ink-secondary-dark: '#A39E94'
ink-disabled-dark: '#5E5A53'
accent-dark: '#D4A574'
border-hairline-dark: '#2E3036'
typography:
title:
note: 'Platform native — iOS Title 1 · Android Headline Small'
body:
note: 'Platform native — iOS Body · Android Body Large'
meta:
note: 'Platform native — iOS Footnote · Android Body Small'
rounded:
sm: 6px
md: 12px
spacing:
'1': 4px
'2': 8px
'3': 12px
'4': 16px
'5': 24px
'6': 32px
---
## Brand & Style
Quill is designed against the grain of contemporary habit apps. Where most products weaponize the user's calendar with streak counters and re-engagement nudges, Quill insists on something quieter — a daily prompt, a place to write, and the unspoken assurance that today's entry is enough. Showing up is the point, not the streak.
The visual language follows. Calm surfaces in warm off-white (light) or deep ink (dark, the default). Generous breathing room. No chromatic color competing for attention except a single warm tobacco that signals save-and-send. Text-first. Hand-on-paper, not buzz-on-screen.
## Colors
The palette is restrained on purpose — a writing surface should not compete with the writing.
- **Warm White (`#FAF9F7`)** is the primary canvas in light mode. Slightly warm to reduce eye strain and keep the surface from feeling clinical.
- **Deep Ink (`#1A1B1F`)** is the dark-mode canvas and the primary body text color in light mode. Quill defaults to dark because most writing happens at night.
- **Tobacco (`#A87434` light / `#D4A574` dark)** is the only chromatic color. Used exclusively for the save indicator and primary action — never for decoration, never for state badges.
- **Hairline (`#E8E4DD` light / `#2E3036` dark)** separates list items at the lowest possible contrast. Anything heavier feels like UI rather than paper.
Avoid: red error fills (Quill is a journal, not a form), gradients (the surface is paper), and saturated accent variants — one accent, used sparingly.
## Typography
Platform conventions are the spec. iOS uses Title 1 / Body / Footnote; Android uses Headline Small / Body Large / Body Small. Dynamic type honored at every level — the largest accessibility setting must still render legibly without truncation.
Headlines are rare. The Today prompt is set in `title`; everything else is `body` or `meta`. No display sizes, no all-caps labels.
## Layout & Spacing
Scale: 4 / 8 / 12 / 16 / 24 / 32 px. The largest gaps land between major surfaces; the smallest sit between tightly related elements. Vertical rhythm follows a hard rule: composer breathes, list items don't.
Mobile margins follow platform conventions (iOS 16pt, Android 16dp). Single-column always; modal stacks one level deep, never two.
## Elevation & Depth
Quill avoids elevation as a visual device. Cards and composer surfaces sit on `surface-raised`, distinguished from `surface-base` only by tone. Shadows are reserved for the rare moment of literal physical metaphor — never for hierarchy. Hierarchy comes from layout and typography, not shadow.
## Shapes
`rounded/sm` (6px) for inputs, list rows, and small surfaces. `rounded/md` (12px) for cards and the composer. Nothing fully rounded; no pills, no perfect circles for surfaces. The aesthetic is paper-with-soft-corners, not iOS-button-pill.
Imagery follows container corners exactly.
## Components
- **Prompt card**`surface-raised`. One per day. Today's prompt in `title`. Tap to open composer. No icon, no decoration; the prompt itself is the affordance.
- **Composer** — Full-screen text view. Clean text field, generous vertical padding, single-line save indicator in the header.
- **Save indicator** — Text only. Uses `ink-secondary`, never a checkmark icon, never a colored badge.
- **Entry row** (Library) — Date in `meta`, first line of body in `body` (truncated to one line). Hairline divider only, no fill.
- **Settings row** — Label left, value or chevron right. Tobacco accent only on destructive confirmations.
## Do's and Don'ts
| Do | Don't |
|---|---|
| Single accent color, used sparingly on save & primary action | Color-code by sentiment, mood, or category |
| Text-only state indicators (`Saved.`) | Iconography for state (✓, ⚠, ●) |
| Hairline dividers at lowest legible contrast | Card shadows, gradient fills, accent fills behind text |
| Generous vertical rhythm in composer | Compress to fit more on screen |
| Honor platform conventions for navigation | Override platform nav with custom drawer or hamburger |

View File

@ -0,0 +1,109 @@
---
name: Drift
description: Focused task tracker for solo founders and small async teams. shadcn/ui on Next.js + Tailwind; this DESIGN.md specifies the brand-layer delta only.
colors:
# Brand overrides on top of shadcn defaults. All unlisted tokens inherit
# from shadcn (background, foreground, muted, muted-foreground, popover,
# popover-foreground, card, card-foreground, border, input, ring, destructive).
primary: '#0F4C81'
primary-foreground: '#FFFFFF'
accent: '#F59E0B'
accent-foreground: '#1A1208'
primary-dark: '#5C8AC2'
primary-foreground-dark: '#0A1A2A'
accent-dark: '#FBC470'
accent-foreground-dark: '#1A1208'
typography:
# Body, label, and muted inherit from shadcn (Geist Sans). Only display is overridden.
display:
fontFamily: 'Instrument Serif'
fontSize: 36px
fontWeight: '400'
lineHeight: '1.15'
letterSpacing: -0.01em
display-sm:
fontFamily: 'Instrument Serif'
fontSize: 24px
fontWeight: '400'
lineHeight: '1.2'
rounded:
# Tighter than shadcn defaults — Drift reads sharper.
sm: 4px
md: 6px
lg: 8px
spacing:
# shadcn / Tailwind defaults inherited; no overrides.
components:
button-primary:
background: '{colors.primary}'
foreground: '{colors.primary-foreground}'
radius: '{rounded.md}'
focus-card:
background: '{colors.accent}'
foreground: '{colors.accent-foreground}'
radius: '{rounded.md}'
border: 'none'
command-palette-result-active:
background: '{colors.accent}'
foreground: '{colors.accent-foreground}'
---
## Brand & Style
Drift is a focused task tracker for solo founders and small async teams. The product premise is that *work is a moving thing* — momentum matters more than perfectly groomed backlogs, and the right tool surfaces what you're working on *now* without making you administer a system to find it. The brand expression follows: a serif display moment in an otherwise sober sans-serif surface, a single warm accent that means *this is what's live*, and visual restraint everywhere else.
Drift inherits shadcn/ui defaults wholesale. This DESIGN.md specifies only the brand-layer deltas — primary color, accent color, display typography, slightly tighter corners, and a handful of brand-specific components. The 80% of components that ship from shadcn (Button, Card, Dialog, Sheet, Command, Popover, Toast) inherit shadcn's visual specs as-is. Customizing those is *explicitly* against the brand discipline — shadcn's defaults are the contract.
## Colors
The Drift palette is two colors of brand-layer plus shadcn defaults for everything else.
- **Primary Navy (`#0F4C81` light / `#5C8AC2` dark)** is the brand color. Used on primary buttons, active nav items, link underlines, and the "current week" indicator. Replaces shadcn's default `primary`.
- **Focus Amber (`#F59E0B` light / `#FBC470` dark)** is the accent. Used exclusively to indicate the task or project currently in focus — the one you're working on *right now*. Never used for chrome, never used decoratively, never used for state badges. Amber means "live."
- **All other tokens** (`background`, `foreground`, `muted`, `muted-foreground`, `border`, `input`, `ring`, `card`, `popover`, `destructive`) inherit from shadcn defaults. If the brand can't justify overriding a token, it doesn't override it.
Avoid: chromatic flourishes, gradient surfaces, custom destructive colors (use shadcn's), more than two brand colors. The discipline is two-colors-and-stop.
## Typography
Body / label / caption inherit shadcn's Geist Sans ramp. Only the `display` role is brand-overridden, set in **Instrument Serif** at 36px (24px small variant). The serif moment appears in:
- Empty-state hero text on Today and project surfaces
- Project titles in the project detail header
- The "Welcome back, {name}" greeting at first session of the day
Everything else stays in Geist Sans. The serif is a punctuation mark, not a default voice.
## Layout & Spacing
shadcn / Tailwind spacing scale inherited as-is (the 4-based scale: 4, 8, 12, 16, 20, 24, 32, 40, 48, 64). Maximum content width: `max-w-3xl` (768px) — Drift is not a wide-table product, and forcing one-column reading keeps the surface focused.
Single-column layout. Sidebar nav on `lg` (1024px+); on smaller viewports, the sidebar becomes a sheet triggered from the top bar.
## Elevation & Depth
Inherited from shadcn — subtle shadow on hover/active states, no elevation as a visual hierarchy device. Drift adds nothing on top of this; brand discipline is "shadcn's shadows are correct."
## Shapes
Tighter than shadcn defaults: `rounded/sm` (4px) for inputs, `rounded/md` (6px) for cards and buttons, `rounded/lg` (8px) for dialogs and the command palette. The crispness reads "tool" rather than "consumer app." Pill shapes (`rounded/full`) appear only on status badges.
## Components
Drift uses the following shadcn components as-is, unchanged: `Button`, `Card`, `Dialog`, `Sheet`, `Popover`, `DropdownMenu`, `Toast`, `Tabs`, `Avatar`, `Separator`. The contract: don't customize these.
Brand-layer-overridden components:
- **Button (primary variant)**`{colors.primary}` fill, `{colors.primary-foreground}` text, `{rounded.md}` corner. Other variants (secondary, outline, ghost, destructive) inherit shadcn defaults.
- **Focus card** — Custom Drift component. The "this is what you're working on now" card on Today and project detail. `{colors.accent}` fill, no border, slightly elevated. Appears at most once per surface.
- **Command palette result (active)** — Override on shadcn's `Command` component: the highlighted/keyboard-selected result row uses `{colors.accent}` instead of shadcn's default `accent` token. Reinforces "this is what will fire if you hit Enter."
## Do's and Don'ts
| Do | Don't |
|---|---|
| Inherit shadcn defaults for everything not in the brand layer | Override shadcn's color tokens beyond `primary` and `accent` |
| Use `{colors.accent}` only for "live / now / in-focus" | Use accent for state, chrome, or hover affordances |
| `display` typography sparingly — empty states, hero greetings | Set body text in `display` to "make it pretty" |
| Tighter corners than shadcn (4 / 6 / 8) | Use shadcn's default 6/8/12 (Drift reads sharper) |
| Single-column layouts inside `max-w-3xl` | Wide multi-column tables (Drift is not a spreadsheet) |

View File

@ -2,17 +2,17 @@
name: Quill
status: final
sources:
- ../prds/quill-2025-08-15/prd.md
- {planning_artifacts}/prds/quill-2025-08-15/prd.md
updated: 2025-09-02
---
# Quill — Design Spine
# Quill — Experience Spine
> Illustrative example. Single-surface mobile (iOS + Android parity). Consumer posture, calm by default. Demonstrates: Voice and Tone as gating discipline, Inspiration & Anti-patterns earning its place, Responsive & Platform omitted (single-surface).
> Illustrative example. Single-surface mobile (iOS + Android parity). Consumer posture, calm by default. Paired with `design-example-mobile.md` (Quill DESIGN.md). Demonstrates: microcopy as gating discipline, Inspiration & Anti-patterns earning its place, Responsive & Platform omitted (single-surface).
## Foundation
Native UIKit + Swift on iOS; Jetpack Compose on Android. Both follow platform conventions for navigation, system gestures, dynamic type. Brand layer (typography pairing, color palette) sits on top of native components. Dark mode is the default surface; light is a setting.
Single-surface mobile, iOS + Android with parity. No UI system named — inherits platform conventions for navigation, system gestures, dynamic type. `DESIGN.md` is the visual identity reference; this spine is the experience. Dark mode is the default surface; light is a setting.
## Information Architecture
@ -29,6 +29,8 @@ Bottom tab bar (Today / Library / Settings). No drawer. Modal stacks one level d
## Voice and Tone
Microcopy. Brand voice and aesthetic posture live in `DESIGN.md`.
| Do | Don't |
|---|---|
| "Today's prompt." | "Time to write!" |
@ -36,36 +38,17 @@ Bottom tab bar (Today / Library / Settings). No drawer. Modal stacks one level d
| "We couldn't reach the cloud — your work is on this device." | "Network error" |
| Short, complete sentences. | Streak counters, encouragement, exclamation marks. |
## Design Tokens
This table is the spec. Dev mirrors values into platform theme modules; the spine wins on any conflict.
| Token | Role | Value (light / dark) |
|---|---|---|
| `surface/base` | Default background | `#FAF9F7` / `#1A1B1F` |
| `surface/raised` | Card / composer background | `#FFFFFF` / `#23252B` |
| `ink/primary` | Body text | `#1A1B1F` / `#F0EDE8` |
| `ink/secondary` | Metadata, timestamps | `#6B655A` / `#A39E94` |
| `ink/disabled` | Inactive controls | `#B5AFA5` / `#5E5A53` |
| `accent` | Save, send, primary action | `#A87434` / `#D4A574` |
| `border/hairline` | List separators | `#E8E4DD` / `#2E3036` |
| `space/1..6` | Spacing scale (pixels) | 4 / 8 / 12 / 16 / 24 / 32 |
| `radius/sm`, `radius/md` | Corners (pixels) | 6, 12 |
| `type/title` | Prompt / section heading | iOS Title 1 · Android Headline Small *(platform convention)* |
| `type/body` | Body text | iOS Body · Android Body Large *(platform convention)* |
| `type/meta` | Timestamps, captions | iOS Footnote · Android Body Small *(platform convention)* |
Contrast: `ink/primary` on `surface/base` ≥ 7:1 in both modes. `accent` on `surface/base` ≥ 4.5:1. Focus indicators ≥ 3:1 against adjacent.
## Component Patterns
| Component | Use | Rules |
Behavioral. Visual specs live in `DESIGN.md.Components`.
| Component | Use | Behavioral rules |
|---|---|---|
| Prompt card | Today | One per day. `surface/raised`. Prompt text in `type/title`. Composer entry point below. |
| Composer | Today + entry detail | Full-screen text view. No formatting toolbar in v1. Autosave on pause ≥ 600ms. |
| Entry row | Library list | Date in `type/meta`, first line of body in `type/body` (1 line, truncated). Tap → entry detail. |
| Save indicator | Composer header | Cycles `Editing…``Saved.` (≥ 800ms visible). Text only — no icons. |
| Settings row | Settings list | Label left, value or chevron right. Tap → detail or toggle. |
| Prompt card | Today | One per day. Tap opens composer. |
| Composer | Today + entry detail | No formatting toolbar in v1. Autosave on pause ≥ 600ms. |
| Entry row | Library list | Tap → entry detail. Long-press reserved for system text selection. |
| Save indicator | Composer header | Cycles `Editing…``Saved.` (≥ 800ms visible). |
| Settings row | Settings list | Tap → detail or toggle. |
## State Patterns
@ -87,8 +70,10 @@ Contrast: `ink/primary` on `surface/base` ≥ 7:1 in both modes. `accent` on `su
## Accessibility Floor
Behavioral. Visual contrast lives in `DESIGN.md`.
- VoiceOver / TalkBack: every interactive element labeled with role + state. Save indicator announces `Saved` on transition.
- Dynamic type honored through `type/*` tokens. UI must remain legible at largest setting — no truncated controls.
- Dynamic type honored through `DESIGN.md` typography tokens. UI must remain legible at largest setting — no truncated controls.
- Reduce Motion: skip the save-indicator fade; show `Saved.` immediately.
- Tap targets ≥ 44pt (iOS) / 48dp (Android).
- Focus traversal follows reading order on every surface.
@ -102,26 +87,26 @@ Contrast: `ink/primary` on `surface/base` ≥ 7:1 in both modes. `accent` on `su
## Key Flows
### Flow 1 — Daily write
### Flow 1 — Daily write (Mira, late evening, after work)
1. User opens app.
1. Mira opens app.
2. Today surface shows today's prompt (cached if offline).
3. User taps composer entry point.
3. She taps the composer entry point.
4. Composer opens, keyboard active.
5. User writes; autosave fires on pause.
6. User taps Back.
5. She writes; autosave fires on pause.
6. She taps Back.
7. **Climax:** Today surface shows `Saved.` and the entry's first line below the prompt — proof the day is captured.
Failure: cold prompt fetch fails → composer still opens with cached generic prompt; banner on Today only after user returns.
Failure: cold prompt fetch fails → composer still opens with cached generic prompt; banner on Today only after Mira returns.
### Flow 2 — Recall past entry
### Flow 2 — Recall past entry (Mira, three weeks later, looking for what she wrote about her mother)
1. User taps Library.
1. Mira taps Library.
2. Scrolls or searches.
3. Taps entry row.
4. Entry detail opens in read mode.
5. User taps anywhere to enter edit mode (cursor at tap point).
5. She taps anywhere to enter edit mode (cursor at tap point).
6. Edits autosave.
7. **Climax:** `Saved.` visible in entry header.
7. **Climax:** `Saved.` visible in entry header — the older self and the present self are in continuous conversation.
Empty state: no entries → message routes back to Today.

View File

@ -0,0 +1,133 @@
---
name: Drift
status: final
sources:
- {planning_artifacts}/prds/drift-2026-03-12/prd.md
updated: 2026-04-02
---
# Drift — Experience Spine
> Illustrative example. Single-surface responsive web. shadcn/ui on Next.js + Tailwind. Paired with `design-example-shadcn.md` (Drift DESIGN.md). Demonstrates: component-library inheritance, keyboard-first interaction primitives, the "shadcn + brand-layer" pattern that covers most modern web SaaS.
## Foundation
Single-surface responsive web. shadcn/ui on Next.js 15+ with Tailwind CSS. The component library does most of the work; brand discipline is "respect the defaults except where the brand layer overrides them." `DESIGN.md` is the visual identity reference and names the override surface; this spine is the experience. Single-tenant per project; users can belong to multiple projects but each project is a self-contained workspace.
## Information Architecture
| Surface | Reached from | Purpose |
|---|---|---|
| Today | App open / `g t` | Current focus, in-progress tasks pulled from all projects |
| Projects | Sidebar / `g p` | List of active and archived projects |
| Project detail | Projects row / `g 1``g 9` | Tasks in this project, organized by lane |
| Search | `⌘K` / `Ctrl+K` | Command palette — surface, navigate, act |
| Settings | Avatar menu | Account, theme, keyboard shortcuts, billing |
Sidebar collapses to icons on `md`; becomes a `Sheet` on `sm`. Modal stacks one level deep (e.g., open `Dialog` on top of a surface, never on top of another dialog).
→ Composition reference: `mockups/today.html`, `mockups/project-detail.html`, `mockups/command-palette.html`. Spine wins on conflict.
## Voice and Tone
Microcopy. Brand voice and aesthetic posture live in `DESIGN.md`.
| Do | Don't |
|---|---|
| "What are you working on?" | "Let's get productive! 🚀" |
| "3 tasks in motion" | "You have 3 active items." |
| "Closed. Nice work." | "Task completed successfully ✓" |
| "Nothing in motion. Pick something." | "No active tasks. Click below to get started!" |
| Manager-facing: counts and verbs. Employee-facing: same. | Different tone per audience — Drift talks to everyone the same way. |
## Component Patterns
Behavioral. Visual specs live in `DESIGN.md.Components` (or in shadcn defaults, when inherited).
| Component | Use | Behavioral rules |
|---|---|---|
| Task row | Projects, Today | Click anywhere on row opens edit dialog. Checkbox toggles done state with optimistic update. Hover reveals quick-actions (`focus`, `defer`, `archive`). |
| Focus card | Today, Project detail | At most one focus card per surface — the task or project marked with `focus` state. `f` keyboard shortcut sets focus on the active row. |
| Command palette | Global (⌘K) | Fuzzy search across all projects, tasks, and commands. `Enter` fires the highlighted result. `→` previews a result. Escape closes. |
| Project header | Project detail | Inline-editable title (click to edit, blur to save). Status pill: active / archived / done. |
| Empty state | Anywhere | shadcn's empty pattern + one Drift-specific sentence. `display-sm` for the headline, body text below, single primary action. |
## State Patterns
| State | Surface | Treatment |
|---|---|---|
| Cold app load | Today | shadcn `Skeleton` rows (4-6) match expected layout. Resolves on data. |
| No focus | Today | `display-sm`: "Nothing in motion. Pick something." Below: list of in-progress tasks from all projects. |
| Empty project | Project detail | `display-sm`: "{Project title} is empty." Body: "Add a first task to get going." Single primary button. |
| Command palette no matches | ⌘K | "No matches. Start typing a task or project name, or pick an action below." Followed by 4-5 common commands. |
| Offline | Global (status bar) | shadcn `Toast` once: "You're offline. Changes will sync when you reconnect." Local writes continue. |
| Permission denied | Projects (others' private) | Surface hidden from sidebar. No "blocked" screen. |
| Stale data | Project detail | If background refresh detects changes, shadcn `Toast`: "Updated by {user_name}. Refresh." Manual refresh, no auto. |
## Interaction Primitives
**Keyboard-first.** Drift's primary audience is developers and power users; the keyboard surface is the product, the mouse is fallback.
- `⌘K` / `Ctrl+K` — Command palette (universal)
- `g t` / `g p` — Go to Today / Projects (vim-style)
- `g 1``g 9` — Go to project by sidebar position
- `f` — Set focus on highlighted task/project
- `c` — Create new task (context-aware: in the active project)
- `Esc` — Close dialogs, exit edit mode, clear command palette
- `/` — Focus search in current surface
**Mouse:** click to act, drag deferred to v2. Hover reveals row actions on `md+` (touch users tap to reveal).
**Banned everywhere:** infinite scroll (pagination only), drag-to-reorder in v1, hover-only affordances on `sm` viewports, modal stacks > 1 level deep.
## Accessibility Floor
Behavioral. Visual contrast lives in `DESIGN.md` (inherits shadcn's WCAG AA-compliant defaults; brand overrides verified to maintain ratios).
- WCAG 2.2 AA across the responsive web surface.
- Screen reader announces page surface on navigation: "Today, focus surface" / "Project: {name}, task list, {N} tasks."
- Keyboard shortcuts available without modifier on most surfaces (vim-style `g t` etc.) — users with motor-control limitations get the same surface as power users.
- `Tab` order matches reading order on every surface. `Esc` always closes the topmost modal/popover.
- Command palette is fully keyboard-operable; results announce as they update via `aria-live`.
- Focus rings inherit shadcn's `ring` token — visible at AA contrast against `background`.
## Responsive & Platform
| Breakpoint | Behavior |
|---|---|
| `≥ lg` (1024px+) | Sidebar visible. Today is a 2-column layout: focus + in-motion list. |
| `md` (7681023px) | Sidebar collapses to icons. Today stacks to single column. |
| `< md` (`sm`) | Sidebar becomes a `Sheet` triggered from top bar. Command palette opens fullscreen. |
Drift is responsive web, not a native mobile app. The product works on phones for read + simple-edit, but the primary surface is desktop / laptop.
## Inspiration & Anti-patterns
- **Lifted from Linear:** the keyboard-first discipline. `⌘K` is the command center; vim-style nav (`g t`); no drag for primary navigation; status pill vocabulary.
- **Lifted from Notion:** inline-editable titles. Click-to-edit on project header, blur to save. No edit/view mode toggle.
- **Lifted from shadcn:** the entire surface vocabulary. Drift's brand is *what we add to shadcn*, not a from-scratch design system. This is a deliberate posture, not a shortcut.
- **Rejected — Streaks, badges, achievement notifications:** Drift is a tool, not a habit app. Task closure is its own reward; no celebratory animation, no "🎉 5-day streak!" toast.
- **Rejected — AI-suggested next tasks:** Drift surfaces what's in motion, doesn't tell the user what to work on. The user picks focus; the tool surfaces consequences.
- **Rejected — Multi-column kanban as default project view:** lists are linear; kanban hides progress behind columns. Optional v2; not the default.
## Key Flows
### Flow 1 — Morning focus (Sarah, solo founder, 8:45am Tuesday)
1. Sarah opens Drift in a browser tab.
2. App loads Today. `display-sm`: "Welcome back, Sarah." Focus card shows yesterday's marked task — "Finish landing page hero copy" — still in motion.
3. She hits `⌘K`, types "ship hero", sees the matching task and presses Enter to open it.
4. Inline edit: she updates the task description with two new bullets. Tab + Tab triggers save.
5. **Climax:** Sarah closes the dialog. Today re-renders: focus card still shows the hero task, but now with the updated body text visible at a glance. She doesn't have to navigate anywhere — the surface that greeted her now reflects the work she just did. She picks up her coffee and starts writing.
Failure: data save fails → shadcn `Toast` (destructive variant): "Couldn't save. Trying again." Inline edit retained; another `Enter` retries.
### Flow 2 — Async handoff (Devon and Mara, small remote team, mid-afternoon)
1. Devon finishes wiring up the auth flow and marks the task `done`.
2. Mara, time-zoned three hours ahead and online during overlap, opens Drift.
3. Today loads; her focus is on her own front-end work, but the in-motion list shows Devon's auth task now marked done and one task below it newly assigned to her — "Wire up post-auth redirect" — that Devon set during checkout.
4. She hits `f` on the row to mark it as her focus, then `Enter` to open it.
5. **Climax:** The focus card swaps. Her surface now shows the post-auth redirect task as the live thing she's working on; the projects sidebar shows the Auth project highlighted; the command palette `⌘K` defaults its first result to "Go to Auth project." The state of the team's progress is *embedded in her surface* — no Slack thread to scroll, no status doc to read.
Failure: Devon hadn't actually assigned the follow-up — Mara mis-assigned to herself. She hits `Esc`, `f` again to unfocus, and reassigns to Devon. No "are you sure" dialog; Drift trusts the user.

View File

@ -16,7 +16,8 @@ Every headless run ends with one of these payloads. Omit keys for artifacts not
{
"status": "complete",
"intent": "create",
"design": "{doc_workspace}/design.md",
"design": "{doc_workspace}/DESIGN.md",
"experience": "{doc_workspace}/EXPERIENCE.md",
"decision_log": "{doc_workspace}/.decision-log.md",
"working_artifacts": ["{doc_workspace}/.working/color-themes-1.html"],
"promoted_artifacts": {
@ -39,7 +40,8 @@ The `working_artifacts` and `promoted_artifacts` keys are optional and omitted e
{
"status": "complete",
"intent": "update",
"design": "{doc_workspace}/design.md",
"design": "{doc_workspace}/DESIGN.md",
"experience": "{doc_workspace}/EXPERIENCE.md",
"decision_log": "{doc_workspace}/.decision-log.md",
"changes_summary": "1-3 sentences describing what changed and why",
"conflicts_with_prior_decisions": [],

View File

@ -4,7 +4,7 @@ Subagent prompt. Fired at Finalize (or during late Discovery once layout decisio
## Inputs
`.decision-log.md`, the current draft `design.md`, `.working/` (especially the chosen color-theme and direction mocks), source PRD. The user names which surfaces to render — typically 2-4: the canonical entry surface, the most complex flow's hero screen, any load-bearing overlay/modal, and (when present) the Week / list / dashboard view.
`.decision-log.md`, the current drafts `DESIGN.md` and `EXPERIENCE.md`, `.working/` (especially the chosen color-theme and direction mocks), source PRD. The user names which surfaces to render — typically 2-4: the canonical entry surface, the most complex flow's hero screen, any load-bearing overlay/modal, and (when present) the Week / list / dashboard view.
## What to render

View File

@ -1,149 +0,0 @@
---
name: Pulse
status: final
sources:
- ../prds/pulse-2025-10-04/prd.md
- ../research/pulse-customer-interviews-2025-09.md
updated: 2025-10-21
---
# Pulse — Design Spine
> Illustrative example. Multi-surface (responsive web admin + native mobile employee app). B2B utility posture. Demonstrates: Responsive & Platform as a required-when-applicable section, keyboard-first Interaction Primitives, anonymity rules as Component Pattern invariants.
## Foundation
Web: React + Tailwind. Mobile: React Native. Tokens defined in this spine; dev mirrors them into a shared theme module both surfaces import. Single-tenant; users belong to one team, SSO via the customer's IdP, no self-serve signup.
## Information Architecture
### Web admin
| Surface | Reached from | Purpose |
|---|---|---|
| Dashboard | Login | Today's response summary, last-7-day trends |
| Question library | Sidebar | Manage rotation of daily prompts |
| Team | Sidebar | Members, roles, removal |
| Settings | User menu | Tenant config, billing, SSO |
### Mobile employee
| Surface | Reached from | Purpose |
|---|---|---|
| Today | App open / push notification | Daily prompt + 1-tap answer |
| History | Tab bar | Own past answers |
| Settings | History header | Account, notifications, sign out |
→ Composition reference: `mockups/dashboard.html`, `mockups/mobile-today.html`. Spine wins on conflict.
## Voice and Tone
| Do | Don't |
|---|---|
| "How's today going?" (employee) | "Time for your daily check-in!" |
| "Thanks — see you tomorrow." | "Submission received." |
| Manager-facing: data words. "23 of 28 responded." | "Great engagement today!" |
| Employee-facing: human words. | Corporate-speak, gamification. |
## Design Tokens
This table is the spec. Dev mirrors values into the shared theme module; the spine wins on any conflict.
| Token | Role | Value (light / dark) |
|---|---|---|
| `surface/base` | Page background | `#FFFFFF` / `#0F1115` |
| `surface/raised` | Cards, panels | `#F7F8FA` / `#191C22` |
| `surface/sunken` | Insets, wells | `#EDEFF3` / `#0A0C10` |
| `ink/primary` | Body text | `#0F1115` / `#F2F4F7` |
| `ink/secondary` | Metadata, helper text | `#5B616B` / `#A0A6B0` |
| `ink/inverse` | Text on accent fills | `#FFFFFF` / `#0F1115` |
| `accent` | Submit / save / primary CTA | `#2D5BFF` / `#7B9AFF` |
| `state/positive` | Sentiment chip — positive | `#0E8556` / `#3DBC8E` |
| `state/caution` | Sentiment chip — caution | `#A86E00` / `#E5A547` |
| `state/negative` | Sentiment chip — negative | `#C03028` / `#F77268` |
| `border/hairline` | Dividers | `#E1E4EA` / `#262A33` |
| `border/strong` | Focused inputs | `#9DA3AE` / `#4A5160` |
| `space/1..8` | Spacing scale (pixels) | 4 / 8 / 12 / 16 / 24 / 32 / 48 / 64 |
| `radius/sm`, `radius/md`, `radius/lg` | Corners (pixels) | 4, 8, 12 |
| `type/display` | Hero / page title | Web: 32px / 1.2 / 600 weight |
| `type/title` | Section heading | Web: 20px / 1.3 / 600 weight |
| `type/body` | Body text | Web: 16px / 1.5 / 400 weight · Mobile: native Body *(platform convention)* |
| `type/meta` | Captions, timestamps | Web: 13px / 1.4 / 400 weight · Mobile: native Caption *(platform convention)* |
Contrast: all text ≥ 4.5:1 in both modes. Sentiment chips ≥ 3:1 against `surface/raised`. Focus rings ≥ 3:1 against adjacent.
## Component Patterns
| Component | Use | Rules |
|---|---|---|
| Sentiment chip | Dashboard, history | Color from `state/*`. Always paired with the word ("Positive 18") — never icon-only. |
| Question card | Question library | Editable inline. Save on blur. Schedule chip next to title. |
| Response cell | Dashboard grid | Anonymous unless team < 5 (then named hard rule, never a setting). Truncate at 2 lines, expand on tap. |
| 1-tap answer button | Mobile Today | Three stacked, full-width. Each ≥ 56dp tall. Selected state uses `accent` fill. |
| Empty state | Anywhere | One sentence, one action. No illustrations in v1. |
## State Patterns
| State | Surface | Treatment |
|---|---|---|
| Cold dashboard load | Web | Skeleton rows (3 weeks). |
| No responses yet today | Dashboard | `0 of N responded. Window closes 6pm.` |
| Team < 5 members | Dashboard | Anonymity banner: `Names shown — team too small to anonymize.` |
| Offline answer | Mobile Today | Save locally. Submit on next online. Header pill: `Will send when online.` |
| SSO error | Web login | Surface vendor message verbatim + `Contact your admin.` |
| Permission denied | Question library | Hide the surface from non-admins; don't show a blocked screen. |
| Notification disabled | Mobile Today | Header banner once: `Daily reminder is off — turn on.` |
## Interaction Primitives
**Web:** click + full keyboard. `Tab` traverses in reading order; `Enter` submits; `Esc` cancels editors; `/` focuses dashboard search; `?` opens shortcuts.
**Mobile:** tap. System swipe gestures only. No long-press except text selection.
**Banned everywhere:** drag-to-reorder in v1, hover-only affordances, modal stacks > 1 level deep.
## Accessibility Floor
- WCAG 2.2 AA across both surfaces.
- Web focus rings ≥ 2px, contrast ≥ 3:1 against adjacent.
- Screen reader announces dashboard summary on load: "23 of 28 responded today, 4 outstanding."
- Mobile VoiceOver labels for each answer button include the question text.
- No information conveyed by color alone — sentiment chips carry the word.
- Submission-window time is announced on entry to Today, not as a live countdown.
## Responsive & Platform
| Breakpoint / Platform | Behavior |
|---|---|
| Web ≥ 1280px | Sidebar nav visible; dashboard grid 4 columns. |
| Web 7681279px | Sidebar collapses to icons; grid 2 columns. |
| Web < 768px | Hamburger nav; grid stacks to 1 column. *Web admin is not designed for phone use; warn on first sub-768 visit.* |
| Mobile native (iOS / Android) | Full feature parity with each other. Token names identical, mapped to platform-native dynamic sizing. |
| Cross-platform parity | Employee surfaces never appear on web. Admin surfaces never appear in the mobile app. |
## Inspiration & Anti-patterns
- **Lifted from Linear:** keyboard-first web admin posture. `/` focuses search, `?` opens shortcuts, no drag for primary nav.
- **Lifted from Officevibe:** the anonymity threshold (< 5 = named) as a hard rule, not a setting. Anonymity claims have to be defensible.
- **Rejected — Slackbot-style threaded questions:** the daily check-in is *one tap*; it isn't a conversation. Long-form follow-ups belong in 1:1s.
- **Rejected — Manager "please respond" nudges:** managers cannot ping non-respondents. Response rate is a culture problem, not a UI problem.
## Key Flows
### Flow 1 — Manager checks today (web)
1. Manager opens dashboard.
2. Skeleton resolves; top row shows today's response count + sentiment mix.
3. Manager scans cells; clicks a low-sentiment one to expand.
4. Expanded view shows verbatim response (or "Anonymous" if team ≥ 5).
5. **Climax:** Manager sees a verbatim line of text from someone on the team — actual signal, no drill, no filter, no query composition.
Failure: data fetch fails → top row stays as skeleton + `Couldn't load today. Retry.` Other surfaces unaffected.
### Flow 2 — Employee answers (mobile)
1. Push notification at 9am: "How's today going?"
2. Tap opens app directly on Today.
3. Three answer buttons visible.
4. Tap one.
5. **Climax:** Button fills with `accent`; screen swaps to `Thanks — see you tomorrow.` in < 200ms. No second screen, no follow-up question, no extra fields.
Failure: offline → answer saves locally, header pill `Will send when online.` Re-confirm thanks message on submit success.

View File

@ -21,23 +21,43 @@ persistent_facts = [
# Runs at workflow completion. String or array of instructions.
on_complete = ""
# Reference spines the distillation subagent reads to anchor shape and lean
# discipline. Multiple examples show the spine adapting to product type
# (single-surface mobile vs. multi-surface web, consumer vs. internal).
# Append entries via override TOML to seed an org-specific canonical shape.
# Reference DESIGN.md spines the distillation subagent reads to anchor shape
# and editorial richness. Convention-compliant with the Google Labs DESIGN.md
# spec (https://github.com/google-labs-code/design.md). Append entries via
# override TOML to seed an org-specific canonical aesthetic.
# Each entry: `file:PATH` (or bare relative path, resolved skill-relative).
spine_examples = [
"assets/spine-example-mobile.md",
"assets/spine-example-web.md",
design_md_examples = [
"assets/design-example-mobile.md",
"assets/design-example-shadcn.md",
"assets/design-example-editorial.md",
]
# Reference EXPERIENCE.md spines for the behavioral/flow/IA layer. Each entry:
# `file:PATH` (or bare relative path, resolved skill-relative).
experience_md_examples = [
"assets/experience-example-mobile.md",
"assets/experience-example-shadcn.md",
]
# Design handoff targets — external tools that can take over the design /
# visual identity work. The user runs the tool externally and saves outputs
# (whatever the tool produces — DESIGN.md, Figma files, React components,
# HTML mocks) to {doc_workspace}.
# Each entry: `tool:NAME: <directive>`, `skill:NAME`, or plain-text descriptor.
# Default: Google Stitch (emits DESIGN.md + per-screen HTML). Other producers:
# Vercel v0, Figma, Galileo, Anima, internal generators.
design_handoffs = [
"Google Stitch (https://stitch.withgoogle.com) — emits DESIGN.md + per-screen HTML. Paste assembled prompt; save outputs to {doc_workspace}.",
]
# HTML skeleton filled in by the validation synthesis pass.
validation_report_template = "assets/validation-report-template.html"
# Run folder. design.md, .decision-log.md, .working/ (creative-tool artifacts),
# imports/ (user-supplied screens / brand decks / Figma exports / sketches),
# optional mockups/ and wireframes/ (promoted artifacts), optional
# validation-report.* all land inside {ux_output_path}/{run_folder_pattern}/.
# Run folder. DESIGN.md, EXPERIENCE.md, .decision-log.md, .working/
# (creative-tool artifacts), imports/ (user-supplied screens / brand decks /
# Figma exports / sketches), optional mockups/ and wireframes/ (promoted
# artifacts), optional validation-report.* all land inside
# {ux_output_path}/{run_folder_pattern}/.
ux_output_path = "{planning_artifacts}/ux-designs"
run_folder_pattern = "ux-{project_name}-{date}"
@ -55,7 +75,7 @@ creative_tools = [
"file:assets/key-screens.md",
]
# Polish passes applied to design.md at finalize.
# Polish passes applied to DESIGN.md and EXPERIENCE.md at finalize.
# Entries: `skill:NAME`, `file:PATH`, or plain text directive.
# Suggested order: structural → content/voice → prose mechanics.
doc_standards = [
@ -70,7 +90,7 @@ external_sources = []
# Routes outputs beyond local files at Finalize. Returned URLs/IDs surfaced
# to the user. Unavailable tools skipped and flagged.
# Example: "Upload design.md to Confluence via corp:confluence_upload (space_key='DESIGN')."
# Example: "Upload DESIGN.md to Confluence via corp:confluence_upload (space_key='DESIGN')."
external_handoffs = []
# Reviewers spawned at Finalize step 4 and at Validate intent, alongside

View File

@ -10,7 +10,7 @@ Decision moments where a visual beats more conversation: picking color tokens, p
## Artifact handling
Every renderer writes to `{doc_workspace}/.working/` with a descriptive filename. `.working/` is the audit trail and survives the run. At Finalize, the facilitator walks `.working/` with the user and promotes artifacts with lasting reference value to `{doc_workspace}/mockups/` (HTML anchoring a brand or layout decision) or `{doc_workspace}/wireframes/` (Excalidraw a dev would glance at). Bar for promotion: *would a future reader of `design.md` open this?* Default is leave-in-`.working/`.
Every renderer writes to `{doc_workspace}/.working/` with a descriptive filename. `.working/` is the audit trail and survives the run. At Finalize, the facilitator walks `.working/` with the user and promotes artifacts with lasting reference value to `{doc_workspace}/mockups/` (HTML anchoring a brand or layout decision) or `{doc_workspace}/wireframes/` (Excalidraw a dev would glance at). Bar for promotion: *would a future reader of `DESIGN.md` or `EXPERIENCE.md` open this?* Default is leave-in-`.working/`.
## Renderer contract

View File

@ -0,0 +1,50 @@
# DESIGN.md Spec — Working Reference
Source of truth: [google-labs-code/design.md](https://github.com/google-labs-code/design.md) (Apache 2.0, Google Labs, April 2026). This file is a working summary; the URL wins on conflict.
## Structure
YAML frontmatter (machine-readable tokens) + markdown body (human-readable rationale, prose sections).
## Frontmatter tokens
| Key | Type | Notes |
|---|---|---|
| `name` | string | Required. Brand or system name. |
| `description` | string | One-line statement of what this system is. |
| `colors` | flat object | Kebab-case keys. Values are hex strings (`'#FBF9F4'`). |
| `typography` | nested object | Each value: an object with any subset of `fontFamily`, `fontSize`, `fontWeight`, `lineHeight`, `letterSpacing`. |
| `rounded` | object | Scale names (`sm`, `md`, `lg`, `xl`, `full`, `DEFAULT`) → CSS dimensions. `full` is conventionally `9999px`. |
| `spacing` | object | Scale levels (`'1'`, `'2'`, ...) or named tokens (`gutter`, `margin-mobile`, `editorial-gap`) → dimensions. |
| `components` | object | Component-name → object of component tokens mapped to values or `{path.to.token}` references. |
## Body sections (omittable, order-locked when present)
1. **Brand & Style** — Aesthetic posture in prose. The editorial voice — what *kind* of thing this is.
2. **Colors** — Per-color story. Why each exists, where it's used, what it's *not* used for.
3. **Typography** — Type roles, ramp, and rules. Platform conventions noted semantically when inherited.
4. **Layout & Spacing** — Spacing scale narrative, grid behavior, margins, gutters, breakpoint rules.
5. **Elevation & Depth** — Shadow language and tonal layering rules.
6. **Shapes** — Corner radii rules and the aesthetic logic behind them.
7. **Components** — Per-component visual specs: anatomy, color usage, sizing, state appearance.
8. **Do's and Don'ts** — Hard visual rules — what to do, what to avoid.
Sections may be omitted when not relevant; order is locked when present.
## Cross-reference syntax
`{path.to.token}` used in prose and inside component objects to reference frontmatter tokens. Examples:
- `{colors.primary}`
- `{typography.body.fontSize}`
- `{rounded.md}`
- `{spacing.4}`
The path follows the YAML structure.
## Common patterns
- **Light/dark mode.** Either separate kebab-case tokens (`surface-base` / `surface-base-dark`) or separate DESIGN.md files per mode. The spec allows either; pick the form that reads cleanest for the product.
- **Platform conventions.** When inheriting from native platforms (iOS UIKit, Android Compose, Apple Human Interface Guidelines), use a `note` field instead of literal values: `{ note: 'iOS Title 1 · Android Headline Small' }`. The spec is the spec; the platform owns the rendered values.
- **UI-system inheritance.** When inheriting from shadcn / MUI / Tailwind / internal design system, reference the system's tokens by name rather than restating values. DESIGN.md specifies only the deltas (brand color overrides, typography swaps, component customizations).
- **Component tokens.** The `components` frontmatter entry maps each named component (e.g., `button-primary`) to its specific token values. Use `{path.to.token}` references freely; the resolver flattens at consumption time.

View File

@ -12,8 +12,8 @@ Free-form structured payload in the first message:
- `intent``"create"`, `"update"`, or `"validate"`. If absent, infer from the artifact set.
- **Create**: any source spec (PRD, brief, requirements list, design-thinking output, prior UX — text, path, or URL) plus brand / platform / accessibility notes; `doc_workspace` if a specific run folder is required.
- **Update**: existing `design.md` path (or workspace containing one) + change signal.
- **Validate**: existing `design.md` path (or workspace). Workspace defaults to the spine's containing directory.
- **Update**: existing workspace containing `DESIGN.md` + `EXPERIENCE.md` (or path to either) + change signal.
- **Validate**: existing workspace containing `DESIGN.md` + `EXPERIENCE.md` (or path to either). Workspace defaults to the spines' containing directory.
Inferences → `assumptions[]`. Gaps needing a human decision → `open_questions[]`. Do not invent persona, brand, accessibility, or scope detail.

View File

@ -1,43 +1,43 @@
# Validate
Critique an existing design spine without changing it. The synthesis pipeline below is also used at the Reviewer Gate during Create / Update Finalize.
Critique an existing spine pair (`DESIGN.md` + `EXPERIENCE.md`) without changing it. The synthesis pipeline below is also used at the Reviewer Gate during Create / Update Finalize.
## Orient
Subagent-extract from `.decision-log.md`, sources in frontmatter, `imports/`, `mockups/`, `wireframes/`, `design.md`. Parent assembles from extracts.
Subagent-extract from `.decision-log.md`, sources in frontmatter, `imports/`, `mockups/`, `wireframes/`, `DESIGN.md`, `EXPERIENCE.md`. Parent assembles from extracts.
## Reviewer Gate
Rubric walker prompt:
> Validate `design.md` as the contract for downstream consumers (architecture, story-dev — human or AI). Can a consumer source-extract cleanly, with every reference resolving and every load-bearing decision committed? Read `{workflow.spine_examples}` first.
> Validate the spine pair (`DESIGN.md` + `EXPERIENCE.md`) as the contract for downstream consumers (architecture, story-dev — human or AI). Can a consumer source-extract cleanly, with every reference resolving and every load-bearing decision committed? Read `{workflow.design_md_examples}` and `{workflow.experience_md_examples}` first.
>
> **Pass 1 — mechanical coverage.** Per category: extract, then list misses with location citations. No misses = **strong**.
>
> 1. **Flow coverage.** Sources frontmatter → extract every UJ / requirement name. Verify each has a Key Flow with numbered steps, a named climax beat, and a failure path where applicable.
> 1. **Flow coverage** (EXPERIENCE.md). Sources frontmatter → extract every UJ / requirement name. Verify each has a Key Flow with named protagonist, numbered steps, a climax beat, and a failure path where applicable.
>
> 2. **Token completeness.** Extract every token reference in `design.md`. Verify each defined. **Color tokens missing hex (or light/dark pairs where applicable) are critical** — downstream code mirrors the spine. Platform conventions (native dynamic type, 8pt grid) may stay semantic. Contrast targets stated for load-bearing combinations.
> 2. **Token completeness** (DESIGN.md). Extract every token in the YAML frontmatter and every `{path.to.token}` reference in the prose. Verify each defined (see `references/design-md-spec.md` for type rules). **Color tokens missing hex (or light/dark pairs where applicable) are critical** — downstream code mirrors the spine. Platform conventions (native dynamic type, 8pt grid) may stay semantic. Contrast targets stated for load-bearing combinations.
>
> 3. **Component coverage.** Extract every component name used anywhere. Verify each has a Component Patterns row with real rules (anatomy, state cues, sizing — not one-word descriptions).
> 3. **Component coverage** (both spines). Extract every component name used anywhere. Verify each has a row in DESIGN.md.Components (visual spec) *and* EXPERIENCE.md.Component Patterns (behavioral spec) — real rules, not one-word descriptions.
>
> 4. **State coverage.** Walk every IA surface. List states it should have (empty, cold-load, focus, error, offline, permission-denied — whichever apply). Verify each covered.
> 4. **State coverage** (EXPERIENCE.md). Walk every IA surface. List states it should have (empty, cold-load, focus, error, offline, permission-denied — whichever apply). Verify each covered.
>
> 5. **Visual reference coverage.** List every file in `mockups/`, `wireframes/`, `imports/`. Spine links to each inline at the relevant section and names what it illustrates; spine-wins-on-conflict stated once. List orphans and unspecific references.
> 5. **Visual reference coverage.** List every file in `mockups/`, `wireframes/`, `imports/`. Spines link to each inline at the relevant section and name what it illustrates; spines-win-on-conflict stated once. List orphans and unspecific references.
>
> **Pass 2 — judgment.** Verdict per category (*strong / adequate / thin / broken*); findings only where they add information.
>
> 6. **Bloat & overspecification.** Pixel specs where tokens cover it; source restatement (personas, FRs, scope); prose where a table works; sections no downstream consumer would read; decorative narrative untied to a decision.
> 6. **Bloat & overspecification.** Pixel specs where tokens cover it; source restatement (personas, FRs, scope); prose where a table works; sections no downstream consumer would read; decorative narrative untied to a decision. DESIGN.md prose may carry editorial voice; EXPERIENCE.md prose should not.
>
> 7. **Inheritance discipline.** `sources` frontmatter resolves. UJ / requirement names verbatim from sources. Glossary identical across spine and sources. Component names identical across all sections within the spine.
> 7. **Inheritance discipline.** `sources` frontmatter resolves. UJ / requirement names verbatim from sources. Glossary identical across spines and sources. Component names identical across all sections in both files. EXPERIENCE.md token references resolve to DESIGN.md tokens by name.
>
> 8. **Shape fit.** Required default tables present (IA, Voice and Tone, Design Tokens, Component Patterns, State Patterns, Interaction Primitives, Accessibility Floor, Key Flows). Dropped defaults defensible. Required-when-applicable present where triggered (Inspiration when sources / log show reference products or rejects; Responsive when multi-surface or breakpoints). Invented sections earn their place.
> 8. **Shape fit.** DESIGN.md sections in canonical order (Brand & Style → Colors → Typography → Layout & Spacing → Elevation & Depth → Shapes → Components → Do's and Don'ts; omittable but order-locked when present). EXPERIENCE.md required defaults present (Foundation, IA, Voice and Tone, Component Patterns, State Patterns, Interaction Primitives, Accessibility Floor, Key Flows). Dropped defaults defensible. Required-when-applicable present where triggered (Inspiration when sources / log show reference products or rejects; Responsive when multi-surface or breakpoints). Invented sections earn their place.
>
> Severity = downstream impact, not fix difficulty.
>
> Write to `{doc_workspace}/review-rubric.md`:
>
> ```markdown
> # Design Spine Review — {spine_name}
> # Spine Pair Review — {project_name}
>
> ## Overall verdict
> [23 sentences]
@ -72,9 +72,10 @@ Re-running overwrites the consolidated report; individual `review-*.md` files pe
## Markdown twin shape
```markdown
# Validation Report — {spine_name}
# Validation Report — {project_name}
- **Spine:** `{spine_path}`
- **DESIGN.md:** `{design_path}`
- **EXPERIENCE.md:** `{experience_path}`
- **Run at:** {ISO timestamp}
- **Grade:** {Excellent | Good | Fair | Poor}