diff --git a/skills/wireframe/SKILL.md b/skills/wireframe/SKILL.md index 26d2a41..78239c8 100644 --- a/skills/wireframe/SKILL.md +++ b/skills/wireframe/SKILL.md @@ -11,22 +11,35 @@ description: > # Wireframe Specification Generator -Generate a `WIREFRAMES.md` that answers one question: **"What does the user see and interact with?"** +Generate a `WIREFRAMES.md` that answers one question: **"What does the user +see and interact with?"** -Two agents consume this file downstream: -- The **wireframe-reviewer** agent checks it immediately after generation — screen coverage, - token validity, component references, internal consistency, and AC traceability (score >= 7 to proceed). -- The **acceptance-reviewer** agent greps design tokens against component templates during - implementation review. Keep the token format exact — the reviewer matches tokens literally. +**`wireframe-reviewer`** consumes this file immediately after generation — +screen coverage, token validity, component references, internal consistency, +AC traceability (score ≥ 7 to proceed). **Token format must be exact** — it +matches tokens literally. -Every line should help someone build or verify the UI. Architecture, data flow, file lists, -and accessibility belong in PLAN.md and TASKS.md, not here. +Its real audience after that is the implementer. No pre-PR agent greps this +spec against the shipped component templates, so wireframe-vs-implementation +fidelity rests on whoever builds the screen and on the specs they write. Make +the file precise enough for a human to diff by eye. + +Every line should help someone build or verify the UI. Architecture, data +flow, file lists, and accessibility belong in PLAN.md and TASKS.md, not +here. + +**Reference example:** `references/issue-board.md` for format +inspiration (note: that file includes extra sections like Visual Narrative +and Bold Choices that are useful for design documentation but are NOT part +of the wireframe spec format). ## Prerequisites -Before generating wireframes, familiarize yourself with the project's design system — read the -CSS/utility framework config (e.g., Tailwind config, UnoCSS config, CSS variables file, theme -file) to understand available tokens and naming conventions. +Before generating wireframes, load the project's token vocabulary. If the +project provides a design-system skill (e.g. `/-design-system`), +invoke it. Otherwise read the CSS/utility framework config (Tailwind config, +UnoCSS config, CSS variables file, theme file) to learn the available tokens +and naming conventions. ## Workflow @@ -37,251 +50,129 @@ Use the canonical algorithm in then read `PLAN.md`. Extract the frontend PR file list, acceptance criteria, and scope. +#### 1a: Zero-UI-scope guard (mandatory) + +Before designing anything, check whether this plan has UI scope at all. +Look at PLAN.md for: + +- Frontend file list under "Approach" (any `.vue`, `frontend/src/**`, components/pages/modals) +- Wireframes section that explicitly says "No frontend changes — wireframes not applicable" +- Acceptance criteria that mention user-visible behaviour (page, button, modal, toast, list) + +If **none** of those are present, this plan is backend-only. Output one +line and stop: + +> "PLAN.md has no UI scope (no frontend files, AC are backend-only). WIREFRAMES.md isn't needed for this feature. Skipping." + +Don't invent screens to fill the gap. If the developer thinks there should +be UI and the plan says otherwise, the right fix is to update PLAN.md, not +to extrapolate here. + +#### 1b: Resume vs. restart + +Check whether `WIREFRAMES.md` already exists in the plan directory. + +- **Doesn't exist** — proceed normally to Step 2. +- **Exists with substantive content** (≥ 1 screen with ASCII wireframe + spec + interactions) — enter **resume mode**: read the existing file, identify which screens are still skeletal or missing, and design only those. Don't overwrite work the developer or a previous run already produced. +- **Exists but empty / placeholder-only** — treat as "doesn't exist" and proceed normally. + +In resume mode, present a one-line summary of what you found +("WIREFRAMES.md has 2 screens covered, 1 placeholder, 1 missing — filling +the placeholder and the missing one") before continuing. + ### Step 2: Research existing layout patterns Find the closest existing screens in the codebase for each planned page: 1. Glob for page/view components in the project's source directories + (overview/list pages, detail/show pages, settings pages, confirm and + form modals) 2. Read 2-3 reference components — note their tokens, layout composition, and action patterns 3. Check shared/common component directories for reusable components to reference (not reinvent) Also check during research: -- **Icons**: What icon components/assets exist? If new icons are needed, flag them as - "needs creation" in the wireframe. -- **Badge/tag patterns**: If the feature needs status badges, check what badge component - exists and what variants/props it supports. Use existing patterns rather than inventing - new color combinations. -- **State management patterns**: How do existing pages fetch and manage data? Some areas - may use stores, others inline API calls. Match the existing pattern for the area you're - wireframing. + +- **Icons** — Glob for the project's icon components. If you'll need icons that don't exist, flag them as "needs creation" in the wireframe with the correct location. +- **Badge/tag patterns** — If the feature needs status badges, read the existing badge component and use its supported variants / colour classes rather than inventing new colour combinations. +- **State management patterns** — How do existing pages fetch and manage data? Some areas may use stores, others inline API calls. Match the existing pattern for the area you're wireframing, and name it the way that area does. ### Step 3: Design interrogation -Before writing the spec, ask the developer design questions using `AskUserQuestion`. The plan -decided *what* to build — you now decide *how it looks and behaves*. This is where visual and -interaction design decisions get made. +Before writing the spec, ask the developer design questions using +`AskUserQuestion`. The plan decided *what* to build — you now decide *how +it looks and behaves*. This is where visual and interaction design +decisions get made. #### What to present first -Show the developer what you found in Step 2 — the reference pages, their layout patterns, and -what you'd reuse. Ground every question in the codebase: +Show the developer what you found in Step 2 — the reference pages, their +layout patterns, and what you'd reuse. Ground every question in the +codebase: -> "The closest existing page is the settings detail page — section cards with accent-border -> titles and detail rows. I'd follow that pattern for the new details section. -> Does that match what you have in mind, or should this page feel different?" +> "The closest existing page is the settings detail page — section cards with accent-border titles and detail rows. I'd follow that pattern for the new details section. Does that match what you have in mind, or should this page feel different?" #### What to ask about -For each screen, ask about design choices that aren't obvious from the plan: +For each screen, ask about design choices that aren't obvious from the +plan: -- **Page structure** — "Should this be one scrollable page with sections, or a tabbed layout?" -- **Information hierarchy** — "This page needs to show plan, usage, and history. What's most - important? Should key metrics be promoted as stat boxes, or is a detail list enough?" -- **New patterns** — "This feature needs a progress bar — that doesn't exist in the app yet. - Should it be a thin inline bar or a standalone component with a percentage label?" -- **Action placement** — "Should 'Manage' be a button in the section header or a separate card?" -- **Empty states** — "What does this page show when there's no data yet?" +- **Page structure** — "Should this be one scrollable page with sections, or tabs like the users domain (Active / Deleted / 2FA)?" +- **Information hierarchy** — "The billing page needs to show plan, seats, billing date, invoices. What's most important? Should plan + seats be promoted as stat boxes at the top, or is a simple detail list enough?" +- **New patterns** — "This feature needs a progress bar for seat usage — that doesn't exist in the app yet. Should it be a thin inline bar under the stat value, or a standalone component with percentage label?" +- **Upgrade prompts** — "When the user hits a limit, should we show a modal (interrupts flow, forces decision) or an inline banner (visible but doesn't block)?" +- **Action placement** — "Should 'Manage subscription' be a button in the section title bar (like edit buttons on detail pages) or a separate card/section?" +- **Empty states** — "What does the billing page show for a free tenant with no payment history? Just the plan details, or an upgrade-focused layout?" -Use `AskUserQuestion` with concrete options — include ASCII previews when comparing layout -alternatives. Show 2-3 options per question, grounded in what exists in the codebase. +Use `AskUserQuestion` with concrete options — include ASCII previews when +comparing layout alternatives. Show 2-3 options per question, grounded in +what exists in the codebase. #### Rules for design questions - **2-3 rounds max.** This is focused design, not a full product discovery session. -- **Always provide options from the codebase.** Show existing patterns and let the developer - pick — not open-ended "how should we lay it out?" -- **Show, don't describe.** Use ASCII previews to let the developer see the difference. -- **Default to existing patterns.** If the codebase already has an obvious pattern for this - type of page, present it as the recommended option. Only ask when there's a genuine choice. -- **Skip questions the plan already answered.** If the plan specifies layout decisions, don't - re-ask them. +- **Always provide options from the codebase.** "The users page uses tabs. The project detail uses a single scrolling page. Which pattern fits billing?" — not "how should we lay it out?" +- **Show, don't describe.** Use ASCII previews in `AskUserQuestion` options to let the developer see the difference between layouts. +- **Default to existing patterns.** If the codebase already has an obvious pattern for this type of page, present it as the recommended option. Only ask when there's a genuine choice. +- **Skip questions the plan already answered.** If the plan says "dedicated billing page in sidebar" — don't ask "should this be a page or a modal?" #### When to skip interrogation Skip design questions entirely when: + - The feature only modifies existing screens (adding a column, a section, a button) - Every screen directly mirrors an existing page pattern with no new components - The developer explicitly says "just follow existing patterns, don't ask" ### Step 4: Write WIREFRAMES.md -Save to the plan directory. The document has **5 sections** — no more. +Save to the plan directory. The document has **exactly 5 sections** — +ASCII Wireframes, Component Breakdown, Shared Components, Screen +Specifications, Interaction Specification. The full template, per-section +rules, token tables, and "what does NOT belong" list live in +[`references/wireframes-template.md`](references/wireframes-template.md). +Load it before writing — `wireframe-reviewer` parses the section names +literally, so the structure has to match exactly. ---- - -## Document Structure - -```markdown -# Feature: Wireframe Specifications - -What the user sees and interacts with. Design tokens throughout. -The acceptance-reviewer checks implementations against these specs. - ---- - -## 1. ASCII Wireframes -## 2. Component Breakdown -## 3. Shared Components -## 4. Screen Specifications -## 5. Interaction Specification -``` +### Step 5: Self-gate with wireframe-reviewer -### What does NOT belong in WIREFRAMES.md +Once `WIREFRAMES.md` is saved, spawn the `wireframe-reviewer` agent with +the plan directory path. It scores 1-10 on screen coverage, token +validity, component references, internal consistency, and AC +traceability. -These are covered by other documents — don't duplicate them here: +If the score is < 7: address the findings, re-run the reviewer, repeat +until it passes. Do not hand off below the threshold. -| Concern | Where it lives | NOT here | -|---------|---------------|----------| -| Data requirements (stores, API calls) | TASKS.md task context | Wireframe names store methods inline but doesn't spec data flow | -| File references (new/modified files) | PLAN.md PR sections | Wireframe names component files but doesn't list all files | -| Acceptance criteria coverage | PLAN.md AC table | Wireframe implements ACs but doesn't re-list them | -| Accessibility | Implementing agent follows existing page patterns | Not a wireframe concern | -| Responsive behavior | A note per screen ("mobile: stack vertically") | Not a separate section | - ---- - -### Section 1: ASCII Wireframes - -Draw ASCII art for each major screen — desktop layout, and mobile only when the layout -changes significantly. This communicates spatial arrangement faster than any other format. - -``` -┌──────────────────────────────────────────────────────┐ -│ SIDEBAR │ MAIN CONTENT │ -│ │ │ -│ Logo │ ┌────────────────────────────┐ │ -│ │ │ BANNER │ │ -│ Nav Item 1 │ └────────────────────────────┘ │ -│ Nav Item 2 │ │ -│ ───────── │ ┌──────┐ ┌──────┐ ┌──────┐ │ -│ Nav Item 3 │ │Stat │ │Stat │ │Stat │ │ -│ ▶ New Page ←NEW │ └──────┘ └──────┘ └──────┘ │ -│ │ │ -│ │ ┌────────────────────────────┐ │ -│ │ │ ▎ Section Card │ │ -│ │ │ Label Value │ │ -│ │ └────────────────────────────┘ │ -└──────────────────────────────────────────────────────┘ -``` - -Rules: -- Box-drawing characters (`┌─┐│└─┘`) -- Label every region -- Include navigation context (sidebar, header, tabs) -- Mark new elements with `← NEW` -- Draw modals with backdrop (`░░░`) and centered box -- One ASCII per distinct layout (don't draw variations separately if they share layout) - -### Section 2: Component Breakdown - -One table mapping every UI region to its component, data source, and what happens on interaction. -This is the implementing agent's primary lookup. - -```markdown -| Region | Component | Data Source | Interactivity | -|--------|-----------|-------------|---------------| -| Banner | `UpgradeBanner` | `store.status.plan` | Click → `createSession()` → redirect | -| Details card | inline in `Overview.vue` | `store.status` | "Manage" → `openPortal()` | -``` - -Rules: -- One row per visual block (not per HTML element) -- Name the exact store property or method -- Interactivity says what happens, not just "clickable" -- For existing shared components, reference by name - -### Section 3: Shared Components - -New components created for this feature that appear in multiple screens. Each gets: -props, emits, variant table (if applicable), and token structure. - -**Component placement rule**: If a component is used by multiple app areas or modules, it -should live in the project's shared/common component directory. Check existing architecture -conventions for placement rules. - -```markdown -### StatusBadge - -**Component:** `StatusBadge.vue` -**Props:** `status: 'active' | 'inactive' | 'pending'` - -| Status | Classes | Label | -|--------|---------|-------| -| active | `bg-success-subtle text-success` | Active | -| pending | `bg-warning-subtle text-warning` | Pending | - -Structure: wraps existing badge component with appropriate variant props. -``` - -### Section 4: Screen Specifications - -Per-screen specs with design tokens. This is what the acceptance-reviewer greps against -component templates. Every visual property uses exact tokens from the project's design system. - -```markdown -### Screen: Overview — Default State - -**Component:** `Overview.vue` -**Route:** `/settings/overview` -**Access:** Admin-only (route guard) -**Store:** `store.fetchStatus()`, `store.createSession()` -**Mobile note:** Stat boxes stack vertically, banner button goes full-width - -#### Layout - -1. **Banner** — `v-if="status.plan === 'free'"` -2. **Stat row** — `flex gap-4 mb-5` - - **Box** — `flex-1 bg-surface border border-default rounded-md p-4` - - Label: `text-xs font-medium text-muted uppercase tracking-wide mb-1` - - Value: `text-base font-semibold` -``` - -Token precision — every visual property must use the project's exact design tokens: - -| Property | Use | Never use | -|----------|-----|-----------| -| Background | Named token (`bg-surface`, `bg-surface-raised`) | Hex values (`#1c1c22`) | -| Text color | Named token (`text-default`, `text-muted`) | `color: var(--text)` | -| Border | Token syntax (`border border-default`) | Raw CSS (`border: 1px solid #363640`) | -| Radius | Token (`rounded-md`, `rounded-lg`) | Raw CSS (`border-radius: 10px`) | -| Font size | Named token (`text-xs`, `text-sm`, `text-base`) | Raw values (`font-size: 13px`) | -| Font weight | Named token (`font-medium`, `font-semibold`) | Raw CSS (`font-weight: bold`) | - -**Always verify tokens against the project's design system config.** The acceptance-reviewer -greps for these exact names in component templates. - -Mobile behavior goes inline as a **Mobile note** on the screen, not in a separate section. - -### Section 5: Interaction Specification - -Every user interaction across all screens. The acceptance-reviewer uses this to verify behavior. - -```markdown -| # | Trigger | Action | Result | -|---|---------|--------|--------| -| 1 | Click "Upgrade" | `store.createSession()` | Redirect to payment page | -| 2 | API returns 402 | HTTP interceptor | Upgrade prompt modal opens | -``` - -Rules: -- One row per distinct interaction -- **Trigger**: what the user does -- **Action**: what code runs (store method, emit, route change) -- **Result**: what the user sees change -- Include error paths (API failures, rate limits) - ---- +When the score is ≥ 7, ask the developer: "Wireframes ready (score X/10). +Proceed to `/task-writer`?" — that's the handoff. `/task-writer` is a +separate skill with its own coverage gate; don't invoke it from inside +this skill. ## Anti-patterns -- **Raw CSS values** — always use design tokens from the project's system -- **Inventing new token names** — use tokens that actually exist in the codebase config -- **Inventing badge/tag styles** — use the existing badge component with its supported variants -- **Shared component in wrong directory** — if multiple areas use it, put it in shared -- **Referencing nonexistent icons** — check existing icons before naming them; flag new ones -- **Undefined stores in interactions** — every store/method in the Interaction table must exist - or be defined in the wireframe -- **Sections beyond the 5** — data flow, file lists, a11y, coverage belong elsewhere -- **Overly long wireframes** — if it's longer than ~250 lines, you're probably duplicating PLAN.md -- **Re-specifying shared components** — reference existing components by name, don't re-describe -- **Inventing new patterns** — copy layout patterns from existing pages +Lessons-from-pain — wireframes that broke a reviewer and forced a rework +— live in [`references/anti-patterns.md`](references/anti-patterns.md). +Skim it once if you've not built a wireframe in this project before; the +list catches the failure modes that the reviewer's scoring penalises +hardest (raw CSS values, undefined stores in interactions, sections +beyond the 5). diff --git a/skills/wireframe/references/anti-patterns.md b/skills/wireframe/references/anti-patterns.md new file mode 100644 index 0000000..1637365 --- /dev/null +++ b/skills/wireframe/references/anti-patterns.md @@ -0,0 +1,16 @@ +# Wireframe anti-patterns + +Lessons-from-pain captured outside SKILL.md so they're loaded once on +demand, not every invocation. Each one represents a wireframe that +shipped, broke `wireframe-reviewer`, and forced a rework. + +- **Raw CSS values** — always design tokens from the project's system. `wireframe-reviewer` validates token names; raw `color:` and `border:` declarations don't match anything and read as "no spec given here." +- **Raw computed sizes** (`text-size-0.82rem`, `font-size: 13px`) — use the project's named tokens (`text-xs`, `text-sm`, `text-base`, ...). The reviewer matches names, not computed sizes. +- **Inventing badge colour classes** — use the existing badge component with its supported variant / tint classes, not custom `bg-success-subtle text-success border-success/30` combos. New colour combinations will fail token validation. +- **Cross-area component in wrong directory** — if two app areas both use it, it goes in the shared directory. The repo's app-boundary architecture test (if it has one) will fail the build otherwise; flagging this in the wireframe saves a CI round-trip. +- **Referencing nonexistent icons** — check existing icons before naming them in specs; flag new ones as "needs creation" with the correct location. The reviewer grep-verifies icon names exist. +- **Undefined stores in interactions** — every store / method in the Interaction table must be defined in the wireframe or confirmed to exist. Areas that use inline API calls instead of stores get named as inline calls, not as a fictional store. +- **Sections beyond the 5** — data flow, file lists, a11y, coverage belong elsewhere. The reviewer counts sections; an extra one causes a structural fail even if the content is fine. +- **470-line wireframes** — if it's longer than ~250 lines, you're probably duplicating PLAN.md. The wireframe is a visual contract, not a re-articulation of the plan. +- **Re-specifying shared components** — reference the existing table or button component by name, don't describe how tables work. Implementing a `` "again" inside the wireframe creates two sources of truth. +- **Inventing new patterns** — copy layout patterns from existing pages. New patterns are the developer's call, made during the Step 3 interrogation, not invented quietly while writing Section 4. diff --git a/skills/wireframe/references/issue-board.md b/skills/wireframe/references/issue-board.md new file mode 100644 index 0000000..6da8032 --- /dev/null +++ b/skills/wireframe/references/issue-board.md @@ -0,0 +1,406 @@ +> **Worked example.** A real board page, written up in full for format inspiration. Issue keys +> appear as `XX-####` — read them as `{{ISSUE_KEY_PREFIX}}-####`. Tokens (`--brand`, `--surface-*`, +> `bg-*`, `text-*`) are the example product's; substitute the repo's own design system. Sections +> beyond the five in `wireframes-template.md` (Creative Provocation, Data Requirements, Responsive +> Behavior, Visual Narrative, Accessibility Notes, Surface Depth Map, Bold Choices Ledger, File +> References) are design-documentation extras, not part of the WIREFRAMES.md spec format. + +# Issue Board — Wireframe Specification + +**Page:** Issue Board (Kanban) +**Route:** `projects.board.overview` +**Purpose:** Active sprint visualization. Issues organized into status-based swim lanes with drag-and-drop reordering. The primary daily workspace for developers tracking their sprint. +**Auth:** Authenticated tenant session. Board is read-only without issue permissions; lane visibility tied to project membership. "Add Issue" gated by `ISSUES.CREATE` permission. "Plan Feature" gated by feature flag + AI key access. +**Primary Action:** Drag an issue card from one lane to another to update its status. + +--- + +## Creative Provocation + +### Personality Test + +If this page walked into a standup meeting, it would be the lead who already has the board open on their laptop, says nothing until someone asks about a blocker, and then gives a three-word answer that resolves it. The board does not explain itself. It shows the state of the sprint and expects you to act on what you see. + +### Opposite Test + +The opposite of this page is a Trello board — rounded corners on everything, pastel lane backgrounds, card covers with stock photos, sticker reactions, and a background image of a sunset. That page wants you to feel good about your work. This page wants you to finish it. + +### Memory Test + +What you remember after closing the tab: horizontal columns of dark cards on a slightly-less-dark background, a red "Add Issue" button in the top-right corner, and the colored dots of issue type icons in the bottom-left of each card. The board reads left-to-right like a pipeline — raw material on the left, finished product on the right. + +--- + +## ASCII Wireframe — Desktop (>768px) + +``` +┌─────────────────────────────────────────────────────────────────────────────────────────┐ +│ SIDEBAR (230px, fixed) │ MAIN CONTENT AREA │ +│ │ │ +│ ┌─────────────────────┐ │ ┌─────────────────────────────────────────────────────────┐ │ +│ │ logo. │ │ │ Board Backlog Issues Epics Reports Settings │ │ +│ │ ▲ red dot │ │ │ ═══╗ │ │ +│ └─────────────────────┘ │ │ ╚══ red underline on active tab │ │ +│ ┌─────────────────────┐ │ └─────────────────────────────────────────────────────────┘ │ +│ │ 📥 Inbox [3] │ │ │ +│ │ 👤 My Issues [7] │ │ ┌─────────────────────────────────────────────────────────┐ │ +│ │ 📁 All Projects │ │ │ FILTER BAR │ │ +│ ├─────────────────────┤ │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────┐ ┌────┐ │ │ +│ │ MY TEAM │ │ │ │🔍 Search │ │Assignees │ │Priorities│ │Types │ │... │ │ │ +│ │ ● Project Alpha │ │ │ └──────────┘ └──────────┘ └──────────┘ └──────┘ └────┘ │ │ +│ │ ● Project Beta │ │ │ [Plan Feature] [+Add]│ │ +│ │ ● Project Gamma │ │ └─────────────────────────────────────────────────────────┘ │ +│ ├─────────────────────┤ │ │ +│ │ WORKSPACE │ │ ┌────────────┐ ┌────────────┐ ┌────────────┐ ┌────────────┐ │ +│ │ Teams │ │ │ Todo [4] │ │In Prog [3] │ │ Review [2] │ │ Done [8] │ │ +│ │ Roles │ │ │ │ │ │ │ │ │ │ │ +│ │ Time Tracking │ │ │ ┌────────┐ │ │ ┌────────┐ │ │ ┌────────┐ │ │ ┌────────┐ │ │ +│ │ Settings │ │ │ │ Title │ │ │ │ Title │ │ │ │ Title │ │ │ │ Title │ │ │ +│ ├─────────────────────┤ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ +│ │ ◀ Collapse │ │ │ │●XX-42 ◐│ │ │ │●XX-15 ◐│ │ │ │●XX-88 ◐│ │ │ │●XX-03 ◐│ │ +│ └─────────────────────┘ │ │ │[Epic] │ │ │ │ │ │ │ │[Epic] │ │ │ │ │ │ │ +│ │ │ └────────┘ │ │ └────────┘ │ │ └────────┘ │ │ └────────┘ │ │ +│ ┌─────────────────────┐ │ │ ┌────────┐ │ │ ┌────────┐ │ │ │ │ ┌────────┐ │ │ +│ │ 🌓 Jane Example │ │ │ │ Title │ │ │ │ Title │ │ │ │ │ │ Title │ │ │ +│ └─────────────────────┘ │ │ │●XX-17 ◐│ │ │ │●XX-29 ◐│ │ │ │ │ │●XX-44 ◐│ │ +│ │ │ └────────┘ │ │ └────────┘ │ │ │ │ └────────┘ │ │ +│ │ └────────────┘ └────────────┘ └────────────┘ └────────────┘ │ +│ │ │ +└─────────────────────────────────────────────────────────────────────────────────────────┘ + +CARD ANATOMY (zoomed): +┌───────────────────────────────┐ +│ Fix login redirect loop │ ← Title (text-base, --text, fw-500) +│ │ +│ 🔴 XX-142 🔀 ▲▲ (●) │ ← Meta row: type icon + key (left), branch + priority + avatar (right) +│ ┌───────────────┐ │ +│ │ Authentication │ │ ← Epic badge (conditional, tint color) +│ └───────────────┘ │ +└───────────────────────────────┘ +``` + +### Legend + +| Symbol | Meaning | +|--------|---------| +| `●` | Issue type icon (colored SVG: red=bug, blue=task, green=feature) | +| `◐` | Profile picture avatar (22px, rounded-full) or unassigned circle | +| `▲▲` | Priority chevrons icon | +| `🔀` | Branch status icon (conditional — only when branch linked) | +| `[3]` | Count badge | +| `═══╗╚══` | Active tab underline (2px `--brand`) | + +--- + +## ASCII Wireframe — Mobile (<768px) + +``` +┌──────────────────────────────────┐ +│ ┌──────────────────────────────┐ │ +│ │ FILTER BAR (collapsed) │ │ +│ │ [▼ Filters] [+Add Issue] │ │ +│ └──────────────────────────────┘ │ +│ │ +│ ← swipe → │ +│ ┌──────────────────────────────┐ │ +│ │ Todo [4] │ │ +│ │ │ │ 85vw per lane +│ │ ┌──────────────────────────┐ │ │ snap-x scrolling +│ │ │ Card title │ │ │ no drag-drop +│ │ │ ● XX-42 ▲▲ (●) │ │ │ +│ │ └──────────────────────────┘ │ │ +│ │ ┌──────────────────────────┐ │ │ +│ │ │ Card title │ │ │ +│ │ │ ● XX-17 ▲▲ (●) │ │ │ +│ │ └──────────────────────────┘ │ │ +│ └──────────────────────────────┘ │ +│ │ +│ ┌──────────────────────────────┐ │ +│ │ Board │ Backlog │ Issues ... │ │ ← fixed bottom tab bar +│ └──────────────────────────────┘ │ +└──────────────────────────────────┘ + +MOBILE FILTER EXPANDED: +┌──────────────────────────────────┐ +│ ┌──────────────────────────────┐ │ +│ │ [▲ Filters ●] [+Add Issue]│ │ ● = red dot when filters active +│ ├──────────────────────────────┤ │ +│ │ 🔍 Search issues... │ │ +│ │ Assignees ▼ │ │ stacked vertically, +│ │ Priorities ▼ │ │ full width +│ │ Types ▼ │ │ +│ │ Epics ▼ │ │ +│ │ Creators ▼ │ │ +│ └──────────────────────────────┘ │ +``` + +--- + +## Component Breakdown + +| Region | Component | Source File | Data Source | Interactivity | +|--------|-----------|------------|-------------|---------------| +| Sidebar | `SidebarLayout` > `Navbar` | `shared/.../SidebarLayout.vue`, `tenant/.../Navbar.vue` | `projectStore`, `teamStore`, `notificationStore`, `userIssueStore` | Click to navigate; collapse/expand toggle; keyboard shortcut | +| Brand header | `AppLogo` | `tenant/.../AppLogo.vue` | Static | Links to `projects.overview` | +| User footer | `SidebarUserFooter` | `shared/.../SidebarUserFooter.vue` | `authService.getLoggedInUser()` | Profile link, theme toggle, logout | +| Tab bar | `MenuTabs` | `shared/.../MenuTabs.vue` | `useProjectTabs()` — 6 tabs, permission/feature-gated | Click to navigate between project views | +| Filter bar | `FilterBar` | `tenant/.../filters/FilterBar.vue` | N/A (slot container) | Mobile: toggle expand/collapse; "Clear all" button when filters active | +| Search filter | `SearchFilter` | `tenant/.../filters/SearchFilter.vue` | `searchTerm` (shared reactive ref) | Type to filter; clear X button | +| Multi-select filters | `MultiSelectFilter` x5 | `tenant/.../filters/MultiSelectFilter.vue` | `selectedAssignees`, `selectedPriorities`, `selectedTypes`, `selectedEpics`, `selectedCreators` | Dropdown with searchable multi-select; count badge overlaps top-right | +| Plan Feature button | `Button` (variant: white) | `shared/.../buttons/Button.vue` | Feature flag `featurePlanner` + `hasAiKeyAccess` | Navigate to feature planner | +| Add Issue button | `Button` (variant: primary) | `shared/.../buttons/Button.vue` | Permission: `ISSUES.CREATE` | Navigate to issue create form | +| Board lanes | `Lane` | `tenant/.../Lane.vue` | `laneStore.getAll` — project-configured status lanes | Container for draggable cards | +| Issue cards | `DragElement` | `tenant/.../issues/DragElement.vue` | `kanbanStore` — filtered + lane-sorted issues | Click title to navigate to issue; drag to change lane/order | +| Drag system | `VueDraggable` | `vue-draggable-plus` | `kanbanStore.data` | Drag ghost: 40% opacity, dashed red border; fires `endDragging` to persist | +| Issue type icon | `IssueTypeIcon` | `tenant/.../IssueTypeIcon.vue` | `issue.type` enum | Visual indicator only | +| Priority icon | `PriorityIcon` | `tenant/.../PriorityIcon.vue` | `issue.priority` enum | Visual indicator only | +| Branch status | `BranchStatusIcons` | `tenant/.../BranchStatusIcons.vue` | `issue.branchLinkStatuses` | Visual indicator with title tooltip | +| Assignee avatar | `ProfilePicture` (size: sm) | `shared/.../ProfilePicture.vue` | `userStore.getById(issue.assigneeId)` | Title tooltip with full name | +| Unassigned icon | Inline `
` + `UserIcon` | — | When `assigneeId` is null | Title: "Unassigned" | +| Epic badge | `EpicBadge` > `SimpleBadge` | `tenant/.../epics/EpicBadge.vue` | `epicStore.getById(issue.epicId)` | Visual indicator with tint color | + +--- + +## Interaction Specification + +| # | Trigger | Action | Result | Loading State | +|---|---------|--------|--------|---------------| +| 1 | **Drag card** to new lane | `VueDraggable` `@end` fires `endDragging()` → `kanbanStore.update()` → `updateBoardForProject()` API call | Card stays in new position; lane counts update reactively | Optimistic — card moves immediately, API call is fire-and-forget unless error | +| 2 | **Drag card** within same lane | Reorder detected by index comparison; same `endDragging` flow | Card order persisted | Same optimistic approach | +| 3 | **Click issue title** | `RouterLink` navigates to `projects.issues.show` with `issue.key` | Issue detail page loads | Route-level suspense fallback ("Loading...") | +| 4 | **Type in search** | `v-model` updates `searchTerm` reactive ref | Board re-filters: `matchesSearch` applied to all issues; lane contents and counts update instantly | None — client-side filtering, no API call | +| 5 | **Select multi-filter option** | Dropdown option toggled in `selected` array | Count badge appears/updates on filter trigger; board re-filters via corresponding `matches*` function | None — client-side filtering | +| 6 | **Click "Clear all"** | `clearAllFilters()` resets all filter refs | All badges disappear; full unfiltered board restores; "Clear all" button hides | None | +| 7 | **Click "Add Issue"** | `navigateToCreateIssue()` → route push to `projects.issues.create` | Issue creation form loads | Route-level suspense | +| 8 | **Click "Plan Feature"** | `navigateToFeaturePlanner()` → route push to `projects.feature-planner.overview` | Feature planner page loads | Route-level suspense | +| 9 | **Mobile: tap "Filters"** | `isExpanded` toggles | Filter panel slides open below bar with vertical stacked filters | None — CSS transition | +| 10 | **Mobile: swipe lanes** | CSS `snap-x snap-mandatory` on lane container | Lanes snap into view one at a time (85vw each) | None — native scroll behavior | +| 11 | **Sidebar collapse** | Toggle button or keyboard shortcut | Sidebar shrinks from 230px to 64px; project items show code abbreviation; 0.2s width transition | CSS transition | + +--- + +## Data Requirements + +### Page Load (parallel `Promise.all`) + +| Store | Method | Returns | Used By | +|-------|--------|---------|---------| +| `laneStore` | `retrieveAll()` | `Lane[]` — id, title, order | Lane headers, lane width calculation | +| `sprintStore` | `retrieveAll()` | `Sprint[]` — id, status | Active sprint detection (filter by `ACTIVE` status) | +| `issueStore` | `retrieveAll()` | `Issue[]` — id, title, key, type, priority, assigneeId, epicId, sprintId, laneId, branchLinkStatuses | Card rendering, filtering, lane assignment | +| `epicStore` | `retrieveAll()` | `Epic[]` — id, title, color | Epic badge on cards | + +### Pre-loaded by Parent (`ProjectLayout.vue`) + +| Store | Returns | +|-------|---------| +| `projectStore` | All projects (needed for sidebar, tab permissions) | +| `userStore` | All users (needed for assignee avatars, filter options) | +| `teamStore` | All teams (needed for sidebar team groups, assignee options) | + +### Computed Filter Options + +| Option Set | Built From | Sorting | +|-----------|-----------|---------| +| `assigneeOptions` | Project members + team members + all users | Alphabetical | +| `priorityOptions` | `PriorityEnum` collection | Enum order | +| `typeOptions` | `TypeEnum` collection | Enum order | +| `epicOptions` | `epicStore.getAll` | Alphabetical | +| `creatorOptions` | Users who created issues in active sprint | Alphabetical | + +### Card Fields + +| Field | Type | Display | +|-------|------|---------| +| `title` | `string` | Card body text — RouterLink | +| `key` | `string` | Bottom-left, uppercase, monospace-weight (`XX-142`) | +| `type` | `TypeEnum` | Colored SVG icon left of key | +| `priority` | `PriorityEnum` | Chevron icon, bottom-right | +| `assigneeId` | `number \| null` | 22px avatar or unassigned circle, bottom-right | +| `epicId` | `number \| null` | Tinted SimpleBadge below meta row (conditional) | +| `branchLinkStatuses` | `BranchLinkStatusEnum[]` | Git status icon, bottom-right (conditional) | + +--- + +## Responsive Behavior + +| Breakpoint | Element | Behavior | +|-----------|---------|----------| +| `>768px` (desktop) | Sidebar | Fixed left, 230px expanded / 64px collapsed, `--surface-1` background | +| `>768px` | Filter bar | Inline horizontal layout — search + 5 multi-selects + action buttons | +| `>768px` | Board lanes | Flex row, `gap-4`, percentage widths (`100 / laneCount`) | +| `>768px` | Drag-drop | Enabled — `VueDraggable` group `drag-board` | +| `>768px` | Tab bar | Top of content area, bottom border, text links | +| `>768px` | "Plan Feature" text | Visible (`lt-md:hidden` removes only on mobile) | +| `<768px` (mobile) | Sidebar | Hidden by default; hamburger opens as overlay (z-50) with backdrop | +| `<768px` | Filter bar | Collapsed to toggle button with filter icon + chevron; expands as panel below | +| `<768px` | Filter active indicator | Red dot (2px, `bg-primary`) on toggle button when filters active and collapsed | +| `<768px` | Board lanes | 85vw per lane, `flex-shrink-0`, `snap-x snap-mandatory` horizontal scroll | +| `<768px` | Drag-drop | Disabled (`isMobile` check) | +| `<768px` | Tab bar | Fixed bottom bar (z-30), `--surface` background, top border instead of bottom, equal-width tabs | +| `<768px` | "Plan Feature" text | Hidden — only sparkle icon visible | +| `<768px` | "Add Issue" text | Shows "Issue" only (no "Add" prefix) | +| `<768px` | Content padding-bottom | Extra 7rem to clear fixed bottom tab bar | + +--- + +## Visual Narrative + +### What This Page Looks Like + +The board is a dark, still surface. The page canvas (`#09090e`) is nearly black — not the blue-black of a night sky but the flat black of a matte desk in a room with one lamp. On top of this darkness sit four (or however many lanes the project defines) vertical columns, each a slightly lighter shade (`#111116`) with softly rounded corners. Inside each column, issue cards float on yet another surface level (`#1c1c22`), bordered by a barely-visible line (`#262630`) and the faintest shadow you have ever seen on a card — a 1px blur at 4% opacity. The overall effect is geological: layers of dark stone, each one fractionally lighter than the one beneath it, with cards sitting like specimens in labeled drawers. + +### Filter Bar + +A single horizontal strip at the top, the same surface tone as the lane backgrounds (`#111116`). The search input is compact (220px) with a dim magnifying glass icon on the left. The multi-select triggers sit in a row, each with its placeholder text in muted gray. When a filter is active, a small red pill (`--brand`, `#c8553a`) floats at the top-right corner of the trigger — offset by -6px so it overlaps the button edge like a notification badge on an app icon. The "Add Issue" button at the far right is the only element on the page with a solid red background. The "Plan Feature" button beside it is more restrained — white variant with a sparkle icon. When active filters exist, a "Clear all" button with a danger-red border appears in the actions area. + +### Board Lanes + +Each lane is a rounded column with a small header area: the lane title in dim text (`#a0a0aa`, semibold) on the left, and a count badge on the right. The count badge sits on `--surface-3` (`#30303b`) — one depth level above the lane itself — with muted text. This badge is compact: 24px tall, minimum 24px wide, centered content. Below the header, cards stack vertically with 6px gaps between them. The lanes divide the available width equally — four lanes each get 25%, three lanes get 33.3%. There is a 16px gap between lanes. The columns do not have visible borders — their rounded-corner `--surface-1` background is enough to separate them from the `--bg` canvas. + +### Issue Cards + +Each card is a compact rectangle — roughly 10px horizontal padding, 14px vertical. The title sits at the top in primary text color (`#e8e8ec`), medium weight, snug leading. Below the title, a meta row splits into two sides. The left side shows the issue type icon (a small colored SVG — red for bugs, blue for tasks, green for features) followed by the issue key in uppercase, wide-tracked, muted text at 11px. The right side clusters three elements tightly: an optional branch status icon (when the issue has a linked git branch), the priority chevron icon, and a 22px circular avatar. When the issue has no assignee, the avatar slot shows a muted circle with a user silhouette icon on a `--surface-1` background. If the issue belongs to an epic, a tinted badge appears on a third row below the meta — a small pill with the epic name in its assigned color. The overall card impression is dense but readable — a business card, not a Post-it note. + +### Drag Ghost + +When you grab a card and begin dragging, the original position shows a ghost: the card at 40% opacity with a dashed red border (`--brand`). The dragged card follows the cursor at full opacity. The animation duration is 150ms — fast enough to feel direct, slow enough to not feel instant. On mobile, dragging is disabled entirely. You swipe between lanes instead. + +--- + +## Accessibility Notes + +### Focus Order + +1. Skip-to-content link (hidden, visible on focus — defined in `theme.css`) +2. Sidebar navigation links (top to bottom: Inbox, My Issues, All Projects, project items, workspace links) +3. Sidebar collapse toggle +4. Tab bar links (Board, Backlog, Issues, Epics, Reports, Settings) +5. Filter bar: Search input, then multi-select triggers left to right, then "Clear all" (if visible), then "Plan Feature" (if visible), then "Add Issue" +6. Board cards: Lane 1 top to bottom, then Lane 2 top to bottom, etc. Each card's RouterLink is the focusable element (stretched-link covers the full card) + +### Keyboard Navigation + +| Key | Context | Action | +|-----|---------|--------| +| `Tab` | Anywhere | Move to next focusable element in focus order | +| `Shift+Tab` | Anywhere | Move to previous focusable element | +| `Enter` / `Space` | Tab link | Navigate to that project view | +| `Enter` / `Space` | Card (RouterLink) | Navigate to issue detail | +| `Enter` / `Space` | Filter toggle (mobile) | Expand/collapse filter panel | +| `Enter` / `Space` | Multi-select trigger | Open dropdown | +| `Escape` | Open dropdown | Close dropdown | + +### ARIA Patterns + +| Element | Attributes | Notes | +|---------|-----------|-------| +| Sidebar | `aria-label="Sidebar"` on `