Case study 02 / Product design / 2026
Ferni Finance
What's left after CPP and EI? An app for your first real paycheque.
Personal finance for people in Canada starting their first career job, with the jargon explained where it appears.
16lessons, plus a 40-term glossary
Role, status and stack
- Role
- Solo, product design and development
- Status
- Live
- Made with
- TypeScript / Next.js / Supabase / PostgreSQL
The problem
Your first real paycheque arrives with a lot of unexplained abbreviations.
What's left after CPP and EI? TFSA or RRSP first? Generic budgeting apps don't answer in Canadian terms, and don't teach you anything along the way. Ferni puts a salary calculator, a budget tracker, savings goals, a first-job checklist and short lessons on one account.
Concept and interaction design
- Jargon, explained in place
- CPP and EI on the salary breakdown, and FHSA on the first-job checklist, open a short definition right where they appear. Nobody should have to leave the page to find out what came off their paycheque.
- Three inputs, one answer
- The salary calculator asks for a salary, a province and how often you're paid, and answers per pay and per year.
- An estimate says so
- The result is labelled an estimate, and the label can't be dismissed, because that's what it is.
Product design
I designed Ferni's look and built it. Every color is a design token in one file, so changing a value updates the whole app. See it with the other designs.
- Color
- A dark palette, lavender primary, rose accent
- Type
- Inter with DM Serif Display
- Identity
- SVG logo and wordmark, branded social image
How it fits together
Next.js front end. App Router pages, with a proxy that checks the session on each request and sends signed-out visitors to login.
Read all 4 steps at once
- Next.js front end. App Router pages, with a proxy that checks the session on each request and sends signed-out visitors to login.
- Supabase auth. Cookie-based sessions, so server-rendered pages know who's asking.
- Postgres with RLS. Nine tables, each with Row Level Security on and policies scoped to the signed-in user.
- Pure tax function. calculateTax() takes a salary, province and pay frequency and returns a breakdown. It touches no database, so it's easy to test.
- Why Row Level Security matters here
- This app holds people's income and spending. With RLS, the database refuses to return a row unless its user_id matches the signed-in user. A bug in my code can't hand one person's budget to another, because the rule doesn't live in my code.
- The tax logic
- Federal and provincial brackets for all 13 provinces and territories, basic personal amounts, CPP and EI with their caps, and Quebec's reduced EI rate. The output is labelled an estimate, with no dismiss button, because that's what it is.
const cpp = Math.min(
Math.max(0, (annualSalary - CPP_BASIC_EXEMPTION) * CPP_RATE),
CPP_MAX_CONTRIBUTION
);
const eiRate = province === "QC" ? EI_RATE_QC : EI_RATE;
const eiMax = province === "QC" ? EI_MAX_PREMIUM_QC : EI_MAX_PREMIUM;
const ei = Math.min(annualSalary * eiRate, eiMax);Results
- provinces and territories in the salary calculator
- 13
- tables with Row Level Security
- 9
- unit tests passing (tax and formatting)
- 60
| Line | Expected |
|---|---|
| Federal tax | $6,644.25 |
| Ontario tax | $2,430.82 |
| CPP | $3,361.75 |
| EI | $996.00 |
| Net annual | $46,567.18 |
| Net per pay | $1,791.05 |
What I learned
What I learned
Writing the schema and security rules first made everything after it simpler. The six modules share one account, so a budget can see a salary without a pile of extra plumbing.
What I'd do differently
Keep the tax tables as dated data with a source for each number, and test against published examples instead of values worked out from my own constants. Tests can only catch what they're pointed at.
Built with an AI coding assistant. I decided the data model and the security rules, and checked the results.
Properties
- Type
- Web app
- Role
- Solo, product design and development
- Year
- 2026
- Stack
- TypeScript, Next.js, Supabase, PostgreSQL
- Also shows
- Product design, Interaction design
- Status
- Live



