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:
riyadhafraa
2026-04-20 12:33:56 +00:00
parent ab2eac47c0
commit ff58444796
5 changed files with 190 additions and 34 deletions
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>
);
}
+91
View File
@@ -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; }
}
+18 -34
View File
@@ -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