Files
initiative/specs/020-fix-zero-hp-opacity/tasks.md

5.5 KiB

Tasks: Fix Zero-HP Opacity

Input: Design documents from /specs/020-fix-zero-hp-opacity/ Prerequisites: plan.md (required), spec.md (required for user stories), research.md, data-model.md, quickstart.md

Tests: No test tasks included (not explicitly requested in spec). Existing tests must continue to pass via pnpm check.

Organization: Tasks are grouped by user story. US1 and US2 share the same root cause fix, so they are addressed together in Phase 2 (Foundational). US3 is verified as part of the same change.

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 setup required. This is a single-file bug fix in an existing codebase with all tooling already configured.

(No tasks — skip to Phase 2)


Phase 2: Foundational (Core Fix)

Purpose: Remove the container-level opacity-50 that cascades to popover/dropdown children and replace it with element-level dimming that preserves the unconscious visual indicator without affecting overlays.

  • T001 Remove opacity-50 from the outer row container <div> (the status === "unconscious" && "opacity-50" expression on line 312) in apps/web/src/components/combatant-row.tsx
  • T002 Apply opacity-50 to the inner grid <div> (line 316) when status === "unconscious" and restructure the JSX so that HpAdjustPopover (currently rendered inside ClickableHp within the grid) is moved to render as a sibling outside the grid container, not a child of it, in apps/web/src/components/combatant-row.tsx
  • T003 Apply opacity-50 to the conditions container <div> (line 388) when status === "unconscious" and restructure the JSX so that ConditionPicker is rendered as a sibling outside the dimmed conditions wrapper, not a child of it, in apps/web/src/components/combatant-row.tsx
  • T004 Verify that the HpAdjustPopover component renders at full opacity (no inherited dimming) when opened on a 0-HP combatant, by running pnpm --filter web dev and manually testing
  • T005 Verify that the ConditionPicker component renders at full opacity (no inherited dimming) when opened on a 0-HP combatant, by running pnpm --filter web dev and manually testing
  • T005a Verify edge case transitions: (a) heal a 0-HP combatant while the HP popout is open — row should transition to normal styling, popout stays functional; (b) reduce a combatant to 0 HP while the HP popout is open — row should transition to dimmed styling, popout stays at full opacity. Manual test via pnpm --filter web dev.

Checkpoint: At this point, both US1 (HP popover) and US2 (condition picker) should render at full opacity for 0-HP combatants.


Phase 3: User Story 3 - Row Visual Distinction (Priority: P2)

Goal: Unconscious combatants remain visually distinguishable from conscious combatants.

Independent Test: View encounter list with a mix of conscious and unconscious combatants; unconscious rows should look dimmed while their popouts are full opacity.

Implementation for User Story 3

  • T006 [US3] Verify that unconscious combatant rows still appear visually dimmed (via the element-level opacity-50 applied in T002/T003) compared to healthy combatants in apps/web/src/components/combatant-row.tsx

Checkpoint: All user stories verified — unconscious rows are dimmed, but popouts/dropdowns render at full opacity.


Phase 4: Polish & Cross-Cutting Concerns

  • T007 Run pnpm check to verify all quality gates pass (knip, format, lint, typecheck, test)
  • T008 Run quickstart.md validation steps to confirm end-to-end fix

Dependencies & Execution Order

Phase Dependencies

  • Phase 2 (Foundational): No dependencies — start immediately. T001 must complete before T002/T003. T002 and T003 can run in parallel. T004/T005 are manual verification after T002/T003.
  • Phase 3 (US3): Depends on Phase 2 completion (visual distinction is a side effect of the fix approach).
  • Phase 4 (Polish): Depends on Phase 2 and Phase 3 completion.

User Story Dependencies

  • US1 (HP popover): Addressed by T001 + T002. No dependency on other stories.
  • US2 (Condition picker): Addressed by T001 + T003. No dependency on other stories.
  • US3 (Visual distinction): Verified by T006. Depends on T002/T003 being correctly implemented.

Parallel Opportunities

  • T002 and T003 can be done in parallel (different sections of the same file, but non-overlapping regions)
  • T004 and T005 can be verified in parallel

Implementation Strategy

MVP First (All Stories in One Pass)

  1. Complete T001 (remove container opacity) — this is the root cause fix
  2. Complete T002 + T003 (apply element-level dimming) — preserves visual distinction
  3. Verify T004 + T005 + T006 (manual testing)
  4. Run T007 (quality gate) + T008 (quickstart validation)
  5. Done — single commit, single file change

This is a small, surgical bug fix. All three user stories are addressed by the same structural change in combatant-row.tsx. The entire fix can be completed in a single pass.


Notes

  • Single file change: apps/web/src/components/combatant-row.tsx
  • No domain or application layer changes
  • No new dependencies
  • No new files
  • The key insight: CSS opacity on a parent creates a stacking context that dims all children. The fix must ensure popovers are never DOM descendants of a dimmed element.