Mechanical reformat — no logic changes. 398 files changed, 19125 insertions(+), 16457 deletions(-). Prettier 3.8.4 with .prettierrc (singleQuote:true, semi:true, tabWidth:2, trailingComma:all, printWidth:100). Isolated per D-13-08 for reviewability.
4.4 KiB
phase: 06-ux-polish plan: "04" subsystem: pwa/styles tags: [css, animation, d-13] dependency_graph: requires: [] provides: - "@keyframes pulse (apps/pwa/src/styles/tokens.css)" affects: - LiveSyncIndicator (reconnecting dot now resolves the global pulse keyframe) - PushPermissionPrompt (redundant local spin redefinition removed) tech_stack: added: [] patterns: - Global keyframe resolution: all animation consumers reference tokens.css keyframes, not local <style> blocks key_files: created: [] modified: - apps/pwa/src/styles/tokens.css - apps/pwa/src/components/PushPermissionPrompt.tsx decisions:
- D-13: @keyframes pulse added globally to tokens.css so the LiveSyncIndicator reconnecting dot animates regardless of component mount order; the redundant local spin redefinition in PushPermissionPrompt.tsx removed for hygiene metrics: duration_minutes: 5 completed_date: "2026-06-10" tasks_completed: 2 files_changed: 2
Phase 06 Plan 04: Sync-Indicator Animations (D-13) Summary
One-liner: Added global @keyframes pulse to tokens.css and removed the redundant local @keyframes spin block from PushPermissionPrompt.tsx so both sync-state animations resolve from the stylesheet.
Tasks Completed
| Task | Name | Commit | Files |
|---|---|---|---|
| 1 | Add @keyframes pulse globally; remove redundant spin redefinition | 81f2678 |
tokens.css, PushPermissionPrompt.tsx |
| 2 (checkpoint) | playwright-cli — confirm spinner spins and reconnecting dot pulses | (verification only) | — |
What Was Built
The plan's corrected diagnosis was that @keyframes spin was already global in tokens.css (lines 140–147) so the SyncStateToast spinner already worked. The two real bugs were:
- Missing
@keyframes pulseintokens.css→LiveSyncIndicator's reconnecting dot (animation: 'pulse 1.4s ease-in-out infinite') never animated. - Redundant local
<style>block inPushPermissionPrompt.tsxthat redefined@keyframes spin— harmless but incorrect; removed for hygiene.
Fix (commit 81f2678):
- Added
@keyframes pulse { 0%, 100% { opacity: 1 } 50% { opacity: 0.4 } }totokens.cssdirectly after@keyframes spin, matching the existing block format (no vendor prefixes, noanimation-fill-modeinside). - Deleted the
<style>block fromPushPermissionPrompt.tsx. The inlineanimation: 'spin 1s linear infinite'style on the Loader2 element was left intact — it still resolves to the global keyframe. LiveSyncIndicator.tsxandSyncStateToast.tsxwere not modified; their inline animation references are correct.
Checkpoint Verification (Task 2)
Verified via playwright-cli against desktop Chromium with DEV_AUTH_BYPASS=true:
- SyncStateToast Loader2 spinner — computed
animationName === 'spin'; transform sampled rotating (1s linear infinite). PASS. - LiveSyncIndicator reconnecting dot — computed
animationName === 'pulse'; opacity oscillating at 1.4s ease-in-out. PASS. - No console errors about undefined keyframes.
No follow-up fixes were needed after the checkpoint.
Residual Device-Only Item
CP-04.3 (iOS device-only): PushPermissionPrompt's spinner renders only inside an installed iOS/standalone PWA — not drivable in desktop Chromium. Code-confirmed: the spinner uses the same inline animation: 'spin 1s linear infinite' that resolves to the global @keyframes spin in tokens.css. A human/device spot-check during Phase 3 Gate 2 or the go-live deploy is sufficient.
Deviations from Plan
None — plan executed exactly as written.
Threat Flags
None. Pure CSS keyframe addition and redundant inline-style deletion. No data, no network, no auth surface touched. T-06-04 accepted per threat model.
Self-Check: PASSED
apps/pwa/src/styles/tokens.css— FOUND (contains@keyframes pulse)apps/pwa/src/components/PushPermissionPrompt.tsx— FOUND (no@keyframes spinblock)- Commit
81f2678— FOUND (fix(06-04): add global pulse keyframe and drop redundant spin redefinition) @keyframes pulsepresent exactly once in tokens.css — CONFIRMED- No
@keyframes spinin PushPermissionPrompt.tsx — CONFIRMED