← All work

— Case Study 03

Tallow Bliss

A team final project shipped to a real client.

Role
Contributor
Year
2026
Team
6-person Class team
Status
shipped
Stack
ReactTypeScriptTailwind v4Vite

The brief

Tallow Bliss Skincare is a small skincare business in Antigonish, Nova Scotia. The owner had been running on Webador — a hosted website builder that does the job but doesn't leave much room for modern UI work — and was navigating a recent family loss while trying to keep her brand visible online. The project, our Winter 2026 PROG3300 Integrated Project, was to give her a modern, accessible, performant frontend that she'd actually be proud to send people to.

I joined as one of four developers on a six-person team. Project Manager: Shayne McNeil. DevOps Engineer: Aaron Hirtle. Product Owner and Lead Developer: Amelia MacDonald. Fellow developers: Hudson Latimer, Morgan Perry, and me. The plan was three two-week sprints — Foundation, Core Features, Refinement & Deployment — every change through Pull Requests, full CI/CD via GitHub Actions, and JSDoc-generated documentation as a graded deliverable.

I want to be clear about scope up front: I was a Developer on this project, not the lead. The architecture decisions, the product vision, and the client communication were Amelia's. My contributions sat in three specific places — and those are what I want to talk about.

What I shipped

Three substantial pieces of work landed under my name on this codebase.

Issue #84 — Formspree contact form. The site needed a working contact form without standing up a backend service for it. I implemented a typed React form component with proper validation, accessibility (real labels, error association, an aria-live status region), and a fetch-based submission flow to Formspree's API. Same pattern I later reused on my own portfolio's contact page, which is itself a small endorsement of the approach.

Issue #94 — TSDoc across 81 files + TypeDoc HTML generation. The course required JSDoc-generated documentation as a final deliverable. Adding doc comments to a TypeScript codebase that's already shipped to a client is the kind of work nobody volunteers for, so I took it. Each component, prop interface, and utility function got a TSDoc block; the build now produces a navigable HTML documentation site that the next person to touch the code can read instead of guessing.

Dark mode. A ThemeContext with localStorage persistence, system-preference detection on first visit, and a toggle that lives in the global navigation. The interesting half of this was the styling layer.

Dark mode, the Tailwind v4 way

Tailwind v4 changed how custom variants are defined. The old darkMode: 'class' config in tailwind.config.js is gone; you declare the variant in CSS instead:

@custom-variant dark (&:where(.dark, .dark *));

That single line tells Tailwind: anything tagged with the dark: prefix should apply when an element has the .dark class or has an ancestor with it. Combined with a ThemeContext provider that toggles the class on <html>, every existing dark:bg-slate-900, dark:text-slate-100 in the codebase lights up correctly — no config file, no plugin.

The migration from "dark mode is a config switch" to "dark mode is a CSS variant you declare" is a quiet but real shift in how Tailwind thinks about themes. Getting it right meant reading the v4 changelog carefully and not just transplanting the v3 pattern.

The team part

The biggest non-technical lesson of this project was about Git, specifically about merge conflicts. With six people working through three sprints into the same upstream repo, my dark-mode PR ran into a particularly gnarly merge — the kind where you walk away, come back with coffee, and resolve hunks file by file while making sure nothing semantic broke. Painful in the moment, formative in retrospect. The instinct to rebase aggressively and squash recklessly will get you in trouble; the instinct to read every conflict carefully and ask the person whose work you're conflicting with what they intended will not.

The other thing six people working on the same codebase teaches you is that documentation isn't a chore — it's a courtesy. A TSDoc block on a prop interface saves the next person ten minutes of reading source. Multiply that by 81 files, six developers, and the lifespan of the project, and the math gets pretty obvious.

What it taught me

Doc work is real work. The TSDoc pass was the least glamorous PR I opened on this project and probably the most useful. Maintainable software is documented software. Most portfolios skip "wrote documentation" as if it doesn't count; on a real team it's load-bearing.

Tailwind v4 is a step change. The CSS-first configuration model — @custom-variant, @theme tokens — feels different to write but ages better. The v3 JavaScript config object is starting to look dated already.

A small business with a real story changes how you build. This wasn't a school project to drop into a portfolio; it was a website for someone going through a hard year. That context made the difference between "make it look nice" and "make it work right, look professional, and be something she can actually maintain or hand off later."