feat(pmr): add interface materialization animations

- Login card fades out with scale animation (200ms)
- Patient banner slides down from top (200ms)
- Sidebar slides in from left (250ms, 50ms delay)
- Main content fades in (300ms, 150ms delay)
- Mobile nav slides up (200ms)
- All animations respect prefers-reduced-motion
- Mark Task 15 complete in IMPLEMENTATION_PLAN.md
This commit is contained in:
2026-02-11 03:22:29 +00:00
parent ef5bc9c3a6
commit 06ebef80c1
4 changed files with 112 additions and 25 deletions
+19 -9
View File
@@ -1,4 +1,5 @@
import { useState, useEffect, useCallback } from 'react'
import { motion } from 'framer-motion'
import { Shield } from 'lucide-react'
import { useAccessibility } from '../contexts/AccessibilityContext'
@@ -13,6 +14,7 @@ export function LoginScreen({ onComplete }: LoginScreenProps) {
const [isTypingUsername, setIsTypingUsername] = useState(true)
const [isTypingPassword, setIsTypingPassword] = useState(false)
const [buttonPressed, setButtonPressed] = useState(false)
const [isExiting, setIsExiting] = useState(false)
const { requestFocusAfterLogin } = useAccessibility()
const fullUsername = 'A.CHARLWOOD'
@@ -22,6 +24,14 @@ export function LoginScreen({ onComplete }: LoginScreenProps) {
? window.matchMedia('(prefers-reduced-motion: reduce)').matches
: false
const triggerComplete = useCallback(() => {
setIsExiting(true)
setTimeout(() => {
requestFocusAfterLogin()
onComplete()
}, prefersReducedMotion ? 0 : 200)
}, [onComplete, requestFocusAfterLogin, prefersReducedMotion])
const startLoginSequence = useCallback(() => {
if (prefersReducedMotion) {
setUsername(fullUsername)
@@ -29,9 +39,8 @@ export function LoginScreen({ onComplete }: LoginScreenProps) {
setTimeout(() => {
setButtonPressed(true)
setTimeout(() => {
requestFocusAfterLogin()
onComplete()
}, 200)
triggerComplete()
}, 100)
}, 300)
return
}
@@ -61,16 +70,15 @@ export function LoginScreen({ onComplete }: LoginScreenProps) {
setTimeout(() => {
setButtonPressed(true)
setTimeout(() => {
requestFocusAfterLogin()
onComplete()
}, 200)
triggerComplete()
}, 100)
}, 150)
}
}, 20)
}, 150)
}
}, 30)
}, [onComplete, prefersReducedMotion, requestFocusAfterLogin])
}, [triggerComplete, prefersReducedMotion])
useEffect(() => {
const cursorInterval = setInterval(() => {
@@ -87,13 +95,15 @@ export function LoginScreen({ onComplete }: LoginScreenProps) {
className="fixed inset-0 flex items-center justify-center z-50"
style={{ backgroundColor: '#1E293B' }}
>
<div
<motion.div
className="bg-white rounded-xl shadow-lg p-8"
style={{
width: '320px',
borderRadius: '12px',
boxShadow: '0 10px 40px rgba(0, 0, 0, 0.3)',
}}
animate={isExiting ? { scale: 1.03, opacity: 0 } : { scale: 1, opacity: 1 }}
transition={{ duration: 0.2, ease: 'easeOut' }}
>
<div className="flex flex-col items-center mb-6">
<div
@@ -196,7 +206,7 @@ export function LoginScreen({ onComplete }: LoginScreenProps) {
Secure clinical system login
</p>
</div>
</div>
</motion.div>
</div>
)
}