import { useState } from "react"; const INCOME = 2800; const FIXED_EXPENSES = [ { id: 1, name: "Rent & Bills", amount: 1209, category: "fixed", icon: "🏠" }, { id: 2, name: "Phone", amount: 38.56, category: "fixed", icon: "📱" }, { id: 3, name: "Gym", amount: 34, category: "fixed", icon: "💪" }, ]; const VARIABLE_CATEGORIES = [ { id: 4, name: "Groceries", amount: 200, icon: "🛒" }, { id: 5, name: "Eating Out", amount: 150, icon: "🍽️" }, { id: 6, name: "Transport", amount: 100, icon: "🚇" }, { id: 7, name: "Entertainment", amount: 80, icon: "🎬" }, { id: 8, name: "Clothing", amount: 50, icon: "👕" }, { id: 9, name: "Personal Care", amount: 40, icon: "✂️" }, ]; export default function Budget() { const [variable, setVariable] = useState(VARIABLE_CATEGORIES); const [savings, setSavings] = useState(200); const [newName, setNewName] = useState(""); const [newAmount, setNewAmount] = useState(""); const [showAdd, setShowAdd] = useState(false); const fixedTotal = FIXED_EXPENSES.reduce((s, e) => s + e.amount, 0); const variableTotal = variable.reduce((s, e) => s + e.amount, 0); const savingsAmount = savings; const totalSpend = fixedTotal + variableTotal + savingsAmount; const remaining = INCOME - totalSpend; const updateVariable = (id, val) => { setVariable(v => v.map(e => e.id === id ? { ...e, amount: Math.max(0, Number(val) || 0) } : e)); }; const removeVariable = (id) => { setVariable(v => v.filter(e => e.id !== id)); }; const addCategory = () => { if (!newName.trim()) return; setVariable(v => [...v, { id: Date.now(), name: newName.trim(), amount: Number(newAmount) || 0, icon: "📌" }]); setNewName(""); setNewAmount(""); setShowAdd(false); }; const pct = (amt) => Math.min(100, (amt / INCOME) * 100).toFixed(1); const statusColor = remaining >= 0 ? "#22c55e" : "#ef4444"; const statusLabel = remaining >= 0 ? "surplus" : "overspent"; return ( <div style={{ minHeight: "100vh", background: "#0f0f13", color: "#e8e6f0", fontFamily: "'DM Mono', 'Courier New', monospace", padding: "0 0 60px", }}> {/* Header */} <div style={{ borderBottom: "1px solid #1e1e2e", padding: "32px 32px 24px", background: "linear-gradient(135deg, #12121a 0%, #0f0f13 100%)", }}> <div style={{ maxWidth: 720, margin: "0 auto" }}> <div style={{ fontSize: 11, letterSpacing: "0.2em", color: "#6b6882", textTransform: "uppercase", marginBottom: 8 }}> Monthly Budget </div> <div style={{ display: "flex", alignItems: "baseline", gap: 16, flexWrap: "wrap" }}> <span style={{ fontSize: 40, fontWeight: 700, color: "#fff", letterSpacing: "-0.03em" }}> £{INCOME.toLocaleString()} </span> <span style={{ fontSize: 14, color: "#6b6882" }}>after-tax income</span> </div> {/* Summary bar */} <div style={{ marginTop: 20, display: "flex", gap: 24, flexWrap: "wrap" }}> {[ { label: "Fixed", value: fixedTotal, color: "#7c6bff" }, { label: "Variable", value: variableTotal, color: "#ff9f43" }, { label: "Savings", value: savingsAmount, color: "#26de81" }, { label: remaining >= 0 ? "Unallocated" : "Overspent", value: Math.abs(remaining), color: statusColor }, ].map(item => ( <div key={item.label}> <div style={{ fontSize: 10, letterSpacing: "0.15em", color: "#6b6882", textTransform: "uppercase", marginBottom: 4 }}> {item.label} </div> <div style={{ fontSize: 20, fontWeight: 600, color: item.color }}> £{item.value.toFixed(2)} </div> </div> ))} </div> {/* Stacked bar */} <div style={{ marginTop: 16, height: 6, borderRadius: 3, background: "#1e1e2e", overflow: "hidden", display: "flex" }}> <div style={{ width: `${pct(fixedTotal)}%`, background: "#7c6bff", transition: "width 0.4s" }} /> <div style={{ width: `${pct(variableTotal)}%`, background: "#ff9f43", transition: "width 0.4s" }} /> <div style={{ width: `${pct(savingsAmount)}%`, background: "#26de81", transition: "width 0.4s" }} /> </div> </div> </div> <div style={{ maxWidth: 720, margin: "0 auto", padding: "0 32px" }}> {/* Remaining callout */} <div style={{ margin: "24px 0", padding: "16px 20px", borderRadius: 10, border: `1px solid ${remaining >= 0 ? "#22c55e33" : "#ef444433"}`, background: remaining >= 0 ? "#22c55e0a" : "#ef44440a", display: "flex", justifyContent: "space-between", alignItems: "center", }}> <span style={{ fontSize: 13, color: "#9d9bb5" }}> {remaining >= 0 ? "You have " : "You are "}<strong style={{ color: statusColor }}>£{Math.abs(remaining).toFixed(2)}</strong> {statusLabel} this month </span> {remaining > 0 && ( <span style={{ fontSize: 11, color: "#6b6882" }}> allocate it below ↓ </span> )} </div> {/* Fixed */} <Section title="Fixed Expenses" color="#7c6bff" total={fixedTotal}> {FIXED_EXPENSES.map(e => ( <Row key={e.id} icon={e.icon} name={e.name} amount={e.amount} readOnly /> ))} </Section> {/* Savings */} <Section title="Savings" color="#26de81" total={savingsAmount}> <EditableRow icon="🏦" name="Monthly Savings" amount={savings} onChange={(v) => setSavings(Math.max(0, Number(v) || 0))} /> </Section> {/* Variable */} <Section title="Variable Expenses" color="#ff9f43" total={variableTotal}> {variable.map(e => ( <EditableRow key={e.id} icon={e.icon} name={e.name} amount={e.amount} onChange={(v) => updateVariable(e.id, v)} onRemove={() => removeVariable(e.id)} /> ))} {showAdd ? ( <div style={{ display: "flex", gap: 8, marginTop: 8, alignItems: "center" }}> <input placeholder="Category name" value={newName} onChange={e => setNewName(e.target.value)} style={inputStyle} /> <input type="number" placeholder="£0" value={newAmount} onChange={e => setNewAmount(e.target.value)} style={{ ...inputStyle, width: 80 }} /> <button onClick={addCategory} style={btnStyle("#7c6bff")}>Add</button> <button onClick={() => setShowAdd(false)} style={btnStyle("#2a2a3e")}>✕</button> </div> ) : ( <button onClick={() => setShowAdd(true)} style={{ marginTop: 10, background: "transparent", border: "1px dashed #2a2a3e", color: "#6b6882", borderRadius: 6, padding: "8px 14px", fontSize: 12, cursor: "pointer", width: "100%", letterSpacing: "0.05em", }} > + Add category </button> )} </Section> {/* Breakdown */} <div style={{ marginTop: 32, padding: "20px", background: "#12121a", borderRadius: 12, border: "1px solid #1e1e2e" }}> <div style={{ fontSize: 11, letterSpacing: "0.15em", color: "#6b6882", textTransform: "uppercase", marginBottom: 16 }}> Budget Breakdown </div> {[ { label: "Fixed", pct: (fixedTotal / INCOME * 100), color: "#7c6bff" }, { label: "Savings", pct: (savingsAmount / INCOME * 100), color: "#26de81" }, { label: "Variable", pct: (variableTotal / INCOME * 100), color: "#ff9f43" }, { label: remaining >= 0 ? "Unallocated" : "Overspent", pct: Math.abs(remaining / INCOME * 100), color: statusColor }, ].map(item => ( <div key={item.label} style={{ marginBottom: 10 }}> <div style={{ display: "flex", justifyContent: "space-between", fontSize: 12, marginBottom: 4 }}> <span style={{ color: "#9d9bb5" }}>{item.label}</span> <span style={{ color: item.color }}>{item.pct.toFixed(1)}%</span> </div> <div style={{ height: 4, background: "#1e1e2e", borderRadius: 2 }}> <div style={{ height: "100%", width: `${Math.min(100, item.pct)}%`, background: item.color, borderRadius: 2, transition: "width 0.4s" }} /> </div> </div> ))} </div> </div> </div> ); } function Section({ title, color, total, children }) { return ( <div style={{ marginTop: 28 }}> <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 12 }}> <div style={{ fontSize: 11, letterSpacing: "0.15em", color, textTransform: "uppercase" }}>{title}</div> <div style={{ fontSize: 13, color, fontWeight: 600 }}>£{total.toFixed(2)}</div> </div> <div style={{ background: "#12121a", borderRadius: 10, border: "1px solid #1e1e2e", overflow: "hidden" }}> {children} </div> </div> ); } function Row({ icon, name, amount }) { return ( <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", padding: "12px 16px", borderBottom: "1px solid #1a1a28", }}> <div style={{ display: "flex", alignItems: "center", gap: 10 }}> <span style={{ fontSize: 16 }}>{icon}</span> <span style={{ fontSize: 13, color: "#c4c1d8" }}>{name}</span> </div> <span style={{ fontSize: 13, color: "#e8e6f0", fontWeight: 600 }}>£{amount.toFixed(2)}</span> </div> ); } function EditableRow({ icon, name, amount, onChange, onRemove }) { return ( <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", padding: "10px 16px", borderBottom: "1px solid #1a1a28", }}> <div style={{ display: "flex", alignItems: "center", gap: 10, flex: 1 }}> <span style={{ fontSize: 16 }}>{icon}</span> <span style={{ fontSize: 13, color: "#c4c1d8" }}>{name}</span> </div> <div style={{ display: "flex", alignItems: "center", gap: 8 }}> <span style={{ fontSize: 13, color: "#6b6882" }}>£</span> <input type="number" value={amount} onChange={e => onChange(e.target.value)} style={{ width: 72, background: "#1a1a28", border: "1px solid #2a2a3e", borderRadius: 6, color: "#e8e6f0", fontSize: 13, padding: "4px 8px", textAlign: "right", outline: "none", }} /> {onRemove && ( <button onClick={onRemove} style={{ background: "none", border: "none", color: "#3a3a52", cursor: "pointer", fontSize: 14, padding: "2px 4px", lineHeight: 1 }} > ✕ </button> )} </div> </div> ); } const inputStyle = { flex: 1, background: "#1a1a28", border: "1px solid #2a2a3e", borderRadius: 6, color: "#e8e6f0", fontSize: 13, padding: "7px 10px", outline: "none", }; const btnStyle = (bg) => ({ background: bg, border: "none", borderRadius: 6, color: "#fff", fontSize: 12, padding: "7px 14px", cursor: "pointer", fontFamily: "inherit", whiteSpace: "nowrap", });
I'll implement this budget tracker in your project. Let me first check the current project structure, then create a polished version using Tailwind CSS instead of inline styles.
Now let me read the input component to understand its styling:
Now I'll create the budget tracker with a dark theme using Tailwind CSS, splitting it into proper components. Let me update the globals.css for the dark budget theme and create the components:
Now let me create the budget components. I'll split them into separate files for better organization:
Now let me create the main budget page and update the layout:
Your budget tracker is now ready! I've converted your inline-styled React component into a properly structured Next.js app using Tailwind CSS and shadcn/ui components. The app features:
/components/budget/ for maintainabilityThe budget automatically recalculates as you adjust values, showing your remaining unallocated funds or overspend amount in real-time.
Is it possible to change the colours
What color scheme would you like for the budget tracker? Warm theme