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:
Lukas
2026-03-05 18:36:39 +01:00
parent 8185fde0e8
commit 1c40bf7889
20 changed files with 1533 additions and 273 deletions

View 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