Budget UI patterns
The budget is the part of your product users feel. These patterns — a clear connect button, a live budget bar, depletion warnings, and an obvious disconnect — turn “AI billing” into something users trust.
The connect button
The first impression. Make it unmistakable and reassuring: a prominent CTA, the voltage bolt for recognition, and a disabled/loading state while the popup is open.
function ConnectButton() {
const { connect, loading } = useWattfare();
return (
<button onClick={connect} disabled={loading} className="wf-connect">
<BoltIcon />
{loading ? "Connecting…" : "Connect AI budget"}
</button>
);
} The budget bar
Once connected, show remaining budget inline — ideally somewhere persistent like a header. Drive
the fill from usage vs. limits, and flag a low state early.
function BudgetBar() {
const { connected, status, remainingUsd } = useWattfare();
if (!connected) return null;
const cap = status.limits?.monthlyUsd ?? null;
const used = status.usage.monthlyUsd;
const pct = cap ? Math.min((used / cap) * 100, 100) : 0;
const low = remainingUsd !== null && remainingUsd < 1;
return (
<div className={`budget-bar${low ? " is-low" : ""}`}>
<div className="budget-track">
<div className="budget-fill" style={{ width: `${pct}%` }} />
</div>
<span className="budget-label">
{remainingUsd !== null ? `$${remainingUsd.toFixed(2)} left` : "Unlimited"}
{low && <em> · running low</em>}
</span>
</div>
);
} Depletion: warn, then gate
Two thresholds, two behaviours:
- Low (e.g. < $1 left) — a soft inline warning. Don't block anything yet.
- Empty (≤ $0) — disable the input and explain why, with a link to raise the cap. A disabled field with a reason beats a request that fails after the user hits enter.
function ChatInput() {
const { connected, remainingUsd } = useWattfare();
const depleted = remainingUsd !== null && remainingUsd <= 0;
if (!connected) return <ConnectButton />;
return (
<form>
<input disabled={depleted} placeholder={depleted ? "Budget used up for this month" : "Ask anything…"} />
{depleted && (
<p className="hint">
You've hit your monthly cap. <a href="/account">Raise it</a> to keep going.
</p>
)}
</form>
);
} Always offer disconnect
Control is the whole pitch. Give users an obvious way to disconnect and to see what they've spent. It costs you nothing and it's the difference between “this app can spend my money” and “I let this app spend my money, and I can stop it.”
function BudgetMenu() {
const { connected, disconnect, status } = useWattfare();
if (!connected) return null;
return (
<details className="budget-menu">
<summary>Budget · {status.usage.monthlyUsd.toFixed(2){"}"} used</summary>
<a href="/account">Manage budget</a>
<button onClick={disconnect}>Disconnect this app</button>
</details>
);
} A trust checklist
- Show the number. Always display remaining budget once connected — never hide it.
- Warn before the wall. A low-budget hint prevents the surprise of a hard stop.
- Explain the block. When gated, say why and link the fix — don't just disable silently.
- Make leaving easy. A visible disconnect builds more trust than any copy could.
- Mirror the cap. The number you request in
sessionHandleris what users approve — keep your UI honest about it.