A CO2 saved-or-emitted counter that counts up to its value on view, with a fixed unit and semantic tint.
npx shadcn@latest add @paragon/carbon-counterAlso installs: number-ticker
"use client";
import * as React from "react";
import { Leaf, Factory } from "lucide-react";
import { NumberTicker } from "@/registry/paragon/ui/number-ticker";
import { cn } from "@/lib/utils";
export interface CarbonCounterProps extends React.ComponentProps<"div"> {
/** Amount of CO2, in the given unit. */
value?: number;
/** Unit rendered after the number. */
unit?: string;
/** "saved" tints success and shows a leaf; "emitted" tints warning. */
variant?: "saved" | "emitted";
/** Caption under the readout. */
label?: string;
/** Small secondary line, e.g. equivalence. */
caption?: string;
/** Renders the target immediately, no count-up. */
static?: boolean;
}
/**
* A CO2 counter that counts up to its value on first view, built on the
* number-ticker spring so the digits settle in tabular figures without layout
* shift. The unit is separated from the animated number so it never jitters.
* A saved / emitted variant swaps the icon and semantic tint. Reduced motion
* (or `static`) shows the final total immediately.
*/
export function CarbonCounter({
value = 1284.6,
unit = "kg",
variant = "saved",
label = "CO₂ avoided this quarter",
caption,
static: isStatic = false,
className,
...props
}: CarbonCounterProps) {
const saved = variant === "saved";
const Icon = saved ? Leaf : Factory;
return (
<div
data-slot="carbon-counter"
className={cn(
"flex w-full max-w-xs flex-col gap-3 rounded-xl bg-card p-6 shadow-border",
className,
)}
{...props}
>
<span
className={cn(
"flex size-9 items-center justify-center rounded-lg",
saved ? "bg-success/10 text-success" : "bg-warning/15 text-warning",
)}
>
<Icon className="size-5" />
</span>
<div className="flex items-baseline gap-1.5">
<NumberTicker
value={value}
static={isStatic}
className="text-4xl font-semibold tracking-tight"
/>
<span className="text-lg font-medium text-muted-foreground">
{unit}
</span>
</div>
<div>
<div className="text-sm font-medium">{label}</div>
{caption && (
<p className="mt-0.5 text-xs text-muted-foreground">{caption}</p>
)}
</div>
</div>
);
}