Template · React · HTML
Account home
A bank account start page in Tiles: a payment card, the balance, a three-state dial and recent transactions.
Preview
React
Install the template with shadcn. It lands in your project as ordinary components that you edit yourself.
npx shadcn@latest add https://mikkelmanniche.dk/lab/r/account-home.jsonnpx shadcn@latest add @manniche/account-homeaccount-home.tsx
// An account home screen in Tiles: the payment card, the balance, a three-state spending dial and recent transactions.
import { cn } from '@/lib/utils'
import { BigNumber, DeltaPill } from '@/registry/manniche/chart-kit/chart-kit'
import { formatValue, type ValueFormat } from '@/registry/manniche/chart-kit/chart-utils'
import { DataTile, TileFact } from '@/registry/manniche/data-tile/data-tile'
import { CardStack, type StackCard } from '@/registry/manniche/card-stack/card-stack'
import { Dial, type DialZone } from '@/registry/manniche/dial/dial'
import { TransactionList, type Transaction } from '@/registry/manniche/transaction-list/transaction-list'
export type AccountHomeProps = {
data: {
card: StackCard
balance: number
change: number
/** Spending pace against the budget: 1 is exactly on budget. */
pace: number
budget: number
transactions: Transaction[]
categories?: Record<string, string>
}
format?: ValueFormat
title?: string
note?: string
/** "YYYY-MM-DD" of today, so the list reads "Today" and "Yesterday". Default the day of `now`. */
today?: string
/** The current time. Used for `today` when that is not given. */
now?: Date
labels?: { card?: string; balance?: string; pace?: string; paceCaption?: string; transactions?: string; budget?: string; zones?: [string, string, string] }
className?: string
}
const DEFAULT_FORMAT: ValueFormat = { currency: 'EUR', decimals: 2 }
/** Account home: card, balance, spending pace dial (under, on track, over) and transactions. */
// "YYYY-MM-DD" of `now` in local time, for TransactionList's "Today" and "Yesterday".
const dayOf = (d: Date) => `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`
export function AccountHome({ data, format = DEFAULT_FORMAT, title = 'Account', note = 'Example data.', today, now, labels, className }: AccountHomeProps) {
const day = today ?? (now ? dayOf(now) : undefined)
const l = { card: 'Card', balance: 'Balance', pace: 'Spending pace', paceCaption: 'of your daily budget', transactions: 'Recent transactions', budget: 'Budget this month', zones: ['Under', 'On track', 'Over'] as [string, string, string], ...labels }
const zones: DialZone[] = [
{ label: l.zones[0], to: 0.85, color: 'var(--chart-1)' },
{ label: l.zones[1], to: 1.1, color: 'var(--success)' },
{ label: l.zones[2], to: 1.6, color: 'var(--destructive)' },
]
return (
<div className={cn('@container w-full font-sans text-foreground', className)}>
<div className="grid gap-4 @3xl:grid-cols-6 @6xl:grid-cols-12">
<h1 className="text-[22px] leading-tight font-semibold tracking-tight @3xl:col-span-6 @6xl:col-span-12">{title}</h1>
<DataTile title={l.card} className="@3xl:col-span-3 @6xl:col-span-4">
<CardStack data={[data.card]} label={l.card} format={format} hideAmount />
</DataTile>
<DataTile title={l.balance} inverted className="@3xl:col-span-3 @6xl:col-span-4" footer={<span>{note}</span>}>
<BigNumber value={data.balance} format={format} size="lg" />
<div className="mt-3">
<DeltaPill value={data.change} format={{ decimals: 1, suffix: '%', sign: true }} />
</div>
<div className="mt-5">
<TileFact label={l.budget}>{formatValue(data.budget, { ...format, decimals: 2 })}</TileFact>
</div>
</DataTile>
<DataTile title={l.pace} className="@3xl:col-span-6 @6xl:col-span-4">
<Dial value={data.pace} zones={zones} format={{ decimals: 2, suffix: '×' }} label={l.pace} caption={l.paceCaption} />
</DataTile>
<DataTile title={l.transactions} className="@3xl:col-span-6 @6xl:col-span-12">
<TransactionList data={data.transactions} label={l.transactions} today={day} format={format} categories={data.categories} limit={5} />
</DataTile>
</div>
</div>
)
}
export default AccountHomeaccount-home-demo.tsx
import { AccountHome } from '@/components/account-home'
// Example data for the demo, not a real account. "Today" is Thursday 8 October 2026.
const data = {
card: { id: 'everyday', name: 'Everyday', kind: 'Debit', ending: '4821', amount: 2091.2 },
balance: 2091.2,
change: 1.8,
pace: 0.92,
budget: 1800,
categories: {
Groceries: 'var(--chart-1)',
Transport: 'var(--chart-2)',
'Eating out': 'var(--chart-3)',
Shopping: 'var(--chart-4)',
Bills: 'var(--chart-5)',
},
transactions: [
{ id: 't1', name: 'Harbour Market', category: 'Groceries', date: '2026-10-08T12:58', amount: -48.2, status: 'pending' as const },
{ id: 't2', name: 'City Transit', category: 'Transport', date: '2026-10-08T08:31', amount: -3.1 },
{ id: 't3', name: 'Corner Bakery', category: 'Eating out', date: '2026-10-08T08:12', amount: -6.4 },
{ id: 't5', name: 'Northwind Books', category: 'Shopping', date: '2026-10-07T16:05', amount: -31.5 },
{ id: 't6', name: 'From Savings', category: 'Transfer', date: '2026-10-07T09:00', amount: 200 },
{ id: 't7', name: 'Lumen Energy', category: 'Bills', date: '2026-10-06T07:00', amount: -74 },
],
}
export default function AccountHomeDemo() {
return <AccountHome data={data} today="2026-10-08" />
}HTML
The whole page as one file with all CSS built in and no JavaScript. Toggles, tabs and filters work with HTML and CSS alone.
The colour in your own app
If you already have shadcn set up, these variables give the same colour. Light in :root, dark in .dark.
:root {
--primary: oklch(0.52 0.2 262);
--primary-foreground: oklch(0.99 0 0);
--accent: oklch(0.95 0.034 262);
--accent-foreground: oklch(0.36 0.140 262);
--ring: oklch(0.52 0.2 262);
}
.dark {
--primary: oklch(0.74 0.150 262);
--primary-foreground: oklch(0.18 0.024 262);
--accent: oklch(0.29 0.050 262);
--accent-foreground: oklch(0.9 0.050 262);
--ring: oklch(0.74 0.150 262);
}Install
npx shadcn@latest add https://mikkelmanniche.dk/lab/r/account-home.json