@@ -64,13 +69,7 @@ export function AuthScreen({ mode, title, hints, children }: { mode: Mode; title
aria-current={m === mode ? 'page' : undefined}
className="lv-auth-tab"
>
- {m === mode ? (
-
- ) : null}
+ {m === mode ?
: null}
{t(`tabs.${m}`)}
))}
@@ -95,6 +94,6 @@ export function AuthScreen({ mode, title, hints, children }: { mode: Mode; title
))}
-
+ >
)
}
diff --git a/frontend/src/shared/easter/BreakableBlock.tsx b/frontend/src/shared/easter/BreakableBlock.tsx
index 2d82558..ed04397 100644
--- a/frontend/src/shared/easter/BreakableBlock.tsx
+++ b/frontend/src/shared/easter/BreakableBlock.tsx
@@ -1,6 +1,6 @@
-import { AnimatePresence, motion, useReducedMotion } from 'motion/react'
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
+import '@shared/motion/motion.css'
import './block.css'
/** Clicks needed before the block shatters: two crack stages then the burst. */
@@ -15,7 +15,6 @@ type Particle = { id: number; dx: string; dy: string }
*/
export function BreakableBlock() {
const { t } = useTranslation('common')
- const reduced = useReducedMotion()
const [cracks, setCracks] = useState(0)
const [particles, setParticles] = useState
([])
const [cycle, setCycle] = useState(0) // bump to replay the drop after a burst
@@ -44,28 +43,15 @@ export function BreakableBlock() {
return (
-
+
-
+
-
- {particles.map((p) => (
-
- ))}
-
+ {particles.map((p) => (
+
+ ))}
)
}
diff --git a/frontend/src/shared/easter/tests/breakable.test.tsx b/frontend/src/shared/easter/tests/breakable.test.tsx
index 56c7936..3999ab4 100644
--- a/frontend/src/shared/easter/tests/breakable.test.tsx
+++ b/frontend/src/shared/easter/tests/breakable.test.tsx
@@ -4,7 +4,7 @@ import { expect, test, vi } from 'vitest'
import { BreakableBlock } from '../BreakableBlock'
import { renderApp } from '@test/render'
-// motion's useReducedMotion reads matchMedia, which jsdom lacks.
+// prefersReducedMotion reads matchMedia, which jsdom lacks.
vi.stubGlobal('matchMedia', (query: string) => ({
matches: !query.includes('reduce'),
media: query,
diff --git a/frontend/src/shared/layout/topbar/MobileMenu.tsx b/frontend/src/shared/layout/topbar/MobileMenu.tsx
index 0b48dd6..cdbdbe9 100644
--- a/frontend/src/shared/layout/topbar/MobileMenu.tsx
+++ b/frontend/src/shared/layout/topbar/MobileMenu.tsx
@@ -1,7 +1,7 @@
-import { AnimatePresence, MotionConfig, motion } from 'motion/react'
import { type RefObject, useEffect, useRef } from 'react'
import { useTranslation } from 'react-i18next'
import { NavLink } from 'react-router'
+import { usePresence } from '@shared/motion/usePresence'
import { LanguageSwitch } from '../../i18n/LanguageSwitch'
import type { NavItem } from './NavPill'
import { AccountSlot } from './parts'
@@ -21,6 +21,7 @@ const FOCUSABLE = 'a[href], button:not([disabled]), [tabindex]:not([tabindex="-1
export function MobileMenu({ id, items, open, onClose, toggleRef }: Props) {
const { t } = useTranslation('common')
const sheetRef = useRef(null)
+ const { mounted, state } = usePresence(open, 220)
useEffect(() => {
if (!open) return
@@ -53,28 +54,20 @@ export function MobileMenu({ id, items, open, onClose, toggleRef }: Props) {
}, [open, onClose, toggleRef])
return (
-
-
- {open && (
+ <>
+ {mounted && (
<>
-
-
-
+