Callout Alert
Original · freeAn editorial callout box in info, tip and warning styles.
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-callout.json"use client";
import { useState, type ReactNode } from "react";
type Variant = "info" | "tip" | "warning" | "danger";
type Callout = {
variant: Variant;
title: string;
body: ReactNode;
dismissible?: boolean;
};
const VARIANTS: Record<
Variant,
{
label: string;
container: string;
icon: string;
title: string;
body: string;
accent: string;
ring: string;
glyph: ReactNode;
}
> = {
info: {
label: "Note",
container:
"border-sky-200/80 bg-sky-50/70 dark:border-sky-400/25 dark:bg-sky-950/40",
icon: "bg-sky-100 text-sky-700 dark:bg-sky-500/15 dark:text-sky-300",
title: "text-sky-900 dark:text-sky-100",
body: "text-sky-800/90 dark:text-sky-200/80",
accent: "bg-sky-500 dark:bg-sky-400",
ring: "focus-visible:ring-sky-500/60",
glyph: (
<path
d="M12 8h.01M11 12h1v4h1"
strokeLinecap="round"
strokeLinejoin="round"
/>
),
},
tip: {
label: "Tip",
container:
"border-emerald-200/80 bg-emerald-50/70 dark:border-emerald-400/25 dark:bg-emerald-950/40",
icon: "bg-emerald-100 text-emerald-700 dark:bg-emerald-500/15 dark:text-emerald-300",
title: "text-emerald-900 dark:text-emerald-100",
body: "text-emerald-800/90 dark:text-emerald-200/80",
accent: "bg-emerald-500 dark:bg-emerald-400",
ring: "focus-visible:ring-emerald-500/60",
glyph: (
<path
d="M9 18h6m-5 3h4M12 3a6 6 0 0 0-3.5 10.9c.6.5 1 1.2 1 2v.1h5v-.1c0-.8.4-1.5 1-2A6 6 0 0 0 12 3Z"
strokeLinecap="round"
strokeLinejoin="round"
/>
),
},
warning: {
label: "Warning",
container:
"border-amber-200/80 bg-amber-50/70 dark:border-amber-400/25 dark:bg-amber-950/40",
icon: "bg-amber-100 text-amber-800 dark:bg-amber-500/15 dark:text-amber-300",
title: "text-amber-900 dark:text-amber-100",
body: "text-amber-800/90 dark:text-amber-200/80",
accent: "bg-amber-500 dark:bg-amber-400",
ring: "focus-visible:ring-amber-500/60",
glyph: (
<path
d="M12 9v4m0 3h.01M10.3 4.3 2.6 17.5A2 2 0 0 0 4.3 20.5h15.4a2 2 0 0 0 1.7-3L13.7 4.3a2 2 0 0 0-3.4 0Z"
strokeLinecap="round"
strokeLinejoin="round"
/>
),
},
danger: {
label: "Important",
container:
"border-rose-200/80 bg-rose-50/70 dark:border-rose-400/25 dark:bg-rose-950/40",
icon: "bg-rose-100 text-rose-700 dark:bg-rose-500/15 dark:text-rose-300",
title: "text-rose-900 dark:text-rose-100",
body: "text-rose-800/90 dark:text-rose-200/80",
accent: "bg-rose-500 dark:bg-rose-400",
ring: "focus-visible:ring-rose-500/60",
glyph: (
<path
d="M12 8v5m0 3h.01M12 3a9 9 0 1 0 0 18 9 9 0 0 0 0-18Z"
strokeLinecap="round"
strokeLinejoin="round"
/>
),
},
};
const CALLOUTS: Callout[] = [
{
variant: "info",
title: "Deploys run on the main branch only",
body: (
<>
Pushes to feature branches build a preview but never touch production.
Merge into <code className="rounded bg-black/5 px-1 py-0.5 font-mono text-[0.85em] dark:bg-white/10">main</code>{" "}
to promote a preview to the live site.
</>
),
},
{
variant: "tip",
title: "Prefetch the next route on hover",
body: (
<>
Wrap navigation links so the target page’s data starts loading the
moment a pointer enters. Users perceive the jump as instant, and you
spend nothing extra on bots that never hover.
</>
),
dismissible: true,
},
{
variant: "warning",
title: "This endpoint is rate-limited",
body: (
<>
You can call <code className="rounded bg-black/5 px-1 py-0.5 font-mono text-[0.85em] dark:bg-white/10">/v1/search</code>{" "}
up to 60 times per minute per API key. Batch your queries or cache
results before you hit the ceiling and start receiving{" "}
<code className="rounded bg-black/5 px-1 py-0.5 font-mono text-[0.85em] dark:bg-white/10">429</code> responses.
</>
),
dismissible: true,
},
{
variant: "danger",
title: "Deleting a workspace is permanent",
body: (
<>
Every project, member invite and stored secret is removed immediately
and cannot be recovered. Export anything you need before you confirm.
</>
),
},
];
function CalloutBox({
data,
onDismiss,
}: {
data: Callout;
onDismiss?: () => void;
}) {
const v = VARIANTS[data.variant];
return (
<div
role="note"
aria-label={v.label}
className={`fcode-callout relative flex gap-4 overflow-hidden rounded-2xl border p-5 pl-6 shadow-sm ${v.container}`}
>
<span
aria-hidden="true"
className={`absolute inset-y-0 left-0 w-1.5 ${v.accent}`}
/>
<span
aria-hidden="true"
className={`mt-0.5 flex h-9 w-9 shrink-0 items-center justify-center rounded-full ${v.icon}`}
>
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth={1.8}
className="h-5 w-5"
>
{v.glyph}
</svg>
</span>
<div className="min-w-0 flex-1">
<div className="flex items-start justify-between gap-3">
<div className="min-w-0">
<span
className={`text-[0.7rem] font-semibold uppercase tracking-[0.14em] ${v.body}`}
>
{v.label}
</span>
<h3
className={`mt-0.5 text-[0.95rem] font-semibold leading-snug ${v.title}`}
>
{data.title}
</h3>
</div>
{data.dismissible && onDismiss ? (
<button
type="button"
onClick={onDismiss}
aria-label={`Dismiss ${v.label.toLowerCase()}: ${data.title}`}
className={`-mr-1 -mt-1 flex h-7 w-7 shrink-0 items-center justify-center rounded-lg text-current/70 outline-none transition hover:bg-black/5 hover:text-current focus-visible:ring-2 dark:hover:bg-white/10 ${v.ring}`}
>
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth={2}
className="h-4 w-4"
aria-hidden="true"
>
<path
d="M6 6l12 12M18 6 6 18"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
</button>
) : null}
</div>
<div className={`mt-1.5 text-sm leading-relaxed ${v.body}`}>
{data.body}
</div>
</div>
</div>
);
}
export default function AlertCallout() {
const [dismissed, setDismissed] = useState<Set<number>>(new Set());
const dismiss = (i: number) =>
setDismissed((prev) => {
const next = new Set(prev);
next.add(i);
return next;
});
const hasReset = CALLOUTS.some((c, i) => c.dismissible && dismissed.has(i));
return (
<section className="relative w-full bg-white px-4 py-16 text-slate-900 sm:px-6 sm:py-20 dark:bg-slate-950 dark:text-slate-100">
<style>{`
@keyframes fcodeCallout_in {
from { opacity: 0; transform: translateY(10px) scale(0.985); }
to { opacity: 1; transform: translateY(0) scale(1); }
}
.fcode-callout {
animation: fcodeCallout_in 0.4s cubic-bezier(0.22, 1, 0.36, 1) both;
}
@media (prefers-reduced-motion: reduce) {
.fcode-callout { animation: none; }
}
`}</style>
<div className="mx-auto max-w-2xl">
<div className="mb-10 text-center">
<span className="inline-block rounded-full border border-slate-200 px-3 py-1 text-xs font-medium uppercase tracking-widest text-slate-500 dark:border-slate-800 dark:text-slate-400">
Documentation
</span>
<h2 className="mt-4 text-2xl font-semibold tracking-tight text-slate-900 sm:text-3xl dark:text-white">
Callouts & editorial notes
</h2>
<p className="mx-auto mt-3 max-w-md text-sm text-slate-500 dark:text-slate-400">
Four semantic styles to draw attention to context, advice, caution
and irreversible actions.
</p>
</div>
<div className="flex flex-col gap-4">
{CALLOUTS.map((c, i) =>
c.dismissible && dismissed.has(i) ? null : (
<CalloutBox
key={i}
data={c}
onDismiss={c.dismissible ? () => dismiss(i) : undefined}
/>
)
)}
</div>
{hasReset ? (
<div className="mt-6 flex justify-center">
<button
type="button"
onClick={() => setDismissed(new Set())}
className="rounded-full border border-slate-200 px-4 py-2 text-xs font-medium text-slate-600 outline-none transition hover:border-slate-300 hover:bg-slate-50 focus-visible:ring-2 focus-visible:ring-slate-400/60 dark:border-slate-800 dark:text-slate-300 dark:hover:border-slate-700 dark:hover:bg-slate-900"
>
Restore dismissed callouts
</button>
</div>
) : null}
</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.
Icon Large Alert
OriginalAn alert led by a large circular icon.

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.

