diff --git a/.claude/commands/design/design-handoff-v2-proposal.md b/.claude/commands/design/design-handoff-v2-proposal.md new file mode 100644 index 0000000..bf64c94 --- /dev/null +++ b/.claude/commands/design/design-handoff-v2-proposal.md @@ -0,0 +1,345 @@ +# Design Handoff v2 — Architecture Proposal + +**Status:** Proposal under refinement — architecture decisions partly settled (see Decisions); not yet implemented +**Author:** Design tooling research, June 2026 +**Supersedes design of:** `.claude/commands/design/design-handoff.md` +**Next gate:** Confirm the two open recommendations, then rewrite the command to v2 + +--- + +## Why revisit this + +The current `design-handoff.md` is a solid 7-phase pipeline, but it was written before the +mechanisms it needs actually existed, and three rounds of research surfaced both correctness +bugs and structural gaps. + +### Correctness bugs (the command would not run today) +- **Wrong Figma file key.** Hardcodes `v8BYFkebTGQNuiRrCXWssa` (an old shared "Canvas" file + with 4 unrelated sections). The real Metrics design work lives in `lSXpuOg6n0qXXwvMjOfU7G`. +- **Non-existent MCP tool names.** Phase 3 references `mcp__figma__get_metadata`; the real + tools are `mcp____get_metadata` / `use_figma` / `get_screenshot`. +- **Ghost dependency.** Every per-feature `FIGMA.md` says "read `~/Dalgo/FIGMA.md` first" — + that file does not exist in the repo. + +### Structural gaps (confirmed against industry practice) +- **Designers are treated as approvers, not authors.** Claude reads the spec and asks + questions; the designer answers. Real design is visual and iterative — sketch, see, react, + revise. There is no loop and no brainstorm mode. +- **Patterns are pasted into prompts.** `{paste relevant excerpts from patterns.md}` is manual, + inconsistent, and the source of cross-frame drift. +- **Design decisions land in the wrong file.** Output is a `## Design` section appended to + `spec.md`; in the Metrics feature, decisions then leaked into `tasks.md` as "v2 overrides." + There is no single design source of truth. +- **Review runs once, at the end.** Phase 4 produces a list of problems for a human to fix + manually, rather than looping until the design passes. +- **Phase 2 hard-blocks the session.** The designer must be present in chat; thinking time and + generation time are coupled. + +--- + +## What the research changed + +Full notes are in the session; the load-bearing findings: + +1. **Figma shipped native Skills (Mar 24, 2026)** — markdown files that make a design system + machine-readable, loaded by the canvas agent automatically. This is exactly what + `patterns.md` / `FIGMA.md` are doing by hand. *We should publish, not paste.* +2. **Spec-driven dev converged on a 3-file split** (Kiro: `requirements.md` / `design.md` / + `tasks.md`; spec-kit: `spec.md` / `plan.md` / `tasks.md`). `design.md` is a first-class, + human-reviewed, **gated** phase — not an appendix to the spec. +3. **The "constitution" pattern** (spec-kit) — one always-loaded, immutable-principles file + that governs every phase. Dalgo's NGO constraints are a constitution scattered across files. +4. **Anthropic's evaluator-optimizer loop** — generate → evaluate against criteria → feed back + → repeat. We have the criteria (`checklist.md`) but run it once instead of in a loop. +5. **Nobody has solved spec→design.** Even Kiro/spec-kit explicitly punt on UX methodology. + This command is novel work, not catch-up — worth getting right. + +--- + +## Decisions (resolved June 2026) + +| # | Decision | Choice | Consequence | +|---|----------|--------|-------------| +| 1 | Feature → Figma file mapping | **One file per feature** | Reuse comes from the published Skill, not from scouting other features. Scout's job narrows to "design-system baseline + this feature's internal state." Phase 0 creates the file on first run and records its key in `design.md`. | +| 2 | Constitution location | **New `.claude/constitution.md`** | Canonical and top-level. `patterns.md`, `checklist.md`, and `CLAUDE.md` point to it instead of restating rules. Loaded by design **and** engineering workflows. | +| 3 | Frame naming standard *(recommended)* | **`S{n} · {Screen Name}` + `S{n}-{State}` for states** | One convention so Scout and the prototype wiring can resolve frames by name. Ends the Metrics-file mix of `–` / `·` / `-`. | +| 4 | Brainstorm storage *(recommended)* | **Same feature file, separate "Explorations" page** | Rejected variants stay out of the handoff surface but remain recoverable. No throwaway files to track. | + +Decisions 3 and 4 are recommendations — flag if you'd rather decide differently; nothing else +depends on them being settled today. + +The **one-file-per-feature** choice also retires the ghost `~/Dalgo/FIGMA.md` dependency: the +cross-feature shared reference is now the **constitution + the design-system Skill**, not a +parent file. Each feature keeps a small `FIGMA.md` that extends the Skill with feature-specific +notes (screen list, file key, canvas positions). + +--- + +## Proposed architecture + +### Principle 1 — Designer drives; the command serves + +Replace the question-and-answer brief with three intent-led modes: + +| Mode | Invocation | Output | When | +|------|-----------|--------|------| +| **Brainstorm** | `--brainstorm` | 2–3 rough variants per open screen, side-by-side in an "Explorations" section. No prototype, no spec change. | Direction is open; designer wants options | +| **Draft** | `--draft` | Lo-fi wireframes, one feedback round per frame, in-place revision. | Direction is chosen; iterating on layout | +| **Ship** (default) | _(none)_ | Full-fidelity frames, prototype wired, `design.md` written, ready for engineering. | Design is settled; producing the handoff | + +Variants-by-default in brainstorm matches where every serious tool landed (Google Stitch ships +2–3; Figma First Draft previews multiple themes). A single output asked "does this work?" is +behind the curve. + +### Principle 2 — Design system as a Figma Skill, not pasted text + +- Publish `patterns.md` + the visual half of `checklist.md` as a **Figma Skill** the canvas + agent loads automatically (native mechanism as of Mar 2026). +- Frame agents stop receiving pasted excerpts. The prompt points at the skill; the agent reads + what it needs as it works. +- This is also what shrinks the cross-frame consistency problem at the source — agents sharing + one skill drift far less, so a consistency check becomes a spot-check rather than a full pass. + +### Principle 3 — A constitution governs every phase + +Create `.claude/constitution.md` holding Dalgo's non-negotiables, today scattered across +`CLAUDE.md` and `patterns.md`: + +- Never expose SQL, schema/table names, or code to non-admin users +- Users are non-technical NGO staff on slow internet and old devices +- Sentence case on buttons; no internal acronyms (RAG, blast radius) in UI copy +- Admin-only fields are visually locked + +It is **canonical**: `patterns.md`, `checklist.md`, and `CLAUDE.md` reference it rather than +restate the rules, so there is no drift. Both the design agents **and** the review checklist +load it. To keep it useful for engineering too, the design-handoff and the engineering +`plan-feature` / `execute-plan` flows should all point at the same file — one source of truth +for "what Dalgo will never ship." + +### Principle 4 — Own `design.md`, don't append to `spec.md` + +Adopt the Kiro split. The handoff produces a gated `features/{name}/v1/design.md` — the design +source of truth — containing screens, node IDs, flows, decisions, and known issues. `spec.md` +keeps a one-line pointer to it. Engineering reads `design.md`; nothing leaks into `tasks.md`. + +### Principle 5 — Review is an evaluator loop, not a final report + +Restructure Phase 4 as evaluator-optimizer: the review agent checks each frame against +`checklist.md` + constitution, and **feeds failures back to the frame agent** until it passes +(bounded retries). Frames arrive already-passing instead of arriving with a to-do list. + +### Principle 6 — Async brief; mark ambiguity, don't block + +Replace the blocking Phase 2 with the spec-kit pattern: +- Write the brief to `features/{name}/v1/design-brief.md`. The designer edits it on their own + time, in their own editor. +- Re-running detects a filled-in brief and continues. +- Where direction is genuinely missing, the command **generates anyway and tags the frame / + decision with `[NEEDS CLARIFICATION]`** rather than halting the whole run. + +--- + +## Design System Integration + +The canonical design system lives in `DalgoT4D/dalgo-design-system` (a separate repo), as two +CSS files that are the **single source of truth** for all visual decisions: + +- **`tokens.css`** — CSS custom properties for color, typography, spacing, radii, shadow, z-index, + and transitions. All prefixed `--color-*`, `--font-*`, `--space-*`, `--spacing-*`, `--radius-*`, + `--shadow-*`, `--z-*`, `--transition-*`. +- **`components.css`** — 24 components, all values referencing `var(--*)` — zero hardcoded values. + +### What `patterns.md` gets wrong today + +`patterns.md` is the current source of design truth inside the command. It has three specific +contradictions with `tokens.css` and one policy conflict with `components.css`: + +| # | patterns.md says | Design system says | Fix | +|---|------------------|--------------------|-----| +| 1 | Uses Shadcn token names (`--primary`, `text-muted-foreground`) | Tokens are `--color-brand-primary`, `--color-text-secondary`, etc. | Replace all token references | +| 2 | "24px page padding" | `--spacing-page-x` (horizontal, 32px); `--spacing-page-y` (vertical, 28px) | Update padding rule to match named tokens | +| 3 | "Lucide icons at 16px (h-4 w-4) everywhere" | Icon sizes are context-specific per component | Replace blanket rule with per-context guidance | +| 4 | "Sentence case on buttons" (constitution rule) | `.btn { text-transform: uppercase }` in `components.css` | **Needs a decision** — is casing set in code (uppercase) or copy (sentence case)? | + +Item 4 is a conflict between the UI constitution rule and the CSS implementation. The most likely +intent: `.btn` sets uppercase as a CSS default that the design system overrides per variant; +`patterns.md` / the constitution should govern copy, not override CSS `text-transform`. Resolution +needed before rewriting the Figma Skill prompt. + +### Missing tokens (gaps in tokens.css) + +RAG status colors — used extensively throughout the app — are **not in `tokens.css`**: + +- on-track (green) +- at-risk (amber) +- off-track (red) +- stale (grey) + +These are referenced in multiple features and need to be added to the design system repo before +the Figma Skill can be authoritative. Until then, they should be documented in `patterns.md` as +a pending addition. + +### Component families (basis for tiered loading) + +`components.css` contains 24 components, grouped into two tiers for context management: + +**Tier 1 — always load (layout skeleton, ~5 components)** + +Every screen shares these; they go into the Figma Skill's always-available section: + +| Component | Role | +|-----------|------| +| Reset | CSS baseline | +| Page Shell | Fixed header + scrollable content pattern | +| Header | Top bar, org switcher | +| Sidebar | Left nav rail | +| Nav Items | Active/hover states | + +**Tier 2 — load per frame (component families, ~19 components)** + +Frame agents request only what their surface needs. Group by screen archetype: + +| Family | Components | Typical surfaces | +|--------|-----------|-----------------| +| Data display | Table, Pagination, Action Buttons | List screens, data grids | +| Input | Forms (label, input, select, tags, frequency, toggle, tabs) | Create/edit flows | +| Overlay | Modal, Empty State | Dialogs, zero-state screens | +| Status/card | Badge Pill, Badge/Avatar, Step Indicator, Card | Dashboards, pipeline status | +| Specialist | Chart Type Icons, Header Org | Analytics screens, multi-org flows | + +The frame agent prompt declares which family it needs; the orchestrator loads only those component +definitions into the agent's context. This keeps each agent's prompt under ~4k tokens for the +design system portion. + +### How patterns.md should change + +`patterns.md` should become a **Tailwind/Shadcn mapping document**: it translates design-system +token names into the framework-specific class names used in `webapp_v2`. It should not restate +spacing values or hex codes — those live in `tokens.css`. Structure: + +``` +# patterns.md (after fix) + +Source of truth: DalgoT4D/dalgo-design-system (tokens.css + components.css) +This file maps design-system tokens → Tailwind / Shadcn class names for webapp_v2. + +## Token mapping +--color-brand-primary → bg-teal-600 / text-teal-600 +--spacing-page-x → px-12 (48px) [verify against tokens.css] +--spacing-page-y → py-7 (28px) [verify against tokens.css] +... + +## Constitution reference +Non-negotiables are in .claude/constitution.md — not restated here. +``` + +### Figma Skill authoring plan + +The Figma Skill is a markdown file that Figma's canvas agent loads automatically. Its content +should be generated **from** `tokens.css` + `components.css`, not from `patterns.md`: + +1. **Tier 1 section** — universal layout tokens (colors, spacing, radii, shadows) +2. **Tier 2 sections** — one section per component family; each references component CSS rules + translated into Figma property names (fills, auto-layout padding, corner radius, etc.) +3. **Constitution section** — Dalgo's non-negotiable UX rules, imported from `.claude/constitution.md` + +Authoring question (still open): is the Skill file authored manually in Figma, or generated by a +script that reads `tokens.css` → outputs Figma Skill markdown? The script approach is more +maintainable but requires confirming Figma's native Skills format. + +--- + +## Agent topology (Anthropic orchestrator-workers + evaluator) + +``` +design-handoff (orchestrator) + │ + ├─ Scout (runs once) establishes the design-system baseline from the Skill, then + │ reads THIS feature's file via get_metadata. Empty file (first + │ run) → start at x=0. Re-run → screenshots existing frames for + │ internal consistency, returns rightmost free x-position. + │ + ├─ Frame agents (parallel) one per surface; load the Figma Skill; consume Scout's + │ └─ return a FIXED schema: context; create the frame + │ {node_id, frame_name, screenshot, decisions[], needs_clarification[]} + │ + ├─ Evaluator (loop) checklist + constitution per frame → feeds failures back to + │ the owning frame agent until pass or retry budget hit + │ + └─ Consistency spot-check screenshots all final frames; flags nav/header/footer drift + (small now that all agents shared one Skill) +``` + +Two guardrails from Anthropic's guidance: +- **Fixed return schema** across parallel frame agents, so the orchestrator's fan-in never has + to reconcile formats. +- **"Add agents only when simpler solutions fall short."** Scout and Evaluator earn their place + (they fix named failure modes). The Consistency spot-check stays deliberately thin. + +--- + +## Proposed phase flow + +| Phase | Current | Proposed | +|-------|---------|----------| +| 0 | — | Resolve mode (`brainstorm`/`draft`/`ship`); load constitution; look up this feature's Figma file key in `design.md` — **create the file if missing** (`create_new_file`) and record the key | +| 1 | Read spec, extract surfaces | _unchanged_ | +| 1.5 | Extract user flows | _unchanged (skipped in brainstorm)_ | +| 2 | **Blocking** designer brief | **Async** brief file; mark `[NEEDS CLARIFICATION]`, continue | +| 2.9 | — | **Scout**: read canvas, return shared context | +| 3 | Frame agents (pasted patterns) | Frame agents load **Figma Skill**; fixed return schema; variants in brainstorm | +| 3.5 | Wire prototype | _unchanged (skipped in brainstorm/draft)_ | +| 4 | Review once → report | **Evaluator loop** → frames return passing | +| 3.9 | — | **Consistency** spot-check | +| 5 | Designer sign-off | _unchanged_ | +| 6 | Append `## Design` to spec.md | Write/own **`design.md`**; spec.md gets a pointer | +| 7 | Signal readiness | _unchanged_ | + +--- + +## Migration / sequencing + +P0 fixes are independent of the bigger redesign and can ship immediately: + +1. **P0 — make it runnable:** remove the hardcoded Figma file key (look up / create per-feature + instead), fix MCP tool names, and retire the ghost `~/Dalgo/FIGMA.md` dependency (replace + with constitution + Skill references). +2. **P0 — fix patterns.md:** correct the three token contradictions (token names, page padding, + icon sizes) so that agents reading it today get accurate information. Reframe it as a + Tailwind/Shadcn mapping doc — no pixel values, no hex codes, a reference to `tokens.css` + as source of truth. +3. **P1 — add missing RAG tokens to dalgo-design-system:** open a PR to `tokens.css` adding + `--color-status-on-track`, `--color-status-at-risk`, `--color-status-off-track`, + `--color-status-stale`. Until merged, document interim values in `patterns.md`. +4. **P1 — constitution:** extract the constitution from `CLAUDE.md` / `patterns.md`; resolve + the button-casing conflict before writing it. Point `patterns.md` and `checklist.md` at it. +5. **P1 — Figma Skill:** author the Skill from `tokens.css` (Tier 1 universal tokens) and + `components.css` (Tier 2 component families). Drop pattern-pasting from agent prompts. +6. **P1 — `design.md` ownership:** switch Phase 6 to write `design.md`; leave a spec.md pointer. +7. **P2 — modes + async brief:** add `--brainstorm` / `--draft`; move the brief to a file with + `[NEEDS CLARIFICATION]` tagging. +8. **P2 — agent topology:** add Scout, the evaluator loop, and the consistency spot-check with + a fixed frame-agent return schema. + +--- + +## Still open + +The four questions from the first draft are now resolved in **Decisions** above (file strategy +and constitution location chosen; naming and brainstorm-storage recommended). What remains +before the command rewrite: + +1. **Confirm the two recommendations** (naming standard, brainstorm storage) — or override them. +2. **Button casing conflict:** `components.css` sets `text-transform: uppercase` on `.btn`; + the constitution rule says "sentence case on buttons." Needs a decision: does the CSS + govern (uppercase always), does copy govern (sentence case, CSS overridden per variant), + or is this a design-system bug to fix upstream? +3. **RAG status tokens:** `on-track` / `at-risk` / `off-track` / `stale` color tokens are + missing from `tokens.css`. Need to be added to `DalgoT4D/dalgo-design-system` before the + Figma Skill can be authoritative for status screens. +4. **Skill authoring approach:** should the Figma Skill file be authored manually in Figma, or + generated by a script that reads `tokens.css` → outputs Skill markdown? Script is more + maintainable. Also need to confirm the Figma workspace has native Skills enabled. +5. **Engineering buy-in on the constitution:** `plan-feature` / `execute-plan` pointing at the + same `.claude/constitution.md` is a cross-team change — worth a nod from engineering before + we wire it in. diff --git a/.claude/commands/design/design-handoff.md b/.claude/commands/design/design-handoff.md index 869f167..e83e3f0 100644 --- a/.claude/commands/design/design-handoff.md +++ b/.claude/commands/design/design-handoff.md @@ -2,364 +2,575 @@ ## Input: $ARGUMENTS -Bridge a feature spec into Figma designs with designer input, then update the spec so engineering can start. +Bridge a feature spec into Figma designs, then produce `design.md` so engineering can start. -Accepts: -- A spec file path: `features/rbac/v1/spec.md` -- A feature folder: `features/rbac/v1/` -- A feature name: `rbac` (looks for `features/rbac/v1/spec.md`) +**Accepts:** +- A spec file path: `features/access-control/v1/spec.md` +- A versioned folder: `features/access-control/v1/` +- A feature folder: `features/access-control/` (selects the highest version with a `spec.md`) +- A feature name: `access-control` (same as feature folder) -Flags: -- `--auto` — skip designer input, generate all surfaces autonomously (for prototyping only) +**Flags:** +- `--brainstorm` — Produces `layout-directions.md`: 2–3 annotated text layout variants per surface for the designer to pick from. No Figma, no `design.md`. Use when direction is still open. +- `--draft` — One lo-fi Figma frame per screen, one feedback round. No prototype wiring. Use when iterating on layout. +- _(default, no flag)_ — Full-fidelity Figma frames, prototype wired, `design.md` written. Use when design is settled and you're producing the engineering handoff. + +--- + +## Phase 0: Setup + +### 1. Parse mode +- Argument contains `--brainstorm` → **Brainstorm mode** +- Argument contains `--draft` → **Draft mode** +- No flag → **Ship mode** + +Strip the flag from `$ARGUMENTS` before locating the spec. + +### 2. Read the constitution +Read `.claude/constitution.md` — these rules govern every phase and every agent spawned. +Do not proceed until this file is loaded. + +### 3. Locate the spec +From the cleaned `$ARGUMENTS`, resolve the spec using these rules in order: + +1. **Ends in `.md`** — use it directly as the spec file. +2. **Ends in `/v{N}/` or is a versioned folder** — look for `spec.md` inside it. +3. **Feature folder or feature name** — list all `v{N}/` subdirectories inside + `features/{name}/`, sort by version number descending, and use the `spec.md` + from the highest version that contains one. If no versioned folder exists, + fall back to `features/{name}/spec.md`. + +Derive `{feature_name}` and `{version}` (e.g. `v1`, `v2`) from the resolved path. + +### 4. Resolve the Figma file key (skip in brainstorm mode) +Look for `features/{feature_name}/{version}/design.md`. If it exists, find the line: +``` +file_key: {key} +``` +Use that key. + +If `design.md` doesn't exist or has no `file_key`, create a new Figma file: +- Use `create_new_file` with the name `"{Feature Name} — {Version}"` (e.g. `"Access Control — v1"`) +- Create `features/{feature_name}/{version}/design.md` with just the file key recorded: + +```markdown +# {Feature Name} — Design ({Version}) + +**Status:** In progress + +## Figma + +file_key: {key} +``` --- ## Phase 1: Read & Extract ### 1. Read the spec -Load the spec file from `$ARGUMENTS`. If a folder is given, look for `spec.md` inside it. If only a feature name is given, check `features/{name}/v1/spec.md` then `features/{name}/spec.md`. +Load the spec file. Read it in full. ### 2. Extract UI surfaces -Parse the spec for every user-facing screen, flow, modal, or component that needs designing. Look for: +Parse the spec for every user-facing screen, flow, modal, or component that needs designing: - Explicit screen names ("User management page", "Invite modal") -- User stories that imply a UI ("As an admin, I want to assign roles...") -- State changes that need visual design (empty states, error states, success states) -- Shared components mentioned (badges, selectors, inline controls) - -For each surface, extract: -- **Purpose:** what the user accomplishes on this screen -- **Persona:** which Dalgo user type interacts with it (admin, PM, field staff) -- **Key decisions:** things that need a designer's judgment before the screen can be built +- User stories implying a UI ("As an admin, I want to assign roles...") +- State changes requiring visual design (empty, error, success states) +- Shared components (badges, selectors, inline controls) + +For each surface extract: +- **Purpose:** what the user accomplishes +- **Persona:** which Dalgo user type (admin, analyst, member) +- **Key decisions:** things needing a designer's judgment before building - **Dependencies:** does this screen reference another surface? -### 3. Load patterns -Read `.claude/skills/design-review/patterns.md` and `.claude/skills/design-review/checklist.md` — these are the design standards all generated frames must pass. +### 3. Load design standards +Read `.claude/skills/design-review/patterns.md` and `.claude/skills/design-review/checklist.md` +into the orchestrator context. These are passed to agents in full — do not paste excerpts. --- -## Phase 1.5: User Flow Extraction +## Phase 1.5: User Flow Extraction (skip in brainstorm mode) -For each surface extracted above, map the full user journey through that screen. This becomes the basis for prototype connections wired in Figma after frames are created. +For each surface, map the full user journey. This becomes the basis for prototype connections. For each surface, produce a flow block: ``` -**Screen: {Screen Name}** +Screen: {Screen Name} + Entry points: - {How the user arrives — e.g. "clicks 'Invite user' button on User management page"} - {Alternative entry — e.g. "redirected here after accepting email invite"} Happy path: -1. {Step 1 — e.g. "User sees modal with Name, Email, Role fields"} -2. {Step 2 — e.g. "User fills in details and clicks 'Send invite'"} -3. {Step 3 — e.g. "Success toast shown, modal closes, user appears in table"} +1. {Step 1} +2. {Step 2} +3. {Step 3} Branch points: -- {Condition → Screen} e.g. "Email already exists → inline field error (stays on modal)" -- {Condition → Screen} e.g. "Last admin demotion attempt → blocked with error banner" -- {Condition → Screen} e.g. "Cancel / Esc → dismiss modal, no change" +- {Condition} → {Screen or state} +- {Condition} → {Screen or state} Exit points: - Success → {target screen or state} - Cancel → {target screen or state} -- Error → {stays on screen / redirects to X} +- Error → {stays / redirects to X} ``` -Group screens into **scenarios** — named end-to-end journeys a user might take: +Group screens into **scenarios** — named end-to-end journeys: - e.g. "Invite a new team member" → Invite modal → Success state → User management table -- e.g. "Change a user's role" → User table → Role selector → Confirm modal → Updated table -- e.g. "Remove a user" → User table → Delete confirm → Empty state (if last user) -Each scenario becomes a **named prototype flow** in Figma. +Each scenario becomes a named prototype flow in Figma. -Store the extracted flows and scenarios for use in Phase 3 (Figma agent prompts) and Phase 3.5 (prototype wiring). +--- + +## Phase 2: Async Brief + +### 1. Check for an existing brief +Look for `features/{feature_name}/{version}/design-brief.md`. + +**If it exists and has content below any `Your direction:` line:** Read it, extract designer +direction per surface, and continue directly to Phase 2.5. + +**If it's missing or blank:** Write the brief file (step 2) and stop. + +### 2. Write the brief file +Create `features/{feature_name}/{version}/design-brief.md`: + +```markdown +# Design Brief — {Feature Name} ({Version}) + +**Status:** Awaiting designer input +**Spec:** {spec path} +**Generated:** {date} + +Fill in your direction below each surface, then re-run: +`/design:design-handoff {spec path}` + +Leave a section blank to let me use best judgment for an NGO audience +(I'll tag those decisions `[NEEDS CLARIFICATION]` in the output). --- -## Phase 2: Design Brief (skip if `--auto`) +{for each surface:} -Present the extracted surfaces to the designer in this format: +## Surface {N}: {Screen Name} -``` -I found {N} screens to design for {Feature Name}. +**Who uses it:** {persona} +**What it does:** {purpose} + +**Decisions needed:** +- {Decision 1} +- {Decision 2} + +**Reference design (if any):** -Before I generate anything in Figma, I need your direction on a few things: +**Your direction:** --- +``` + +Print: +``` +Brief written to features/{feature_name}/{version}/design-brief.md + +Open it, fill in direction for each surface, then re-run: +/design:design-handoff {spec path} +``` + +**Stop here.** Do not proceed to layout directions or Figma. -**Screen 1: {Screen Name}** -Who uses it: {persona} -What it does: {purpose} -Decisions needed: -- {Decision 1 — e.g. "Inline role edit or separate modal?"} -- {Decision 2 — e.g. "What happens when you demote the last admin?"} -- {Decision 3 — e.g. "Any reference design or existing Dalgo screen to match?"} +### 3. Parse a completed brief +When re-running and the brief has responses, extract direction per surface. +Where a `Your direction:` field is blank or missing, note that surface as `[NEEDS CLARIFICATION]` +and use best judgment for a non-technical NGO audience. --- -**Screen 2: {Screen Name}** +## Phase 2.5: Layout Directions + +Generate layout directions for every surface. This is a text-only step — no Figma yet. + +For each surface, produce **2–3 annotated layout variants** as structured text. +Each variant should describe: +- Overall layout (sidebar position, content zones, modal vs full-page) +- Key UI elements and their placement +- Copy decisions (labels, headings, CTAs, helper text) +- How the designer direction (or best judgment) shaped the choice +- One sentence on the trade-off vs the other variants + +Use this format per surface: + +``` +## {Screen Name} + +Designer direction: {direction from brief, or "[NEEDS CLARIFICATION] — using best judgment"} + +### Variant A — {short name} +Layout: {description} +Key elements: {list} +Copy: {headings, CTAs, labels} +Trade-off: {what this gains vs other variants} + +### Variant B — {short name} +... + +### Variant C — {short name} (include only if meaningfully different) ... +**Recommended:** Variant {X} — {one sentence why, grounded in NGO audience and constitution rules} +``` + +Write the full output to `features/{feature_name}/{version}/layout-directions.md`. + --- -Reply with your direction for each screen. You can: -- Answer the decisions ("Screen 1: inline edit, block last-admin demotion") -- Skip a screen ("skip Screen 3 — not needed for v1") -- Add context ("reference the existing Reports share modal for Screen 2") -- Point to inspiration ("Screen 4 should look like Notion's permission picker") +### Brainstorm mode: stop here -I won't generate anything until you reply. +If mode is `--brainstorm`, print: + +``` +Layout directions written to features/{feature_name}/{version}/layout-directions.md + +Review the variants for each surface, then tell me which to build: +"go with Variant B for surface 2, Variant A for the rest" + +Or run with --draft or no flag to proceed to Figma once you've picked. ``` -**Wait for designer reply before proceeding.** Do not spawn any Figma agents until the designer has responded. +**Stop here.** Do not proceed to Figma in brainstorm mode. + +--- + +### Draft / Ship mode: pick variants + +If mode is `--draft` or ship (no flag): + +Check `layout-directions.md` for any designer pick annotations (e.g. "→ build this" or similar +freeform notes added after the file was generated). If picks exist, use them. If not, use the +Recommended variant for each surface and note it. + +--- + +## Phase 2.9: Scout (draft and ship mode only) + +Spawn a Scout agent with this prompt: + +``` +You are reading a Figma file to establish baseline context for frame agents. + +Figma file key: {file_key} + +1. Call get_metadata on the file. Collect all existing frames: their names, node IDs, and x positions. +2. If the file has no frames, return next_x = 0. +3. If frames exist, return the rightmost x position across all frames + 100px gap as next_x. +4. Return this exact JSON: +{ + "file_key": "{file_key}", + "existing_frames": [{"name": "...", "node_id": "...", "x": 0}], + "next_x": 0, + "notes": "any relevant observations about canvas state" +} +``` -### Parse designer reply -Read the reply and map each direction to its surface: -- Extract copy/label decisions ("use 'Full access' not 'Admin'") -- Note referenced screens or external inspiration -- Note skipped surfaces -- Note any specific layout preferences ("single-step modal", "use the existing empty state pattern") +Wait for Scout to complete before spawning frame agents. --- -## Phase 3: Generate Designs in Figma +## Phase 3: Generate Designs in Figma (draft and ship mode only) + +### Tool choice +Use the `figma-generate-design` skill to build each frame. **Do not use raw Plugin API +JavaScript via `use_figma` to construct frames** — that approach is fragile and expensive. +`use_figma` is reserved for prototype connection wiring only (Phase 3.5). + +Load `skill://figma/figma-generate-design/SKILL.md` before spawning frame agents. + +### Mode behaviour + +| Mode | Page | Fidelity | Prototype | +|------|------|----------|-----------| +| Draft | "Designs" | Lo-fi wireframe | No | +| Ship | "Designs" | Full-fidelity | Yes | + +### Frame naming +- `S{n} · {Screen Name}` (e.g. `S1 · User Management`) +- States: `S{n}-{State}` (e.g. `S1-Empty`, `S1-Error`) -### 1. Determine Figma page name -Use `"{Feature Name} Designs"` as the page name (e.g. "RBAC Designs"). Check if it already exists using `mcp__figma__get_metadata` on file key `v8BYFkebTGQNuiRrCXWssa`. +### Frame agent prompt +Spawn one agent per surface in parallel. Each **must** return this fixed JSON schema: -### 2. Spawn one Figma agent per surface (in parallel) -For each surface (excluding skipped ones), spawn an agent with this context: +```json +{ + "node_id": "string", + "frame_name": "string", + "screenshot_url": "string", + "decisions": ["string"], + "needs_clarification": ["string"] +} +``` + +Agent prompt template: ``` -You are a UX designer for Dalgo — an open-source data platform for non-technical NGO program managers. +You are a UX designer for Dalgo — an open-source data platform for non-technical NGO program +managers. Users are non-technical, on slow internet and old devices. Simplicity matters more +than visual richness. Feature: {Feature Name} Screen: {Screen Name} Persona: {persona} Purpose: {purpose} +Mode: {draft | ship} -Designer direction: -{designer's direction for this screen} +Selected layout variant: +{variant text from layout-directions.md} User flow for this screen: -{paste the flow block from Phase 1.5 for this surface — entry points, happy path, branch points, exit points} - -Scenario(s) this screen belongs to: -{list scenario names from Phase 1.5 that include this screen} - -Dalgo Design System: -- Primary color: #00897B (teal) -- Font: Anek Latin -- Icons: Lucide 16px -- Cards: 1px border, 8px radius, 16px padding -- Spacing: 4px grid, 24px page padding -- Primary CTA: ghost variant, teal background (#00897B), white text -- Secondary: outline button -- Sentence case on all buttons ("Cancel" not "CANCEL") -- Status colors: green #16a34a (on track), amber #d97706 (at risk), red #dc2626 (off track) -- Never expose SQL, schema table names, or code to non-admin users -- Lock icon + "Contact admin to change" for admin-only fields - -Key patterns to follow: -{paste relevant excerpts from patterns.md for this surface type} - -Figma file: v8BYFkebTGQNuiRrCXWssa -Page: {page name} -Frame name: "{Screen Name}" -Frame size: 1440×900px -Position: x={calculated offset}, y=0 +{flow block from Phase 1.5} -Instructions: -1. Invoke the /figma-use skill first — mandatory before calling use_figma -2. Create the frame on the "{page name}" page (create the page if it doesn't exist) -3. Name the frame exactly as "{Screen Name}" — this name is used to wire prototype connections in the next phase -4. After creating, call get_screenshot on the created node and verify it matches the brief -5. Return the node ID, frame name, and a one-line description of what was built -``` +Design standards — apply in full: +{full content of patterns.md} -Position frames left to right with 100px gaps: x=0, x=1540, x=3080, etc. +Constitution — never violate these: +{full content of .claude/constitution.md} -### 3. Collect node IDs -Wait for all agents to complete. Collect the node ID, frame name, and description for each frame. +Figma file key: {file_key} +Page: "Designs" +Frame name: {frame name per naming convention} +Frame size: 1440×900px +Position: x={scout.next_x + (surface_index × 1540)}, y=0 + +Steps: +1. Load skill://figma/figma-generate-design/SKILL.md before designing. +2. Create the "Designs" page if it does not exist. +3. Build the frame using figma-generate-design guidance. +4. After creating, call get_screenshot on the node. +5. Record design decisions and any [NEEDS CLARIFICATION] items. +6. Return the fixed JSON schema. +``` ---- +### Collect results +Wait for all agents. Validate every response matches the fixed schema. +If an agent returns malformed output, re-prompt once requesting the correct JSON. -## Phase 3.5: Wire Prototype Connections +--- -Using the user flows and scenarios from Phase 1.5, add prototype connections between frames in Figma. This turns the static frames into a navigable, clickable prototype. +## Phase 3.5: Wire Prototype Connections (ship mode only) ### 1. Build the connection map -For each scenario, map out every connection: +Using flows and scenarios from Phase 1.5: ``` Scenario: {Scenario Name} Connections: -- From: {Frame Name} | Trigger: {element clicked — e.g. "'Send invite' button"} → To: {Frame Name} -- From: {Frame Name} | Trigger: {"Cancel" button click} → To: {Frame Name} -- From: {Frame Name} | Trigger: {error condition} → To: {Frame Name} +- From: {Frame Name} | Trigger: {element} → To: {Frame Name} +- From: {Frame Name} | Trigger: Cancel → To: {Frame Name} +- From: {Frame Name} | Trigger: {error} → To: {Frame Name} ``` -### 2. Invoke `/figma-use` skill -Mandatory before calling `use_figma` for prototype wiring. +### 2. Add connections via use_figma +Load `skill://figma/figma-use/SKILL.md` before calling `use_figma`. -### 3. Add connections via `use_figma` -For each connection in the map, use `use_figma` to: -- Select the trigger element on the source frame (button, link, overlay area) -- Add a prototype interaction: `On click → Navigate to → {target frame node ID}` -- For overlays/modals: use `Open overlay` instead of Navigate -- For back/cancel flows: use `Navigate to` with `Back` or the specific target frame +This is the only place `use_figma` is called — for adding prototype connections only, +not for building frames. -Wire all connections in one `use_figma` call per scenario where possible. +For each connection: +- Select the trigger element on the source frame by node ID +- Add interaction: `On click → Navigate to → {target frame node ID}` +- Overlays / modals: use `Open overlay` instead of Navigate +- Back / cancel flows: `Navigate to` with the specific target frame node ID -### 4. Set starting frames per scenario -For each scenario, mark the first frame as the **starting point** of that named flow in Figma: -- Use `use_figma` to set the flow start frame with the scenario name as the flow label -- This creates named flows in Figma's prototype panel (e.g. "Invite a new team member") +### 3. Set flow start frames +For each scenario, mark the first frame as the starting point in Figma's prototype panel, +labelled with the scenario name. -### 5. Collect wiring results -For each scenario, confirm: -- All connections were added successfully -- Flow start frame is set -- Note any connections that could not be wired (e.g. element not found in frame) for manual follow-up +### 4. Record wiring results +Note any connections that could not be wired for manual follow-up. --- -## Phase 4: Design Review +## Phase 4: Evaluator Loop (skip in brainstorm mode) -For each created frame, run the design review checklist from `.claude/skills/design-review/checklist.md`. Specifically check: +For each frame, spawn an evaluator agent: -**Content & Copy:** -- [ ] Sentence case on all buttons -- [ ] No SQL, schema names, or code visible to non-admin users -- [ ] No internal acronyms (RAG, blast radius) in UI copy -- [ ] Placeholder text uses "e.g." not "Choose" +``` +You are reviewing a Figma frame against Dalgo's design standards. -**NGO User Specific:** -- [ ] Screen purpose clear in 5 seconds -- [ ] Creation flows are user-task-first (not data-model-first) -- [ ] Admin-only fields are visually locked (lock icon) +Frame: {frame_name} (node_id: {node_id}) +Screenshot: {screenshot_url} -**Blast Radius (if applicable):** -- [ ] "Used By" count shown if entity has dependents -- [ ] Edit modal shows amber impact banner if entity is shared -- [ ] Delete confirmation lists dependents as links when Used By > 0 +Review checklist: +{full content of checklist.md} -**Status & KPI (if applicable):** -- [ ] RAG color is dominant visual signal (not just a text label) -- [ ] Trend language is unambiguous +Constitution (must never be violated): +{full content of .claude/constitution.md} -Produce a review table: +Check the screenshot against every item. Return this exact JSON: +{ + "frame_name": "string", + "passed": true or false, + "failures": [ + {"item": "checklist item text", "issue": "what is wrong", "fix": "specific correction"} + ] +} +``` +**If failures:** Send the failure list back to the frame agent: ``` -| Screen | Node ID | Issues | Status | -|--------|---------|--------|--------| -| User management table | 11900:63 | None | ✅ Ready | -| Invite user modal | 11901:63 | "Admin" label → "Full access" | ⚠ Fix needed | +Your frame "{frame_name}" failed design review. Fix these issues and regenerate the frame, +then return the updated JSON schema. + +Failures: +{failures list} ``` +**Retry limit:** 3 rounds per frame. If still failing after round 3, mark the frame +`[REVIEW FAILED — manual fix needed]` and continue. + +--- + +## Phase 3.9: Consistency Spot-Check (draft and ship mode only) + +After all frames pass (or exhaust retries), spawn one consistency agent: + +``` +Take a screenshot of each frame listed below and compare them for visual consistency. + +Frames: {list of node_ids and frame names} + +Check for drift in: +- Sidebar (width, active state style) +- Header (height, element positions) +- Page padding (should be --spacing-page-x 32px horizontal, --spacing-page-y 28px vertical) +- Primary CTA button (colour, style) +- Typography (heading sizes and weights) + +Return: +{"consistent": true} +— or — +{"consistent": false, "issues": [{"frames": ["S1 · ...", "S2 · ..."], "property": "...", "description": "..."}]} +``` + +If drift is found, note it for the designer in Phase 5. Do not auto-fix. + --- ## Phase 5: Designer Sign-off -Present the review results and ask: +### Brainstorm mode +``` +Layout directions ready: features/{feature_name}/{version}/layout-directions.md + +{N} surfaces, {total} variants +Review and tell me which variants to build, then re-run without --brainstorm. ``` -Designs are ready on the "{page name}" Figma page. -Review summary: -{review table} +### Draft / Ship mode +``` +Designs ready — "Designs" page in Figma. + +| Frame | Node ID | Status | +|-------|---------|--------| +| S1 · {Screen Name} | ... | ✅ Passed | +| S2 · {Screen Name} | ... | ⚠ [NEEDS CLARIFICATION] — {what was assumed} | +| S3 · {Screen Name} | ... | 🔴 [REVIEW FAILED] — fix manually | -{if issues exist} -{N} screen(s) need attention before I update the spec: -- {Screen}: {issue} → Suggested fix: {fix from patterns.md} +{if consistency issues} +Consistency issues flagged: +- S1 and S3: page padding differs Options: -1. Fix the issues and regenerate the flagged frames -2. Accept as-is and note the issues in the spec for engineering to handle -3. Revise a specific frame ("redo Screen 2 with X change") +1. Approve — I'll write design.md and engineering can start +2. Revise a frame: "redo S2 with inline role picker, not a modal" +3. Revisit layout directions: "brainstorm S3 again" ``` -Wait for designer response before updating the spec. +Wait for designer response before writing `design.md`. --- -## Phase 6: Update the Spec +## Phase 6: Write design.md (draft and ship mode only) -Append a `## Design` section to the spec file: +Write `features/{feature_name}/{version}/design.md`: ```markdown -## Design +# {Feature Name} — Design ({Version}) -**Status:** {Ready for engineering / Pending fixes} -**Figma page:** {URL to page} +**Status:** Ready for engineering +**Spec:** {spec path} +**Figma:** https://www.figma.com/design/{file_key} **Last updated:** {date} -### Screens +## Screens -| Screen | Node ID | Figma Link | Status | -|--------|---------|-----------|--------| -| {Screen Name} | {node_id} | [View →]({figma_url}) | ✅ Ready | +| Screen | Frame | Node ID | Figma link | Status | +|--------|-------|---------|------------|--------| +| {Screen Name} | S1 · {Screen Name} | {node_id} | [View →](https://www.figma.com/design/{file_key}?node-id={node_id}) | ✅ Ready | -### User Flows & Prototype +## User Flows & Prototype -| Scenario | Starting Frame | Prototype Link | Connections | -|----------|---------------|----------------|-------------| -| {Scenario Name} | {Frame Name} | [Preview →]({figma_prototype_url}) | {N} wired | +| Scenario | Starting frame | Connections wired | +|----------|---------------|-------------------| +| {Scenario Name} | S1 · {Screen Name} | {N} | -### Design Decisions +## Design Decisions -Decisions made during design brief that engineering must implement: -- **Role labels:** Use "Full access / Can edit / Can view only" — not "Admin/Editor/Viewer" -- **Last-admin protection:** Block role change that would remove the last org admin; show inline error -- {other decisions from designer replies} +{All decisions[] collected from frame agents, deduped and attributed to screen} -### Known Issues +## Known Issues -{if any screens were accepted with issues} -- {Screen}: {issue} — to be resolved in v2 +{Any [REVIEW FAILED] or unresolved [NEEDS CLARIFICATION] items} -### Design Checklist +## Figma -- [ ] All screens reviewed against patterns.md -- [ ] NGO user lens applied -- [ ] Blast radius handled where applicable -- [ ] Designer has signed off +file_key: {file_key} +``` + +Then add a one-line pointer in `spec.md` if not already present: +```markdown +**Design:** [v{N}/design.md](v{N}/design.md) ``` --- ## Phase 7: Signal Readiness -Print: - ``` -Design gate complete. Spec updated at {spec path}. +Design gate complete. {N} screens designed → {M} passed review → {K} issues noted -Next: /engineering/plan-feature {spec path} -``` +design.md: features/{feature_name}/{version}/design.md -If there are unresolved issues, print: +Next: /engineering/plan-feature features/{feature_name}/{version}/spec.md +``` +If open issues remain: ``` -⚠ Design gate has {K} open issue(s). Resolve before engineering starts, -or run with known issues and track them in the spec. +⚠ {K} issue(s) need manual attention before engineering starts. +See design.md → Known Issues. -Next: /engineering/plan-feature {spec path} +Next: /engineering/plan-feature features/{feature_name}/{version}/spec.md ``` --- ## Quality Checklist -- [ ] Designer was given a brief and responded before any generation started (unless `--auto`) -- [ ] Every screen has a node ID and was verified with a screenshot -- [ ] User flows extracted for every surface (entry points, happy path, branches, exits) -- [ ] Screens grouped into named scenarios -- [ ] Prototype connections wired for all scenarios -- [ ] Named flow start frames set per scenario in Figma prototype panel -- [ ] Design review was run against the updated checklist -- [ ] Designer signed off (or explicitly accepted issues) -- [ ] Spec `## Design` section is complete with node IDs, scenario table, decisions, and known issues +- [ ] Mode resolved (brainstorm / draft / ship) +- [ ] Constitution loaded before any agent was spawned +- [ ] Figma file key resolved — or skipped for brainstorm mode +- [ ] Design brief written or read +- [ ] Layout directions written to `layout-directions.md` +- [ ] Brainstorm mode stopped after layout-directions.md +- [ ] Scout ran and returned canvas state and `next_x` (draft/ship only) +- [ ] Frame agents used `figma-generate-design` skill — not raw use_figma JS +- [ ] All frame agents returned the fixed JSON schema (draft/ship only) +- [ ] Evaluator loop ran for every frame (draft and ship modes) +- [ ] Consistency spot-check ran across all final frames (draft/ship only) +- [ ] Designer signed off +- [ ] `design.md` written with node IDs, decisions, known issues, and `file_key` (draft/ship only) +- [ ] `spec.md` updated with one-line pointer to `design.md` - [ ] Next step printed clearly diff --git a/.claude/constitution.md b/.claude/constitution.md new file mode 100644 index 0000000..f676520 --- /dev/null +++ b/.claude/constitution.md @@ -0,0 +1,50 @@ +# Dalgo — Product Constitution + +Non-negotiables. Every design and engineering decision must honour these. +Files that restate these rules should reference this file instead. + +Referenced by: `.claude/skills/design-review/patterns.md` · `.claude/skills/design-review/checklist.md` +· `.claude/commands/design/design-handoff.md` · `.claude/commands/engineering/plan-feature.md` + +--- + +## Users + +Dalgo users are non-technical program managers, data coordinators, and field staff at small NGOs. +Assume: +- No SQL knowledge, no data engineering background +- Slow internet (2G/3G common in the field) +- Old devices (budget Android phones and laptops from 2018–2020) +- Small teams — often one person manages the whole data workflow +- English may not be their first language + +## Data Access Rules + +- **Never expose SQL** to non-admin users — not in UI copy, not in error messages, not in logs +- **Never expose schema or table names** (e.g. `public.beneficiary_data`) to non-admin users +- **Never expose internal code or stack traces** in user-facing messages +- Admin-only fields must be **visually locked**: lock icon + "Contact your admin to change this" + +## Language & Copy + +- **Sentence case on all interactive labels** — "Create pipeline" not "CREATE PIPELINE" +- **No internal acronyms** in user-facing copy — not RAG, blast radius, dbt, ELT, ETL +- **No jargon** without a plain-English explanation alongside it +- Error messages must state **what went wrong AND what to do next** +- Success messages must be **specific** ("Report sent to 3 recipients" not "Done") +- Loading states must be **specific** ("Syncing data..." not "Loading...") + +## Design Standards + +- Performance over visual richness — every added element must earn its place +- Progressive disclosure — hide advanced options until the user needs them +- Smart defaults — minimise required user input +- Every screen must communicate its purpose within 5 seconds +- Every destructive action requires explicit confirmation +- Every empty state must have a message and a primary action CTA +- Multi-tenant — users must only ever see their own organisation's data + +## Platform + +- Open source (AGPL-3.0) — no proprietary dependencies without team approval +- ~20 partner NGOs, budget-constrained — no features requiring expensive infrastructure diff --git a/.claude/skills/design-review/patterns.md b/.claude/skills/design-review/patterns.md index b7107a7..a384e51 100644 --- a/.claude/skills/design-review/patterns.md +++ b/.claude/skills/design-review/patterns.md @@ -1,51 +1,142 @@ # Dalgo UI Patterns Reference -Established patterns used across the Dalgo platform. Use these as the baseline when reviewing new UI work. +**Source of truth:** [`DalgoT4D/dalgo-design-system`](https://github.com/DalgoT4D/dalgo-design-system) +— `tokens.css` (design tokens) + `components.css` (component specs, all values reference `var(--*)`). + +This file maps design-system tokens → Tailwind / Shadcn class names for `webapp_v2`. +Do not hardcode hex values or pixel sizes — reference CSS variables or the mappings below. + +--- ## Component Library - **Primary library:** Shadcn UI (Radix UI headless primitives with custom styling) - **Components location:** `webapp_v2/components/ui/` -- **Icon library:** Lucide icons, 16px (h-4 w-4) - -## Color Palette +- **Icon library:** Lucide (size is context-specific — see Icon Sizes section) + +--- + +## Design Tokens → Tailwind / Shadcn Mapping + +### Colors + +| Design system token | Value | Tailwind / Shadcn equivalent | Role | +|---------------------|-------|------------------------------|------| +| `--color-brand-primary` | `#00897b` | `style={{ backgroundColor: 'var(--color-brand-primary)' }}` | CTAs, active states, brand | +| `--color-brand-primary-hover` | `#00796b` | hover on brand elements | Button hover | +| `--color-brand-primary-light` | `#e8f4f3` | — | Light teal backgrounds | +| `--color-text-primary` | `#1a1a2e` | `text-foreground` | Main body text | +| `--color-text-secondary` | `#5c5c6d` | `text-muted-foreground` | Subtext, descriptions, subtitles | +| `--color-text-tertiary` | `#7a7a8c` | — | De-emphasised labels | +| `--color-text-placeholder` | `#b0b0be` | `placeholder:text-muted-foreground` | Input placeholders | +| `--color-bg` | `#f8fafb` | `bg-background` | Page background | +| `--color-surface` | `#ffffff` | `bg-white` / `bg-card` | Cards, panels, header | +| `--color-surface-hover` | `#f5f7f8` | `hover:bg-muted` | Table row hover | +| `--color-border` | `#e8ecef` | `border` | Default borders | +| `--color-row-divider` | `#f1f5f9` | — | Between table rows | +| `--color-alert` | `#ef5350` | `text-destructive` | Errors, delete actions | + +**Rule:** Never hardcode hex values. Reference `var(--color-*)` directly, or the Tailwind class equivalents above. + +**Missing (not yet in `tokens.css`):** RAG status colors — on-track, at-risk, off-track, stale. +These need to be added to the design system repo. Use the design-system token names when they land: +`--color-status-on-track`, `--color-status-at-risk`, `--color-status-off-track`, `--color-status-stale`. + +### Typography + +| Design system token | Value | Tailwind equivalent | Usage | +|---------------------|-------|---------------------|-------| +| `--font-size-3xl` | 32px | `text-3xl` | Page headings | +| `--font-size-2xl` | 28px | `text-[28px]` | Large modal titles | +| `--font-size-xl` | 26px | `text-[26px]` | Section headings | +| `--font-size-lg` | 22px | `text-[22px]` | Card titles | +| `--font-size-md` | 16px | `text-base` | Form labels, body text | +| `--font-size-base` | 15px | `text-[15px]` | Standard body | +| `--font-size-sm` | 14px | `text-sm` | Table cells, hints, secondary text | +| `--font-size-xs` | 13px | `text-xs` | Badges, timestamps, metadata | + +| Design system token | Value | Usage | +|---------------------|-------|-------| +| `--font-weight-regular` | 400 | Body text | +| `--font-weight-medium` | 500 | Labels, nav items | +| `--font-weight-semibold` | 600 | Section headings | +| `--font-weight-bold` | 700 | Page headings | +| `--line-height-normal` | 1.5 | Default body | +| `--letter-spacing-button` | 0.3px | Button labels | + +**Font:** `--font-sans: 'Anek Latin'` — set globally, never per-component. +Requires: `` + +### Spacing + +The spacing scale is **not Tailwind's standard 4px multiples** — use the named semantic aliases: + +| Semantic alias | Resolves to | Value | Use | +|----------------|-------------|-------|-----| +| `--spacing-page-x` | `--space-12` | 32px | Horizontal page padding | +| `--spacing-page-y` | `--space-11` | 28px | Vertical page padding | +| `--spacing-section` | `--space-11` | 28px | Between major sections | +| `--spacing-form-group` | `--space-10` | 24px | Between form field groups | +| `--spacing-label` | `--space-3` | 8px | Label → input gap | +| `--spacing-header-gap` | `--space-7` | 16px | Elements inside the header | +| `--spacing-tag-gap` | `--space-2` | 6px | Between tags | +| `--spacing-frequency-gap` | `--space-3` | 8px | Between frequency buttons | + +Raw scale (partial): `--space-3`=8px · `--space-5`=12px · `--space-7`=16px · `--space-9`=20px · `--space-10`=24px · `--space-11`=28px · `--space-12`=32px · `--space-13`=40px · `--space-14`=48px + +### Border Radius | Token | Value | Usage | |-------|-------|-------| -| `--primary` | `#00897B` (teal) | CTAs, active states, brand elements | -| `text-muted-foreground` | (from Shadcn) | Subtext, descriptions, subtitles | -| `text-destructive` | (from Shadcn) | Delete buttons, error states | -| `bg-background` | (from Shadcn) | Page/section backgrounds | -| `text-foreground` | (from Shadcn) | Main text color | +| `--radius-sm` | 6px | Inputs, tags, small chips | +| `--radius-md` | 8px | Cards, modals, buttons | +| `--radius-lg` | 12px | Large panels | +| `--radius-full` | 50% | Avatars, circular badges | -**Rule:** Never hardcode hex values. Always use CSS variables or Tailwind theme classes. +### Layout Dimensions -## Typography +| Token | Value | Usage | +|-------|-------|-------| +| `--sidebar-width` | 224px | Left navigation rail | +| `--header-height` | 60px | Top bar | + +### Shadows -| Class | Usage | +| Token | Usage | |-------|-------| -| `text-3xl font-bold` | Page headings (Charts, Pipelines, etc.) | -| `text-xl font-semibold` | Section headings, card titles, modal titles | -| `text-base` | Form labels, body text | -| `text-sm` | Table cells, form hints, secondary text | -| `text-xs` | Badges, timestamps, metadata | +| `--shadow-modal` | Dialog / modal overlay | +| `--shadow-dropdown` | Dropdown menus, popovers | +| `--shadow-focus` | Focus ring on interactive elements (3px, brand-primary-ring) | -**Font:** Anek Latin via `var(--font-anek-latin)`, set globally. Never set per-component. +### Transitions + +| Token | Value | Usage | +|-------|-------|-------| +| `--transition-fast` | 0.10s ease | Hover on small elements | +| `--transition-normal` | 0.15s ease | Most interactive states | +| `--transition-slow` | 0.25s ease | Page-level transitions | + +--- ## Page Layout Pattern -All list/index pages follow this structure: +All list / index pages follow this structure: ``` -Fixed header (border-bottom, bg-background) - ├── Title (text-3xl font-bold) + Subheading (text-muted-foreground) +Fixed header (border-bottom, bg: --color-surface, height: --header-height 60px) + ├── Title (--font-size-3xl / --font-weight-bold / --color-text-primary) + ├── Subheading (--font-size-sm / --color-text-secondary) └── Optional CTA button (top-right) + Scrollable content area + padding: --spacing-page-y (28px) --spacing-page-x (32px) └── Tables, cards, lists, etc. ``` **Reference:** `app/charts/page.tsx`, `components/pipeline/pipeline-list.tsx` +--- + ## Button Patterns ### Primary CTA @@ -53,10 +144,10 @@ Scrollable content area ``` @@ -80,66 +171,95 @@ Scrollable content area ``` +**Note:** `components.css` applies `text-transform: uppercase` on `.btn`. In `webapp_v2` (Tailwind/Shadcn) +this is not inherited; follow the label casing of existing Shadcn buttons in the codebase. +This is an open conflict — see the constitution once it is written. + +--- + +## Icon Sizes + +Sizes are context-specific — do not apply a blanket 16px rule: + +| Context | Size | Tailwind class | +|---------|------|----------------| +| Button icons | 16px | `h-4 w-4` | +| Nav rail icons | 18px | `h-[18px] w-[18px]` | +| Table action icons | 14px | `h-3.5 w-3.5` | +| Empty state illustration | 40–48px | `h-10 w-10` or `h-12 w-12` | + +--- + ## Dialog / Modal Pattern - Component: Shadcn `Dialog` +- Shadow: `--shadow-modal` - Max width: `sm:max-w-md` for forms -- Mobile: Should use bottom sheets for complex forms +- Mobile: bottom sheets for complex forms - Reference: `components/reports/share-via-email-dialog.tsx` +--- + ## Form Patterns - **Complex forms:** React Hook Form (uncontrolled) - **Simple inputs:** useState (controlled) +- **Label gap:** `--spacing-label` (8px) between label and input - **Labels:** `