Files
initiative/specs/027-ui-polish/tasks.md

12 KiB
Raw Blame History

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.

  • 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.
  • 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.
  • 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.

  • 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.
  • 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).
  • 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.

  • 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.

  • 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 <span> 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.
  • 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.

  • 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.

  • 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

  • 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
  • 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
  • T014 Verify touch device behavior: "+" condition button and "×" remove button are accessible via tap/focus even without hover
  • T015 [P] Style browser scrollbars to match dark UI by adding scrollbar-color and scrollbar-width properties in apps/web/src/index.css
  • 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
  • T017 [P] Remove the "Initiative Tracker" <header> 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

# 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