# Tasks: Combatant Row UI Polish **Input**: Design documents from `/specs/027-ui-polish/` **Prerequisites**: plan.md (required), spec.md (required for user stories), research.md, data-model.md **Tests**: No new tests required — this feature is purely presentational. Existing domain tests must continue passing. **Organization**: Tasks are grouped by user story to enable independent implementation and testing of each story. ## 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 **Purpose**: No new project setup needed — all infrastructure exists. This phase is empty. --- ## Phase 2: Foundational (Layout Restructure) **Purpose**: Restructure the combatant row grid layout to support inline conditions. This MUST be complete before individual user stories can be implemented, since it changes the core grid structure. **⚠️ CRITICAL**: US1 restructures the grid and name column — all subsequent stories build on this new layout. - [x] T001 Move `ConditionTags` and the "+" condition button from the separate conditions row (the `div` with `ml-[calc(3rem+0.75rem)]`) into the name column's flex container in `apps/web/src/components/combatant-row.tsx`. Place them after the name text inside the `1fr` grid cell. Add `flex-wrap` to the container so conditions wrap gracefully. Apply `min-w-0` and `truncate` on the name text so it truncates when conditions need space. Do NOT use `shrink-0` — the name must be allowed to shrink to make room for inline conditions. Remove the entire separate conditions row `div` below the main grid. - [x] T002 Make the "+" condition button hover-only by adding `opacity-0 group-hover:opacity-100 focus:opacity-100 transition-opacity` classes in `apps/web/src/components/combatant-row.tsx`. When no conditions exist, the "+" should be the only inline element after the name and should only appear on hover. When conditions exist, the "+" appears after the last condition icon on hover. - [x] T003 Verify the `ConditionPicker` dropdown positioning still works correctly from its new inline position in `apps/web/src/components/condition-picker.tsx`. The existing flip logic (checking `rect.bottom > window.innerHeight`) should still work since positioning is `absolute` relative to the picker's parent. Adjust if the new inline context changes the reference point. **Checkpoint**: Conditions render inline after creature name. "+" appears on hover only. No second row below combatants. All condition add/remove interactions work as before. --- ## Phase 3: User Story 2 — Row Click Opens Stat Block (Priority: P1) **Goal**: Make the entire combatant row clickable to open the stat block, removing the dedicated BookOpen icon. **Independent Test**: Click on the creature name or empty row space for a bestiary combatant — stat block panel opens. Click on interactive elements (HP, AC, initiative, conditions) — their own actions fire, stat block does not open. - [x] T004 [US2] Remove the BookOpen icon button from the name area in the `EditableName` section of `apps/web/src/components/combatant-row.tsx`. Remove the `BookOpen` import from `lucide-react` if no longer used elsewhere in the file. - [x] T005 [US2] Add an `onClick` handler to the row container `div` in `apps/web/src/components/combatant-row.tsx` that calls `onShowStatBlock?.()` when the combatant has a `creatureId`. Add `cursor-pointer` to the row container for bestiary combatants. Confirm that the parent component's `onShowStatBlock` handler implements toggle behavior (clicking the same row again closes the stat block panel, per spec edge case). - [x] T006 [US2] Add `event.stopPropagation()` to all interactive child element click handlers in `apps/web/src/components/combatant-row.tsx` — specifically: concentration button, initiative display (d20 button, edit click, display click), condition icon buttons, "+" condition button, AC display click, HP display click, and remove button. This prevents row-level stat block opening when interacting with these elements. **Checkpoint**: Clicking non-interactive row areas opens stat block for bestiary combatants. BookOpen icon is gone. All existing interactions (HP, AC, initiative, conditions, concentration, remove) still work without triggering stat block. --- ## Phase 4: User Story 3 — Hover-Only Remove Button (Priority: P2) **Goal**: Hide the × remove button by default, showing it only on row hover. **Independent Test**: Hover over a combatant row — × appears. Move mouse away — × disappears. Row layout does not shift. - [x] T007 [US3] Add `opacity-0 group-hover:opacity-100 focus:opacity-100 transition-opacity` classes to the remove button (X icon) in `apps/web/src/components/combatant-row.tsx`. Keep the button in the DOM (preserving the `2rem` grid column) so no layout shift occurs. This follows the same pattern used by the concentration button. **Checkpoint**: Remove button hidden by default, visible on hover, no layout shift. Removal still works. --- ## Phase 5: User Story 4 — AC Shield Shape (Priority: P2) **Goal**: Display the AC number inside a shield-shaped outline instead of beside a shield icon. **Independent Test**: View any combatant with AC set — number appears inside a shield-shaped SVG outline. Click to edit AC — editing works as before. - [x] T008 [P] [US4] Create new `AcShield` component in `apps/web/src/components/ac-shield.tsx`. Render an inline SVG shield outline (stroke-based, `currentColor`, approximately 28×32px viewBox) with a centered `` overlay for the AC number using relative/absolute positioning. Accept `value` (number | undefined), `onClick` callback, and `className` props. Show a placeholder (e.g., "—") when no value is set. - [x] T009 [US4] Replace the `AcDisplay` sub-component's non-edit rendering in `apps/web/src/components/combatant-row.tsx` to use the new `AcShield` component instead of the Lucide `Shield` icon + number. Keep the edit-mode input behavior unchanged — when clicked, the shield is replaced by the existing input field. Remove the `Shield` import from `lucide-react` if no longer used elsewhere in the file. **Checkpoint**: AC displays inside shield shape. Edit mode still works. Single-digit, double-digit, and triple-digit values render centered. --- ## Phase 6: User Story 5 — Expanded Concentration Click Target (Priority: P3) **Goal**: Widen the concentration button's clickable area to fill the gutter between the left border and initiative column. **Independent Test**: Click in the space left of the initiative number — concentration toggles. Brain icon stays visually centered. - [x] T010 [US5] Widen the first grid column from `1.25rem` to `2rem` in the grid template `grid-cols-[1.25rem_3rem_1fr_auto_auto_2rem]` in `apps/web/src/components/combatant-row.tsx`. Make the concentration button fill the full column width (`w-full h-full`) while keeping the brain icon visually centered. Verify the concentration shake/glow animation still works with the wider button. **Checkpoint**: Concentration clickable across full left gutter. Brain icon centered. Animation intact. --- ## Phase 7: User Story 6 — Larger D20 Icon (Priority: P3) **Goal**: Increase the d20 roll-initiative icon size for better recognizability. **Independent Test**: View a bestiary combatant without initiative — d20 icon is visibly larger and recognizable as a die. - [x] T011 [P] [US6] Change the D20Icon className from `h-5 w-5` to `h-7 w-7` in the `InitiativeDisplay` sub-component within `apps/web/src/components/combatant-row.tsx`. Verify the icon fits within the 3rem initiative column without overflow. **Checkpoint**: D20 icon at 28px is recognizable. No overflow. Roll interaction works. --- ## Phase 8: Polish & Cross-Cutting Concerns - [x] T012 Run `pnpm check` to verify all existing tests pass, linting is clean, types check, and no unused exports are flagged by Knip in the project root - [x] T013 Visual verification: check all combatant states (active turn, concentrating, unconscious/dimmed, bloodied, no conditions, many conditions, no AC, no initiative, custom combatant without creatureId) render correctly with the new layout - [x] T014 Verify touch device behavior: "+" condition button and "×" remove button are accessible via tap/focus even without hover - [x] T015 [P] Style browser scrollbars to match dark UI by adding `scrollbar-color` and `scrollbar-width` properties in `apps/web/src/index.css` - [x] T016 [P] Redesign top bar buttons: replace Previous/Next text+chevron with StepBack/StepForward outline icon buttons (`border-foreground`, white), keep d20/trash as ghost buttons (grey), group d20+trash tightly with spacing before Next in `apps/web/src/components/turn-navigation.tsx` - [x] T017 [P] Remove the "Initiative Tracker" `
` heading from `apps/web/src/App.tsx` to maximize vertical combatant space --- ## Dependencies & Execution Order ### Phase Dependencies - **Foundational (Phase 2)**: No prerequisites — restructures the core layout - **US2 Row Click (Phase 3)**: Depends on Phase 2 (BookOpen removal and row click both affect the name area) - **US3 Hover Remove (Phase 4)**: Depends on Phase 2 (needs stable grid layout) - **US4 AC Shield (Phase 5)**: Independent — can run in parallel with Phase 3/4 - **US5 Concentration (Phase 6)**: Depends on Phase 2 (modifies the same grid template) - **US6 D20 Size (Phase 7)**: Independent — can run in parallel with any phase after Phase 2 - **Polish (Phase 8)**: Depends on all previous phases ### User Story Dependencies - **US1 (Inline Conditions)**: Foundational — extracted to Phase 2 since all stories depend on it - **US2 (Row Click Stat Block)**: Depends on US1 layout restructure (Phase 2) - **US3 (Hover Remove)**: Depends on US1 layout restructure (Phase 2) - **US4 (AC Shield)**: Independent — new component + replacement in AcDisplay - **US5 (Concentration Target)**: Depends on US1 grid template change (Phase 2) - **US6 (D20 Size)**: Independent — single className change ### Within Each User Story - Implementation tasks are sequential within each story - Stories are independently testable at each checkpoint ### Parallel Opportunities After Phase 2 (Foundational) completes: - T007 (US3), T008 (US4), T010 (US5), and T011 (US6) can all start in parallel - T004-T006 (US2) can run in parallel with T008 (US4) and T011 (US6) --- ## Parallel Example: After Phase 2 ```bash # These can all launch in parallel after foundational layout restructure: Task: "T007 [US3] Hover-only remove button in combatant-row.tsx" Task: "T008 [US4] Create AcShield component in ac-shield.tsx" Task: "T011 [US6] Increase D20Icon size in combatant-row.tsx" # Then sequentially: Task: "T009 [US4] Integrate AcShield into AcDisplay in combatant-row.tsx" (after T008) Task: "T010 [US5] Widen concentration grid column in combatant-row.tsx" (after T007 or independently) ``` --- ## Implementation Strategy ### MVP First (Phase 2 + User Story 2) 1. Complete Phase 2: Layout restructure (inline conditions, hover-only "+") 2. Complete Phase 3: Row click stat block + remove BookOpen icon 3. **STOP and VALIDATE**: Conditions inline, row click works, no regressions 4. This alone delivers the biggest visual improvement ### Incremental Delivery 1. Phase 2 → Inline conditions (biggest layout change) 2. Phase 3 → Row click stat block (biggest UX improvement) 3. Phase 4 → Hover-only remove (visual cleanup) 4. Phase 5 → AC shield shape (visual polish) 5. Phase 6+7 → Concentration target + D20 size (minor refinements) 6. Phase 8 → Polish and verify all states --- ## Notes - All tasks modify `apps/web/src/components/` only — no domain or application layer changes - Only one new file created: `apps/web/src/components/ac-shield.tsx` - The main file `combatant-row.tsx` is modified by most tasks — be careful with parallel edits to this file - Existing domain tests must pass after all changes (`pnpm check`) - Commit after each phase or logical group for clean history