Componentdata0.97 KB gzipZero dependencies
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-progressOr install the full npm package:
npm install bigbulluiUsage
Open in StackBlitzimport { BudgetProgress } from "@/components/ui/budget-progress";
<BudgetProgress />Usage guidance
Use Budget Progress when a ticket stub surface needs Budget spending bar with category breakdown and overrun warning. Keep props minimal and prefer composition with sibling stubs.
Avoid Budget Progress for long form flows or dense data grids where a dedicated layout block fits better.
Source code
budget-progress.tsxZero 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-warning" : "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
| Prop | Type | Description |
|---|---|---|
| categories | BudgetCategory[] | |
| currency | string |