From 55d322a72727bd41dfda9afb6c9d1aee7e04e553 Mon Sep 17 00:00:00 2001 From: Lukas Date: Wed, 11 Mar 2026 11:54:22 +0100 Subject: [PATCH] Fix concentration glow clipping at container edges Add padding to the inner combatant list container so the box-shadow glow from the concentration-damage animation renders fully on all sides, preventing clipping by the scrollable parent's implicit overflow-x. Co-Authored-By: Claude Opus 4.6 --- CLAUDE.md | 2 + apps/web/src/App.tsx | 2 +- .../checklists/requirements.md | 35 ++++++ .../data-model.md | 3 + specs/033-fix-concentration-glow-clip/plan.md | 69 +++++++++++ .../quickstart.md | 20 +++ .../research.md | 35 ++++++ specs/033-fix-concentration-glow-clip/spec.md | 68 +++++++++++ .../033-fix-concentration-glow-clip/tasks.md | 115 ++++++++++++++++++ 9 files changed, 348 insertions(+), 1 deletion(-) create mode 100644 specs/033-fix-concentration-glow-clip/checklists/requirements.md create mode 100644 specs/033-fix-concentration-glow-clip/data-model.md create mode 100644 specs/033-fix-concentration-glow-clip/plan.md create mode 100644 specs/033-fix-concentration-glow-clip/quickstart.md create mode 100644 specs/033-fix-concentration-glow-clip/research.md create mode 100644 specs/033-fix-concentration-glow-clip/spec.md create mode 100644 specs/033-fix-concentration-glow-clip/tasks.md diff --git a/CLAUDE.md b/CLAUDE.md index 04b8c6f..2cf766a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -83,6 +83,8 @@ The constitution (`.specify/memory/constitution.md`) governs all feature work: ## Active Technologies - TypeScript 5.8 (strict mode, `verbatimModuleSyntax`) + React 19, Tailwind CSS v4, Lucide React, class-variance-authority (cva) (032-inline-confirm-buttons) - N/A (no persistence changes — confirm state is ephemeral) (032-inline-confirm-buttons) +- TypeScript 5.8, CSS (Tailwind CSS v4) + React 19, Tailwind CSS v4 (033-fix-concentration-glow-clip) +- N/A (no persistence changes) (033-fix-concentration-glow-clip) ## Recent Changes - 032-inline-confirm-buttons: Added TypeScript 5.8 (strict mode, `verbatimModuleSyntax`) + React 19, Tailwind CSS v4, Lucide React, class-variance-authority (cva) diff --git a/apps/web/src/App.tsx b/apps/web/src/App.tsx index 3d4022e..a41710f 100644 --- a/apps/web/src/App.tsx +++ b/apps/web/src/App.tsx @@ -155,7 +155,7 @@ export function App() { {/* Scrollable area — combatant list */}
-
+
{encounter.combatants.length === 0 ? (

No combatants yet — add one to get started diff --git a/specs/033-fix-concentration-glow-clip/checklists/requirements.md b/specs/033-fix-concentration-glow-clip/checklists/requirements.md new file mode 100644 index 0000000..f2fb52d --- /dev/null +++ b/specs/033-fix-concentration-glow-clip/checklists/requirements.md @@ -0,0 +1,35 @@ +# Specification Quality Checklist: Fix Concentration Shake Glow Clipping + +**Purpose**: Validate specification completeness and quality before proceeding to planning +**Created**: 2026-03-11 +**Feature**: [spec.md](../spec.md) + +## Content Quality + +- [x] No implementation details (languages, frameworks, APIs) +- [x] Focused on user value and business needs +- [x] Written for non-technical stakeholders +- [x] All mandatory sections completed + +## Requirement Completeness + +- [x] No [NEEDS CLARIFICATION] markers remain +- [x] Requirements are testable and unambiguous +- [x] Success criteria are measurable +- [x] Success criteria are technology-agnostic (no implementation details) +- [x] All acceptance scenarios are defined +- [x] Edge cases are identified +- [x] Scope is clearly bounded +- [x] Dependencies and assumptions identified + +## Feature Readiness + +- [x] All functional requirements have clear acceptance criteria +- [x] User scenarios cover primary flows +- [x] Feature meets measurable outcomes defined in Success Criteria +- [x] No implementation details leak into specification + +## Notes + +- All items pass. The spec is a focused bug fix with clear acceptance criteria and well-defined scope. +- The Assumptions section mentions CSS techniques as examples of what might cause the issue, which is acceptable context without prescribing a solution. diff --git a/specs/033-fix-concentration-glow-clip/data-model.md b/specs/033-fix-concentration-glow-clip/data-model.md new file mode 100644 index 0000000..2c8cb6d --- /dev/null +++ b/specs/033-fix-concentration-glow-clip/data-model.md @@ -0,0 +1,3 @@ +# Data Model: Fix Concentration Glow Clipping + +No data model changes required. This is a CSS-only visual bug fix. The existing domain model, state transitions, and component props remain unchanged. diff --git a/specs/033-fix-concentration-glow-clip/plan.md b/specs/033-fix-concentration-glow-clip/plan.md new file mode 100644 index 0000000..ceeaac7 --- /dev/null +++ b/specs/033-fix-concentration-glow-clip/plan.md @@ -0,0 +1,69 @@ +# Implementation Plan: Fix Concentration Shake Glow Clipping + +**Branch**: `033-fix-concentration-glow-clip` | **Date**: 2026-03-11 | **Spec**: [spec.md](spec.md) +**Input**: Feature specification from `/specs/033-fix-concentration-glow-clip/spec.md` + +## Summary + +The concentration-damage glow animation (`box-shadow`) is clipped on left and right edges because the scrollable combatant list container's `overflow-y: auto` implicitly sets `overflow-x: auto` per CSS spec, creating a clipping context. The fix adds horizontal padding to the inner container so the glow has room to render within the overflow area. + +## Technical Context + +**Language/Version**: TypeScript 5.8, CSS (Tailwind CSS v4) +**Primary Dependencies**: React 19, Tailwind CSS v4 +**Storage**: N/A (no persistence changes) +**Testing**: Vitest (visual verification — CSS-only fix) +**Target Platform**: Web (modern browsers, 320px+ viewports) +**Project Type**: Web application +**Performance Goals**: 60fps animation, zero layout shift +**Constraints**: No horizontal scrollbar, no layout shift, mobile-compatible +**Scale/Scope**: Single CSS class change in one file + +## Constitution Check + +*GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.* + +| Principle | Status | Notes | +|-----------|--------|-------| +| I. Deterministic Domain Core | PASS | No domain changes | +| II. Layered Architecture | PASS | Change is in adapter layer (web/UI) only | +| III. Clarification-First | PASS | Root cause is clear, fix is straightforward | +| IV. Escalation Gates | PASS | Fix matches spec scope exactly | +| V. MVP Baseline Language | PASS | N/A for bug fix | +| VI. No Gameplay Rules | PASS | N/A — visual fix only | + +**Post-design re-check**: All gates still pass. No architectural changes introduced. + +## Root Cause + +1. The combatant list lives inside `

` (App.tsx:157) +2. CSS spec: when `overflow-y` is non-`visible`, `overflow-x` computes to `auto` (not `visible`) +3. The `concentration-glow` animation applies `box-shadow: 0 0 4px 2px #c084fc` (~6px outward spread) +4. The implicit `overflow-x: auto` clips this box-shadow on left and right edges + +## Fix Approach + +Add horizontal padding (`px-2`, 8px) to the inner `
` at App.tsx:158. This gives the box-shadow room to render within the container's content area, avoiding clipping. `box-shadow` is paint-only and does not affect layout, so no scrollbar or shift will result. + +## Project Structure + +### Documentation (this feature) + +```text +specs/033-fix-concentration-glow-clip/ +├── spec.md +├── plan.md # This file +├── research.md # Root cause analysis and fix strategy +├── data-model.md # No data model changes +├── quickstart.md # Verification steps +└── tasks.md # Task breakdown (generated by /speckit.tasks) +``` + +### Source Code (affected files) + +```text +apps/web/src/ +└── App.tsx # Line ~158: add px-2 to inner combatant list container +``` + +**Structure Decision**: Existing monorepo structure. Only one file in the adapter layer (apps/web) is modified. No new files, no structural changes. diff --git a/specs/033-fix-concentration-glow-clip/quickstart.md b/specs/033-fix-concentration-glow-clip/quickstart.md new file mode 100644 index 0000000..ffd097a --- /dev/null +++ b/specs/033-fix-concentration-glow-clip/quickstart.md @@ -0,0 +1,20 @@ +# Quickstart: Fix Concentration Glow Clipping + +## What Changed + +The scrollable combatant list container clips the `box-shadow` glow effect from the concentration-damage animation. The fix adds horizontal padding to the inner container so the glow has room to render. + +## Files to Modify + +1. **`apps/web/src/App.tsx`** — Add horizontal padding to the inner `
` inside the scrollable combatant list area (line ~158) + +## How to Verify + +1. Run `pnpm --filter web dev` +2. Add a combatant to an encounter +3. Toggle concentration on the combatant +4. Reduce the combatant's HP (deal damage) +5. Observe the purple glow animation — it should be fully visible on left and right edges +6. Test at mobile widths (320px+) — no horizontal scrollbar should appear +7. Add multiple concentrating combatants, deal damage to all simultaneously — each row's glow should be independently visible without overlap artifacts +8. With a long encounter list (6+ combatants), scroll so a concentrating combatant is at the top or bottom edge of the visible area, deal damage — glow should still be fully visible diff --git a/specs/033-fix-concentration-glow-clip/research.md b/specs/033-fix-concentration-glow-clip/research.md new file mode 100644 index 0000000..0898d77 --- /dev/null +++ b/specs/033-fix-concentration-glow-clip/research.md @@ -0,0 +1,35 @@ +# Research: Fix Concentration Glow Clipping + +## Root Cause Analysis + +### Decision: The glow is clipped by the scrollable parent container's implicit overflow-x + +**Rationale**: The combatant list is wrapped in a `
` (App.tsx:157). Per the CSS specification, when one overflow axis is set to a non-`visible` value (here `overflow-y: auto`), the other axis (`overflow-x`) computes to `auto` rather than remaining `visible`. This creates a clipping context that cuts off the `box-shadow` from the `concentration-glow` animation on the left and right edges. + +The glow is defined as: +```css +box-shadow: 0 0 4px 2px #c084fc; +``` +This extends ~6px outward from the combatant row element. The parent's implicit `overflow-x: auto` clips this outward extension. + +**Alternatives considered**: +- `border-radius` on the row causing clipping — rejected; `border-radius` shapes `box-shadow` but does not clip it +- `translate` in the shake animation pushing the element off-screen — rejected; max displacement is only 3px and returns to 0 + +## Fix Strategy + +### Decision: Add horizontal padding to the scrollable container to accommodate the glow spread + +**Rationale**: The simplest fix is to add left/right padding to the scrollable container (or its inner flex-col child) so the `box-shadow` has room to render within the overflow area without being clipped. This avoids changing the overflow behavior (which is needed for vertical scrolling) and avoids restructuring the component hierarchy. + +The glow spread is `4px blur + 2px spread = ~6px` outward. Adding `px-2` (8px) padding to the inner `
` at App.tsx:158 would provide enough space. + +**Alternatives considered**: +1. **Change `overflow-y-auto` to `overflow-y-scroll` + `overflow-x-visible`** — rejected; CSS spec still forces `overflow-x` to `auto` when `overflow-y` is non-`visible` +2. **Use `outline` instead of `box-shadow`** — viable but changes the visual appearance; `outline` doesn't respect `border-radius` in all browsers +3. **Use `inset box-shadow`** — would avoid overflow clipping but changes the visual effect from an outer glow to an inner glow +4. **Negative margin + padding trick on the scrollable container** — more complex, same net effect as simple padding + +### Decision: Verify no horizontal scrollbar is introduced + +**Rationale**: Adding padding to the inner container should not cause a horizontal scrollbar since the content still fits within the parent. The `box-shadow` renders in the padding area but does not affect layout (box-shadow is a paint-only effect, not a layout-affecting property). No additional `overflow-x: hidden` should be needed. diff --git a/specs/033-fix-concentration-glow-clip/spec.md b/specs/033-fix-concentration-glow-clip/spec.md new file mode 100644 index 0000000..d4f804a --- /dev/null +++ b/specs/033-fix-concentration-glow-clip/spec.md @@ -0,0 +1,68 @@ +# Feature Specification: Fix Concentration Shake Glow Clipping + +**Feature Branch**: `033-fix-concentration-glow-clip` +**Created**: 2026-03-11 +**Status**: Draft +**Input**: User description: "Fix concentration shake glow clipped at container edges" + +## User Scenarios & Testing *(mandatory)* + +### User Story 1 - Concentration Damage Glow Visible (Priority: P1) + +As a game master viewing the encounter tracker, when a concentrating creature takes damage and the shake + glow animation plays, I see the full glow effect without any visual clipping on the left or right edges. + +**Why this priority**: This is the core bug being fixed. The glow animation is the primary visual feedback for concentration damage events, and clipping undermines its purpose. + +**Independent Test**: Can be tested by dealing damage to a concentrating creature and visually verifying the glow extends fully without being cut off at container boundaries. + +**Acceptance Scenarios**: + +1. **Given** a creature is concentrating on a spell, **When** the creature takes damage and the shake + glow animation triggers, **Then** the glow effect is fully visible on both the left and right sides throughout the entire animation duration +2. **Given** a creature is concentrating and the encounter list has its default layout, **When** damage triggers the animation, **Then** no part of the glow is clipped or hidden by overflow constraints + +--- + +### User Story 2 - No Layout Side Effects (Priority: P1) + +As a user on any device, the glow fix does not introduce horizontal scrollbars, layout shifts, or visual artifacts outside the animation area. + +**Why this priority**: A fix that trades one visual bug for another (e.g., scroll jank) would be a regression. This is equally critical as the glow fix itself. + +**Independent Test**: Can be tested by triggering the animation repeatedly on both desktop and mobile viewports and confirming no horizontal scrollbar appears and no elements shift position. + +**Acceptance Scenarios**: + +1. **Given** the encounter tracker is displayed at desktop width, **When** the shake + glow animation plays, **Then** no horizontal scrollbar appears on the page or encounter container +2. **Given** the encounter tracker is displayed at mobile width (320px - 480px), **When** the shake + glow animation plays, **Then** no layout shift occurs and the animation renders correctly within the viewport + +--- + +### Edge Cases + +- What happens when multiple concentrating creatures take damage simultaneously? The glow on each row must remain independently visible without overlap artifacts. +- What happens when the creature row is at the very top or bottom of a scrollable encounter list? The glow must still be fully visible. +- What happens on very narrow viewports (below 320px)? The glow should degrade gracefully without causing overflow. + +## Requirements *(mandatory)* + +### Functional Requirements + +- **FR-001**: The concentration damage glow effect MUST be fully visible on both left and right edges of the combatant row throughout the shake animation +- **FR-002**: The glow fix MUST NOT introduce a horizontal scrollbar on the page or any parent container +- **FR-003**: The glow fix MUST NOT cause any layout shift or repositioning of other elements during or after the animation +- **FR-004**: The shake + glow animation MUST render correctly on mobile-width screens (320px and above) +- **FR-005**: The glow effect MUST remain visually correct when multiple combatant rows animate simultaneously + +## Success Criteria *(mandatory)* + +### Measurable Outcomes + +- **SC-001**: The glow effect is visually complete (no clipping) on 100% of concentration damage events across all supported viewport widths (320px and above) +- **SC-002**: Zero horizontal scrollbar appearances caused by the glow animation at any viewport width +- **SC-003**: Zero measurable layout shift (CLS = 0) introduced by the animation fix + +## Assumptions + +- The existing shake animation behavior and timing are correct and should be preserved; only the glow clipping needs to be fixed. +- The glow effect uses CSS techniques (e.g., box-shadow, outline, or pseudo-elements) that may be clipped by `overflow: hidden` or similar properties on ancestor containers. +- "Mobile-width screens" refers to viewports of 320px width and above, consistent with standard mobile device support. diff --git a/specs/033-fix-concentration-glow-clip/tasks.md b/specs/033-fix-concentration-glow-clip/tasks.md new file mode 100644 index 0000000..c0b4351 --- /dev/null +++ b/specs/033-fix-concentration-glow-clip/tasks.md @@ -0,0 +1,115 @@ +# Tasks: Fix Concentration Shake Glow Clipping + +**Input**: Design documents from `/specs/033-fix-concentration-glow-clip/` +**Prerequisites**: plan.md (required), spec.md (required for user stories), research.md, data-model.md, quickstart.md + +**Tests**: No test tasks — this is a CSS-only visual fix. Verification is done via manual inspection per quickstart.md. + +**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) +- Include exact file paths in descriptions + +## Phase 1: Setup + +**Purpose**: No setup needed — existing project, no new dependencies or configuration changes. + +*(No tasks in this phase)* + +--- + +## Phase 2: Foundational (Blocking Prerequisites) + +**Purpose**: No foundational work needed — the fix modifies existing CSS layout only. + +*(No tasks in this phase)* + +--- + +## Phase 3: User Story 1 - Concentration Damage Glow Visible (Priority: P1) MVP + +**Goal**: The concentration-damage glow effect is fully visible on both left and right edges without clipping. + +**Independent Test**: Deal damage to a concentrating creature and verify the purple glow extends fully on all sides without being cut off at container edges. + +### Implementation for User Story 1 + +- [x] T001 [US1] Add horizontal padding (`px-2`) to the inner combatant list container `
` in `apps/web/src/App.tsx` (line ~158) so the `box-shadow` glow renders within the overflow area instead of being clipped + +**Checkpoint**: At this point, the glow should be fully visible on left and right edges. + +--- + +## Phase 4: User Story 2 - No Layout Side Effects (Priority: P1) + +**Goal**: The padding fix does not introduce horizontal scrollbars, layout shifts, or visual artifacts. + +**Independent Test**: Trigger the animation at desktop and mobile widths (320px+), confirm no horizontal scrollbar appears and no elements shift position. + +### Implementation for User Story 2 + +- [x] T002 [US2] Verify at desktop and mobile viewports (320px+) that the padding added in T001 does not cause a horizontal scrollbar or layout shift in `apps/web/src/App.tsx`; also verify that multiple concentrating rows animating simultaneously display independent glows without overlap artifacts; adjust padding value if needed + +**Checkpoint**: Animation renders correctly at all viewport widths with no side effects. + +--- + +## Phase 5: Polish & Cross-Cutting Concerns + +**Purpose**: Final validation and quality gates. + +- [x] T003 Run `pnpm check` to ensure all quality gates pass (lint, typecheck, tests, coverage) +- [x] T004 Run quickstart.md validation steps from `specs/033-fix-concentration-glow-clip/quickstart.md` + +--- + +## Dependencies & Execution Order + +### Phase Dependencies + +- **Phase 3 (US1)**: No prerequisites — single file change +- **Phase 4 (US2)**: Depends on T001 completion (verifies T001 doesn't regress) +- **Phase 5 (Polish)**: Depends on T001 and T002 completion + +### User Story Dependencies + +- **User Story 1 (P1)**: Independent — core fix +- **User Story 2 (P1)**: Depends on US1 — verification of no side effects + +### Within Each User Story + +- US1: Single task (T001) +- US2: Single verification task (T002) dependent on T001 + +### Parallel Opportunities + +- No parallel opportunities — this is a 1-file, 1-line fix with sequential verification + +--- + +## Implementation Strategy + +### MVP First (User Story 1 Only) + +1. Complete T001: Add padding to inner container +2. **STOP and VALIDATE**: Visually verify glow is no longer clipped +3. Complete T002: Verify no layout side effects +4. Complete T003-T004: Quality gates and quickstart validation + +### Incremental Delivery + +1. T001 → Glow fix applied +2. T002 → No regressions confirmed +3. T003-T004 → Quality gates pass, ready to merge + +--- + +## Notes + +- This is a minimal CSS-only fix: one Tailwind class addition to one file +- The root cause is CSS spec behavior: `overflow-y: auto` forces `overflow-x: auto`, clipping `box-shadow` +- `box-shadow` is paint-only and does not affect layout, so padding accommodates the glow without shifting content +- If `px-2` (8px) is insufficient for the 6px glow spread, increase to `px-3` (12px)