Skip to content
All Taste components

Animated charts

Animated bar chart

A twelve-month revenue chart whose bars grow up from the axis one after another, ending on this month’s total.

12 bars · staggered growth · highlighted month · tooltip · range tabs

Revenue
₹18.6L+12.4%
Oct · ₹18.6L
import { TrendingUp } from "lucide-react";
import { cn } from "@/lib/utils";

const data = [
  { month: "Nov", value: 42 }, { month: "Dec", value: 55 }, { month: "Jan", value: 48 }, { month: "Feb", value: 62 },
  { month: "Mar", value: 58 }, { month: "Apr", value: 71 }, { month: "May", value: 66 }, { month: "Jun", value: 74 },
  { month: "Jul", value: 69 }, { month: "Aug", value: 83 }, { month: "Sep", value: 78 }, { month: "Oct", value: 92 },
];
const max = 100; // values are % of the chart height here; scale yours to the largest value

export function AnimatedBarChart() {
  const last = data[data.length - 1];
  return (
    <section aria-label="Revenue chart" className="mx-auto grid max-w-2xl gap-4 rounded-lg border border-border bg-bg p-[18px] font-sans">
      <div className="flex flex-wrap items-start justify-between gap-3">
        <div>
          <p className="text-xs font-medium text-fg-subtle">Revenue</p>
          <div className="mt-1 flex items-center gap-2.5">
            <span className="text-[28px] font-medium tabular-nums tracking-tight text-fg">₹18.6L</span>
            <span className="inline-flex items-center gap-1 rounded-full bg-success-soft px-2 py-0.5 text-xs font-medium text-success-fg"><TrendingUp className="size-3" />+12.4%</span>
          </div>
        </div>
      </div>

      <div className="relative h-[180px]">
        <div aria-hidden className="absolute inset-0 flex flex-col justify-between">
          {[0, 1, 2, 3].map((i) => <span key={i} className="border-t border-dashed border-border" />)}
        </div>
        <div className="relative grid h-full grid-cols-12 items-end gap-1 sm:gap-2.5">
          {data.map((d, i) => (
            <div
              key={d.month}
              title={d.month + ": " + d.value}
              className={cn(
                "origin-bottom rounded-t-sm transition-colors animate-grow-y motion-reduce:animate-none",
                i === data.length - 1 ? "bg-primary" : "bg-primary-soft hover:bg-primary hover:opacity-50",
              )}
              style={{ height: (d.value / max) * 100 + "%", animationDelay: 150 + i * 55 + "ms" }}
            />
          ))}
        </div>
        <div
          className="absolute right-0 origin-bottom-right whitespace-nowrap rounded bg-fg px-2 py-1 text-xs font-medium text-bg shadow-lg animate-pop [animation-delay:1050ms] motion-reduce:animate-none"
          style={{ bottom: "calc(" + (last.value / max) * 100 + "% + 8px)" }}
        >
          {last.month} · ₹18.6L
        </div>
      </div>
      <div aria-hidden className="-mt-2 grid grid-cols-12 gap-1 text-center text-[10.5px] text-fg-subtle sm:gap-2.5">
        {data.map((d) => <span key={d.month}>{d.month}</span>)}
      </div>
    </section>
  );
}

The code uses the Taste Tailwind preset. Make yours on the Taste page with Export my system.