Components
Fraud Card
Fraud Card
A security alert card inspired by Clerk’s landing page, showcasing blocked emails with smooth hover animations.
Email Security Enhancements
Improve account integrity and reduce fake registrations by identifying temporary inboxes and filtering suspicious patterns in email addresses used.
Malicious email activity flagged
06:37
bad_actor+1@gamil.com
Blocked Aug 9 at 14:09
spammer123@mailor.com
Blocked Aug 10 at 11:23
fake+prmo@tempmail.com
Blocked Aug 11 at 09:45
bot@disposablemail.org
Blocked Aug 12 at 16:02
*Tap to animate - full experience on desktop.
Installation
1
Install the packages
npm i motion react-icons clsx tailwind-merge
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
fraud-card.tsx
"use client";
import { cn } from "@/lib/utils";
import { TbCircleDotted } from "react-icons/tb";
import { RxCross2 } from "react-icons/rx";
import { GoDotFill } from "react-icons/go";
import { motion, Variants } from "motion/react";
import { useState } from "react";
type BlockedEmail = {
email: string;
time: string;
};
type FraudCardProps = {
blockedEmails: BlockedEmail[];
};
const FraudCard = ({ blockedEmails }: FraudCardProps) => {
const [hovered, setHovered] = useState(false);
const parentvariant = {
open: {
transition: {
staggerChildren: 0.08,
delayChildren: 0.15,
},
},
close: {
transition: {
staggerChildren: 0.075,
delayChildren: 0.15,
},
},
};
const emailvariant = {
open: {
opacity: 1,
filter: "blur(0px)",
y: 0,
transition: { duration: 0.3 },
},
close: {
opacity: 0,
filter: "blur(10px)",
y: 5,
transition: { duration: 0.3 },
},
};
const iconvariant = {
open: {
opacity: 1,
scale: 1,
transition: { duration: 0.3 },
},
close: {
opacity: 0,
scale: 0.85,
transition: { duration: 0.3 },
},
};
const timevariant = {
open: {
opacity: 1,
filter: "blur(0px)",
y: 0,
transition: { duration: 0.3 },
},
close: {
opacity: 0,
filter: "blur(5px)",
y: 10,
transition: { duration: 0.3 },
},
};
const circlevariant: Variants = {
open: {
rotate: 360,
transition: {
ease: "linear",
duration: 2.5,
repeat: Number.POSITIVE_INFINITY,
},
},
close: {
rotate: 0,
transition: {
ease: "easeInOut",
duration: 0.1,
repeat: 0,
},
},
};
return (
<motion.div
onClick={() => setHovered((prev) => !prev)}
onHoverStart={() => setHovered(true)}
onHoverEnd={() => setHovered(false)}
variants={parentvariant}
animate={hovered ? "open" : "close"}
initial="close"
className={cn(
"h-[34rem] min-h-[34rem] w-[350px] max-w-[350px]",
"group overflow-hidden border shadow-md",
"clbeam-container relative flex flex-col items-center",
"rounded-md bg-neutral-50 text-white dark:bg-neutral-900",
)}
>
<div className={cn("flex flex-col gap-2 px-4 pt-4")}>
<h2 className="text-[14px] font-bold text-primary">
Email Security Enhancements
</h2>
<p className="text-[11px] text-neutral-500 sm:text-xs">
Improve account integrity and reduce fake registrations by identifying
temporary inboxes and filtering suspicious patterns in email addresses
used.
</p>
</div>
<div className="relative flex h-full w-[300px] flex-col">
<div className="mt-8 py-3">
<div className="relative z-[10] flex items-center justify-center gap-2 rounded-[6px] bg-neutral-50 p-0.5 shadow-md dark:bg-black">
<div className="flex h-full w-full items-center justify-between gap-3 rounded-[4px] bg-neutral-100 p-3 dark:bg-neutral-800">
<div className="flex items-center justify-center gap-4">
<motion.div variants={circlevariant} className="h-4 w-4">
<TbCircleDotted className="h-full w-full text-primary" />
</motion.div>
<p className="font-mono text-[10px] text-neutral-600 transition-all duration-300 group-hover:text-neutral-900 dark:text-neutral-400 dark:group-hover:text-neutral-100">
Malicious email activity flagged
</p>
</div>
<p className="text-[10px] text-neutral-500">
{new Date().toLocaleTimeString([], {
hour: "2-digit",
minute: "2-digit",
hour12: false,
})}
</p>
</div>
</div>
</div>
<div className="absolute inset-0 h-full w-full">
<svg
className="h-full w-full stroke-current text-neutral-400 dark:text-neutral-700"
width="100%"
height="100%"
viewBox="0 0 52 50"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<g strokeWidth="0.1">
<path d="M 3.7 0 v 5.8 l 6.7 5.9 v 60" />
</g>
<g mask="url(#clbeam-mask-1)">
<circle
className="clbeam clbeam-line-1"
cx="0"
cy="0"
r="12"
fill="url(#clbeam-red-grad)"
/>
</g>
<defs>
<mask id="clbeam-mask-1">
<path
d="M 3.7 0 v 5.8 l 6.7 5.9 v 60"
stroke="white"
strokeWidth="0.15"
/>
</mask>
<radialGradient id="clbeam-red-grad" fx="1">
<stop offset="0%" stopColor="#ef4444" />
<stop offset="100%" stopColor="transparent" />
</radialGradient>
</defs>
</svg>
</div>
<div className="absolute inset-x-12 top-[130px] flex w-fit flex-col items-center justify-center">
<div className="flex h-full w-full flex-col items-center justify-center gap-9">
{blockedEmails.map(({ email, time }) => (
<div key={email} className="flex h-full w-full justify-start">
<div className="relative mr-2 mt-1.5 h-6 w-6">
<div className="absolute inset-0 flex items-center justify-center rounded-full bg-black/10 dark:bg-white/10">
<GoDotFill className="h-2.5 w-2.5 text-neutral-400 dark:text-neutral-500" />
</div>
<motion.div
variants={iconvariant}
className="absolute inset-0 flex items-center justify-center rounded-full bg-red-500 p-1"
>
<RxCross2 className="h-4 w-4 text-neutral-100 dark:text-neutral-800" />
</motion.div>
</div>
<div className="flex flex-col items-start justify-center gap-1 p-1">
<motion.h2
variants={emailvariant}
className="text-[10px] font-semibold text-neutral-800 dark:text-neutral-200 sm:text-xs"
>
{email}
</motion.h2>
<motion.p
variants={timevariant}
className="font-mono text-[9px] text-neutral-500"
>
Blocked {time}
</motion.p>
</div>
</div>
))}
</div>
</div>
</div>
</motion.div>
);
};
export default FraudCard;
"use client";
import { cn } from "@/lib/utils";
import { TbCircleDotted } from "react-icons/tb";
import { RxCross2 } from "react-icons/rx";
import { GoDotFill } from "react-icons/go";
import { motion, Variants } from "motion/react";
import { useState } from "react";
type BlockedEmail = {
email: string;
time: string;
};
type FraudCardProps = {
blockedEmails: BlockedEmail[];
};
const FraudCard = ({ blockedEmails }: FraudCardProps) => {
const [hovered, setHovered] = useState(false);
const parentvariant = {
open: {
transition: {
staggerChildren: 0.08,
delayChildren: 0.15,
},
},
close: {
transition: {
staggerChildren: 0.075,
delayChildren: 0.15,
},
},
};
const emailvariant = {
open: {
opacity: 1,
filter: "blur(0px)",
y: 0,
transition: { duration: 0.3 },
},
close: {
opacity: 0,
filter: "blur(10px)",
y: 5,
transition: { duration: 0.3 },
},
};
const iconvariant = {
open: {
opacity: 1,
scale: 1,
transition: { duration: 0.3 },
},
close: {
opacity: 0,
scale: 0.85,
transition: { duration: 0.3 },
},
};
const timevariant = {
open: {
opacity: 1,
filter: "blur(0px)",
y: 0,
transition: { duration: 0.3 },
},
close: {
opacity: 0,
filter: "blur(5px)",
y: 10,
transition: { duration: 0.3 },
},
};
const circlevariant: Variants = {
open: {
rotate: 360,
transition: {
ease: "linear",
duration: 2.5,
repeat: Number.POSITIVE_INFINITY,
},
},
close: {
rotate: 0,
transition: {
ease: "easeInOut",
duration: 0.1,
repeat: 0,
},
},
};
return (
<motion.div
onClick={() => setHovered((prev) => !prev)}
onHoverStart={() => setHovered(true)}
onHoverEnd={() => setHovered(false)}
variants={parentvariant}
animate={hovered ? "open" : "close"}
initial="close"
className={cn(
"h-[34rem] min-h-[34rem] w-[350px] max-w-[350px]",
"group overflow-hidden border shadow-md",
"clbeam-container relative flex flex-col items-center",
"rounded-md bg-neutral-50 text-white dark:bg-neutral-900",
)}
>
<div className={cn("flex flex-col gap-2 px-4 pt-4")}>
<h2 className="text-[14px] font-bold text-primary">
Email Security Enhancements
</h2>
<p className="text-[11px] text-neutral-500 sm:text-xs">
Improve account integrity and reduce fake registrations by identifying
temporary inboxes and filtering suspicious patterns in email addresses
used.
</p>
</div>
<div className="relative flex h-full w-[300px] flex-col">
<div className="mt-8 py-3">
<div className="relative z-[10] flex items-center justify-center gap-2 rounded-[6px] bg-neutral-50 p-0.5 shadow-md dark:bg-black">
<div className="flex h-full w-full items-center justify-between gap-3 rounded-[4px] bg-neutral-100 p-3 dark:bg-neutral-800">
<div className="flex items-center justify-center gap-4">
<motion.div variants={circlevariant} className="h-4 w-4">
<TbCircleDotted className="h-full w-full text-primary" />
</motion.div>
<p className="font-mono text-[10px] text-neutral-600 transition-all duration-300 group-hover:text-neutral-900 dark:text-neutral-400 dark:group-hover:text-neutral-100">
Malicious email activity flagged
</p>
</div>
<p className="text-[10px] text-neutral-500">
{new Date().toLocaleTimeString([], {
hour: "2-digit",
minute: "2-digit",
hour12: false,
})}
</p>
</div>
</div>
</div>
<div className="absolute inset-0 h-full w-full">
<svg
className="h-full w-full stroke-current text-neutral-400 dark:text-neutral-700"
width="100%"
height="100%"
viewBox="0 0 52 50"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<g strokeWidth="0.1">
<path d="M 3.7 0 v 5.8 l 6.7 5.9 v 60" />
</g>
<g mask="url(#clbeam-mask-1)">
<circle
className="clbeam clbeam-line-1"
cx="0"
cy="0"
r="12"
fill="url(#clbeam-red-grad)"
/>
</g>
<defs>
<mask id="clbeam-mask-1">
<path
d="M 3.7 0 v 5.8 l 6.7 5.9 v 60"
stroke="white"
strokeWidth="0.15"
/>
</mask>
<radialGradient id="clbeam-red-grad" fx="1">
<stop offset="0%" stopColor="#ef4444" />
<stop offset="100%" stopColor="transparent" />
</radialGradient>
</defs>
</svg>
</div>
<div className="absolute inset-x-12 top-[130px] flex w-fit flex-col items-center justify-center">
<div className="flex h-full w-full flex-col items-center justify-center gap-9">
{blockedEmails.map(({ email, time }) => (
<div key={email} className="flex h-full w-full justify-start">
<div className="relative mr-2 mt-1.5 h-6 w-6">
<div className="absolute inset-0 flex items-center justify-center rounded-full bg-black/10 dark:bg-white/10">
<GoDotFill className="h-2.5 w-2.5 text-neutral-400 dark:text-neutral-500" />
</div>
<motion.div
variants={iconvariant}
className="absolute inset-0 flex items-center justify-center rounded-full bg-red-500 p-1"
>
<RxCross2 className="h-4 w-4 text-neutral-100 dark:text-neutral-800" />
</motion.div>
</div>
<div className="flex flex-col items-start justify-center gap-1 p-1">
<motion.h2
variants={emailvariant}
className="text-[10px] font-semibold text-neutral-800 dark:text-neutral-200 sm:text-xs"
>
{email}
</motion.h2>
<motion.p
variants={timevariant}
className="font-mono text-[9px] text-neutral-500"
>
Blocked {time}
</motion.p>
</div>
</div>
))}
</div>
</div>
</div>
</motion.div>
);
};
export default FraudCard;
4
Add animations in globals CSS File
css
.clbeam {
offset-anchor: 10px 0px;
animation: none;
}
.clbeam-container:hover .clbeam {
animation: clbeam-animation-path;
animation-iteration-count: infinite;
animation-timing-function: cubic-bezier(0.05, 0.05, 0.05, 0.03);
animation-duration: 3s;
animation-delay: 0s;
}
.clbeam-line-1 {
offset-path: path("M 3.7 -5 v 5.8 l 6.7 5.9 v 80");
}
@keyframes clbeam-animation-path {
0% {
offset-distance: 0%;
}
50% {
offset-distance: 100%;
}
100% {
offset-distance: 100%;
}
}
.clbeam {
offset-anchor: 10px 0px;
animation: none;
}
.clbeam-container:hover .clbeam {
animation: clbeam-animation-path;
animation-iteration-count: infinite;
animation-timing-function: cubic-bezier(0.05, 0.05, 0.05, 0.03);
animation-duration: 3s;
animation-delay: 0s;
}
.clbeam-line-1 {
offset-path: path("M 3.7 -5 v 5.8 l 6.7 5.9 v 80");
}
@keyframes clbeam-animation-path {
0% {
offset-distance: 0%;
}
50% {
offset-distance: 100%;
}
100% {
offset-distance: 100%;
}
}
5
Update the import paths to match your project setup
Props
Prop | Type | Default | Description |
---|---|---|---|
blockedEmails | BlockedEmail[] | [] | An array of objects containing blocked email addresses and the time they were flagged. |