Add animated background visuals and improve layout on the login page
Replace static login image with animated SVG art, introduce gradient orbs and particles, and refine element positioning for a cleaner interface. Replit-Commit-Author: Agent Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66 Replit-Commit-Checkpoint-Type: full_checkpoint Replit-Commit-Event-Id: d05c5e2f-4508-444c-8333-9962b72fd989 Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/YSMil5R Replit-Helium-Checkpoint-Created: true
This commit is contained in:
Binary file not shown.
|
Before Width: | Height: | Size: 1.1 MiB |
@@ -0,0 +1,81 @@
|
||||
export function AnimatedArt() {
|
||||
return (
|
||||
<div className="absolute inset-0 overflow-hidden bg-[hsl(222_47%_9%)]">
|
||||
<div className="absolute inset-0 art-gradient" aria-hidden />
|
||||
|
||||
<div className="absolute -top-24 -left-24 w-[420px] h-[420px] rounded-full art-orb art-orb-a" aria-hidden />
|
||||
<div className="absolute top-1/3 -right-32 w-[480px] h-[480px] rounded-full art-orb art-orb-b" aria-hidden />
|
||||
<div className="absolute -bottom-32 left-1/4 w-[420px] h-[420px] rounded-full art-orb art-orb-c" aria-hidden />
|
||||
|
||||
<svg
|
||||
className="absolute inset-0 w-full h-full"
|
||||
viewBox="0 0 800 1000"
|
||||
preserveAspectRatio="none"
|
||||
aria-hidden
|
||||
>
|
||||
<defs>
|
||||
<linearGradient id="line-a" x1="0" y1="0" x2="1" y2="1">
|
||||
<stop offset="0%" stopColor="hsl(217 91% 60%)" stopOpacity="0" />
|
||||
<stop offset="50%" stopColor="hsl(217 91% 60%)" stopOpacity="0.9" />
|
||||
<stop offset="100%" stopColor="hsl(262 83% 70%)" stopOpacity="0" />
|
||||
</linearGradient>
|
||||
<linearGradient id="line-b" x1="0" y1="0" x2="1" y2="1">
|
||||
<stop offset="0%" stopColor="hsl(172 70% 55%)" stopOpacity="0" />
|
||||
<stop offset="50%" stopColor="hsl(172 70% 55%)" stopOpacity="0.9" />
|
||||
<stop offset="100%" stopColor="hsl(217 91% 60%)" stopOpacity="0" />
|
||||
</linearGradient>
|
||||
<linearGradient id="line-c" x1="0" y1="0" x2="1" y2="1">
|
||||
<stop offset="0%" stopColor="hsl(262 83% 70%)" stopOpacity="0" />
|
||||
<stop offset="50%" stopColor="hsl(262 83% 70%)" stopOpacity="0.85" />
|
||||
<stop offset="100%" stopColor="hsl(172 70% 55%)" stopOpacity="0" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<path
|
||||
d="M -50 200 Q 200 100 400 250 T 850 200"
|
||||
stroke="url(#line-a)"
|
||||
strokeWidth="1.5"
|
||||
fill="none"
|
||||
className="art-line art-line-1"
|
||||
/>
|
||||
<path
|
||||
d="M -50 400 Q 250 320 450 480 T 850 420"
|
||||
stroke="url(#line-b)"
|
||||
strokeWidth="1.5"
|
||||
fill="none"
|
||||
className="art-line art-line-2"
|
||||
/>
|
||||
<path
|
||||
d="M -50 600 Q 200 520 400 680 T 850 620"
|
||||
stroke="url(#line-c)"
|
||||
strokeWidth="1.5"
|
||||
fill="none"
|
||||
className="art-line art-line-3"
|
||||
/>
|
||||
<path
|
||||
d="M -50 800 Q 250 720 450 880 T 850 820"
|
||||
stroke="url(#line-a)"
|
||||
strokeWidth="1.5"
|
||||
fill="none"
|
||||
className="art-line art-line-4"
|
||||
/>
|
||||
<path
|
||||
d="M -50 100 Q 300 50 500 150 T 850 80"
|
||||
stroke="url(#line-b)"
|
||||
strokeWidth="1"
|
||||
fill="none"
|
||||
className="art-line art-line-5"
|
||||
/>
|
||||
</svg>
|
||||
|
||||
<div className="absolute inset-0 art-particles" aria-hidden>
|
||||
<span className="art-particle" style={{ left: "12%", top: "18%", animationDelay: "0s" }} />
|
||||
<span className="art-particle" style={{ left: "78%", top: "32%", animationDelay: "1.4s" }} />
|
||||
<span className="art-particle" style={{ left: "34%", top: "62%", animationDelay: "2.6s" }} />
|
||||
<span className="art-particle" style={{ left: "62%", top: "78%", animationDelay: "0.8s" }} />
|
||||
<span className="art-particle" style={{ left: "22%", top: "88%", animationDelay: "3.2s" }} />
|
||||
<span className="art-particle" style={{ left: "86%", top: "12%", animationDelay: "4s" }} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -208,3 +208,94 @@
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 14px 36px rgba(15, 23, 42, 0.08);
|
||||
}
|
||||
|
||||
|
||||
/* Animated login art — flowing gradient lines + drifting orbs */
|
||||
.art-gradient {
|
||||
background:
|
||||
radial-gradient(60% 80% at 20% 20%, hsl(217 91% 30% / 0.55), transparent 70%),
|
||||
radial-gradient(60% 80% at 80% 70%, hsl(262 83% 35% / 0.55), transparent 70%),
|
||||
radial-gradient(50% 70% at 50% 100%, hsl(172 70% 30% / 0.45), transparent 70%),
|
||||
linear-gradient(160deg, hsl(222 47% 8%), hsl(222 47% 14%));
|
||||
animation: artBgShift 18s ease-in-out infinite alternate;
|
||||
}
|
||||
@keyframes artBgShift {
|
||||
0% { filter: hue-rotate(0deg) saturate(1); }
|
||||
100% { filter: hue-rotate(15deg) saturate(1.15); }
|
||||
}
|
||||
|
||||
.art-orb {
|
||||
filter: blur(60px);
|
||||
opacity: 0.55;
|
||||
mix-blend-mode: screen;
|
||||
will-change: transform;
|
||||
}
|
||||
.art-orb-a {
|
||||
background: radial-gradient(circle, hsl(217 91% 60%), transparent 70%);
|
||||
animation: artOrbFloatA 14s ease-in-out infinite alternate;
|
||||
}
|
||||
.art-orb-b {
|
||||
background: radial-gradient(circle, hsl(262 83% 70%), transparent 70%);
|
||||
animation: artOrbFloatB 17s ease-in-out infinite alternate;
|
||||
}
|
||||
.art-orb-c {
|
||||
background: radial-gradient(circle, hsl(172 70% 55%), transparent 70%);
|
||||
animation: artOrbFloatC 20s ease-in-out infinite alternate;
|
||||
}
|
||||
@keyframes artOrbFloatA {
|
||||
0% { transform: translate(0, 0) scale(1); }
|
||||
100% { transform: translate(80px, 60px) scale(1.15); }
|
||||
}
|
||||
@keyframes artOrbFloatB {
|
||||
0% { transform: translate(0, 0) scale(1); }
|
||||
100% { transform: translate(-60px, -50px) scale(1.1); }
|
||||
}
|
||||
@keyframes artOrbFloatC {
|
||||
0% { transform: translate(0, 0) scale(1); }
|
||||
100% { transform: translate(40px, -70px) scale(1.2); }
|
||||
}
|
||||
|
||||
.art-line {
|
||||
stroke-dasharray: 600;
|
||||
stroke-dashoffset: 600;
|
||||
stroke-width: 2.5;
|
||||
animation: artLineDraw 8s ease-in-out infinite;
|
||||
filter: drop-shadow(0 0 8px hsl(217 91% 60% / 0.9));
|
||||
}
|
||||
.art-line-1 { animation-delay: 0s; }
|
||||
.art-line-2 { animation-delay: 1.2s; }
|
||||
.art-line-3 { animation-delay: 2.4s; }
|
||||
.art-line-4 { animation-delay: 3.6s; }
|
||||
.art-line-5 { animation-delay: 4.8s; }
|
||||
@keyframes artLineDraw {
|
||||
0% { stroke-dashoffset: 600; opacity: 0; }
|
||||
20% { opacity: 1; }
|
||||
60% { stroke-dashoffset: 0; opacity: 1; }
|
||||
100% { stroke-dashoffset: -600; opacity: 0; }
|
||||
}
|
||||
|
||||
.art-particle {
|
||||
position: absolute;
|
||||
width: 4px;
|
||||
height: 4px;
|
||||
border-radius: 9999px;
|
||||
background: white;
|
||||
box-shadow: 0 0 12px 2px white;
|
||||
opacity: 0;
|
||||
animation: artParticleFloat 6s ease-in-out infinite;
|
||||
}
|
||||
@keyframes artParticleFloat {
|
||||
0% { opacity: 0; transform: translateY(0) scale(0.8); }
|
||||
50% { opacity: 0.9; transform: translateY(-30px) scale(1.1); }
|
||||
100% { opacity: 0; transform: translateY(-60px) scale(0.8); }
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.art-gradient,
|
||||
.art-orb,
|
||||
.art-line,
|
||||
.art-particle {
|
||||
animation: none !important;
|
||||
}
|
||||
.art-line { stroke-dashoffset: 0; opacity: 1; }
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import { useQueryClient } from "@tanstack/react-query";
|
||||
import { useAppName, useAppSettings } from "@/hooks/use-app-settings";
|
||||
import i18n from "@/i18n";
|
||||
import { Eye, EyeOff, Loader2, AlertCircle } from "lucide-react";
|
||||
import loginArt from "@/assets/login-art.png";
|
||||
import { AnimatedArt } from "@/components/animated-art";
|
||||
|
||||
export default function LoginPage() {
|
||||
const { t, i18n: i18nInstance } = useTranslation();
|
||||
@@ -54,41 +54,25 @@ export default function LoginPage() {
|
||||
lang === "ar" ? settings?.footerTextAr : settings?.footerTextEn;
|
||||
|
||||
return (
|
||||
<div className="min-h-screen flex w-full text-foreground" dir={dir}>
|
||||
{/* Left art panel — hidden on small screens, no logo, no text */}
|
||||
<div className="hidden lg:block w-1/2 relative overflow-hidden os-bg">
|
||||
<img
|
||||
src={loginArt}
|
||||
alt=""
|
||||
aria-hidden="true"
|
||||
className="absolute inset-0 w-full h-full object-cover"
|
||||
/>
|
||||
{/* Soft brand-tinted overlay so the art feels integrated with the OS palette */}
|
||||
<div
|
||||
className="absolute inset-0 mix-blend-overlay pointer-events-none"
|
||||
style={{
|
||||
background:
|
||||
"linear-gradient(135deg, hsl(var(--primary) / 0.25), transparent 55%, hsl(var(--accent) / 0.25))",
|
||||
}}
|
||||
/>
|
||||
<div className="min-h-screen flex w-full text-foreground relative" dir={dir}>
|
||||
{/* Language toggle — outside os-bg so it stays fixed to viewport */}
|
||||
<button
|
||||
type="button"
|
||||
onClick={toggleLanguage}
|
||||
className={`absolute top-4 z-50 text-sm font-medium hover:bg-foreground/5 px-3 py-1.5 rounded-md transition-colors text-foreground bg-background/70 backdrop-blur-sm border border-border/50 ${
|
||||
dir === "rtl" ? "left-4 md:left-8" : "right-4 md:right-8"
|
||||
}`}
|
||||
>
|
||||
{lang === "ar" ? "EN" : "عربي"}
|
||||
</button>
|
||||
|
||||
{/* Animated art panel — hidden on small screens, no logo, no text */}
|
||||
<div className="hidden lg:block lg:w-1/2 relative overflow-hidden shrink-0">
|
||||
<AnimatedArt />
|
||||
</div>
|
||||
|
||||
{/* Right form panel */}
|
||||
<div className="flex-1 flex flex-col items-center justify-center p-6 sm:p-8 relative os-bg">
|
||||
{/* Language toggle */}
|
||||
<div
|
||||
className={`absolute top-4 ${
|
||||
dir === "rtl" ? "left-4 md:left-8" : "right-4 md:right-8"
|
||||
}`}
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
onClick={toggleLanguage}
|
||||
className="text-sm font-medium hover:bg-foreground/5 px-3 py-1.5 rounded-md transition-colors text-foreground"
|
||||
>
|
||||
{lang === "ar" ? "EN" : "عربي"}
|
||||
</button>
|
||||
</div>
|
||||
{/* Form panel */}
|
||||
<div className="flex-1 min-w-0 flex flex-col items-center justify-center p-6 sm:p-8 relative os-bg">
|
||||
|
||||
<div className="w-full max-w-[400px]">
|
||||
<div className="glass-panel rounded-2xl p-6 sm:p-8">
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.1 MiB |
Reference in New Issue
Block a user