# Phase 22 — Animated Background: Make It Actually Animate **Source:** `TODO.md` L5 — *"Fix background animation not working, just blinking"* **Story:** `.agents/user_stories/background-animation.md` (created by task 02) **Context:** `frontend/assets/styles.css` — the phase-08 animated background block (~lines 60–100): `body::before` (44px drifting grid, 1px lines at ~35% `--line` alpha, radial mask `radial-gradient(120% 90% at 50% 0%, black 25%, transparent 78%)`, `animation: bg-grid-drift 60s linear infinite`) and `body::after` (two soft radial glows, `animation: bg-glow-breathe 14s ease-in-out infinite alternate`, opacity 0.65↔1 + scale 1↔1.05). Both layers are `position: fixed; inset: 0; z-index: -1; pointer-events: none`. `html` owns the `var(--bg)` canvas and `body` is `background: transparent` (~lines 42–54) — if any later rule occludes that, the layers vanish. The **phase-08 design comments are the spec** for what "working" means. ## Objective Owner report 2026-08-24: the background "just blinks" — i.e. the motion the phase-08 design promised (a slow, seamless grid drift + a gentle glow breathe) is not perceived; at most a flicker/blink is visible. Diagnose which layer(s) actually fail in a real Chromium viewport, fix the CSS, and leave a background that visibly and smoothly animates as designed — no blink, no static frame, no jank. ## Owner-confirmed (2026-08-24, roadmap A3) 1. **Intended effect = the phase-08 design comments:** seamless 60s grid drift (one cell per loop) + 14s ease glow breathing. The fix serves that design, not a redesign. 2. **Pure CSS, zero JS** (phase-08 anchor) — no animation JS, no new assets, no `filter: blur` (perf note in the block). ## Design / diagnostic plan The fix is found, not guessed — work through this checklist in a real Chromium window (dev server, full page, ~15s of observation): 1. **Per-layer visibility:** toggle each pseudo-element (DevTools generated-content / a temp outline) and screenshot — is the grid visible at all? Is only the glow (the "blink" the user perceives) alive? 2. **Grid layer:** sample `background-position` on `body::before` at two timestamps — is it actually moving? Is the radial mask fading the visible region so small that the 44px/60s drift is imperceptible? (If the drift is real but too faint: raise the grid line alpha and/or the mask's visible radius — smallest change that reads as "smooth drift".) 3. **Glow layer:** is the 14s breathe reading as a *blink*? (If the opacity swing 0.65↔1 is perceived as pulsing: lengthen the period and/or narrow the opacity delta so it reads as breathing.) 4. **Occlusion check:** confirm nothing later in `styles.css` (or in `html`/`body` rules) paints an opaque background over the `z-index: -1` layers — the phase-08 comment at ~line 42 is the contract. 5. **Apply the fix in `styles.css`** — document the found root cause in the phase report (screenshot before/after in `.agents/screenshots/22_background_animation/`). ## Dependencies - `08_story_dark_tech_theme` (complete) — owns the layers, the palette, and the "pure CSS, zero JS" anchor this phase must respect. - `07_story_responsive_polish` (complete) — no new overflow at 360px (both layers are `fixed; inset: 0` — keep it that way). ## Tasks 1. `01_fix_background_animation.md` — diagnosis + the CSS fix + source-level unit pins. 2. `02_e2e_story_suite_commit.md` — `tests/e2e/test_background_animation.py` (the story gate, isolated), regression suites, story file, final validation, the single atomic commit, phase move to `complete/`. ## Locked decisions - **Phase-08 anchor honored** — pure CSS, zero JS, no `filter: blur`, WCAG AA palette untouched (background layers carry no text). **A11 untouched** — no CDN, no new assets. **A16 honored** — one new story E2E suite + adapted regressions. No anchor changed. ## Testing & Quality - **Unit (source-level, new `tests/unit/test_background_animation.py`, repo source-pin pattern):** `styles.css` still defines `@keyframes bg-grid-drift` and `@keyframes bg-glow-breathe`; `body::before` animates `bg-grid-drift` with `linear infinite`; `body::after` animates `bg-glow-breathe`; both layers remain `position: fixed; z-index: -1; pointer-events: none`; `html` keeps `background: var(--bg)` and `body` keeps `background: transparent` (the no-occlusion contract). Pin the **final** values the fix lands on (durations/opacities may move per the design plan). - **Integration:** none (no `app/` changes). - **Coverage:** frontend-only; the >90% `app/` gate is unaffected. - **E2E:** `tests/e2e/test_background_animation.py` (task 02), green **in isolation** (prereq `podman compose up -d db`). - **Lint/types:** `uv run ruff check . && uv run pyright` clean. ## Completion Criteria - [ ] In a real Chromium viewport, the background visibly and smoothly animates (grid drift + glow breathe) — screenshot before/after in the phase report; owner's "just blinking" perception gone. - [ ] Root cause documented in `.agents/reports/22_background_animation/`. - [ ] `uv run pytest` green; `uv run pytest --cov=app --cov-report=term-missing` ≥ today's number. - [ ] `uv run pytest tests/e2e/test_background_animation.py -v --no-cov` green in isolation; regressions green in isolation: `test_dark_tech_theme.py`, `test_responsive_polish.py`. - [ ] `uv run ruff check . && uv run pyright` clean. - [ ] UI Structure Check (AGENTS.md rule 5): layers stay behind content (`z-index: -1`, `pointer-events: none`), no text/contrast impact, no 360px overflow. - [ ] `.agents/user_stories/background-animation.md` exists. - [ ] One `--no-gpg-sign` commit (below); `.agents/phases/todo/22_background_animation/` moved to `.agents/phases/complete/`. ## Commit ```bash git add -A .agents/ frontend/ tests/ && git commit --no-gpg-sign -m "fix(ui): animated background actually animates — grid drift and glow breathe per the phase-08 design" ```