


The product, as designed: pick a brand, see today, book the next class.
| Company/Role: Freelance Project — Designer & App Developer | Target Platform: Mobile (iOS & Android) |
| Team Size: Solo product design and mobile app frontend development | Tools: Figma, React Expo |
| Project Length: 3 months | Techniques: Persona, information architecture, booking state machine, UI design, component library |
AirFitness is the member-facing app for a fitness group that runs more than one brand under one roof — airstudio for boutique classes and airfitness for the gym floor. Members join a branch, hold a contract, and book into timetabled classes: hot stone yoga, pilates, zumba, toning fit, super core.
The operational side of that already worked. Staff had a schedule, a membership database and a front desk. What members had was a timetable they had to translate — is this class full, have I already booked it, can I still cancel, did last week's session actually count? Each of those questions has a definite answer sitting in the system, and none of them were being answered on a screen.
The result is the failure mode every gym knows: members book and then don't turn up. A no-show is not usually a change of heart. It is a class that slipped past someone who meant to be there, or a full class whose waitlist never moved because the person holding the slot forgot to release it.
How might we make attending the next class the easiest thing a member can do on their phone?
I owned the product end to end as the product designer — from who it is for, to the rules it keeps, to the screens that enforce them:
Version 3.0 settled on four tabs. Not a map of the gym's database objects — four jobs a member actually has.




Home, Classes, Bookings, Profile. Each tab answers one question: what is today, what can I book, what have I committed to, who am I here.
The persona work landed on someone specific rather than a demographic bracket. David, 32, works long hours in a demanding desk job. He values fitness and wants to keep an active lifestyle in spite of his schedule — he is not unmotivated, he is squeezed. He is fluent with his phone and impatient with apps that make him hunt.
Two lines of that persona did most of the work later on. “Reminders to help plan the day and avoid missing classes” is why notifications are a first-class surface — a bell on Home, not a settings toggle. And “variety of classes at different times” is why the timetable is filtered, not scrolled.


The two persona requests, as screens: a reminder that reaches him, and a timetable that can be cut down to the hour he actually has.
Before drawing a single screen I mapped booking as a state machine. A booking is never just “made” or “not made”; it moves through waiting, booked, cancellable, checked-in, attended and expired, and the state alone decides what the member is allowed to do next.
Writing the rules down first meant the interface could make a promise it kept everywhere: one class card, one primary action, always the right one. The member is never offered a button that will fail.
Book → Scan → Attend. The rules that govern each transition, mapped before any UI existed.

The wider product path around those rules: brand choice, guest vs member, then book, manage, check in.
That map collapses into a single lookup table, which is what the class card component actually implements. Every row below is a real variant in the library — not a special case drawn on one screen.

The class card in every legal state: Book Now, Join waitlist, Cancel, Show QR, Attended, Expired, Leave waitlist, Cancelled.
| Not booked, space available | Book Now |
| Not booked, class full | Join waitlist |
| On the waitlist | Leave waitlist |
| Booked, more than 60 minutes before start | Cancel Booking |
| Booked, less than 60 minutes before start | Show QR |
| Scanned at the gym | Attended |
| Never scanned | Expired |
| Cancelled | Cancelled — no action |
The 60-minute cutoff is the one rule that is doing real work. Before it, cancelling is free and the seat goes back to the waitlist while someone can still act on it. After it, cancelling stops being offered at all and the card flips to the QR code — because at that point the only useful thing the app can do is get the member through the door. The same rule protects the gym's occupancy and removes the member's decision, which is exactly the trade a busy person wants.



The same rules on real screens: waiting, checking in, and the empty state that sends you back to book.
Check-in is a QR scan at reception, which introduced a constraint from the operations side: the scanning device has to be signed in as an admin account, so the member's phone displays the code and the gym's device reads it, never the other way round. And because a timetable is a living thing, any change to a class's time, location or trainer pushes a notification rather than waiting to be discovered.
Version 1.0's tab bar was Home, Classes, Booking, Profile, More. It was a faithful map of the system behind it, and that was the problem — it was organised around the gym's objects rather than the member's intent.
Two tabs were not earning their place. “Classes” and “Booking” describe the same act to a member who just wants to be in a room on Tuesday evening, and “More” had quietly become a drawer for everything without a home: branches, feedback, language, legal, logout. Meanwhile the two things the persona actually asked for — reminders, and a fast way through a busy timetable — had no permanent surface at all.
Version 1.0 to 2.0: dissolving two tabs to promote the ones the persona was asking for.
Version 2.0 tried Notifications and Shop as tabs. That was the right pressure test — reminders deserved a surface, and the business wanted a commercial one. Living with that IA made the next cut obvious.
Version 3.0 is the product that shipped: Home, Classes, Bookings, Profile. Bookings came back as its own tab because managing a commitment is a different job from discovering a class. Notifications moved onto Home, behind the bell, which is where a squeezed member already looks at the start of the day. Shop did not survive — it was a business wish, not a member job, and four tabs is the budget a thumb can keep.
The group runs two brands, so the app opens on a decision the identity has to make painless. A shared splash resolves into “Start your journey” with two doors: airstudio and airfitness.

Holding air constant and varying only the suffix makes the choice a member is being asked to make legible in a single glance.
The palette is a single confident blue carried across both brands, so a member who switches never feels like they have left the product.
The screens are assembled from a component library rather than drawn one by one: buttons, inputs, navigation and tab bars, class cards, calendars, filters, accordions, radios, toggles, loaders and top bars, each with its states defined.

Colour, type and token notes — Montserrat, one blue, a small set of neutrals and status colours.
Building the library first is what made the version 2.0 and 3.0 restructures affordable. Rearranging the navigation was a change to where components sit, not a redraw of every screen — which is the only reason a change that large was worth making at all. The class card's eight states are the same component; only the property changes.
Mapping booking as states and transitions before opening a canvas meant the class card had exactly one job: read the state, show the one action that is valid. Almost every ambiguity that would have surfaced late in design surfaced instead as a question in the flow, where it was cheap to answer.
The two lines that mattered — reminders, and variety across the day — are why notifications live on Home and why the timetable filters by time. Without going back to the persona I would have left both as buried preferences, which is where version 1.0 had put them.
The version 1.0 to 3.0 path was argued from first principles and the shipped IA holds up, but it was a judgement call. A card sort or a first-click test with a handful of members would have cost very little and would have either confirmed the change or caught Shop before a full set of screens was built around it.
Sixty minutes is a reasonable number, not a measured one. The right value depends on how late waitlisted members realistically arrive, and that is knowable from booking records. It is the first thing I would instrument after launch.
A no-network QR screen, a waitlist that never clears, a cancelled class — these are the moments where a booking app either keeps or loses trust. The empty bookings state made it into the library; the failure states still came after the happy path rather than alongside it.