CSP + Alpine x-transition: modals need explicit CSS transitions
On this page
Overview
Under the strict Content Security Policy (style-src 'self'), canopy-web cannot use inline style= attributes. Alpine’s x-transition directive needs a real CSS transition property on the element to drive its animation. When inline styles were externalised for CSP, the implicit transition timing the browser previously supplied was lost — so x-transition waits forever for a transitionend event that never fires, and Playwright’s stability check times out clicking buttons inside the modal. This runbook records the hazard so a future CSP refactor doesn’t silently reintroduce it.
Symptom
A Playwright test fails with Timeout waiting for element to be visible, enabled, and stable on a button inside a modal that uses x-show + x-transition. First known consumer: the deny modal in tests/e2e/specs/applications.spec.ts. Alpine leaves the modal in a never-resolving "in-transition" state; Playwright’s 500ms layout-quiescence check intermittently lands there and times out on submitBtn.click().
Root cause
x-transition with no CSS transition property → no transitionend event → Alpine never completes the transition. Commit b4ee102 ("fix: strict CSP") refactored inline style= attributes into utility classes to satisfy style-src 'self'. Structurally correct for CSP, but it dropped the implicit transition timing the inline styles carried. The bug was latent until the Stage 1 Orchard CSS work changed the resolution timing of the modal dialog’s background: var(--orchard-surface) (served from the dynamic /theme.css endpoint).
Fix (load-bearing CSS)
services/canopy-web/static/css/canopy-web.css — .u-modal-overlay and .u-modal-dialog MUST carry explicit transition properties:
.u-modal-overlay { /* … */ transition: opacity 150ms ease-in-out; }
.u-modal-dialog { /* … */ transition: transform 150ms ease-in-out; }
A /* … load-bearing … */ comment sits above these rules so a future refactor doesn’t strip them. Fixed in Stage 3 MR1 (closes #511) via these four lines.
Prevention checklist
-
When any Playwright test times out on "visible, enabled, and stable" for a button inside an
x-show+x-transitionmodal: confirm the modal’s CSS class carries an explicittransition. -
When a CSP / inline-style refactor lands on canopy-web:
grepforx-transitionin the touched templates and verify the corresponding utility classes still carrytransitionproperties. -
The same hazard applies to any Alpine-driven animation that was previously implicit via inline styles.
Related files
-
services/canopy-web/templates/applications/process.html— deny modal (first known consumer). -
services/canopy-web/templates/base.html— loadsalpine-csp.min.js(sinceb4ee102). -
tests/e2e/specs/applications.spec.ts— the spec that surfaced the regression. -
Configuration Reference — the BFF Content Security Policy.