ComponentsCard KPI con kicker mono, valore numerico in tabular-nums, unità opzionale, e delta direzionale (▲ +12% / ▼ −3%) con colore semantico. Composabile in griglie.
Dashboard KPI Server
Esempio 01 Anteprima Codice
stat-card.tsx tsx src/components/stat-card.tsx
import type { ReactNode } from "react";
import { ArrowDownRight, ArrowUpRight, Minus } from "lucide-react";
import { cn } from "@/lib/utils";
export type StatTrend = "up" | "down" | "flat";
export type StatCardProps = {
/** Mono kicker label, top of the card. */
label: string;
/** Primary value: numeric or string. */
value: ReactNode;
/** Optional unit appended to the value (e.g. "%"), styled smaller and in fg-muted. */
unit?: string;
/** Optional delta: e.g. "+12%" or { value: 12, trend: "up" }. */
delta?: string | { value: string; trend: StatTrend };
/** Body caption shown below value. */
caption?: string;
/** Render in dense mode for small grids. */
dense?: boolean;
className?: string;
};
const TREND_ICON = { up: ArrowUpRight, down: ArrowDownRight, flat: Minus };
const TREND_TEXT = {
up: "text-emerald-600 dark:text-emerald-400",
down: "text-rose-600 dark:text-rose-400",
flat: "text-fg-muted",
};
function normalizeDelta(delta: StatCardProps["delta"]): { value: string; trend: StatTrend } | null {
if (!delta) return null;
if (typeof delta === "string") {
const trend: StatTrend = delta.startsWith("-") ? "down" : delta.startsWith("+") ? "up" : "flat";
return { value: delta, trend };
}
return delta;
}
export function StatCard({ label, value, unit, delta, caption, dense, className }: StatCardProps) {
const d = normalizeDelta(delta);
const Trend = d ? TREND_ICON[d.trend] : null;
return (
<div
className={cn(
"flex flex-col gap-1 rounded-md border border-border bg-bg",
dense ? "px-3 py-2.5" : "px-4 py-3.5",
className,
)}
>
<span className="font-mono text-[10px] uppercase tracking-[0.08em] text-fg-soft">
{label}
</span>
<div className="flex flex-wrap items-baseline gap-x-2 gap-y-1">
<span
className={cn(
"font-medium tabular-nums tracking-tight text-fg",
dense ? "text-[18px]" : "text-2xl sm:text-[28px]",
)}
>
{value}
</span>
{unit ? (
<span className="text-[13px] tabular-nums text-fg-muted">{unit}</span>
) : null}
{d && Trend ? (
<span
className={cn(
"ml-auto inline-flex items-center gap-0.5 font-mono text-[11px] tabular-nums",
TREND_TEXT[d.trend],
)}
>
<Trend className="h-3 w-3" aria-hidden />
{d.value}
</span>
) : null}
</div>
{caption ? (
<span className="text-[12.5px] leading-[1.5] text-fg-muted">{caption}</span>
) : null}
</div>
);
}
Note — Il delta accetta sia una stringa ('+18%') che un oggetto { value, trend }. Il segno determina automaticamente il trend (su/giù/piatto) e il colore semantico associato.
Prompt LLM 02 Incolla in Claude o ChatGPT per generare la tua variante. Include il contesto del brand, i token e i vincoli del progetto.
Sei un senior frontend engineer. Stai lavorando su un sito Next.js 16 + React 19 + Tailwind v4 in italiano, look chanhdai-inspired: colonna stretta 672px, Geist Sans + Geist Mono, hairline 1px, divisori a stripe diagonale, palette zinc.
Token CSS disponibili: --bg, --bg-alt, --fg, --fg-muted, --fg-soft, --border, --border-strong, --accent. Usa SEMPRE queste variabili tramite le utility tailwind generate (bg-bg, text-fg-muted, border-border, ecc.). Helper "cn" da "@/lib/utils". Niente librerie UI extra: solo lucide-react e tailwind-merge.
Genera un componente <StatCard>: una KPI card minimale per dashboard.
Props:
- label: string — kicker mono in alto, uppercase tracking
- value: ReactNode — valore principale (es. "12.4k", 982, ecc.)
- unit?: string — unità mostrata in piccolo a destra del valore (es. "%")
- delta?: string | { value: string; trend: "up" | "down" | "flat" } — variazione opzionale
- caption?: string — caption sotto il valore
- dense?: boolean — variante più compatta per griglie strette
- className?: string
Layout: card rounded-md border bg-bg con padding px-4 py-3.5 (px-3 py-2.5 in dense). Kicker mono text-[10px] uppercase tracking-[0.08em] text-fg-soft. Valore text-2xl sm:text-[28px] font-medium tabular-nums tracking-tight. Delta inline a destra (ml-auto) con icona ArrowUpRight/ArrowDownRight/Minus, color emerald-600 / rose-600 / fg-muted.
Constraints: server component, tabular-nums per allineare in griglia, supporto dark mode automatico.
Output: file completo .tsx.
Uso tipico 03 <StatCard label="Visite" value="12.4k" delta="+18%" caption="vs. mese scorso" />
Dipendenze 04 lucide-react tailwind-merge clsx
Ti è servito? Dimmelo, oppure proponi il prossimo componente.