Vorlage · React · HTML
Account home
Eine Startseite für ein Bankkonto in Tiles: eine Zahlungskarte, der Kontostand, eine Anzeige mit drei Zuständen und letzte Transaktionen.
Vorschau
React
Installiere die Vorlage mit shadcn. Sie landet als normale Komponenten in deinem Projekt, die du selbst anpasst.
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
Die ganze Seite als eine Datei mit eingebautem CSS und ohne JavaScript. Umschalter, Tabs und Filter funktionieren allein mit HTML und CSS.
Die Farbe in deiner eigenen App
Wenn shadcn bereits eingerichtet ist, ergeben diese Variablen dieselbe Farbe. Hell in :root, dunkel 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);
}Installieren
npx shadcn@latest add https://mikkelmanniche.dk/lab/r/account-home.json