# 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. - [x] T001 Remove `opacity-50` from the outer row container `
` (the `status === "unconscious" && "opacity-50"` expression on line 312) in `apps/web/src/components/combatant-row.tsx` - [x] T002 Apply `opacity-50` to the inner grid `
` (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` - [x] T003 Apply `opacity-50` to the conditions container `
` (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` - [x] 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 - [x] 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 - [x] 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 - [x] 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 - [x] T007 Run `pnpm check` to verify all quality gates pass (knip, format, lint, typecheck, test) - [x] 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.