beautify: page tranistion / loginform

This commit is contained in:
DeathKaioken 2025-10-28 20:17:13 +01:00
parent 5709f48dc3
commit f7205ed8f6
2 changed files with 138 additions and 45 deletions

View File

@ -2,34 +2,102 @@
import { motion, AnimatePresence } from 'framer-motion';
import { usePathname } from 'next/navigation';
import React from 'react';
import Image from 'next/image';
import React, { useEffect, useRef, useState } from 'react';
import { createPortal } from 'react-dom';
const PageTransitionEffect = ({ children }: { children: React.ReactNode }) => {
const pathname = usePathname();
const DELAY_MS = 200;
const EXIT_DURATION = 0.7; // slow the fade/slide-out a bit
const variants = {
hidden: { opacity: 0, x: 0, y: 20 },
enter: { opacity: 1, x: 0, y: 0 },
exit: { opacity: 0, x: 0, y: -20 },
};
const [mounted, setMounted] = useState(false);
const [showOverlay, setShowOverlay] = useState(true);
const [overlayExit, setOverlayExit] = useState(false);
const delayT = useRef<number | null>(null);
useEffect(() => setMounted(true), []);
// Exit overlay shortly after route change (200ms)
useEffect(() => {
setShowOverlay(true);
setOverlayExit(false);
if (delayT.current) clearTimeout(delayT.current);
delayT.current = window.setTimeout(() => setOverlayExit(true), DELAY_MS);
return () => {
if (delayT.current) clearTimeout(delayT.current);
};
}, [pathname]);
// Prevent scroll while overlay is visible
useEffect(() => {
if (!mounted) return;
const prev = document.documentElement.style.overflow;
if (showOverlay) document.documentElement.style.overflow = 'hidden';
return () => {
document.documentElement.style.overflow = prev;
};
}, [showOverlay, mounted]);
return (
<AnimatePresence
mode="wait"
onExitComplete={() => window.scrollTo(0, 0)}
>
<motion.div
key={pathname}
variants={variants}
initial="hidden"
animate="enter"
exit="exit"
transition={{ type: 'tween', duration: 0.3 }}
className="w-full"
>
{children}
</motion.div>
</AnimatePresence>
<>
<AnimatePresence mode="wait" onExitComplete={() => window.scrollTo(0, 0)}>
<motion.div
key={pathname}
variants={{
hidden: { opacity: 0, x: 0, y: 20 },
enter: { opacity: 1, x: 0, y: 0 },
exit: { opacity: 0, x: 0, y: -20 },
}}
initial="hidden"
animate="enter"
exit="exit"
transition={{ type: 'tween', duration: 0.3 }}
className="w-full"
>
{children}
</motion.div>
</AnimatePresence>
{/* Client-only portal overlay with header gradient (no delay, default timing) */}
{mounted &&
showOverlay &&
createPortal(
<motion.div
initial={false}
animate={overlayExit ? { y: '-100%', opacity: 0 } : { y: 0, opacity: 1 }}
transition={{ duration: EXIT_DURATION, ease: [0.22, 1, 0.36, 1] }}
onAnimationComplete={() => {
if (overlayExit) {
setShowOverlay(false);
window.scrollTo(0, 0);
}
}}
className="fixed inset-0 z-[999999] flex items-center justify-center"
style={{
background:
'linear-gradient(135deg, #0F1D37 0%, #0A162A 50%, #081224 100%)',
}}
>
<div className="flex flex-col items-center">
<Image
src="/images/logos/pp_logo_gold_transparent.png"
alt="Profit Planet"
width={160}
height={160}
className="w-32 h-32 object-contain"
priority
/>
<div
role="status"
aria-live="polite"
className="mt-6 h-10 w-10 rounded-full border-4 border-[#D4AF37] border-t-transparent animate-spin"
/>
</div>
</motion.div>,
document.body
)}
</>
);
};

View File

@ -82,7 +82,32 @@ export default function LoginForm() {
})
}
const isMobile = typeof window !== 'undefined' && window.innerWidth < 768
const screenWidth = typeof window !== 'undefined' ? window.innerWidth : 1200
const isMobile = screenWidth < 768
const isTablet = screenWidth >= 768 && screenWidth <= 1024
const isSmallLaptop = screenWidth > 1024 && screenWidth <= 1366
// Calculate responsive values
const getFormWidth = () => {
if (isMobile) return '98vw'
if (isTablet) return '85vw'
if (isSmallLaptop) return '50vw'
return '40vw'
}
const getFormScale = () => {
if (isMobile) return undefined
if (isTablet) return 'scale(0.95)'
if (isSmallLaptop) return 'scale(0.9)'
return 'scale(0.85)'
}
const getFormMaxWidth = () => {
if (isMobile) return 'none'
if (isTablet) return '600px'
if (isSmallLaptop) return '650px'
return '700px'
}
return (
<div
@ -103,13 +128,13 @@ export default function LoginForm() {
<div
className="bg-white rounded-2xl shadow-2xl flex flex-col items-center relative border-t-4 border-[#8D6B1D]"
style={{
width: isMobile ? '98vw' : '40vw',
maxWidth: isMobile ? 'none' : '700px',
width: getFormWidth(),
maxWidth: getFormMaxWidth(),
minWidth: isMobile ? '0' : '400px',
minHeight: isMobile ? '320px' : '320px',
padding: isMobile ? '0.5rem' : '2rem',
marginTop: isMobile ? '0.5rem' : undefined,
transform: isMobile ? undefined : 'scale(0.85)',
transform: getFormScale(),
transformOrigin: 'top center',
display: 'flex',
flexDirection: 'column',
@ -199,14 +224,14 @@ export default function LoginForm() {
{/* Content */}
<div style={{
marginTop: isMobile ? '0.5rem' : '1.5rem',
marginBottom: isMobile ? '1.5rem' : '2rem',
marginTop: isMobile ? '0.5rem' : isTablet ? '1rem' : '1.5rem',
marginBottom: isMobile ? '1.5rem' : isTablet ? '1.75rem' : '2rem',
width: '100%',
}}>
<h1
className="mb-2 text-center text-4xl font-extrabold text-[#0F172A] tracking-tight drop-shadow-lg"
style={{
fontSize: isMobile ? '2rem' : undefined,
fontSize: isMobile ? '2rem' : isTablet ? '2.25rem' : undefined,
marginTop: isMobile ? '0.5rem' : undefined,
}}
>
@ -215,8 +240,8 @@ export default function LoginForm() {
<p
className="mb-8 text-center text-lg text-[#8D6B1D] font-medium"
style={{
fontSize: isMobile ? '0.95rem' : undefined,
marginBottom: isMobile ? '1rem' : undefined,
fontSize: isMobile ? '0.95rem' : isTablet ? '1rem' : undefined,
marginBottom: isMobile ? '1rem' : isTablet ? '1.5rem' : undefined,
}}
>
Welcome back! Login to continue.
@ -225,7 +250,7 @@ export default function LoginForm() {
<form
className="space-y-7 w-full"
style={{
gap: isMobile ? '0.75rem' : undefined,
gap: isMobile ? '0.75rem' : isTablet ? '1rem' : undefined,
}}
onSubmit={handleSubmit}
>
@ -235,7 +260,7 @@ export default function LoginForm() {
htmlFor="email"
className="block text-base font-semibold text-[#0F172A] mb-1"
style={{
fontSize: isMobile ? '0.875rem' : undefined,
fontSize: isMobile ? '0.875rem' : isTablet ? '0.9rem' : undefined,
marginBottom: isMobile ? '0.25rem' : undefined,
}}
>
@ -250,8 +275,8 @@ export default function LoginForm() {
onChange={handleInputChange}
className="appearance-none block w-full px-4 py-3 border border-gray-300 rounded-lg placeholder-slate-500 focus:outline-none focus:ring-2 focus:ring-[#8D6B1D] focus:border-[#8D6B1D] text-base bg-white text-[#0F172A] transition"
style={{
fontSize: isMobile ? '0.875rem' : undefined,
padding: isMobile ? '0.4rem 0.75rem' : undefined,
fontSize: isMobile ? '0.875rem' : isTablet ? '0.9rem' : undefined,
padding: isMobile ? '0.4rem 0.75rem' : isTablet ? '0.5rem 0.875rem' : undefined,
}}
placeholder="deine@email.com"
required
@ -264,7 +289,7 @@ export default function LoginForm() {
htmlFor="password"
className="block text-base font-semibold text-[#0F172A] mb-1"
style={{
fontSize: isMobile ? '0.875rem' : undefined,
fontSize: isMobile ? '0.875rem' : isTablet ? '0.9rem' : undefined,
marginBottom: isMobile ? '0.25rem' : undefined,
}}
>
@ -280,8 +305,8 @@ export default function LoginForm() {
onChange={handleInputChange}
className="appearance-none block w-full px-4 py-3 pr-12 border border-gray-300 rounded-lg placeholder-slate-500 focus:outline-none focus:ring-2 focus:ring-[#8D6B1D] focus:border-[#8D6B1D] text-base bg-white text-[#0F172A] transition"
style={{
fontSize: isMobile ? '0.875rem' : undefined,
padding: isMobile ? '0.4rem 2.5rem 0.4rem 0.75rem' : '0.75rem 3rem 0.75rem 1rem',
fontSize: isMobile ? '0.875rem' : isTablet ? '0.9rem' : undefined,
padding: isMobile ? '0.4rem 2.5rem 0.4rem 0.75rem' : isTablet ? '0.5rem 2.75rem 0.5rem 0.875rem' : '0.75rem 3rem 0.75rem 1rem',
}}
placeholder="Dein Passwort"
required
@ -347,8 +372,8 @@ export default function LoginForm() {
: 'bg-gradient-to-r from-[#8D6B1D] via-[#A67C20] to-[#C49225] hover:from-[#7A5E1A] hover:to-[#B8851F] focus:outline-none focus:ring-2 focus:ring-[#8D6B1D] focus:ring-offset-2'
}`}
style={{
fontSize: isMobile ? '0.9rem' : undefined,
padding: isMobile ? '0.6rem 1rem' : undefined,
fontSize: isMobile ? '0.9rem' : isTablet ? '0.95rem' : undefined,
padding: isMobile ? '0.6rem 1rem' : isTablet ? '0.7rem 1.25rem' : undefined,
}}
>
{loading ? (
@ -378,7 +403,7 @@ export default function LoginForm() {
<div
className="mt-10 w-full"
style={{
marginTop: isMobile ? '1rem' : undefined,
marginTop: isMobile ? '1rem' : isTablet ? '1.5rem' : undefined,
}}
>
<div className="relative">
@ -388,7 +413,7 @@ export default function LoginForm() {
<div
className="relative flex justify-center text-base"
style={{
fontSize: isMobile ? '0.875rem' : undefined,
fontSize: isMobile ? '0.875rem' : isTablet ? '0.9rem' : undefined,
}}
>
<a href="/register" className="px-3 bg-white text-[#8D6B1D]">Noch kein Account?</a>
@ -397,13 +422,13 @@ export default function LoginForm() {
<div
className="mt-7 text-center"
style={{
marginTop: isMobile ? '0.75rem' : undefined,
marginTop: isMobile ? '0.75rem' : isTablet ? '1rem' : undefined,
}}
>
<p
className="text-base text-slate-700"
style={{
fontSize: isMobile ? '0.8rem' : undefined,
fontSize: isMobile ? '0.8rem' : isTablet ? '0.85rem' : undefined,
}}
>
Profit Planet is available by invitation only.
@ -411,7 +436,7 @@ export default function LoginForm() {
<p
className="text-base text-[#8D6B1D] mt-2"
style={{
fontSize: isMobile ? '0.8rem' : undefined,
fontSize: isMobile ? '0.8rem' : isTablet ? '0.85rem' : undefined,
}}
>
Contact us for an invitation!