Dashboard Calendar Widget
Original · freemini calendar dashboard widget
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/dash-calendar-widget.json"use client";
import { useCallback, useMemo, useRef, useState } from "react";
import type { KeyboardEvent as ReactKeyboardEvent } from "react";
import { motion, AnimatePresence, useReducedMotion } from "motion/react";
type EventKind = "call" | "ship" | "focus" | "review";
type CalEvent = {
id: string;
/** ISO date, YYYY-MM-DD */
date: string;
time: string;
title: string;
detail: string;
kind: EventKind;
};
const MONTHS = [
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December",
];
const WEEKDAYS = ["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"];
const WEEKDAYS_FULL = [
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday",
"Sunday",
];
const KIND_META: Record<
EventKind,
{ label: string; dot: string; chip: string; rail: string }
> = {
call: {
label: "Call",
dot: "bg-indigo-500 dark:bg-indigo-400",
chip: "bg-indigo-50 text-indigo-700 ring-indigo-200 dark:bg-indigo-500/10 dark:text-indigo-300 dark:ring-indigo-400/25",
rail: "bg-indigo-500 dark:bg-indigo-400",
},
ship: {
label: "Ship",
dot: "bg-emerald-500 dark:bg-emerald-400",
chip: "bg-emerald-50 text-emerald-700 ring-emerald-200 dark:bg-emerald-500/10 dark:text-emerald-300 dark:ring-emerald-400/25",
rail: "bg-emerald-500 dark:bg-emerald-400",
},
focus: {
label: "Focus",
dot: "bg-violet-500 dark:bg-violet-400",
chip: "bg-violet-50 text-violet-700 ring-violet-200 dark:bg-violet-500/10 dark:text-violet-300 dark:ring-violet-400/25",
rail: "bg-violet-500 dark:bg-violet-400",
},
review: {
label: "Review",
dot: "bg-amber-500 dark:bg-amber-400",
chip: "bg-amber-50 text-amber-700 ring-amber-200 dark:bg-amber-500/10 dark:text-amber-300 dark:ring-amber-400/25",
rail: "bg-amber-500 dark:bg-amber-400",
},
};
const EVENTS: CalEvent[] = [
{
id: "e1",
date: "2026-03-02",
time: "09:30",
title: "Sprint kickoff — Billing v3",
detail: "Scope the metered-usage migration with Priya and Tom.",
kind: "call",
},
{
id: "e2",
date: "2026-03-02",
time: "14:00",
title: "Deep work: invoice reconciler",
detail: "No meetings block. Finish the retry queue.",
kind: "focus",
},
{
id: "e3",
date: "2026-03-05",
time: "11:15",
title: "Design review — onboarding flow",
detail: "Second pass on the three-step signup. Bring the Figma prototype.",
kind: "review",
},
{
id: "e4",
date: "2026-03-09",
time: "08:45",
title: "Customer call — Northwind",
detail: "Renewal check-in. They asked about SSO pricing tiers.",
kind: "call",
},
{
id: "e5",
date: "2026-03-11",
time: "16:00",
title: "Ship 3.4.0 to production",
detail: "Canary at 10% for two hours, then full rollout.",
kind: "ship",
},
{
id: "e6",
date: "2026-03-11",
time: "17:30",
title: "Postmortem — March 4 outage",
detail: "Blameless writeup, action items owned by end of week.",
kind: "review",
},
{
id: "e7",
date: "2026-03-17",
time: "10:00",
title: "Quarterly roadmap sync",
detail: "Trim Q2 to three bets. Everything else is a maybe.",
kind: "call",
},
{
id: "e8",
date: "2026-03-17",
time: "13:00",
title: "Focus: API docs rewrite",
detail: "Rewrite the auth section around real curl examples.",
kind: "focus",
},
{
id: "e9",
date: "2026-03-17",
time: "15:45",
title: "Ship docs site refresh",
detail: "New search index plus the versioned sidebar.",
kind: "ship",
},
{
id: "e10",
date: "2026-03-20",
time: "09:00",
title: "Interview — senior platform eng",
detail: "System design round. Prompt: multi-region write path.",
kind: "call",
},
{
id: "e11",
date: "2026-03-24",
time: "12:30",
title: "Ship webhooks beta",
detail: "Twelve design partners get the signing-secret rotation.",
kind: "ship",
},
{
id: "e12",
date: "2026-03-27",
time: "15:00",
title: "Pricing experiment review",
detail: "Two weeks of data on the annual toggle. Read the cohort split.",
kind: "review",
},
{
id: "e13",
date: "2026-04-01",
time: "10:30",
title: "Board prep — Q1 numbers",
detail: "Net revenue retention story needs one clean chart.",
kind: "review",
},
{
id: "e14",
date: "2026-04-03",
time: "09:00",
title: "Focus: latency budget",
detail: "p99 is 380ms. Target is 200ms. Start with the fan-out.",
kind: "focus",
},
{
id: "e15",
date: "2026-02-25",
time: "14:30",
title: "Retro — February",
detail: "Two shipped bets, one dropped. Talk about the drop.",
kind: "review",
},
];
const TODAY_ISO = "2026-03-11";
function pad(n: number): string {
return n < 10 ? `0${n}` : `${n}`;
}
function toIso(y: number, m: number, d: number): string {
return `${y}-${pad(m + 1)}-${pad(d)}`;
}
function daysInMonth(y: number, m: number): number {
return new Date(y, m + 1, 0).getDate();
}
/** Monday-first offset for the first of the month. */
function leadingBlanks(y: number, m: number): number {
const js = new Date(y, m, 1).getDay();
return (js + 6) % 7;
}
type Cell = { day: number; iso: string; weekdayIndex: number };
function ChevronIcon({ dir }: { dir: "left" | "right" }) {
return (
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth={2}
strokeLinecap="round"
strokeLinejoin="round"
className="h-4 w-4"
aria-hidden="true"
>
{dir === "left" ? (
<path d="M15 6l-6 6 6 6" />
) : (
<path d="M9 6l6 6-6 6" />
)}
</svg>
);
}
function ClockIcon() {
return (
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth={1.75}
strokeLinecap="round"
strokeLinejoin="round"
className="h-3.5 w-3.5"
aria-hidden="true"
>
<circle cx="12" cy="12" r="9" />
<path d="M12 7v5l3 2" />
</svg>
);
}
function SparkIcon() {
return (
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth={1.75}
strokeLinecap="round"
strokeLinejoin="round"
className="h-4 w-4"
aria-hidden="true"
>
<path d="M12 3v4M12 17v4M3 12h4M17 12h4M5.6 5.6l2.8 2.8M15.6 15.6l2.8 2.8M18.4 5.6l-2.8 2.8M8.4 15.6l-2.8 2.8" />
</svg>
);
}
export default function DashCalendarWidget() {
const reduced = useReducedMotion();
const [year, setYear] = useState<number>(2026);
const [month, setMonth] = useState<number>(2);
const [selected, setSelected] = useState<string>(TODAY_ISO);
const [dir, setDir] = useState<1 | -1>(1);
const [filter, setFilter] = useState<EventKind | "all">("all");
const gridRef = useRef<HTMLDivElement | null>(null);
const eventsByDate = useMemo(() => {
const map = new Map<string, CalEvent[]>();
for (const ev of EVENTS) {
if (filter !== "all" && ev.kind !== filter) continue;
const list = map.get(ev.date);
if (list) list.push(ev);
else map.set(ev.date, [ev]);
}
for (const list of map.values()) list.sort((a, b) => a.time.localeCompare(b.time));
return map;
}, [filter]);
const cells = useMemo<Cell[]>(() => {
const total = daysInMonth(year, month);
const out: Cell[] = [];
for (let d = 1; d <= total; d++) {
out.push({
day: d,
iso: toIso(year, month, d),
weekdayIndex: (new Date(year, month, d).getDay() + 6) % 7,
});
}
return out;
}, [year, month]);
const blanks = useMemo(() => leadingBlanks(year, month), [year, month]);
const selectedEvents = eventsByDate.get(selected) ?? [];
const monthCount = useMemo(() => {
let n = 0;
for (const [iso, list] of eventsByDate) {
if (iso.startsWith(`${year}-${pad(month + 1)}`)) n += list.length;
}
return n;
}, [eventsByDate, year, month]);
const shift = useCallback(
(delta: 1 | -1) => {
setDir(delta);
setMonth((m) => {
const next = m + delta;
if (next < 0) {
setYear((y) => y - 1);
return 11;
}
if (next > 11) {
setYear((y) => y + 1);
return 0;
}
return next;
});
},
[]
);
const focusIso = useCallback((iso: string) => {
const node = gridRef.current?.querySelector<HTMLButtonElement>(
`[data-iso="${iso}"]`
);
node?.focus();
}, []);
const moveSelection = useCallback(
(deltaDays: number) => {
const [y, m, d] = selected.split("-").map(Number);
const next = new Date(y, m - 1, d + deltaDays);
const nIso = toIso(next.getFullYear(), next.getMonth(), next.getDate());
setSelected(nIso);
if (next.getMonth() !== month || next.getFullYear() !== year) {
setDir(deltaDays > 0 ? 1 : -1);
setMonth(next.getMonth());
setYear(next.getFullYear());
window.setTimeout(() => focusIso(nIso), 0);
} else {
focusIso(nIso);
}
},
[selected, month, year, focusIso]
);
const onGridKeyDown = useCallback(
(e: ReactKeyboardEvent<HTMLDivElement>) => {
switch (e.key) {
case "ArrowLeft":
e.preventDefault();
moveSelection(-1);
break;
case "ArrowRight":
e.preventDefault();
moveSelection(1);
break;
case "ArrowUp":
e.preventDefault();
moveSelection(-7);
break;
case "ArrowDown":
e.preventDefault();
moveSelection(7);
break;
case "Home": {
e.preventDefault();
const [y, m, d] = selected.split("-").map(Number);
const wd = (new Date(y, m - 1, d).getDay() + 6) % 7;
moveSelection(-wd);
break;
}
case "End": {
e.preventDefault();
const [y, m, d] = selected.split("-").map(Number);
const wd = (new Date(y, m - 1, d).getDay() + 6) % 7;
moveSelection(6 - wd);
break;
}
case "PageUp":
e.preventDefault();
moveSelection(-daysInMonth(year, month === 0 ? 11 : month - 1));
break;
case "PageDown":
e.preventDefault();
moveSelection(daysInMonth(year, month));
break;
default:
break;
}
},
[moveSelection, selected, year, month]
);
const goToday = useCallback(() => {
setDir(TODAY_ISO > selected ? 1 : -1);
setSelected(TODAY_ISO);
setYear(2026);
setMonth(2);
window.setTimeout(() => focusIso(TODAY_ISO), 0);
}, [selected, focusIso]);
const selectedLabel = useMemo(() => {
const [y, m, d] = selected.split("-").map(Number);
const wd = (new Date(y, m - 1, d).getDay() + 6) % 7;
return `${WEEKDAYS_FULL[wd]}, ${MONTHS[m - 1]} ${d}`;
}, [selected]);
const slideX = reduced ? 0 : dir === 1 ? 18 : -18;
return (
<section className="relative w-full bg-slate-50 px-4 py-16 sm:px-6 sm:py-20 dark:bg-slate-950">
<style>{`
@keyframes dcw-pulse-ring {
0% { transform: scale(0.85); opacity: 0.55; }
70% { transform: scale(1.6); opacity: 0; }
100% { transform: scale(1.6); opacity: 0; }
}
@keyframes dcw-sheen {
0% { background-position: -220% 0; }
100% { background-position: 220% 0; }
}
.dcw-ring::after {
content: "";
position: absolute;
inset: 0;
border-radius: 9999px;
border: 1px solid currentColor;
animation: dcw-pulse-ring 2.8s cubic-bezier(0.16, 1, 0.3, 1) infinite;
}
.dcw-sheen {
background-image: linear-gradient(
100deg,
transparent 38%,
rgba(99, 102, 241, 0.14) 50%,
transparent 62%
);
background-size: 220% 100%;
animation: dcw-sheen 7s linear infinite;
}
@media (prefers-reduced-motion: reduce) {
.dcw-ring::after,
.dcw-sheen {
animation: none !important;
}
.dcw-sheen { background-image: none; }
}
`}</style>
<div className="mx-auto w-full max-w-5xl">
<header className="mb-8 flex flex-col gap-2">
<p className="text-xs font-semibold uppercase tracking-[0.18em] text-indigo-600 dark:text-indigo-400">
Workspace
</p>
<h2 className="text-2xl font-semibold tracking-tight text-slate-900 sm:text-3xl dark:text-slate-50">
Schedule at a glance
</h2>
<p className="max-w-xl text-sm leading-relaxed text-slate-600 dark:text-slate-400">
Pick a day to read its agenda. Arrow keys move the selection, Page
Up and Page Down jump a month.
</p>
</header>
<div className="grid gap-4 lg:grid-cols-[minmax(0,1.15fr)_minmax(0,1fr)]">
{/* Calendar card */}
<div className="relative overflow-hidden rounded-2xl border border-slate-200 bg-white shadow-sm dark:border-slate-800 dark:bg-slate-900">
<div
className="pointer-events-none absolute inset-x-0 top-0 h-px dcw-sheen"
aria-hidden="true"
/>
<div className="flex items-center justify-between gap-3 border-b border-slate-200 px-4 py-3.5 sm:px-5 dark:border-slate-800">
<div className="min-w-0">
<div
className="truncate text-sm font-semibold text-slate-900 dark:text-slate-100"
aria-live="polite"
>
{MONTHS[month]} {year}
</div>
<div className="mt-0.5 text-xs text-slate-500 dark:text-slate-400">
{monthCount === 0
? "Nothing scheduled"
: `${monthCount} ${monthCount === 1 ? "entry" : "entries"}`}
</div>
</div>
<div className="flex shrink-0 items-center gap-1.5">
<button
type="button"
onClick={goToday}
className="rounded-lg border border-slate-200 px-2.5 py-1.5 text-xs font-medium text-slate-700 transition-colors hover:bg-slate-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 focus-visible:ring-offset-white dark:border-slate-700 dark:text-slate-200 dark:hover:bg-slate-800 dark:focus-visible:ring-offset-slate-900"
>
Today
</button>
<button
type="button"
onClick={() => shift(-1)}
aria-label={`Previous month, ${
MONTHS[month === 0 ? 11 : month - 1]
}`}
className="rounded-lg border border-slate-200 p-1.5 text-slate-600 transition-colors hover:bg-slate-100 hover:text-slate-900 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 focus-visible:ring-offset-white dark:border-slate-700 dark:text-slate-300 dark:hover:bg-slate-800 dark:hover:text-slate-50 dark:focus-visible:ring-offset-slate-900"
>
<ChevronIcon dir="left" />
</button>
<button
type="button"
onClick={() => shift(1)}
aria-label={`Next month, ${MONTHS[month === 11 ? 0 : month + 1]}`}
className="rounded-lg border border-slate-200 p-1.5 text-slate-600 transition-colors hover:bg-slate-100 hover:text-slate-900 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 focus-visible:ring-offset-white dark:border-slate-700 dark:text-slate-300 dark:hover:bg-slate-800 dark:hover:text-slate-50 dark:focus-visible:ring-offset-slate-900"
>
<ChevronIcon dir="right" />
</button>
</div>
</div>
<div className="px-3 pb-4 pt-3 sm:px-4">
<div className="mb-1 grid grid-cols-7 gap-1" aria-hidden="true">
{WEEKDAYS.map((w, i) => (
<div
key={w}
className={`py-1.5 text-center text-[11px] font-semibold uppercase tracking-wider ${
i > 4
? "text-slate-400 dark:text-slate-600"
: "text-slate-500 dark:text-slate-400"
}`}
>
{w}
</div>
))}
</div>
<AnimatePresence mode="wait" initial={false}>
<motion.div
key={`${year}-${month}`}
initial={{ opacity: 0, x: slideX }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: -slideX }}
transition={{ duration: reduced ? 0 : 0.22, ease: [0.16, 1, 0.3, 1] }}
>
<div
ref={gridRef}
role="grid"
aria-label={`${MONTHS[month]} ${year}`}
onKeyDown={onGridKeyDown}
className="grid grid-cols-7 gap-1"
>
{Array.from({ length: blanks }).map((_, i) => (
<div key={`blank-${i}`} role="presentation" />
))}
{cells.map((cell) => {
const list = eventsByDate.get(cell.iso) ?? [];
const isToday = cell.iso === TODAY_ISO;
const isSel = cell.iso === selected;
const weekend = cell.weekdayIndex > 4;
const kinds = Array.from(new Set(list.map((e) => e.kind))).slice(
0,
3
);
return (
<button
key={cell.iso}
type="button"
role="gridcell"
data-iso={cell.iso}
tabIndex={isSel ? 0 : -1}
aria-selected={isSel}
aria-current={isToday ? "date" : undefined}
aria-label={`${MONTHS[month]} ${cell.day}, ${year}${
list.length
? `, ${list.length} ${
list.length === 1 ? "event" : "events"
}`
: ", no events"
}`}
onClick={() => setSelected(cell.iso)}
className={[
"relative flex aspect-square flex-col items-center justify-center gap-1 rounded-xl text-sm transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 focus-visible:ring-offset-white dark:focus-visible:ring-offset-slate-900",
isSel
? "bg-slate-900 font-semibold text-white dark:bg-slate-100 dark:text-slate-900"
: weekend
? "text-slate-400 hover:bg-slate-100 dark:text-slate-500 dark:hover:bg-slate-800"
: "text-slate-700 hover:bg-slate-100 dark:text-slate-300 dark:hover:bg-slate-800",
!isSel && isToday
? "font-semibold text-indigo-600 dark:text-indigo-400"
: "",
].join(" ")}
>
{isToday && !isSel ? (
<span
className="dcw-ring pointer-events-none absolute inset-1.5 rounded-full text-indigo-500/70 dark:text-indigo-400/70"
aria-hidden="true"
/>
) : null}
<span className="relative tabular-nums leading-none">
{cell.day}
</span>
<span
className="flex h-1.5 items-center gap-[3px]"
aria-hidden="true"
>
{kinds.map((k) => (
<span
key={k}
className={[
"h-1.5 w-1.5 rounded-full",
isSel
? "bg-white/70 dark:bg-slate-900/60"
: KIND_META[k].dot,
].join(" ")}
/>
))}
</span>
</button>
);
})}
</div>
</motion.div>
</AnimatePresence>
</div>
<div className="flex flex-wrap items-center gap-1.5 border-t border-slate-200 px-3 py-3 sm:px-4 dark:border-slate-800">
<span className="mr-1 text-[11px] font-medium uppercase tracking-wider text-slate-500 dark:text-slate-400">
Filter
</span>
{(["all", "call", "ship", "focus", "review"] as const).map((k) => {
const active = filter === k;
return (
<button
key={k}
type="button"
aria-pressed={active}
onClick={() => setFilter(k)}
className={[
"inline-flex items-center gap-1.5 rounded-full px-2.5 py-1 text-xs font-medium ring-1 transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 focus-visible:ring-offset-white dark:focus-visible:ring-offset-slate-900",
active
? "bg-slate-900 text-white ring-slate-900 dark:bg-slate-100 dark:text-slate-900 dark:ring-slate-100"
: "bg-transparent text-slate-600 ring-slate-200 hover:bg-slate-100 dark:text-slate-300 dark:ring-slate-700 dark:hover:bg-slate-800",
].join(" ")}
>
{k !== "all" ? (
<span
className={`h-1.5 w-1.5 rounded-full ${
active ? "bg-current" : KIND_META[k].dot
}`}
aria-hidden="true"
/>
) : null}
{k === "all" ? "All" : KIND_META[k].label}
</button>
);
})}
</div>
</div>
{/* Agenda card */}
<div className="flex flex-col rounded-2xl border border-slate-200 bg-white shadow-sm dark:border-slate-800 dark:bg-slate-900">
<div className="border-b border-slate-200 px-4 py-3.5 sm:px-5 dark:border-slate-800">
<div className="text-sm font-semibold text-slate-900 dark:text-slate-100">
{selectedLabel}
</div>
<div className="mt-0.5 text-xs text-slate-500 dark:text-slate-400">
{selected === TODAY_ISO ? "Today · " : ""}
{selectedEvents.length === 0
? "Open calendar"
: `${selectedEvents.length} ${
selectedEvents.length === 1 ? "entry" : "entries"
}`}
</div>
</div>
<div
className="min-h-[19rem] flex-1 px-3 py-3 sm:px-4"
aria-live="polite"
aria-atomic="false"
>
<AnimatePresence mode="wait" initial={false}>
<motion.ul
key={`${selected}-${filter}`}
initial={{ opacity: 0, y: reduced ? 0 : 6 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: reduced ? 0 : -6 }}
transition={{ duration: reduced ? 0 : 0.18, ease: [0.16, 1, 0.3, 1] }}
className="space-y-2"
>
{selectedEvents.length === 0 ? (
<li className="flex h-[17rem] flex-col items-center justify-center gap-2 text-center">
<span className="text-slate-400 dark:text-slate-600">
<SparkIcon />
</span>
<p className="text-sm font-medium text-slate-700 dark:text-slate-300">
No entries on this day
</p>
<p className="max-w-[16rem] text-xs leading-relaxed text-slate-500 dark:text-slate-400">
{filter === "all"
? "A clear day. Good time to pull work forward."
: `Nothing tagged ${KIND_META[filter].label.toLowerCase()} here — try the All filter.`}
</p>
</li>
) : (
selectedEvents.map((ev, i) => (
<motion.li
key={ev.id}
initial={{ opacity: 0, y: reduced ? 0 : 8 }}
animate={{ opacity: 1, y: 0 }}
transition={{
duration: reduced ? 0 : 0.24,
delay: reduced ? 0 : i * 0.05,
ease: [0.16, 1, 0.3, 1],
}}
className="relative overflow-hidden rounded-xl border border-slate-200 bg-slate-50/60 p-3 pl-4 dark:border-slate-800 dark:bg-slate-800/40"
>
<span
className={`absolute inset-y-0 left-0 w-1 ${KIND_META[ev.kind].rail}`}
aria-hidden="true"
/>
<div className="flex items-start justify-between gap-3">
<div className="min-w-0">
<p className="text-sm font-medium leading-snug text-slate-900 dark:text-slate-100">
{ev.title}
</p>
<p className="mt-1 text-xs leading-relaxed text-slate-600 dark:text-slate-400">
{ev.detail}
</p>
</div>
<span
className={`shrink-0 rounded-full px-2 py-0.5 text-[11px] font-medium ring-1 ${KIND_META[ev.kind].chip}`}
>
{KIND_META[ev.kind].label}
</span>
</div>
<div className="mt-2 inline-flex items-center gap-1.5 text-xs tabular-nums text-slate-500 dark:text-slate-400">
<ClockIcon />
{ev.time}
</div>
</motion.li>
))
)}
</motion.ul>
</AnimatePresence>
</div>
</div>
</div>
</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 →
Dashboard KPI Row
Originalrow of KPI stat cards with trends

Dashboard Stat Widgets
Originalassorted dashboard stat widgets

Dashboard Activity
Originalrecent activity feed widget

Dashboard Chart Widget
Originalchart panel widget with tabs

Dashboard Revenue
Originalrevenue overview card with chart

Dashboard Users
Originalactive users widget

Dashboard Tasks
Originaltasks/checklist widget

Dashboard Notifications
Originalnotifications panel widget

Dashboard Overview
Originalcompact dashboard overview grid

Spotlight Hero
OriginalA centred hero with a soft radial spotlight, badge and dual call-to-action.

Split Hero
OriginalA two-column hero pairing a headline and CTAs with a product mock and social proof.

Gradient Spotlight Hero
OriginalA minimal centred hero with a soft gradient-mesh backdrop, announcement pill and dual call-to-action buttons.

