Case study · Behind the decisions
Happly: the conditions behind the decisions
Being the only product designer on a two-sided, white-label, bilingual AI platform, and what I built around the design work so it held up in production.
Senior Product Designer · Feb 2025 to present · Product design, design system, UX writing, requirements, design QA

The applications board: five stages, one card per opportunity. Demo data.
At a glance
Role
Senior Product Designer, the first and only designer. Product design, design system, UX writing, product requirements and design QA.
Timeline
February 2025 to present.
Platform
Web app (Seeker for founders, white-label HapplyOS for funders), the GrantGPT Chrome extension and the marketing site. A React component library on Radix primitives.
Team
A PM, the CEO and the engineering team. After a PM handoff I also owned the product-direction docs.
Status
Live and ongoing.
A platform that grew faster than its rules
Happly connects Canadian business owners with grants and funding programs, and runs the same platform white-label for the organizations that fund them. When I joined, it had grown feature by feature for years: three visual styles, half-finished flows, and no written source of truth for how things should behave.
That last part was the real problem.
“A badge rule, a notification rule or a money rule gets re-argued every sprint unless it’s written where engineering can see it.”
The HapplyAI case study covers what I decided. This page covers the conditions those decisions came out of: five challenges, the options on the table for each, and the rule each one left behind.
Evidence
By the numbers
Seeker
HapplyOS
One codebase
2
Sides of the marketplace on one codebase
EN
FR
2
Languages on every surface, English and French
Apply
Apply
Apply
→
Apply
3 → 1
Visual styles merged into one component library
01
02
03
04
05
Silent
Drafting onward
5
Funding Journey stages, silent through the first
41 / 6
Document types and document areas in the library
PRD snapshot
What this body of work had to do
Goal
Make one product feel coherent across two audiences, many tenants and two languages, while shipping continuously.
Users, one on each side
Seeker
Founders and small-business operators
HapplyOS, white-label
Program staff, reviewers and admins at funding organizations
Done means
Designs ship as designed.
The same concept reads the same everywhere.
Numbers on dashboards agree with the lists beneath them.
AI output is never presented as more certain than it is.
Not in scope
×
A new brand per tenant.
×
AI making decisions on anyone’s behalf.
Constraints
One designer
Years of existing code
English and French on every surface
Tenant theming from one codebase
AI output that can be slow, missing or wrong
Challenge 01 of 05
There was no written source of truth, so I wrote it
Situation
Changes often reached engineering as a verbal description or a screenshot. Fine for small fixes; it fails for anything with states: a pipeline, a permission model, a review queue.
What made it hard
I wasn’t hired as a PM. But every unwritten rule became a bug later: two developers, two versions of “deadline reminder”.
Options on the table
Passed
Heavier annotations in Figma
Passed
Clarify it ticket by ticket
Chosen
Write the rules as requirements, before any screen
The decision
I wrote the Funding Journey requirements and the HapplyOS overview as rules (“silent until Drafting”, “never fall back to the program maximum”, “the AI score is advisory”), then designed against them. After the PM handoff I kept the product-direction docs current.
Requirements · excerpt
Redacted
Silent until Drafting.
Never fall back to the program maximum.
The AI score is advisory.
Recreated from the requirements. The three rules quoted on this page stay readable; everything else is redacted.
What it changed
Both documents are still the team’s reference. The rules held when screens changed, because the screens weren’t the spec.
Rule 01
Write the rule before drawing the screen.
Challenge 02 of 05
A design system isn’t real until someone checks every state
Situation
I unified three visual styles into one component library that themes per tenant. Adoption in an existing codebase was the hard part: older CSS tokens kept getting used, and components built before the library didn’t pick up tenant colors.
What made it hard
Breakage was quiet. A dropdown checkbox showed the default blue instead of the organization’s brand color. An icon from “the icon set” rendered differently in production. Each is tiny; across dozens of screens, two languages and several tenants, they make a white-label product look like a template.
Options on the table
Passed
Fix bugs as customers report them
Chosen
Treat QA as part of the design deliverable
The decision
I designed and led the QA; AI agents ran the test passes across the member portal, back office and prototype, in both languages. Each finding became a ticket with a screenshot, the expected state and the token or component to use, then a retest. Source-of-truth disputes (Figma or production? which icon set?) got settled once, in writing.
Design QA · finding
No real IDs
Component
Dropdown checkbox
Actual
Default blue
Expected
The organization’s brand color
Use
Library component, tenant color token
Retest
Passed
The quiet failure, and the format every finding followed: screenshot, expected state, the token to use, then a retest.
What it changed
Tenant brand color now reaches components it used to miss, and new work starts from library components rather than one-off CSS.
Rule 02
A component isn’t done until it’s checked in two tenant colors and both languages.
Challenge 03 of 05
Designing for AI that is sometimes absent, slow or wrong
Situation
AI shows up in four places: matching, the reviewer’s fit score, GrantGPT (a Chrome extension that drafts and fills applications on external sites) and the Business Plan Builder.
What made it hard
AI fails differently from normal features: late, empty, or confidently wrong. The badge decision on the HapplyAI page handled confidence. I also had to design for absence.
Options on the table
Passed
A spinner everywhere
Passed
Show raw AI output and let people judge
Chosen
Explicit states: not run, agrees, refined the tier
The decision
No reasoning means no “why” affordance: we hide the control rather than open an empty explanation. On the provider side the fit score sits next to the reviewer’s decision, never in place of it. Copy says what the model looked at, not how sure it feels.
Decision
The reviewer
Fit score · AI
Advisory, shown beside it
Solid line: the decision. Dashed line: advice that sits next to it, never in its place. The same rule runs on all four AI surfaces: matching, the fit score, GrantGPT and the Business Plan Builder.
AI state matrix: not run, reasoning, tier refined, failed, on the card and on the detail page
Four states, drawn for the card and the detail page, so absence is designed too.
What it changed
One principle across four AI surfaces. New AI features ship faster when the trust rules already exist.
Rule 03
The AI advises. It never decides.
Challenge 04 of 05
Two languages, many tenants, one layout
Situation
Every surface ships in English and French, and organizations write their own content (programs, courses, onboarding questions) on top of ours.
What made it hard
French runs noticeably longer, so layouts that fit in one language broke in the other. And a translation field with nothing to translate from confuses admins.
Before and after
Before
Layouts fit the English string, and French was where layout bugs showed up, late. Translation fields appeared with nothing to translate.
After
Designed and QA’d in both languages from the start, with room for the longer string. Translation inputs only when there’s source text, in both directions, because not every organization writes in English first.
EN
Save and continue
FR
Enregistrer et continuer
EN → FR
FR → EN
Only with source text
Example strings. The slot is sized for the French label, and translation runs both ways because not every organization writes in English first.

English. Demo data.

French. Demo data.
One layout, sized for the longer string.
What it changed
French stopped being the place where layout bugs got found late.
Rule 04
Design every screen in both languages from the first draft.
Challenge 05 of 05
Numbers that have to agree with each other
Situation
HapplyOS reporting and the Seeker dashboard both summarize data that also appears in lists elsewhere: members, applications, programs, pipeline items.
What made it hard
A tile that computes its own count eventually disagrees with the list one click away. For an organization reporting to its funders, that breaks trust in every number on the page.
Risk and decision
Risk
A tile counts on its own and drifts from its list. An “all time” range that starts at an arbitrary early date pads charts with empty years.
Decision
Every dashboard number comes from the same source lists as the views beneath it. “All time” starts when the organization joined.
Dashboard tile
4
Applications
= count(list) →
The list it links to
01
Application
02
Application
03
Application
04
Application
Demo data. The tile counts the same list it links to, so the two can’t drift apart.
What it changed
A rule engineering can check, and a reason to trust every other number on the page.
Rule 05
If a tile and its list disagree, it’s a bug, not a rounding difference.
Design decisions and system
What carried over to every flow
Rules before screens
Each major flow has a short rule list: quiet, money, AI, freshness.
One source per concept
One capture modal. One stage-token source for stage colors and labels across Kanban, table and calendar.
States are designed
Empty, plan-locked, loading, AI-absent and error-recovery states are drawn and written, not left to defaults.
Two brand colors
Every new component is checked in two tenant colors before it’s done.
Prototyping in code
I prototype on the real component library, designed and built with AI coding tools, with me directing and reviewing. Engineering reviews behavior, not pictures.
Recording: a code prototype on the component library (demo data)
A working prototype, reviewed for behavior.
What I learned
Writing the rule is design work. The decisions that lasted were the ones I wrote down before anyone drew a screen.
QA is where a design system becomes real. I’d budget for it from day one instead of fitting it in.
Instrument early. I argued several of these decisions from user conversations and product logic. A number would have ended those arguments faster.
Next: tie design-QA findings to release metrics, so design debt shows up as a number the team can prioritize.
What I’d measure next
Not measured yet
Share of pipeline items that reach Drafting
—
Notification opt-out rate, before and after the quiet rule
—
Design-QA defects per release: found by design vs. reported by users
—
Time from design sign-off to shipped
—
Empty on purpose: these are the numbers that would have ended arguments faster.
