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

Seeker applications board: funding applications in columns from Interested to Submitted, with Won and Not selected collapsed at the side. Demo data.

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

Source: all five figures are public on the HapplyAI case study.

Source: all five figures are public on the HapplyAI case study.

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.

One component, two tenants: default blue vs. the organization’s brand color

One component, two tenants: default blue vs. the organization’s brand color

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.

A program list in English. Demo data.

English. Demo data.

The same program list in French, with longer strings in the same layout. 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.