bigbullui logobigbullui
Componentdata

Budget Progress

Budget spending bar with category breakdown and overrun warning.

Preview

Live Preview
Stage Production800 / 1.000

Installation

Tokens guide →

Add to your project via CLI (zero dependencies, code you own):

npx bigbullui add budget-progress

Or install the full npm package:

npm install bigbullui

Usage

import { BudgetProgress } from "@/components/ui/budget-progress";

<BudgetProgress />

Source code

budget-progress.tsx

Zero dependencies (only React + utils). Copy and paste directly into your project:

"use client";

import * as React from "react";
import { cn } from "./lib/utils";

export interface BudgetCategory {
  id: string;
  label: string;
  spent: number;
  limit: number;
}

export interface BudgetProgressProps extends React.HTMLAttributes<HTMLDivElement> {
  categories: BudgetCategory[];
  currency?: string;
}

/** Budget spending tracker: categorized with over-budget warning. */
export function BudgetProgress({ categories, currency = "₺", className, ...props }: BudgetProgressProps) {
  return (
    <div className={cn("w-full max-w-sm space-y-3", className)} {...props}>
      <style>{`@keyframes bpFill { from { transform: scaleX(0); } }`}</style>
      {categories.map((cat, idx) => {
        const pct = Math.min(100, Math.round((cat.spent / Math.max(1, cat.limit)) * 100));
        const over = cat.spent > cat.limit;
        const warn = !over && pct >= 85;
        return (
          <div key={cat.id} className="animate-[fade-in-up_0.3s_ease-out_both] motion-reduce:animate-none" style={{ animationDelay: `${idx * 70}ms` }}>
            <div className="flex items-baseline justify-between text-xs">
              <span className="font-medium">{cat.label}</span>
              <span className={cn("font-mono tabular-nums", over ? "text-destructive" : "text-muted-foreground")}>
                {cat.spent.toLocaleString("tr-TR")} / {cat.limit.toLocaleString("tr-TR")} {currency}
              </span>
            </div>
            <div className="mt-1 h-2 overflow-hidden rounded-full bg-border/50">
              <div
                className={cn(
                  "h-full origin-left rounded-full transition-colors duration-300 motion-reduce:transition-none",
                  over ? "bg-destructive" : warn ? "bg-amber-500" : "bg-accent"
                )}
                style={{ width: `${pct}%`, animation: "bpFill 0.6s cubic-bezier(0.16,1,0.3,1) both", animationDelay: `${idx * 70}ms` }}
              />
            </div>
            {over && <p className="mt-0.5 font-mono text-[9px] text-destructive">Budget exceeded by {(cat.spent - cat.limit).toLocaleString("en-US")} {currency}</p>}
          </div>
        );
      })}
    </div>
  );
}

Props

PropTypeDescription
categoriesBudgetCategory[]
currencystring