Phase 1:
- S0: Fix open redirect (safeNavigate), delete AuthContext/legacy auth, encrypt API keys, gitignore .env files
- S1: Split client.ts god object into 5 modules, unify toast system, delete unused Sidebar
- S2: Add glass button variant, migrate 32 z-index to SUMI tokens, fix card dark mode
- S3: Skip nav link, aria-hidden on icons, focus-visible ring fixes, alt attrs, aria-live regions
- S4: React.memo on list items, fix key={index}, loading=lazy on images
- S5: Branded loading screen, page transitions respect reduced-motion, LikeButton micro-interaction, i18n sidebar/header
Phase 2 Sprint 6:
- Wire Tailwind shadow utilities to SUMI tokens in @theme block (fixes 50+ files)
- Define shadow-card/shadow-card-hover tokens
- Remove dark:shadow-none workarounds from card.tsx (SUMI handles per-theme shadows)
Co-authored-by: Cursor <cursoragent@cursor.com>
41 lines
988 B
TypeScript
41 lines
988 B
TypeScript
import { motion } from 'framer-motion';
|
|
import { useLocation } from 'react-router-dom';
|
|
import { ReactNode, useMemo } from 'react';
|
|
|
|
interface PageTransitionProps {
|
|
children: ReactNode;
|
|
}
|
|
|
|
/**
|
|
* S5.3: Subtile page transitions (Linear-style fade + slide-up)
|
|
* Respects prefers-reduced-motion for accessibility.
|
|
*/
|
|
export function PageTransition({ children }: PageTransitionProps) {
|
|
const location = useLocation();
|
|
|
|
const prefersReducedMotion = useMemo(
|
|
() =>
|
|
typeof window !== 'undefined' &&
|
|
window.matchMedia('(prefers-reduced-motion: reduce)').matches,
|
|
[],
|
|
);
|
|
|
|
if (prefersReducedMotion) {
|
|
return <div key={location.pathname}>{children}</div>;
|
|
}
|
|
|
|
return (
|
|
<motion.div
|
|
key={location.pathname}
|
|
initial={{ opacity: 0, y: 8 }}
|
|
animate={{ opacity: 1, y: 0 }}
|
|
exit={{ opacity: 0, y: -8 }}
|
|
transition={{
|
|
duration: 0.2,
|
|
ease: [0.25, 0.1, 0.25, 1],
|
|
}}
|
|
>
|
|
{children}
|
|
</motion.div>
|
|
);
|
|
}
|