diff --git a/core/http/react-ui/e2e/design-system.spec.js b/core/http/react-ui/e2e/design-system.spec.js index fa9c4e009..2ca03407c 100644 --- a/core/http/react-ui/e2e/design-system.spec.js +++ b/core/http/react-ui/e2e/design-system.spec.js @@ -17,4 +17,12 @@ test.describe('Editorial design system', () => { const shadow = await active.evaluate(el => getComputedStyle(el).boxShadow) expect(shadow).not.toBe('none') }) + + test('page reveal animation is defined on .page-transition', async ({ page }) => { + await page.goto('/app/settings') + const pt = page.locator('.page-transition').first() + await expect(pt).toBeVisible({ timeout: 15_000 }) + const name = await pt.evaluate(el => getComputedStyle(el).animationName) + expect(name).toBe('pageReveal') + }) }) diff --git a/core/http/react-ui/src/App.css b/core/http/react-ui/src/App.css index b450b8403..b52086356 100644 --- a/core/http/react-ui/src/App.css +++ b/core/http/react-ui/src/App.css @@ -2900,7 +2900,7 @@ select.input { /* Page route transitions */ .page-transition { - animation: fadeIn 200ms ease; + animation: pageReveal var(--duration-reveal) var(--ease-reveal) both; display: flex; flex-direction: column; flex: 1; @@ -2908,6 +2908,18 @@ select.input { min-width: 0; } +@keyframes pageReveal { + from { opacity: 0; transform: translateY(10px); } + to { opacity: 1; transform: translateY(0); } +} + +/* Orchestrated child stagger - opt-in via .reveal-stagger on a container. + Each direct child reveals with an incremental delay driven by --reveal-index. */ +.reveal-stagger > * { + animation: pageReveal var(--duration-reveal) var(--ease-reveal) both; + animation-delay: calc(var(--reveal-index, 0) * 60ms); +} + /* Chat-specific styles */ .chat-layout { display: flex; diff --git a/core/http/react-ui/src/hooks/useStagger.js b/core/http/react-ui/src/hooks/useStagger.js new file mode 100644 index 000000000..ce6fa3c38 --- /dev/null +++ b/core/http/react-ui/src/hooks/useStagger.js @@ -0,0 +1,8 @@ +// Returns an inline style setting --reveal-index for orchestrated reveals. +// Usage: