← Portfolio

Case study · Prototype to production · Web app

Padel Court Booking

A fair-access booking system, designed mobile first, vibe-coded with Codex and shipped to production

Year
2026
Role
Designer and builder
Type
Web app in production
Platform
Mobile-first web, plus admin desktop
Habib University Padel Court landing page

01 — The problem

Hundreds of students, faculty and staff share one padel court, and everyone wants the same few hours. I designed a booking system that makes access fair by rule: who can book, how often, and with whom. Then I built it and shipped it.

45 minslots, 15 minutes apart
4 playersper booking, all from the university
2 bookingsper 30-day window
1 QR passfor each approved player
610registered users
222bookings made
121approved bookings
79matches played in the last 30 days

02 — The process

From a problem to production

  1. 1
    Identify the problemOne court, hundreds of people and the same few popular hours. Without rules, access goes to whoever asks first.
  2. 2
    One conversationIt began with a simple idea: a booking system where the rules are assigned up front, not argued over later.
  3. 3
    Policies firstSlot length, booking limits, team size, who plays with whom and approval were agreed as rules before any screen was drawn.
  4. 4
    Low-fidelity screensI sketched the flow as low-fidelity screens, mobile first, for players, admins and court staff.
  5. 5
    Vibe-coding with CodexI described each screen and rule to Codex and iterated on the result, working in a TypeScript monorepo with a typed API.
  6. 6
    Shipped to productionMicrosoft sign-in, email, QR passes, an admin area and live monitoring, running on Linux behind nginx.

03 — Design system

Calm operations, a confident court

A court booking is a small decision made quickly, so the interface has to be obvious. Warm neutrals keep operations calm, plum carries the university identity, and gold marks the court experience. Players and admins share one system, so a slot looks and behaves the same everywhere.

Colour

Brand

Plum, primary#512047
Plum hover#6A2F60
Gold, accent#BB852A
Warm white#FBF8F5
Ink#1B1319
Line#EBE5DF
Muted#7A7177
Gold soft#FFF6E5

Status and roles

Green#197347
Green soft#E8F7EE
Blue#1769AA
Blue soft#EBF5FF
Orange#B86113
Orange soft#FFF3E8
Red#C93636
Red soft#FFF0F0

Plum is reserved for the next meaningful action and selected states. Destructive actions stay red and visibly apart.

Typography and spacing

Space Grotesk and Inter

Book the court. Make it count.

Section heading, 27px

Body text uses Inter at 15px with a 1.5 line height. Muted copy supports, and plum is kept for actions, navigation and selected states.

4px8px12px16px24px32px48px

Components

Actions and fields

Continue bookingView rulesCancelReject booking

Roles

StudentFacultyStaffAlumni

Booking status

PendingApprovedRejected

Slots and notices

5:00 – 5:45 PMAvailable
6:00 – 6:45 PMSelected
7:00 – 7:45 PMUnavailable
ⓘ
Community booking rulesFaculty, Staff and Alumni may book together. Students book with Students.
⚑
Fair accessTwo pending or approved bookings are allowed within the 30-day period.
✓
Team completeAll four players are added. Continue to review.

Principles

  • Clarity firstDates, time, availability and quota sit beside the decision being made.
  • One strong actionPlum marks the next meaningful step. Destructive actions are visibly separate.
  • Accessible by defaultStrong contrast, 44px touch targets and clearly visible keyboard focus.
  • Unavailable is explainedBlocked and closed slots stay visible, struck through, so no rule is hidden.
  • Same pattern at every widthProgress, choice, then confirmation, on a phone and on a desktop.
  • Rules in plain languageEvery limit is stated where it applies, then accepted once before booking.

04 — How it’s built

One TypeScript monorepo

The whole app is TypeScript with pnpm. The API is described once in OpenAPI, and the typed client and validation schemas for the frontend are generated from it, so screens and server cannot drift apart.

FrontendReact, Vite, Tailwind CSSReact Query for data, Clerk React for sessions.
BackendExpress 5 APIBundled with esbuild.
DatabasePostgreSQLDrizzle ORM for typed queries.
Sign-inMicrosoft Entra IDOAuth, turned into Clerk sessions and tokens.
API contractOpenAPIOrval generates the React Query client and Zod schemas.
Email and directoryMicrosoft GraphTeammate lookup and confirmation emails.
QR passesGenerated locallyCreated with the qrcode library, one per player.
HostingLinux on EC2nginx serves the frontend and proxies to the API service.