Implement the 010-ui-baseline feature that establishes a modern UI using Tailwind CSS v4 and shadcn/ui-style components for the encounter screen
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
36
specs/010-ui-baseline/checklists/requirements.md
Normal file
36
specs/010-ui-baseline/checklists/requirements.md
Normal file
@@ -0,0 +1,36 @@
|
||||
# Specification Quality Checklist: UI Baseline
|
||||
|
||||
**Purpose**: Validate specification completeness and quality before proceeding to planning
|
||||
**Created**: 2026-03-05
|
||||
**Feature**: [spec.md](../spec.md)
|
||||
|
||||
## Content Quality
|
||||
|
||||
- [x] No implementation details (languages, frameworks, APIs)
|
||||
- [x] Focused on user value and business needs
|
||||
- [x] Written for non-technical stakeholders
|
||||
- [x] All mandatory sections completed
|
||||
|
||||
## Requirement Completeness
|
||||
|
||||
- [x] No [NEEDS CLARIFICATION] markers remain
|
||||
- [x] Requirements are testable and unambiguous
|
||||
- [x] Success criteria are measurable
|
||||
- [x] Success criteria are technology-agnostic (no implementation details)
|
||||
- [x] All acceptance scenarios are defined
|
||||
- [x] Edge cases are identified
|
||||
- [x] Scope is clearly bounded
|
||||
- [x] Dependencies and assumptions identified
|
||||
|
||||
## Feature Readiness
|
||||
|
||||
- [x] All functional requirements have clear acceptance criteria
|
||||
- [x] User scenarios cover primary flows
|
||||
- [x] Feature meets measurable outcomes defined in Success Criteria
|
||||
- [x] No implementation details leak into specification
|
||||
|
||||
## Notes
|
||||
|
||||
- Technology choices (Tailwind, shadcn/ui) are mentioned only in the Assumptions section as adapter-layer decisions, not in requirements or success criteria.
|
||||
- All 11 functional requirements are testable through visual inspection of the rendered UI.
|
||||
- No clarification markers needed — the feature description was detailed and scope is well-bounded (UI-only, no domain changes).
|
||||
78
specs/010-ui-baseline/contracts/ui-components.md
Normal file
78
specs/010-ui-baseline/contracts/ui-components.md
Normal file
@@ -0,0 +1,78 @@
|
||||
# UI Component Contracts: UI Baseline
|
||||
|
||||
**Feature**: 010-ui-baseline | **Date**: 2026-03-05
|
||||
|
||||
## Layout Contract
|
||||
|
||||
The encounter screen follows a single-column layout with three zones:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────┐
|
||||
│ EncounterHeader │
|
||||
│ Title + Round/Turn status │
|
||||
├─────────────────────────────────┤
|
||||
│ CombatantList │
|
||||
│ ┌─ CombatantRow (active) ───┐ │
|
||||
│ │ Init │ Name │ HP │ Actions│ │
|
||||
│ └───────────────────────────┘ │
|
||||
│ ┌─ CombatantRow ────────────┐ │
|
||||
│ │ Init │ Name │ HP │ Actions│ │
|
||||
│ └───────────────────────────┘ │
|
||||
│ ... (scrollable if overflow) │
|
||||
│ │
|
||||
│ [EmptyState if no combatants] │
|
||||
├─────────────────────────────────┤
|
||||
│ ActionBar │
|
||||
│ [Name input] [Add] [Next Turn] │
|
||||
└─────────────────────────────────┘
|
||||
```
|
||||
|
||||
## CombatantRow Contract
|
||||
|
||||
**Props**:
|
||||
- `combatant: Combatant` — domain entity
|
||||
- `isActive: boolean` — whether this is the active turn
|
||||
- `onRename: (id, newName) => void`
|
||||
- `onSetInitiative: (id, value) => void`
|
||||
- `onRemove: (id) => void`
|
||||
- `onSetHp: (id, maxHp) => void`
|
||||
- `onAdjustHp: (id, delta) => void`
|
||||
|
||||
**Visual contract**:
|
||||
- Row uses consistent column widths across all combatant rows
|
||||
- Active row has visually distinct highlight (accent background or left border)
|
||||
- Name column truncates with ellipsis at max width
|
||||
- Remove action is an icon button (no text label)
|
||||
- All inputs use design system styling (no browser defaults)
|
||||
|
||||
**Interaction contract**:
|
||||
- Click name → enter inline edit mode
|
||||
- Enter/blur in edit mode → commit change
|
||||
- Escape in edit mode → cancel
|
||||
- Initiative input is always visible (not click-to-edit), direct typing only
|
||||
- HP inputs are direct-entry text fields with numeric keyboard (`inputmode="numeric"`)
|
||||
- All numeric inputs: no browser spinners, ch-based widths (`6ch`), tabular numerals, centered text
|
||||
|
||||
## ActionBar Contract
|
||||
|
||||
**Props**:
|
||||
- `onAddCombatant: (name: string) => void`
|
||||
- `onAdvanceTurn: () => void`
|
||||
|
||||
**Visual contract**:
|
||||
- Visually separated from combatant list (spacing, background, or border)
|
||||
- Add form: text input + submit button in a row
|
||||
- Next Turn: distinct button, visually secondary to Add
|
||||
|
||||
**Interaction contract**:
|
||||
- Enter in name input → add combatant + clear input
|
||||
- Empty name → no action (button may be disabled or form simply ignores)
|
||||
|
||||
## EmptyState Contract
|
||||
|
||||
**Displayed when**: `encounter.combatants.length === 0`
|
||||
|
||||
**Visual contract**:
|
||||
- Centered message in the combatant list area
|
||||
- Muted/secondary text color
|
||||
- Suggests adding a combatant
|
||||
75
specs/010-ui-baseline/data-model.md
Normal file
75
specs/010-ui-baseline/data-model.md
Normal file
@@ -0,0 +1,75 @@
|
||||
# Data Model: UI Baseline
|
||||
|
||||
**Feature**: 010-ui-baseline | **Date**: 2026-03-05
|
||||
|
||||
## Domain Entities (UNCHANGED)
|
||||
|
||||
This feature makes no domain changes. The existing domain types are consumed as-is by the UI layer.
|
||||
|
||||
### Combatant (read-only from UI perspective)
|
||||
|
||||
| Field | Type | Notes |
|
||||
|-------|------|-------|
|
||||
| id | CombatantId (branded string) | Unique identifier |
|
||||
| name | string | Display name, editable inline |
|
||||
| initiative | number \| undefined | Sort order, editable inline |
|
||||
| maxHp | number \| undefined | Maximum hit points |
|
||||
| currentHp | number \| undefined | Current hit points (present when maxHp is set) |
|
||||
|
||||
### Encounter (read-only from UI perspective)
|
||||
|
||||
| Field | Type | Notes |
|
||||
|-------|------|-------|
|
||||
| combatants | readonly Combatant[] | Sorted by initiative descending |
|
||||
| activeIndex | number | Index of current turn's combatant |
|
||||
| roundNumber | number | Current round counter |
|
||||
|
||||
## UI Component Model (NEW)
|
||||
|
||||
These are adapter-layer visual components — not domain entities.
|
||||
|
||||
### CombatantRow
|
||||
|
||||
Renders a single combatant as a structured row with consistent column alignment.
|
||||
|
||||
| Column | Content | Behavior |
|
||||
|--------|---------|----------|
|
||||
| Initiative | Number or placeholder | Text input with `inputmode="numeric"`, no spinners, direct typing only |
|
||||
| Name | Combatant name | Inline editable, click-to-edit. Truncates with ellipsis |
|
||||
| HP | currentHp / maxHp | Text inputs with `inputmode="numeric"`, no spinners, direct entry for both values |
|
||||
| Actions | Remove icon button | Compact icon (X or trash), tooltip on hover |
|
||||
|
||||
**Numeric field styling**: All numeric inputs use `type="text"` with `inputmode="numeric"` to suppress browser spinners while showing the numeric keyboard on mobile. Initiative uses `6ch` width (1–2 digit values typical). HP fields use `7ch` width (supports up to 4-digit values like 1000). All use tabular numerals (`font-variant-numeric: tabular-nums`) for column alignment and centered text.
|
||||
|
||||
**Visual states**:
|
||||
- Default row
|
||||
- Active row (highlighted background/border for current turn)
|
||||
- Editing state (inline input replaces display text)
|
||||
|
||||
### ActionBar
|
||||
|
||||
Groups primary encounter controls.
|
||||
|
||||
| Element | Type | Behavior |
|
||||
|---------|------|----------|
|
||||
| Name input | Text input | Enter combatant name |
|
||||
| Add button | Primary button | Adds combatant to encounter |
|
||||
| Next Turn button | Secondary/outline button | Advances to next combatant |
|
||||
|
||||
### EncounterHeader
|
||||
|
||||
Displays encounter status.
|
||||
|
||||
| Element | Content |
|
||||
|---------|---------|
|
||||
| Title | "Initiative Tracker" |
|
||||
| Status | Round number and active combatant name |
|
||||
|
||||
### EmptyState
|
||||
|
||||
Displayed when combatants list is empty.
|
||||
|
||||
| Element | Content |
|
||||
|---------|---------|
|
||||
| Message | "No combatants yet" or similar |
|
||||
| Hint | Prompt to add first combatant |
|
||||
75
specs/010-ui-baseline/plan.md
Normal file
75
specs/010-ui-baseline/plan.md
Normal file
@@ -0,0 +1,75 @@
|
||||
# Implementation Plan: UI Baseline
|
||||
|
||||
**Branch**: `010-ui-baseline` | **Date**: 2026-03-05 | **Spec**: [spec.md](./spec.md)
|
||||
**Input**: Feature specification from `/specs/010-ui-baseline/spec.md`
|
||||
|
||||
## Summary
|
||||
|
||||
Establish a modern UI baseline for the encounter screen by integrating Tailwind CSS v4 and shadcn/ui into the existing Vite + React 19 web app. Replace all unstyled HTML with a consistent design system: structured combatant rows with aligned columns, active turn highlight, grouped action bar, icon remove buttons, and consistent typography. No domain or application layer changes — all work is in the `apps/web` adapter layer.
|
||||
|
||||
## Technical Context
|
||||
|
||||
**Language/Version**: TypeScript 5.8 (strict mode, verbatimModuleSyntax)
|
||||
**Primary Dependencies**: React 19, Vite 6, Tailwind CSS v4, shadcn/ui, Lucide React (icons)
|
||||
**Storage**: N/A (no storage changes — localStorage persistence unchanged)
|
||||
**Testing**: Vitest (existing layer boundary tests must pass; no new visual tests in MVP baseline)
|
||||
**Target Platform**: Modern browsers (desktop-first, not broken on mobile)
|
||||
**Project Type**: Web application (monorepo: apps/web + packages/domain + packages/application)
|
||||
**Performance Goals**: No perceptible rendering delay; encounter screen usable during live tabletop play
|
||||
**Constraints**: UI-only changes; domain and application layers untouched
|
||||
**Scale/Scope**: Single screen (encounter tracker), ~6 component areas to restyle
|
||||
|
||||
## Constitution Check
|
||||
|
||||
*GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.*
|
||||
|
||||
| Principle | Status | Notes |
|
||||
|-----------|--------|-------|
|
||||
| I. Deterministic Domain Core | PASS | No domain changes |
|
||||
| II. Layered Architecture | PASS | All changes in adapter layer (apps/web) only |
|
||||
| III. Agent Boundary | N/A | No agent features involved |
|
||||
| IV. Clarification-First | PASS | Spec is fully specified, no ambiguities |
|
||||
| V. Escalation Gates | PASS | Feature stays within spec scope |
|
||||
| VI. MVP Baseline Language | PASS | Dark theme, full responsive noted as "not in MVP baseline" |
|
||||
| VII. No Gameplay Rules | PASS | No gameplay logic involved |
|
||||
|
||||
## Project Structure
|
||||
|
||||
### Documentation (this feature)
|
||||
|
||||
```text
|
||||
specs/010-ui-baseline/
|
||||
├── plan.md # This file
|
||||
├── research.md # Phase 0: Tailwind v4 + shadcn/ui setup research
|
||||
├── data-model.md # Phase 1: UI component model (no domain changes)
|
||||
├── quickstart.md # Phase 1: Developer quickstart
|
||||
├── contracts/ # Phase 1: UI component contracts
|
||||
│ └── ui-components.md
|
||||
└── tasks.md # Phase 2 output (created by /speckit.tasks)
|
||||
```
|
||||
|
||||
### Source Code (repository root)
|
||||
|
||||
```text
|
||||
apps/web/
|
||||
├── src/
|
||||
│ ├── main.tsx # Add global CSS import
|
||||
│ ├── index.css # NEW: Tailwind directives + CSS variables
|
||||
│ ├── lib/
|
||||
│ │ └── utils.ts # NEW: cn() helper (clsx + twMerge)
|
||||
│ ├── components/
|
||||
│ │ └── ui/ # NEW: shadcn/ui primitives (Button, Input, Card, etc.)
|
||||
│ ├── App.tsx # Refactored: use new components + Tailwind classes
|
||||
│ └── hooks/
|
||||
│ └── use-encounter.ts # UNCHANGED
|
||||
└── package.json # Updated: new dependencies
|
||||
|
||||
packages/domain/ # UNCHANGED
|
||||
packages/application/ # UNCHANGED
|
||||
```
|
||||
|
||||
**Structure Decision**: Follows existing monorepo layout. New UI components live in `apps/web/src/components/ui/` following shadcn/ui convention. No new packages or layers introduced.
|
||||
|
||||
## Complexity Tracking
|
||||
|
||||
No constitution violations. No complexity justifications needed.
|
||||
62
specs/010-ui-baseline/quickstart.md
Normal file
62
specs/010-ui-baseline/quickstart.md
Normal file
@@ -0,0 +1,62 @@
|
||||
# Quickstart: UI Baseline
|
||||
|
||||
**Feature**: 010-ui-baseline | **Date**: 2026-03-05
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Node.js 18+
|
||||
- pnpm 10.6+
|
||||
|
||||
## Setup
|
||||
|
||||
```bash
|
||||
# Install dependencies (from repo root)
|
||||
pnpm install
|
||||
|
||||
# Start dev server
|
||||
pnpm --filter web dev
|
||||
# → http://localhost:5173
|
||||
```
|
||||
|
||||
## New Dependencies (to be added)
|
||||
|
||||
```bash
|
||||
# Tailwind CSS v4 with Vite plugin
|
||||
pnpm --filter web add tailwindcss @tailwindcss/vite
|
||||
|
||||
# shadcn/ui utilities
|
||||
pnpm --filter web add clsx tailwind-merge class-variance-authority
|
||||
|
||||
# Radix UI primitives (used by shadcn/ui components)
|
||||
pnpm --filter web add @radix-ui/react-slot @radix-ui/react-tooltip
|
||||
|
||||
# Icons
|
||||
pnpm --filter web add lucide-react
|
||||
```
|
||||
|
||||
## Key Files
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `apps/web/src/index.css` | Tailwind directives + CSS custom properties |
|
||||
| `apps/web/src/lib/utils.ts` | `cn()` class merge utility |
|
||||
| `apps/web/src/components/ui/` | shadcn/ui primitives (Button, Input, etc.) |
|
||||
| `apps/web/src/components/combatant-row.tsx` | Combatant row component |
|
||||
| `apps/web/src/App.tsx` | Main layout (header, list, action bar) |
|
||||
| `apps/web/vite.config.ts` | Updated with Tailwind plugin |
|
||||
|
||||
## Quality Gate
|
||||
|
||||
```bash
|
||||
# Must pass before commit
|
||||
pnpm check
|
||||
```
|
||||
|
||||
This runs: knip → format → lint → typecheck → test
|
||||
|
||||
## Design System
|
||||
|
||||
- **Styling**: Tailwind CSS v4 utility classes
|
||||
- **Components**: shadcn/ui (copied into project, not a package dependency)
|
||||
- **Icons**: Lucide React
|
||||
- **Class merging**: `cn()` from `lib/utils.ts` (clsx + tailwind-merge)
|
||||
72
specs/010-ui-baseline/research.md
Normal file
72
specs/010-ui-baseline/research.md
Normal file
@@ -0,0 +1,72 @@
|
||||
# Research: UI Baseline
|
||||
|
||||
**Feature**: 010-ui-baseline | **Date**: 2026-03-05
|
||||
|
||||
## R1: Tailwind CSS Version Choice
|
||||
|
||||
**Decision**: Use Tailwind CSS v4 (latest stable)
|
||||
**Rationale**: Tailwind v4 uses a CSS-first configuration approach with `@import "tailwindcss"` and CSS-based theme customization via `@theme`. This simplifies setup — no `tailwind.config.ts` is strictly required for basic usage. Vite has first-class support via `@tailwindcss/vite` plugin.
|
||||
**Alternatives considered**:
|
||||
- Tailwind v3: Mature but requires JS config file and PostCSS plugin. v4 is stable and recommended for new projects.
|
||||
|
||||
## R2: shadcn/ui Integration Approach
|
||||
|
||||
**Decision**: Use shadcn/ui CLI to scaffold components into `apps/web/src/components/ui/`
|
||||
**Rationale**: shadcn/ui is not a package dependency — it copies component source code into the project. This gives full control over styling and avoids version lock-in. Components use Tailwind classes + Radix UI primitives.
|
||||
**Alternatives considered**:
|
||||
- Radix UI directly: More low-level, requires writing all styles manually. shadcn/ui provides pre-styled Tailwind components.
|
||||
- Material UI / Chakra UI: Heavier runtime, opinionated styling that conflicts with Tailwind approach.
|
||||
|
||||
## R3: Tailwind v4 + Vite Setup
|
||||
|
||||
**Decision**: Use `@tailwindcss/vite` plugin instead of PostCSS
|
||||
**Rationale**: Tailwind v4 provides a dedicated Vite plugin that is faster than the PostCSS approach. Setup is:
|
||||
1. Install `tailwindcss @tailwindcss/vite`
|
||||
2. Add plugin to `vite.config.ts`
|
||||
3. Create `index.css` with `@import "tailwindcss"`
|
||||
4. Import `index.css` in `main.tsx`
|
||||
**Alternatives considered**:
|
||||
- PostCSS plugin: Works but slower; Vite plugin is recommended for Vite projects.
|
||||
|
||||
## R4: Icon Library
|
||||
|
||||
**Decision**: Use Lucide React for icons (remove button, etc.)
|
||||
**Rationale**: Lucide is the default icon set for shadcn/ui. Tree-shakeable, consistent style, and already expected by shadcn/ui component templates.
|
||||
**Alternatives considered**:
|
||||
- Heroicons: Good quality but not the shadcn/ui default.
|
||||
- Inline SVG: Too manual for maintenance.
|
||||
|
||||
## R5: CSS Utility Helper (cn function)
|
||||
|
||||
**Decision**: Use `clsx` + `tailwind-merge` via a `cn()` utility function
|
||||
**Rationale**: Standard shadcn/ui pattern. `clsx` handles conditional classes, `tailwind-merge` deduplicates conflicting Tailwind classes. The `cn()` helper is placed in `lib/utils.ts`.
|
||||
**Alternatives considered**:
|
||||
- `clsx` alone: Doesn't deduplicate conflicting Tailwind classes (e.g., `p-2 p-4`).
|
||||
|
||||
## R6: Component Decomposition
|
||||
|
||||
**Decision**: Extract App.tsx into focused components while keeping them in a single file or minimal files
|
||||
**Rationale**: The current App.tsx (~280 lines) has inline components (EditableName, MaxHpInput, CurrentHpInput). For the UI baseline, we'll restructure into:
|
||||
- `App.tsx` — layout shell (header, combatant list, action bar)
|
||||
- `components/combatant-row.tsx` — single combatant row with all controls
|
||||
- `components/ui/` — shadcn/ui primitives (Button, Input, Card)
|
||||
|
||||
This keeps the change focused while establishing a scalable component structure.
|
||||
**Alternatives considered**:
|
||||
- Keep everything in App.tsx: Gets unwieldy with Tailwind classes added.
|
||||
- Full atomic decomposition: Over-engineered for current scope.
|
||||
|
||||
## R7: verbatimModuleSyntax Compatibility
|
||||
|
||||
**Decision**: shadcn/ui components work with `verbatimModuleSyntax` since they use standard ESM imports
|
||||
**Rationale**: shadcn/ui generates standard TypeScript files with explicit type imports. The `cn` utility and Radix imports use value imports. No special handling needed.
|
||||
|
||||
## R8: Biome 2.0 Compatibility
|
||||
|
||||
**Decision**: No conflicts expected; Tailwind class strings are just strings
|
||||
**Rationale**: Biome formats/lints TypeScript and JSX. Tailwind classes in `className` props are plain strings, which Biome ignores content-wise. The shadcn/ui generated code follows standard formatting conventions. May need to run `pnpm format` after generating components.
|
||||
|
||||
## R9: Knip Compatibility
|
||||
|
||||
**Decision**: May need to configure Knip to recognize shadcn/ui component exports
|
||||
**Rationale**: shadcn/ui components are copied into the project. If not all are immediately used, Knip may flag them as unused. Solution: only install the shadcn/ui components we actually need (Button, Input, Card/container).
|
||||
140
specs/010-ui-baseline/spec.md
Normal file
140
specs/010-ui-baseline/spec.md
Normal file
@@ -0,0 +1,140 @@
|
||||
# Feature Specification: UI Baseline
|
||||
|
||||
**Feature Branch**: `010-ui-baseline`
|
||||
**Created**: 2026-03-05
|
||||
**Status**: Draft
|
||||
**Input**: User description: "Establish a modern UI baseline for the encounter screen using Tailwind + shadcn/ui."
|
||||
|
||||
## User Scenarios & Testing *(mandatory)*
|
||||
|
||||
### User Story 1 - Structured Combatant Layout (Priority: P1)
|
||||
|
||||
As a game master viewing the encounter screen, I see each combatant displayed as a structured row with initiative, name, HP, and actions aligned in consistent columns, so I can quickly scan the battlefield state.
|
||||
|
||||
**Why this priority**: The core value of a UI baseline is replacing the unstyled list with a consistent, scannable layout. Every other visual improvement builds on this.
|
||||
|
||||
**Independent Test**: Can be fully tested by adding 3+ combatants and verifying that all data fields (initiative, name, HP, actions) are visually aligned in a grid/table-like layout.
|
||||
|
||||
**Acceptance Scenarios**:
|
||||
|
||||
1. **Given** an encounter with multiple combatants, **When** the screen loads, **Then** each combatant is displayed as a row with initiative, name, HP, and actions in consistent columns.
|
||||
2. **Given** combatants with varying name lengths, **When** displayed, **Then** columns remain aligned and do not shift or overlap.
|
||||
3. **Given** a combatant with no initiative or HP set, **When** displayed, **Then** placeholder or empty state is shown in the appropriate column without breaking alignment.
|
||||
|
||||
---
|
||||
|
||||
### User Story 2 - Active Combatant Highlight (Priority: P1)
|
||||
|
||||
As a game master during combat, I see the active combatant clearly highlighted so I can instantly identify whose turn it is without reading text markers.
|
||||
|
||||
**Why this priority**: Identifying the active turn is the primary interaction during live play. A clear visual highlight is essential for usability.
|
||||
|
||||
**Independent Test**: Can be tested by advancing turns and verifying the active combatant has a distinct visual treatment (background color, border, or similar).
|
||||
|
||||
**Acceptance Scenarios**:
|
||||
|
||||
1. **Given** an encounter in progress, **When** viewing the combatant list, **Then** the active combatant's row has a visually distinct highlight (e.g., accent background, left border indicator).
|
||||
2. **Given** the turn advances, **When** a new combatant becomes active, **Then** the highlight moves to the new active combatant and is removed from the previous one.
|
||||
|
||||
---
|
||||
|
||||
### User Story 3 - Grouped Action Bar (Priority: P2)
|
||||
|
||||
As a game master, I see primary encounter controls (add combatant, next turn) grouped in a clearly defined action bar, so controls are easy to find and visually separated from the combatant list.
|
||||
|
||||
**Why this priority**: Grouping controls improves discoverability and reduces visual clutter, but is less critical than the combatant layout itself.
|
||||
|
||||
**Independent Test**: Can be tested by verifying that the "Add Combatant" form and "Next Turn" button are visually grouped in a distinct bar area, separated from the combatant list.
|
||||
|
||||
**Acceptance Scenarios**:
|
||||
|
||||
1. **Given** the encounter screen, **When** viewing controls, **Then** the "Add Combatant" input and "Next Turn" button are grouped in a visually distinct action bar.
|
||||
2. **Given** the action bar, **When** inspecting layout, **Then** it is clearly separated from the combatant list by spacing, background, or border.
|
||||
|
||||
---
|
||||
|
||||
### User Story 4 - Inline Editing with Consistent Styling (Priority: P2)
|
||||
|
||||
As a game master, I can click on a combatant's name to edit it inline, and all editable controls (including initiative and HP inputs) match the overall visual style of the application.
|
||||
|
||||
**Why this priority**: Inline editing already exists functionally. This story ensures the edit states are visually consistent with the new design system rather than appearing as unstyled browser defaults.
|
||||
|
||||
**Independent Test**: Can be tested by clicking a combatant name to enter edit mode and verifying the input field matches the application's visual style.
|
||||
|
||||
**Acceptance Scenarios**:
|
||||
|
||||
1. **Given** a combatant row, **When** I click the name, **Then** it transitions to an inline text input styled consistently with the design system.
|
||||
2. **Given** a combatant row, **When** I edit initiative or HP values, **Then** the number inputs are styled consistently with the design system.
|
||||
|
||||
---
|
||||
|
||||
### User Story 5 - Remove Action as Icon Button (Priority: P3)
|
||||
|
||||
As a game master, I see the remove action as a small icon button rather than a full text button, so it takes up less space and reduces visual noise.
|
||||
|
||||
**Why this priority**: This is a refinement that improves information density. The remove action is infrequently used, so a compact icon button is appropriate.
|
||||
|
||||
**Independent Test**: Can be tested by verifying each combatant row has a small icon-sized remove button (e.g., trash or X icon) instead of a text "Remove" button.
|
||||
|
||||
**Acceptance Scenarios**:
|
||||
|
||||
1. **Given** a combatant row, **When** viewing the actions column, **Then** the remove action is displayed as a small icon button (not a text label).
|
||||
2. **Given** the icon button, **When** hovering, **Then** a tooltip or visual feedback indicates the action is "Remove".
|
||||
|
||||
---
|
||||
|
||||
### User Story 6 - Consistent Typography and Spacing (Priority: P2)
|
||||
|
||||
As a game master, the encounter screen uses consistent font sizes, weights, and spacing throughout, creating a cohesive and professional appearance.
|
||||
|
||||
**Why this priority**: Typography and spacing consistency is foundational to a "modern UI baseline" and affects the perceived quality of every other element.
|
||||
|
||||
**Independent Test**: Can be tested by verifying that heading sizes, body text, input text, and spacing follow a consistent scale across the entire screen.
|
||||
|
||||
**Acceptance Scenarios**:
|
||||
|
||||
1. **Given** the encounter screen, **When** inspecting typography, **Then** headings, labels, and body text use a consistent type scale.
|
||||
2. **Given** the encounter screen, **When** inspecting spacing, **Then** padding and margins follow a consistent spacing scale.
|
||||
|
||||
---
|
||||
|
||||
### Edge Cases
|
||||
|
||||
- What happens when no combatants exist? The screen should display an empty state message (e.g., "No combatants yet") rather than a blank area.
|
||||
- What happens with very long combatant names? Names should truncate with ellipsis rather than breaking the layout.
|
||||
- What happens on narrow viewports? The layout should remain usable, though a fully responsive mobile design is not in the MVP baseline for this feature.
|
||||
- What happens with 20+ combatants? The list should scroll without the action bar or header disappearing.
|
||||
|
||||
## Requirements *(mandatory)*
|
||||
|
||||
### Functional Requirements
|
||||
|
||||
- **FR-001**: System MUST display combatants in a structured row layout with columns for initiative, name, HP (current/max), and actions. All numeric fields use text inputs with `inputmode="numeric"` (no browser spinners), ch-based widths, tabular numerals, and centered text.
|
||||
- **FR-002**: System MUST visually highlight the active combatant's row with a distinct background, border, or accent treatment.
|
||||
- **FR-003**: System MUST group the "Add Combatant" form and "Next Turn" button in a visually distinct action bar area.
|
||||
- **FR-004**: System MUST display the remove action as a compact icon button (not a text label) on each combatant row.
|
||||
- **FR-005**: System MUST style all form inputs (text fields, number inputs) consistently using the design system's components.
|
||||
- **FR-006**: System MUST apply consistent typography (font family, sizes, weights) and spacing (margins, padding) from a defined scale.
|
||||
- **FR-007**: System MUST show a round/turn status indicator (current round number and active combatant name) in a header or status area.
|
||||
- **FR-008**: System MUST display an empty state message when no combatants are present.
|
||||
- **FR-009**: System MUST truncate long combatant names with ellipsis rather than breaking the layout.
|
||||
- **FR-010**: System MUST NOT introduce any domain logic changes; all changes are confined to the adapter/UI layer.
|
||||
- **FR-011**: System MUST NOT display domain events in the main UI layout.
|
||||
|
||||
## Success Criteria *(mandatory)*
|
||||
|
||||
### Measurable Outcomes
|
||||
|
||||
- **SC-001**: All combatant data fields (initiative, name, HP, actions) are visually aligned in consistent columns across all combatant rows.
|
||||
- **SC-002**: The active combatant is identifiable within 1 second of glancing at the screen, without reading text labels.
|
||||
- **SC-003**: A new user can locate the "Add Combatant" and "Next Turn" controls within 3 seconds of viewing the screen.
|
||||
- **SC-004**: All interactive elements (buttons, inputs) are styled consistently with no browser-default styled controls visible.
|
||||
- **SC-005**: The encounter screen presents a cohesive visual appearance with no mismatched fonts, inconsistent spacing, or unstyled elements.
|
||||
|
||||
## Assumptions
|
||||
|
||||
- Tailwind CSS and shadcn/ui will be used as the design system and component library. These are adapter-layer technology choices.
|
||||
- The existing functional behavior (inline editing, HP controls, add/remove/advance) is preserved exactly; only visual presentation changes.
|
||||
- A dark theme or theme toggle is not included in the MVP baseline for this feature.
|
||||
- Full mobile/responsive optimization is not included in the MVP baseline for this feature, though the layout should not be broken on smaller screens.
|
||||
- Domain events will be removed from the main UI display (they were a development aid, not a user-facing feature).
|
||||
207
specs/010-ui-baseline/tasks.md
Normal file
207
specs/010-ui-baseline/tasks.md
Normal file
@@ -0,0 +1,207 @@
|
||||
# Tasks: UI Baseline
|
||||
|
||||
**Input**: Design documents from `/specs/010-ui-baseline/`
|
||||
**Prerequisites**: plan.md (required), spec.md (required), research.md, data-model.md, contracts/ui-components.md
|
||||
|
||||
**Tests**: No new tests requested in spec. Existing tests (layer boundary checks) must continue to pass.
|
||||
|
||||
**Organization**: Tasks grouped by user story. US1+US2 are combined (both P1, same component).
|
||||
|
||||
## Format: `[ID] [P?] [Story] Description`
|
||||
|
||||
- **[P]**: Can run in parallel (different files, no dependencies)
|
||||
- **[Story]**: Which user story this task belongs to (e.g., US1, US2, US3)
|
||||
- Include exact file paths in descriptions
|
||||
|
||||
## Phase 1: Setup (Shared Infrastructure)
|
||||
|
||||
**Purpose**: Install Tailwind CSS v4, shadcn/ui utilities, and configure the build pipeline
|
||||
|
||||
- [x] T001 Install `tailwindcss` and `@tailwindcss/vite` as devDependencies and add the Tailwind Vite plugin to `apps/web/vite.config.ts`
|
||||
- [x] T002 [P] Create `apps/web/src/index.css` with `@import "tailwindcss"` directive and `@theme` block defining CSS custom properties for the design system (colors, radii, fonts)
|
||||
- [x] T003 [P] Install `clsx` and `tailwind-merge` as dependencies in `apps/web` and create the `cn()` utility in `apps/web/src/lib/utils.ts`
|
||||
- [x] T004 Import `./index.css` in `apps/web/src/main.tsx`
|
||||
- [x] T005 [P] Install `lucide-react` as a dependency in `apps/web`
|
||||
|
||||
---
|
||||
|
||||
## Phase 2: Foundational (Blocking Prerequisites)
|
||||
|
||||
**Purpose**: Create shadcn/ui-style primitive components that all user stories depend on
|
||||
|
||||
**Warning**: No user story work can begin until this phase is complete
|
||||
|
||||
- [x] T006 [P] Install `class-variance-authority` as a dependency in `apps/web` and create Button component in `apps/web/src/components/ui/button.tsx` following shadcn/ui pattern (variant props: default, outline, ghost, icon; size props: default, sm, icon) using `cn()` and `class-variance-authority`
|
||||
- [x] T007 [P] Create Input component in `apps/web/src/components/ui/input.tsx` following shadcn/ui pattern (styled text/number input replacing browser defaults) using `cn()`
|
||||
|
||||
**Note**: T008 merged into T006 (install CVA + create Button in one task).
|
||||
|
||||
**Checkpoint**: Foundation ready — shadcn/ui primitives available, Tailwind active
|
||||
|
||||
---
|
||||
|
||||
## Phase 3: User Story 1 + 2 — Structured Layout + Active Highlight (Priority: P1) MVP
|
||||
|
||||
**Goal**: Replace the unstyled `<ul>/<li>` combatant list with a structured row layout (initiative | name | HP | actions columns) and visually highlight the active combatant's row
|
||||
|
||||
**Independent Test**: Add 3+ combatants, verify columns are aligned. Set initiative and HP on some. Advance turns and confirm the active row has a distinct highlight that moves correctly.
|
||||
|
||||
### Implementation
|
||||
|
||||
- [x] T009 [US1] Extract `CombatantRow` component to `apps/web/src/components/combatant-row.tsx` — accepts `combatant`, `isActive`, and action callbacks per the UI contract. Render a grid/flex row with four columns: initiative (number input), name (click-to-edit text), HP (current/max with +/- buttons), actions (remove button placeholder). Apply active row highlight (accent left border + subtle background) when `isActive` is true. Move `EditableName`, `MaxHpInput`, and `CurrentHpInput` inline components into this file.
|
||||
- [x] T010 [US1] Refactor `apps/web/src/App.tsx` to use `CombatantRow` — replace the `<ul>` list with a styled container. Add encounter header section showing title ("Initiative Tracker") and round/turn status. Remove domain events display section entirely (FR-011). Keep `useEncounter` hook usage and all callbacks wired through.
|
||||
|
||||
**Checkpoint**: Combatants display in aligned columns with active highlight. All existing functionality preserved.
|
||||
|
||||
---
|
||||
|
||||
## Phase 4: User Story 3 — Grouped Action Bar (Priority: P2)
|
||||
|
||||
**Goal**: Group the "Add Combatant" form and "Next Turn" button into a visually distinct action bar separated from the combatant list
|
||||
|
||||
**Independent Test**: Verify controls are grouped in a distinct bar area with visual separation (background, border, or spacing) from the combatant list.
|
||||
|
||||
### Implementation
|
||||
|
||||
- [x] T011 [US3] Extract `ActionBar` component to `apps/web/src/components/action-bar.tsx` — accepts `onAddCombatant` and `onAdvanceTurn` callbacks. Render a styled bar with the add-combatant form (Input + Button) and Next Turn button (outline/secondary variant). Apply visual separation from the combatant list.
|
||||
- [x] T012 [US3] Update `apps/web/src/App.tsx` to use `ActionBar` component — replace inline form and button with the extracted component.
|
||||
|
||||
**Checkpoint**: Action bar is visually grouped and separated from combatant list.
|
||||
|
||||
---
|
||||
|
||||
## Phase 5: User Story 4 — Inline Editing with Consistent Styling (Priority: P2)
|
||||
|
||||
**Goal**: Ensure all inline edit states (name, initiative, HP inputs) use the design system Input component instead of unstyled browser defaults
|
||||
|
||||
**Independent Test**: Click a combatant name to edit — the input should match the design system style. Verify initiative and HP number inputs are also styled consistently.
|
||||
|
||||
### Implementation
|
||||
|
||||
- [x] T013 [US4] Update `EditableName`, `MaxHpInput`, `CurrentHpInput`, and initiative input in `apps/web/src/components/combatant-row.tsx` to use the shadcn/ui-style `Input` component from `components/ui/input.tsx`. Ensure edit-mode inputs match display-mode styling for seamless transitions.
|
||||
|
||||
**Checkpoint**: All form inputs across the encounter screen use consistent design system styling.
|
||||
|
||||
---
|
||||
|
||||
## Phase 6: User Story 5 — Remove Action as Icon Button (Priority: P3)
|
||||
|
||||
**Goal**: Replace the text "Remove" button with a compact icon button using a Lucide icon
|
||||
|
||||
**Independent Test**: Each combatant row shows a small icon button (X or Trash2) instead of a text "Remove" button. Hovering shows tooltip feedback.
|
||||
|
||||
### Implementation
|
||||
|
||||
- [x] T014 [US5] Replace the remove `<button>` in `apps/web/src/components/combatant-row.tsx` with a `Button` (ghost/icon variant) wrapping a Lucide `X` or `Trash2` icon. Add `title` attribute for hover tooltip ("Remove combatant").
|
||||
|
||||
**Checkpoint**: Remove action is a compact icon button with hover feedback.
|
||||
|
||||
---
|
||||
|
||||
## Phase 7: User Story 6 — Consistent Typography and Spacing (Priority: P2)
|
||||
|
||||
**Goal**: Apply a consistent type scale and spacing scale across the entire encounter screen
|
||||
|
||||
**Independent Test**: Inspect the screen — headings, body text, labels, and inputs use a consistent font family, size scale, and spacing rhythm with no arbitrary values.
|
||||
|
||||
### Implementation
|
||||
|
||||
- [x] T015 [US6] Review and refine typography and spacing across all components — ensure `apps/web/src/index.css` theme defines a consistent type scale (heading, body, label sizes) and spacing tokens. Update `apps/web/src/components/combatant-row.tsx`, `apps/web/src/components/action-bar.tsx`, and `apps/web/src/App.tsx` to use consistent Tailwind spacing/typography utilities (no arbitrary pixel values).
|
||||
|
||||
**Checkpoint**: The entire encounter screen has cohesive typography and spacing.
|
||||
|
||||
---
|
||||
|
||||
## Phase 8: Polish & Cross-Cutting Concerns
|
||||
|
||||
**Purpose**: Edge cases, cleanup, and quality gate validation
|
||||
|
||||
- [x] T016 Add empty state message in `apps/web/src/App.tsx` — when `encounter.combatants.length === 0`, display a centered muted message ("No combatants yet — add one to get started") instead of an empty list area
|
||||
- [x] T017 Add long name truncation with CSS `text-overflow: ellipsis` on the name column in `apps/web/src/components/combatant-row.tsx` and make the combatant list area scrollable when it overflows (sticky header + action bar)
|
||||
- [x] T018 Run `pnpm check` (knip + format + lint + typecheck + test) and fix all issues — ensure no unused imports from shadcn/ui, Biome formatting passes, TypeScript compiles, and layer boundary tests pass
|
||||
|
||||
---
|
||||
|
||||
## Dependencies & Execution Order
|
||||
|
||||
### Phase Dependencies
|
||||
|
||||
- **Setup (Phase 1)**: No dependencies — start immediately
|
||||
- **Foundational (Phase 2)**: Depends on T001 (Tailwind installed) and T003 (cn() available)
|
||||
- **US1+US2 (Phase 3)**: Depends on Phase 2 (Button, Input primitives available)
|
||||
- **US3 (Phase 4)**: Depends on Phase 3 (App.tsx refactored with layout shell)
|
||||
- **US4 (Phase 5)**: Depends on Phase 3 (CombatantRow exists with inline edit components)
|
||||
- **US5 (Phase 6)**: Depends on Phase 3 (CombatantRow exists) + T005 (lucide-react installed)
|
||||
- **US6 (Phase 7)**: Depends on Phases 3-6 (all components exist to audit typography)
|
||||
- **Polish (Phase 8)**: Depends on all user stories complete
|
||||
|
||||
### User Story Dependencies
|
||||
|
||||
- **US1+US2 (P1)**: Can start after Phase 2 — no other story dependencies
|
||||
- **US3 (P2)**: Depends on US1+US2 (App.tsx layout shell must exist)
|
||||
- **US4 (P2)**: Depends on US1+US2 (CombatantRow must exist)
|
||||
- **US5 (P3)**: Depends on US1+US2 (CombatantRow must exist)
|
||||
- **US6 (P2)**: Depends on US3, US4, US5 (all components must exist to audit)
|
||||
|
||||
### Within Each User Story
|
||||
|
||||
- Component extraction before integration with App.tsx
|
||||
- Structural changes before styling refinements
|
||||
|
||||
### Parallel Opportunities
|
||||
|
||||
- T002, T003, T005 can run in parallel (different files, no dependencies)
|
||||
- T006, T007 can run in parallel (different files)
|
||||
- US3 (T011-T012) and US4 (T013) and US5 (T014) can run in parallel after Phase 3 (different files)
|
||||
|
||||
---
|
||||
|
||||
## Parallel Example: Phase 1 Setup
|
||||
|
||||
```bash
|
||||
# These three tasks touch different files and can run simultaneously:
|
||||
Task T002: "Create index.css with Tailwind directives in apps/web/src/index.css"
|
||||
Task T003: "Create cn() utility in apps/web/src/lib/utils.ts"
|
||||
Task T005: "Install lucide-react in apps/web"
|
||||
```
|
||||
|
||||
## Parallel Example: Phase 2 Foundational
|
||||
|
||||
```bash
|
||||
# These tasks create independent component files:
|
||||
Task T006: "Install CVA + create Button in apps/web/src/components/ui/button.tsx"
|
||||
Task T007: "Create Input in apps/web/src/components/ui/input.tsx"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Implementation Strategy
|
||||
|
||||
### MVP First (US1+US2 Only)
|
||||
|
||||
1. Complete Phase 1: Setup (Tailwind + utilities)
|
||||
2. Complete Phase 2: Foundational (Button, Input primitives)
|
||||
3. Complete Phase 3: US1+US2 (structured layout + active highlight)
|
||||
4. **STOP and VALIDATE**: Combatants display in aligned columns, active turn highlighted
|
||||
5. This alone delivers the core visual upgrade
|
||||
|
||||
### Incremental Delivery
|
||||
|
||||
1. Setup + Foundational -> Build pipeline ready
|
||||
2. US1+US2 -> Structured layout with active highlight (MVP!)
|
||||
3. US3 -> Grouped action bar
|
||||
4. US4 -> Styled inline editing
|
||||
5. US5 -> Icon remove button
|
||||
6. US6 -> Typography/spacing audit
|
||||
7. Polish -> Edge cases, quality gate
|
||||
8. Each phase adds visual polish without breaking previous work
|
||||
|
||||
---
|
||||
|
||||
## Notes
|
||||
|
||||
- All changes are in `apps/web/` only — domain and application packages are untouched (FR-010)
|
||||
- Domain events display is removed in T010 (FR-011)
|
||||
- No new test files created — existing Vitest layer boundary tests must pass (T018)
|
||||
- shadcn/ui components are hand-written following the pattern (not CLI-generated) to ensure Biome/Knip compatibility
|
||||
- Run `pnpm format` after each phase to keep Biome happy
|
||||
Reference in New Issue
Block a user