Breeze hired me for three contracts over six months, and my role grew each time. I started by building a live PDF resume editor, then designed and built a Chrome extension that wasn't in the original plan, and finally designed the job search and owned the architecture behind it.
The Outcome
Breeze helps job seekers build resumes that pass applicant tracking systems, apply faster, and find jobs that fit their resume. Each contract gave me more responsibility than the last.
Phase 1 · Resume editor
I built the frontend for a live PDF resume editor from their Figma designs.

Phase 2 · Chrome extension
They brought me back to design and build a Chrome extension, which wasn't in the original plan.

Phase 3 · Job search
I designed the search interface and owned the backend: database, scraping architecture, and API.

Resume Editor
The PDF preview updates as the user types in any section of the form. Templates and themes switch instantly, and an ATS score tracks eleven completeness fields. I organized the codebase with Feature-Sliced Design, which I studied in depth before adopting.
The editor is still the center of Breeze. These screenshots are from the live app today, after the team's later design updates.
Try It
The editor, reproduced here as a demo. Type in any field and the page re-renders as you go, and the ATS panel re-checks eleven fields as the resume fills in.
Product Designer
Experience
Product Designer
2023 - Present
Northwind Health
Redesigned appointment booking, cutting drop-off by a third. Built the team's first component library in Figma.
UX Designer
2020 - 2023
Contoso Retail
Led checkout research and shipped a one-page flow used by two million shoppers a month.
ATS Score
Extension
Users were already pasting their resumes into ChatGPT to answer application questions. The extension puts that workflow in the browser, using the resume data they already keep in Breeze.
It shows each saved resume and cover letter field by field, each with a copy button. A Generate tab writes answers to application questions from the selected resume, at one, two, or three-plus sentences. When the 14-day trial ends, the tabs give way to an upgrade prompt. Before the high-fidelity designs, I researched each interface pattern (tabs, cards, filters, upgrade prompts) and documented why I chose it.
I built it in under three weeks, and the team took it to version 1.0 after my handoff.
Resumes, Cover Letters, and Generate tabs
Sign-in
The extension borrows the session the user already has in the Breeze web app. The popup injects a small script into an open Breeze tab, reads the session token, and sends it with every request after that. The user never types a password into the extension.
Popup
React · Zustand
Breeze tab
Content script
Supabase
Postgres REST
Edge function
Gemini 2.0 Flash
01Inject a script into the open Breeze tab
extension scripting API
02Hand back the session token
from the signed-in web app
03Fetch resumes and cover letters
Bearer token on every request
04Rows come back and are saved
kept in extension storage
05Send the question, length, and resume
06Return the answer as checked JSON
profanity filter · required fields
Popup → Breeze tab
Inject a script into the open Breeze tab
extension scripting API
Breeze tab → Popup
Hand back the session token
from the signed-in web app
Popup → Supabase
Fetch resumes and cover letters
Bearer token on every request
Supabase → Popup
Rows come back and are saved
kept in extension storage
Popup → Edge function
Send the question, length, and resume
Edge function → Popup
Return the answer as checked JSON
profanity filter · required fields
My first version routed state through a background script over an RPC layer. Once it was clear that state only flowed one way, from the page into storage, I removed that layer and let the popup read and write storage directly.
Job Search
I designed the search page in three panels (filters, results, and job details) and then built it. Each listing carries a HIGH, MED, or LOW rating for how well it matches the user's resume, and the detail panel's “Breeze Breakdown” lists the matching keywords. I built the ratings into the interface against mock data. Computing them from a real resume was next when the work stopped.
Trace a Search
Every search writes its stages to the logs under one trace ID. This is that trace, drawn as a waterfall. Run a search to watch the cache miss and the board get fetched through the proxy, run it again to see the shared cache answer, then ask for the next page to see the read resume where it stopped.
Title
Location
Pick a title and a location, then search. Run the same search twice, then try the next page.
The order of the stages, the cache rules, the backoff, and the dedupe key follow the code. Timings, which proxy regions get blocked, and the number of listings are simulated.
Architecture
Every job board sits behind one interface. Past the sources, the caching, deduplication, page cursors, and logging never need to know where a job came from. After my handoff, job boards kept catching the scraping proxies as bots, which made results unreliable no matter how the pages were parsed. The team moved to a commercial job-data API, and because only the source layer had to change, their new search function kept my cache, deduplication, logging, and input validation. Use the toggle to see what carried over.
August 2025: scrapers behind one interface. One board is live; two more are built but switched off.
Sources
Swappable
Contracts
What the core depends on
Shared services
Every source runs through these
Postgres
9 tables
One per job board
The core depends only on these interfaces. A job board is a class that implements Source; fetching, parsing, and logging come in through the constructor, so each piece can be tested with a fake.
Sketch · what the core depends on
// The core only knows these shapes.
interface Source {
readonly board: string;
search(query: SearchQuery): Promise<Listing[]>;
}
interface Fetcher {
get(url: string): Promise<Page>;
}
interface Logger {
event(name: string, detail?: Record<string, unknown>): void;
}143 backend tests across 18 files cover the sources, parsers, fetcher, logger, and salary parsing. Parsing for the other two boards, listing expiry, and the recommended-jobs feed were still on the list at handoff.
The Stack
React Router v7, shadcn/ui, Tailwind, WXT, and Zustand were all new to me when the work started. I read the documentation for each before writing production code with it.
01 · Resume editor
Built from their designs
02 · Chrome extension
Designed and built
03 · Job search
Designed, built, and architected
Working Together
You can start with a small, clearly defined project and decide from there. With Breeze, the first contract led to design work, and the design work led to owning the architecture. The structure I set up in the first phase also made the third phase easier to manage.