Icon Large Alert
Original · freeAn alert led by a large circular icon.
Copy prompt gives Claude Code, Cursor or v0 a ready-to-paste prompt that recreates this exact component.
npx shadcn@latest add https://webinnoventix.com/r/alert-icon-large.json"use client";
import { useState, type ReactNode } from "react";
import { AnimatePresence, motion, useReducedMotion } from "motion/react";
type Tone = "info" | "success" | "warning" | "error";
type AlertItem = {
id: string;
tone: Tone;
title: string;
body: string;
action: string;
dismiss: string;
};
const TONES: Record<
Tone,
{
ring: string;
iconWrap: string;
iconText: string;
accent: string;
action: string;
focus: string;
pulse: string;
icon: ReactNode;
}
> = {
info: {
ring: "border-sky-200/80 dark:border-sky-500/25",
iconWrap: "bg-sky-100 dark:bg-sky-500/15 ring-sky-200/70 dark:ring-sky-400/20",
iconText: "text-sky-600 dark:text-sky-300",
accent: "text-sky-900 dark:text-sky-100",
action:
"bg-sky-600 text-white hover:bg-sky-500 dark:bg-sky-500 dark:hover:bg-sky-400",
focus: "focus-visible:outline-sky-500",
pulse: "bg-sky-400/40 dark:bg-sky-400/25",
icon: (
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M12 9.5v4.75M12 7.25h.01M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z"
/>
),
},
success: {
ring: "border-emerald-200/80 dark:border-emerald-500/25",
iconWrap:
"bg-emerald-100 dark:bg-emerald-500/15 ring-emerald-200/70 dark:ring-emerald-400/20",
iconText: "text-emerald-600 dark:text-emerald-300",
accent: "text-emerald-900 dark:text-emerald-100",
action:
"bg-emerald-600 text-white hover:bg-emerald-500 dark:bg-emerald-500 dark:hover:bg-emerald-400",
focus: "focus-visible:outline-emerald-500",
pulse: "bg-emerald-400/40 dark:bg-emerald-400/25",
icon: (
<path
strokeLinecap="round"
strokeLinejoin="round"
d="m8 12.5 2.75 2.75L16.5 9.5M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z"
/>
),
},
warning: {
ring: "border-amber-200/80 dark:border-amber-500/25",
iconWrap:
"bg-amber-100 dark:bg-amber-500/15 ring-amber-200/70 dark:ring-amber-400/20",
iconText: "text-amber-600 dark:text-amber-300",
accent: "text-amber-900 dark:text-amber-100",
action:
"bg-amber-500 text-white hover:bg-amber-400 dark:bg-amber-500 dark:hover:bg-amber-400",
focus: "focus-visible:outline-amber-500",
pulse: "bg-amber-400/40 dark:bg-amber-400/25",
icon: (
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M12 9.25v3.5M12 16h.01M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0Z"
/>
),
},
error: {
ring: "border-rose-200/80 dark:border-rose-500/25",
iconWrap: "bg-rose-100 dark:bg-rose-500/15 ring-rose-200/70 dark:ring-rose-400/20",
iconText: "text-rose-600 dark:text-rose-300",
accent: "text-rose-900 dark:text-rose-100",
action:
"bg-rose-600 text-white hover:bg-rose-500 dark:bg-rose-500 dark:hover:bg-rose-400",
focus: "focus-visible:outline-rose-500",
pulse: "bg-rose-400/40 dark:bg-rose-400/25",
icon: (
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M15 9l-6 6m0-6 6 6M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z"
/>
),
},
};
const INITIAL: AlertItem[] = [
{
id: "backup",
tone: "success",
title: "Nightly backup completed",
body: "All 14 project databases were snapshotted and verified at 03:12 UTC. The last restore point is now under 6 hours old.",
action: "View restore points",
dismiss: "Dismiss",
},
{
id: "seats",
tone: "warning",
title: "You're almost out of seats",
body: "9 of 10 seats on the Growth plan are in use. Invite one more teammate and the next invite will require an upgrade.",
action: "Manage plan",
dismiss: "Remind me later",
},
{
id: "webhook",
tone: "error",
title: "Webhook delivery is failing",
body: "The endpoint https://api.acme.co/hooks returned 502 on the last 5 attempts. Deliveries are paused until it responds with a 2xx.",
action: "Inspect logs",
dismiss: "Ignore",
},
{
id: "digest",
tone: "info",
title: "Your weekly digest is ready",
body: "Traffic to your docs grew 18% and three articles crossed 1,000 reads this week. The full breakdown is waiting in your inbox.",
action: "Open digest",
dismiss: "Close",
},
];
export default function AlertIconLarge() {
const prefersReduced = useReducedMotion();
const [items, setItems] = useState<AlertItem[]>(INITIAL);
const dismiss = (id: string) =>
setItems((prev) => prev.filter((item) => item.id !== id));
const reset = () => setItems(INITIAL);
return (
<section className="relative w-full bg-white px-4 py-20 dark:bg-neutral-950 sm:px-6 lg:px-8">
<style>{`
@keyframes ail-ripple {
0% { transform: scale(0.85); opacity: 0.55; }
70% { transform: scale(1.9); opacity: 0; }
100% { transform: scale(1.9); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
.ail-ripple { animation: none !important; }
}
`}</style>
<div className="mx-auto max-w-2xl">
<header className="mb-10 flex flex-col gap-2">
<span className="text-xs font-semibold uppercase tracking-[0.18em] text-indigo-600 dark:text-indigo-400">
Notifications
</span>
<h2 className="text-2xl font-semibold tracking-tight text-neutral-900 dark:text-neutral-50 sm:text-3xl">
Everything that needs your eyes
</h2>
<p className="text-sm text-neutral-500 dark:text-neutral-400">
Four live alerts, each led by a large status icon. Dismiss what you've
handled — the stack settles smoothly.
</p>
</header>
<div className="flex flex-col gap-4" role="region" aria-label="Alerts">
<AnimatePresence initial={false} mode="popLayout">
{items.map((item) => {
const t = TONES[item.tone];
return (
<motion.div
key={item.id}
layout={!prefersReduced}
initial={
prefersReduced
? { opacity: 0 }
: { opacity: 0, y: 12, scale: 0.98 }
}
animate={
prefersReduced
? { opacity: 1 }
: { opacity: 1, y: 0, scale: 1 }
}
exit={
prefersReduced
? { opacity: 0 }
: { opacity: 0, x: 24, scale: 0.97 }
}
transition={{ duration: 0.28, ease: [0.22, 1, 0.36, 1] }}
role="alert"
className={`relative flex gap-4 overflow-hidden rounded-2xl border ${t.ring} bg-white/90 p-4 shadow-sm ring-1 ring-black/[0.02] backdrop-blur-sm dark:bg-neutral-900/70 dark:ring-white/5 sm:gap-5 sm:p-5`}
>
<div className="relative flex-shrink-0">
{!prefersReduced && item.tone !== "success" && (
<span
aria-hidden="true"
className={`ail-ripple pointer-events-none absolute inset-0 rounded-full ${t.pulse}`}
style={{
animation: "ail-ripple 2.6s ease-out infinite",
}}
/>
)}
<div
className={`relative flex h-14 w-14 items-center justify-center rounded-full ring-1 ${t.iconWrap} sm:h-16 sm:w-16`}
>
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth={1.6}
aria-hidden="true"
className={`h-7 w-7 ${t.iconText} sm:h-8 sm:w-8`}
>
{t.icon}
</svg>
</div>
</div>
<div className="flex min-w-0 flex-1 flex-col">
<h3
className={`text-sm font-semibold ${t.accent} sm:text-base`}
>
{item.title}
</h3>
<p className="mt-1 text-sm leading-relaxed text-neutral-600 dark:text-neutral-300">
{item.body}
</p>
<div className="mt-4 flex flex-wrap items-center gap-2">
<button
type="button"
className={`inline-flex items-center rounded-lg px-3 py-1.5 text-xs font-semibold shadow-sm outline-none transition focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 ${t.action} ${t.focus}`}
>
{item.action}
</button>
<button
type="button"
onClick={() => dismiss(item.id)}
className="inline-flex items-center rounded-lg px-3 py-1.5 text-xs font-semibold text-neutral-600 outline-none transition hover:bg-neutral-100 hover:text-neutral-900 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-neutral-400 dark:text-neutral-300 dark:hover:bg-neutral-800 dark:hover:text-neutral-50"
>
{item.dismiss}
</button>
</div>
</div>
<button
type="button"
onClick={() => dismiss(item.id)}
aria-label={`Dismiss alert: ${item.title}`}
className="absolute right-3 top-3 flex h-7 w-7 items-center justify-center rounded-md text-neutral-400 outline-none transition hover:bg-neutral-100 hover:text-neutral-700 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-neutral-400 dark:text-neutral-500 dark:hover:bg-neutral-800 dark:hover:text-neutral-200"
>
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth={1.8}
aria-hidden="true"
className="h-4 w-4"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M6 6l12 12M18 6 6 18"
/>
</svg>
</button>
</motion.div>
);
})}
</AnimatePresence>
</div>
<AnimatePresence>
{items.length === 0 && (
<motion.div
initial={prefersReduced ? { opacity: 0 } : { opacity: 0, y: 8 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.25 }}
className="flex flex-col items-center gap-4 rounded-2xl border border-dashed border-neutral-200 py-14 text-center dark:border-neutral-800"
>
<div className="flex h-14 w-14 items-center justify-center rounded-full bg-emerald-100 text-emerald-600 dark:bg-emerald-500/15 dark:text-emerald-300">
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth={1.6}
aria-hidden="true"
className="h-7 w-7"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="m8 12.5 2.75 2.75L16.5 9.5M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z"
/>
</svg>
</div>
<div>
<p className="text-sm font-semibold text-neutral-900 dark:text-neutral-50">
Inbox zero
</p>
<p className="mt-1 text-sm text-neutral-500 dark:text-neutral-400">
You've cleared every alert. Nice work.
</p>
</div>
<button
type="button"
onClick={reset}
className="inline-flex items-center rounded-lg bg-neutral-900 px-3.5 py-2 text-xs font-semibold text-white outline-none transition hover:bg-neutral-700 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-neutral-500 dark:bg-neutral-50 dark:text-neutral-900 dark:hover:bg-neutral-200"
>
Restore alerts
</button>
</motion.div>
)}
</AnimatePresence>
</div>
</section>
);
}Dependencies
Licence
Built by Web Innoventix. Free for personal and commercial use, no attribution required.
Built by Web Innoventix
Need a custom interface, a full website, or SEO that gets you cited by AI? We design, build and rank it end to end.
Get a free quoteSimilar components
Browse all →
Soft Variants Alert
OriginalSoft-tinted alerts: success, info, warning and error, each with an icon.

Solid Variants Alert
OriginalSolid-colour alerts across the four states.

Outline Variants Alert
OriginalOutlined alerts across the four states.

Left Accent Alert
OriginalAlerts with a coloured left accent bar and icon.

Dismissible Alert
OriginalDismissible alerts with a working close button.

With Action Alert
OriginalAn alert with title, body and action buttons.

Banner Top Alert
OriginalA full-width top announcement banner, dismissible.

Toast Stack Alert
OriginalA stack of dismissible toasts in a corner.

Toast Slide Alert
OriginalA toast that slides in with an icon and auto-dismiss progress.

Progress Alert
OriginalAn alert with a countdown progress bar.

Gradient Alert
OriginalA gradient promo banner alert with a call to action.

Inline Form Alert
OriginalAn alert containing an inline email input and button.

