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-50from the outer row container<div>(thestatus === "unconscious" && "opacity-50"expression on line 312) inapps/web/src/components/combatant-row.tsx - T002 Apply
opacity-50to the inner grid<div>(line 316) whenstatus === "unconscious"and restructure the JSX so thatHpAdjustPopover(currently rendered insideClickableHpwithin the grid) is moved to render as a sibling outside the grid container, not a child of it, inapps/web/src/components/combatant-row.tsx - T003 Apply
opacity-50to the conditions container<div>(line 388) whenstatus === "unconscious"and restructure the JSX so thatConditionPickeris rendered as a sibling outside the dimmed conditions wrapper, not a child of it, inapps/web/src/components/combatant-row.tsx - T004 Verify that the
HpAdjustPopovercomponent renders at full opacity (no inherited dimming) when opened on a 0-HP combatant, by runningpnpm --filter web devand manually testing - T005 Verify that the
ConditionPickercomponent renders at full opacity (no inherited dimming) when opened on a 0-HP combatant, by runningpnpm --filter web devand 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-50applied in T002/T003) compared to healthy combatants inapps/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 checkto 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)
- Complete T001 (remove container opacity) — this is the root cause fix
- Complete T002 + T003 (apply element-level dimming) — preserves visual distinction
- Verify T004 + T005 + T006 (manual testing)
- Run T007 (quality gate) + T008 (quickstart validation)
- 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
opacityon a parent creates a stacking context that dims all children. The fix must ensure popovers are never DOM descendants of a dimmed element.