Solo, end to end — design and the front end
NovaPay had no client and no brief, so every stage below is mine, in the order I built it:
- Design system — the Figma tokens for colour, type and spacing that everything else is built on.
- Component library — 20+ reusable components built on those tokens.
- Information architecture & flows — the screen map and the three core flows (send, request, dispute), including their edge cases and trust states.
- Visual design — every screen, styled against the token set rather than one-off.
- Front-end development — the working prototype, coded in Next.js and Tailwind, so the design had to hold up in a browser, not just in Figma.
Banking, redesigned
NovaPay is a fintech banking app I built from nothing, in the order I think it should be built: tokens, components, screens, code. No one was going to hand me a style guide for it, so I made one, and then I made the app to find out whether the style guide was any good.
The tension I kept hitting is that a bank has to look like it can be trusted with your money and also let you check a balance in three seconds. Trust tends to mean density. Speed tends to mean taking things away. Most of what follows is me picking a side, screen by screen.

Trust and speed pull in opposite directions
A banking app has two jobs that fight each other. It has to look and feel like it can be trusted with someone’s money, which tends to mean more information on screen and more weight. And it has to let someone check a balance or move money in seconds, which tends to mean stripping that away. Most fintech UI picks a side and lets the other suffer — dense screens that feel secure but slow, or minimal screens that feel fast but thin. The problem this project set out to solve was designing against both at once, screen by screen, rather than trading one for the other.
From concept to prototype
I worked in the order below. It matters more than it looks: tokens before components, components before screens, screens before code. Skip back a step and you end up reconciling two versions of the same card.
- Fintech competitive audit
- User needs analysis
- Feature prioritisation
- Information architecture
- Color token system
- Typography scale
- Spacing & radius variables
- Figma variable setup
- 20+ Figma components
- Variant architecture
- Responsive behaviour
- Interaction states
- 22 mobile screens (375px)
- 19 desktop screens (1440px)
- Dark mode variants
- User flow diagrams
- Next.js implementation
- Tailwind CSS styling
- Framer Motion animations
- Responsive & dark mode
Every screen mapped
| Screen | Purpose | Key Elements |
|---|---|---|
| Dashboard | Financial overview at a glance | Available balance · Account cards · Upcoming bills · Spending overview chart · Recent transactions |
| Transactions | Complete transaction history | Filterable list · Category badges · Date grouping · Search · Amount display |
| Transfer | Send money between accounts | Recipient selection · Amount input · Account picker · Confirmation flow |
| Cards | Card management | Virtual/physical card display · Card details · Freeze/unfreeze · Spending limits |
| Budgets | Spending category tracking | Category budgets · Progress bars · Monthly comparison · Overspend alerts |
| Pay Bills | Bill payment management | Upcoming bills · Payment history · Auto-pay setup · Reminders |
| Analytics | Financial insights & trends | Income vs spending · Category breakdown · Monthly trends · Savings rate |
| Profile & Settings | Account configuration | Personal info · Security settings · Notification preferences · Theme toggle |
| Request Money | Ask a contact to pay you | Contact picker · Amount & note · Sent vs. invited state |
| Recipients | Saved payees for transfers | Recipient list · Verified/unverified badge · Add new recipient |
| Disputes & Support | Flag and track a disputed transaction | Reason picker · Evidence upload · Status timeline |
| Receipts | Proof of a completed transaction | Itemized breakdown · Share or export · Merchant details |
Eight destinations, mapped as a tree
- NovaPayApp root
- OnboardingLog inSign upForgot passwordOTP verify
- DashboardBottom tab
- TransactionsBottom tabDisputes & SupportReceipts
- TransferBottom tabRequest MoneyRecipients
- CardsBottom tab
- BudgetsSidebar · More
- Pay BillsSidebar · More
- AnalyticsBottom tab
- Profile & SettingsSidebar · More
Bottom tab is one of the five icons in the mobile tab bar — Dashboard, Transactions, Transfer, Cards, Analytics. Sidebar · More is the other three: the desktop sidebar has room to list them directly, the 375px build tucks the same three behind a More screen instead. Same eight destinations, two different amounts of space to put them in.
Three flows, followed to the edge
The three flows below cover everything that moves money or asks for it to move: sending, requesting, and disputing. Each is drawn as it actually ships, branch points included — how it can end isn't an afterthought bolted on after the happy path.
Send money
Confirm is its own screen, restating the recipient and amount, rather than a checkbox on Review — the one step in the app that moves money doesn't share a screen with anything else.
Request money
No money moves at any point in this flow — only a request does. The note field exists because “Alex requested $40” with no context reads like a mistake to whoever gets it.
Dispute a transaction
Disputed sits between Submit and either ending — the transaction shows it honestly, as under review, rather than holding the previous status until someone decides.
Structure before polish
Most of the app is a variation on four layouts. These are the structural pass they went through before colour and type — the dashboard's balance-and-activity split, the transfer flow's four discrete steps rather than one long form, the transactions list, and the budgets readout.
Dashboard, the four-step transfer flow, transactions, and budgets — the screens most of the app's other 36 are built from the same bones as.
Designed for what goes wrong
A banking app earns trust in the moments that aren't clean: a transfer still pending, a recipient nobody has verified yet, a card someone froze on purpose. The seven states below are what actually ships; the four cases after them are what the design had to hold up under.
Seven states, one honest signal
Pending and Cancelled share a tone; Reversed and Refunded share another. The label and the icon are what actually tell them apart — colour is the last signal here, never the only one.
The edge cases that decide trust
Every choice justified
Here are the calls I made and the ones I turned down. This was a self-directed project with no users to test against, so these are reasoned decisions rather than validated ones. I've kept the rejected column because that half is usually more revealing.
| Decision | Rationale | Alternative Rejected |
|---|---|---|
| Dashboard as financial hub | Most visits to a banking app last ten seconds: how much is in there, what went out, what's due. Putting those three on one screen means the routine visit needs no navigation at all. | A balance-only dashboard. It looks calmer in a portfolio shot and sends you digging for the two things you opened the app for. |
| Token-based design system | One edit to a variable updates all 41 screens, and the light and dark values live next to each other instead of in two separate files. A developer reads the numbers off the variables rather than off my redlines. | Hard-coded colour values. Fine for five screens. At 41 it turns dark mode into a manual rewrite. |
| Mobile-first with desktop adaptation | People bank on their phones. Starting at 375px forced every core flow to survive the tightest space first, and the 1440px versions then spend the extra room on density instead of the phone inheriting a squashed desktop layout. | Desktop-first. The mobile version becomes a compression job, with tap targets sized for a cursor. |
| Dark mode as first-class feature | People check their balance at 6am and at midnight. Dark mode went into the token system on day one, which is the only cheap time to do it, and it saves battery on OLED screens besides. | Light only. Every dark value then has to be invented afterwards, one screen at a time. |
| Lucide icon system | The same stroke weight everywhere, and the same set exists as Figma components and as React components. The icon in the mockup is the icon in the build, with nothing redrawn in between. | Icons pulled from wherever. Stroke weights stop agreeing, and the tab bar is where you notice it. |
Interactive walkthrough
This one you can actually use. Move money between accounts, freeze a card, switch the theme. It's built in Next.js with Tailwind and Framer Motion, and building it is the step that catches what a static mockup hides.
Mobile — 14 core screens
Captured from the running build at 390px. Onboarding through settings, every flow proven end to end.














Desktop — the same system at 1440px
One component library, two densities. Nothing is re-drawn for the wider layout; the grid and the nav adapt.













The prototype below is the running build — click it to interact.
Twenty components, forty-one screens, one system
By the time screens started, there was nowhere left for an inconsistency to hide. Every card, badge, input, and nav element on all 41 screens draws from the same ~30-page component library — Avatar, Badge, Toast, Modal, OTP Input, Skeleton Loader, Chip, Empty State, Transaction Row, Stat Card, Sidebar Nav, Tab Bar and more — so a transaction's status reads the same badge shape on the dashboard, the transactions list, and its own detail page.
Token-based foundations
Everything sits on Figma variables: colour, type, spacing, radius. I did this first because I've done it the other way round and spent the back half of a project hunting hard-coded hex values. It is about an hour of tedious setup. It pays for itself the first time someone asks for a dark mode.
| Foundation | Details | Purpose |
|---|---|---|
| Colour Tokens | Primary, secondary, success, warning, danger palettes with light/dark mode values | Named by job, not by hue, so the theme can switch and contrast stays checkable in one place |
| Typography | Inter font family · Display, heading, body, caption scales | In a banking app the numbers have to outrank their labels. The scale makes that automatic |
| Spacing | 4px base unit · 8-point grid · Consistent padding and margins | Every gap lands on the same grid, so nothing drifts when a layout changes width |
| Border Radius | 4px (inputs) · 8px (cards) · 12px (panels) · Full (avatars, badges) | Rounded enough to feel current, not so rounded that a bank starts looking like a toy |
20+ reusable components
There are 20+ components in the Figma library, all built with variants. The one I redrew most was the transaction row. It has to carry a merchant name, a category, a date and an amount, and at 375px something has to give. I let the merchant name truncate, because that is the part you can usually guess. The amount never does. Every component has default, hover, active and disabled states and works at both breakpoints.
Visual foundations
From design to code
| Layer | Technology | Why Chosen |
|---|---|---|
| Design | Figma (Variables, Components, Auto Layout) | Variables are the reason. Nothing else I use handles a token-based design system as directly |
| Frontend | Next.js · TypeScript | One route per screen, which matched how the Figma file was already organised. TypeScript for the props |
| Styling | Tailwind CSS | The spacing and colour utilities map almost one to one onto my tokens, so the code reads like the design file |
| Animation | Framer Motion | Page transitions and chart reveals without hand-writing keyframes for each one |
| Icons | Lucide React | The same set as the Figma file, so no icon had to be traced or swapped for a near match |
What the system covers
I did all of it: the tokens, the components, 41 screens, and the code underneath them. The last part is the one I'd point at. Plenty of design systems look consistent in Figma and come apart the moment someone builds them, and the only way I know to find out is to build it yourself. What I can't tell you is how real customers used it. Nobody did. This was self-directed, so it shows the craft, not the outcome.
Tokens catch a wrong value. They don't catch two equal ones
Building the token layer first is meant to prevent exactly the kind of bug that got through anyway. Deep in the dark-mode palette, two background variables — the page ground and the surface that sits above it, cards and sheets and bars — resolved to the same hex value. Nothing in Figma flagged it: two tokens agreeing isn't a contrast failure, it's just two names for one colour. The first sign was in the browser, not the file — every card in dark mode sat flush with the page behind it, and every hover state that depended on that surface colour changing did nothing at all.
The fix was one line — separating the two values back out — but finding it meant actually opening the prototype in dark mode and noticing something was wrong, not reading the token list and trusting it. Next time, that's the step I'd move earlier: a quick contrast pass between adjacent surface tokens, in both themes, before screens are built on top of them, not after.
I did all of it: the tokens, the components, 41 screens, and the code underneath them. The last part is the one I'd point at. Plenty of design systems look consistent in Figma and come apart the moment someone builds them, and the only way I know to find out is to build it yourself. What I can't tell you is how real customers used it. Nobody did. This was self-directed, so it shows the craft, not the outcome.