Components
Animated Form Card
Animated Form Card
Modern glassmorphism form card with smooth slide-in animations and floating labels.
Welcome back
Sign in to your account to continue
Or continue with
Don't have an account?
By signing in, you agree to our Terms of Service and Privacy Policy
Installation
1
Install the packages
npm i motion react-icons clsx tailwind-merge lucide-react
2
Add util file
lib/util.ts
import { ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
import { ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
3
Copy and paste the following code into your project
animated-form-card.tsx
"use client";
import { motion } from "motion/react";
import { useEffect, useState } from "react";
import { cn } from "@/lib/utils";
import { Mail, Lock, Eye, EyeOff, User, ArrowRight } from "lucide-react";
type AnimatedFormCardProps = {
delay?: number;
title?: string;
subtitle?: string;
};
const AnimatedFormCard = ({
delay = 6000,
title = "Welcome Back",
subtitle = "Sign in to your account",
}: AnimatedFormCardProps) => {
const [animationKey, setAnimationKey] = useState(0);
const delayTime = Math.max(delay, 6000);
useEffect(() => {
const interval = setInterval(() => {
setAnimationKey((prev) => prev + 1);
}, delayTime);
return () => clearInterval(interval);
}, [delayTime]);
return <AnimatedFormCardComponent key={animationKey} title={title} subtitle={subtitle} />;
};
export default AnimatedFormCard;
const AnimatedFormCardComponent = ({
title,
subtitle
}: {
title: string;
subtitle: string;
}) => {
const [showPassword, setShowPassword] = useState(false);
const formFields = [
{
icon: <Mail className="w-4 h-4" />,
placeholder: "Enter your email",
value: "john.doe@example.com",
type: "email",
delay: 0.8,
},
{
icon: <Lock className="w-4 h-4" />,
placeholder: "Enter your password",
value: "••••••••••",
type: "password",
delay: 1.6,
},
];
return (
<div className="w-full max-w-md mx-auto">
{/* Background blur effect */}
<div className="absolute inset-0 bg-gradient-to-br from-blue-50 to-purple-50 dark:from-blue-950/20 dark:to-purple-950/20 rounded-2xl blur-3xl opacity-60" />
{/* Main card */}
<motion.div
initial={{ opacity: 0, y: 20, scale: 0.95 }}
animate={{ opacity: 1, y: 0, scale: 1 }}
transition={{ duration: 0.6, ease: "easeOut" }}
className={cn(
"relative backdrop-blur-xl bg-white/70 dark:bg-gray-900/70",
"border border-white/20 dark:border-gray-700/30",
"rounded-2xl p-8 shadow-2xl",
"before:absolute before:inset-0 before:rounded-2xl",
"before:bg-gradient-to-br before:from-white/10 before:to-transparent",
"before:backdrop-blur-sm"
)}
>
{/* Header */}
<motion.div
initial={{ opacity: 0, y: -10 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.2, duration: 0.5 }}
className="text-center mb-8"
>
<motion.div
initial={{ scale: 0 }}
animate={{ scale: 1 }}
transition={{ delay: 0.3, duration: 0.4, type: "spring" }}
className="w-16 h-16 mx-auto mb-4 bg-gradient-to-br from-blue-500 to-purple-600 rounded-full flex items-center justify-center"
>
<User className="w-8 h-8 text-white" />
</motion.div>
<motion.h2
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 0.4, duration: 0.5 }}
className="text-2xl font-bold bg-gradient-to-r from-gray-900 to-gray-600 dark:from-gray-100 dark:to-gray-400 bg-clip-text text-transparent"
>
{title}
</motion.h2>
<motion.p
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 0.5, duration: 0.5 }}
className="text-gray-600 dark:text-gray-400 mt-2"
>
{subtitle}
</motion.p>
</motion.div>
{/* Form fields */}
<div className="space-y-4">
{formFields.map((field, index) => (
<motion.div
key={index}
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ delay: field.delay, duration: 0.5, ease: "easeOut" }}
className="relative"
>
<div className={cn(
"relative flex items-center",
"bg-white/50 dark:bg-gray-800/50",
"border border-gray-200/50 dark:border-gray-600/50",
"rounded-xl p-4 transition-all duration-300",
"hover:bg-white/70 dark:hover:bg-gray-800/70",
"hover:border-blue-300/50 dark:hover:border-blue-500/50",
"focus-within:bg-white/80 dark:focus-within:bg-gray-800/80",
"focus-within:border-blue-500/50",
"focus-within:shadow-lg focus-within:shadow-blue-500/10"
)}>
<div className="text-gray-500 dark:text-gray-400 mr-3">
{field.icon}
</div>
<div className="flex-1">
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: field.delay + 0.3, duration: 0.3 }}
className="text-xs text-gray-500 dark:text-gray-400 mb-1"
>
{field.placeholder}
</motion.div>
<motion.div
initial={{ opacity: 0, y: 5 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: field.delay + 0.5, duration: 0.4 }}
className="text-gray-900 dark:text-gray-100 font-medium"
>
{field.value}
</motion.div>
</div>
{field.type === "password" && (
<motion.button
initial={{ opacity: 0, scale: 0 }}
animate={{ opacity: 1, scale: 1 }}
transition={{ delay: field.delay + 0.7, duration: 0.3 }}
onClick={() => setShowPassword(!showPassword)}
className="text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200 ml-2"
>
{showPassword ? <EyeOff className="w-4 h-4" /> : <Eye className="w-4 h-4" />}
</motion.button>
)}
</div>
</motion.div>
))}
</div>
{/* Sign in button */}
<motion.button
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 2.5, duration: 0.5, ease: "easeOut" }}
className={cn(
"w-full mt-6 bg-gradient-to-r from-blue-500 to-purple-600",
"hover:from-blue-600 hover:to-purple-700",
"text-white font-semibold py-3 px-6 rounded-xl",
"flex items-center justify-center space-x-2",
"transform transition-all duration-300",
"hover:scale-[1.02] hover:shadow-lg hover:shadow-blue-500/25",
"active:scale-[0.98]"
)}
>
<span>Sign In</span>
<motion.div
animate={{ x: [0, 4, 0] }}
transition={{
delay: 3,
duration: 1,
repeat: Infinity,
repeatDelay: 2,
ease: "easeInOut"
}}
>
<ArrowRight className="w-4 h-4" />
</motion.div>
</motion.button>
{/* Footer */}
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 3.2, duration: 0.5 }}
className="text-center mt-6"
>
<p className="text-sm text-gray-600 dark:text-gray-400">
Don't have an account?{" "}
<span className="text-blue-600 dark:text-blue-400 hover:underline cursor-pointer font-medium">
Sign up
</span>
</p>
</motion.div>
{/* Decorative elements */}
<motion.div
animate={{
rotate: 360,
scale: [1, 1.1, 1]
}}
transition={{
rotate: { duration: 20, repeat: Infinity, ease: "linear" },
scale: { duration: 4, repeat: Infinity, ease: "easeInOut" }
}}
className="absolute -top-4 -right-4 w-8 h-8 bg-gradient-to-br from-blue-400 to-purple-500 rounded-full opacity-20 blur-sm"
/>
<motion.div
animate={{
rotate: -360,
scale: [1, 1.2, 1]
}}
transition={{
rotate: { duration: 15, repeat: Infinity, ease: "linear" },
scale: { duration: 5, repeat: Infinity, ease: "easeInOut" }
}}
className="absolute -bottom-6 -left-6 w-12 h-12 bg-gradient-to-br from-purple-400 to-pink-500 rounded-full opacity-15 blur-sm"
/>
</motion.div>
</div>
);
};
"use client";
import { motion } from "motion/react";
import { useEffect, useState } from "react";
import { cn } from "@/lib/utils";
import { Mail, Lock, Eye, EyeOff, User, ArrowRight } from "lucide-react";
type AnimatedFormCardProps = {
delay?: number;
title?: string;
subtitle?: string;
};
const AnimatedFormCard = ({
delay = 6000,
title = "Welcome Back",
subtitle = "Sign in to your account",
}: AnimatedFormCardProps) => {
const [animationKey, setAnimationKey] = useState(0);
const delayTime = Math.max(delay, 6000);
useEffect(() => {
const interval = setInterval(() => {
setAnimationKey((prev) => prev + 1);
}, delayTime);
return () => clearInterval(interval);
}, [delayTime]);
return <AnimatedFormCardComponent key={animationKey} title={title} subtitle={subtitle} />;
};
export default AnimatedFormCard;
const AnimatedFormCardComponent = ({
title,
subtitle
}: {
title: string;
subtitle: string;
}) => {
const [showPassword, setShowPassword] = useState(false);
const formFields = [
{
icon: <Mail className="w-4 h-4" />,
placeholder: "Enter your email",
value: "john.doe@example.com",
type: "email",
delay: 0.8,
},
{
icon: <Lock className="w-4 h-4" />,
placeholder: "Enter your password",
value: "••••••••••",
type: "password",
delay: 1.6,
},
];
return (
<div className="w-full max-w-md mx-auto">
{/* Background blur effect */}
<div className="absolute inset-0 bg-gradient-to-br from-blue-50 to-purple-50 dark:from-blue-950/20 dark:to-purple-950/20 rounded-2xl blur-3xl opacity-60" />
{/* Main card */}
<motion.div
initial={{ opacity: 0, y: 20, scale: 0.95 }}
animate={{ opacity: 1, y: 0, scale: 1 }}
transition={{ duration: 0.6, ease: "easeOut" }}
className={cn(
"relative backdrop-blur-xl bg-white/70 dark:bg-gray-900/70",
"border border-white/20 dark:border-gray-700/30",
"rounded-2xl p-8 shadow-2xl",
"before:absolute before:inset-0 before:rounded-2xl",
"before:bg-gradient-to-br before:from-white/10 before:to-transparent",
"before:backdrop-blur-sm"
)}
>
{/* Header */}
<motion.div
initial={{ opacity: 0, y: -10 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.2, duration: 0.5 }}
className="text-center mb-8"
>
<motion.div
initial={{ scale: 0 }}
animate={{ scale: 1 }}
transition={{ delay: 0.3, duration: 0.4, type: "spring" }}
className="w-16 h-16 mx-auto mb-4 bg-gradient-to-br from-blue-500 to-purple-600 rounded-full flex items-center justify-center"
>
<User className="w-8 h-8 text-white" />
</motion.div>
<motion.h2
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 0.4, duration: 0.5 }}
className="text-2xl font-bold bg-gradient-to-r from-gray-900 to-gray-600 dark:from-gray-100 dark:to-gray-400 bg-clip-text text-transparent"
>
{title}
</motion.h2>
<motion.p
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 0.5, duration: 0.5 }}
className="text-gray-600 dark:text-gray-400 mt-2"
>
{subtitle}
</motion.p>
</motion.div>
{/* Form fields */}
<div className="space-y-4">
{formFields.map((field, index) => (
<motion.div
key={index}
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ delay: field.delay, duration: 0.5, ease: "easeOut" }}
className="relative"
>
<div className={cn(
"relative flex items-center",
"bg-white/50 dark:bg-gray-800/50",
"border border-gray-200/50 dark:border-gray-600/50",
"rounded-xl p-4 transition-all duration-300",
"hover:bg-white/70 dark:hover:bg-gray-800/70",
"hover:border-blue-300/50 dark:hover:border-blue-500/50",
"focus-within:bg-white/80 dark:focus-within:bg-gray-800/80",
"focus-within:border-blue-500/50",
"focus-within:shadow-lg focus-within:shadow-blue-500/10"
)}>
<div className="text-gray-500 dark:text-gray-400 mr-3">
{field.icon}
</div>
<div className="flex-1">
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: field.delay + 0.3, duration: 0.3 }}
className="text-xs text-gray-500 dark:text-gray-400 mb-1"
>
{field.placeholder}
</motion.div>
<motion.div
initial={{ opacity: 0, y: 5 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: field.delay + 0.5, duration: 0.4 }}
className="text-gray-900 dark:text-gray-100 font-medium"
>
{field.value}
</motion.div>
</div>
{field.type === "password" && (
<motion.button
initial={{ opacity: 0, scale: 0 }}
animate={{ opacity: 1, scale: 1 }}
transition={{ delay: field.delay + 0.7, duration: 0.3 }}
onClick={() => setShowPassword(!showPassword)}
className="text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200 ml-2"
>
{showPassword ? <EyeOff className="w-4 h-4" /> : <Eye className="w-4 h-4" />}
</motion.button>
)}
</div>
</motion.div>
))}
</div>
{/* Sign in button */}
<motion.button
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 2.5, duration: 0.5, ease: "easeOut" }}
className={cn(
"w-full mt-6 bg-gradient-to-r from-blue-500 to-purple-600",
"hover:from-blue-600 hover:to-purple-700",
"text-white font-semibold py-3 px-6 rounded-xl",
"flex items-center justify-center space-x-2",
"transform transition-all duration-300",
"hover:scale-[1.02] hover:shadow-lg hover:shadow-blue-500/25",
"active:scale-[0.98]"
)}
>
<span>Sign In</span>
<motion.div
animate={{ x: [0, 4, 0] }}
transition={{
delay: 3,
duration: 1,
repeat: Infinity,
repeatDelay: 2,
ease: "easeInOut"
}}
>
<ArrowRight className="w-4 h-4" />
</motion.div>
</motion.button>
{/* Footer */}
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 3.2, duration: 0.5 }}
className="text-center mt-6"
>
<p className="text-sm text-gray-600 dark:text-gray-400">
Don't have an account?{" "}
<span className="text-blue-600 dark:text-blue-400 hover:underline cursor-pointer font-medium">
Sign up
</span>
</p>
</motion.div>
{/* Decorative elements */}
<motion.div
animate={{
rotate: 360,
scale: [1, 1.1, 1]
}}
transition={{
rotate: { duration: 20, repeat: Infinity, ease: "linear" },
scale: { duration: 4, repeat: Infinity, ease: "easeInOut" }
}}
className="absolute -top-4 -right-4 w-8 h-8 bg-gradient-to-br from-blue-400 to-purple-500 rounded-full opacity-20 blur-sm"
/>
<motion.div
animate={{
rotate: -360,
scale: [1, 1.2, 1]
}}
transition={{
rotate: { duration: 15, repeat: Infinity, ease: "linear" },
scale: { duration: 5, repeat: Infinity, ease: "easeInOut" }
}}
className="absolute -bottom-6 -left-6 w-12 h-12 bg-gradient-to-br from-purple-400 to-pink-500 rounded-full opacity-15 blur-sm"
/>
</motion.div>
</div>
);
};
4
Update the import paths to match your project setup
Props
Prop | Type | Default | Description |
---|---|---|---|
title | string | Welcome Back | The title to display on the form card. |
subtitle | string | Sign in to your account | The subtitle text below the main title. |
delay | number | 6000 | Time interval (in milliseconds) after which the animation restarts. |