Saffron Table
A restaurant website and booking system for a fictional coastal Indian kitchen, built as a reusable product.
- Kind
- Live demo
- For
- A fictional business
- What it is
- Restaurant website
- My role
- Design and engineering
- Built
- 2026


The idea
A cinematic, appetite-first site for a fictional coastal Indian kitchen: a menu that reads well on a phone, a guided table booking and a staff console. Running as a public demonstration: bookings use fictional availability and nothing you enter is saved.
- Who it's for
- Independent restaurants and cafés that need a fast, mobile-first site for menus, opening hours and reservations.
- The problem
- A restaurant's website usually stops at a menu and a phone number. Taking bookings online means respecting real rules: opening hours, closures, party sizes, how long a table is held and how many guests the kitchen can start at once. And two people must never be given the same last table.
What I built
Everything below works in the public demonstration today.
I designed and built Saffron Table end to end: the public site, the booking engine, the staff console, the database schema and the tests.
What works in the demo
- Cinematic home with an interactive 3D plate on desktop and a photo hero on phones and tablets
- Menu with search, dietary and allergen filters, dish pages and a quick view
- Table booking walkthrough on fictional availability (demonstration only, nothing is saved or sent)
- Manage-a-booking flow using fictional sample references
- Read-only staff console with fictional bookings, menu, settings and analytics
- Light and dark themes, keyboard support and reduced-motion support
The experience
Real screens, captured from the live site as a first-time visitor sees them.



The engineering
How it is put together, and the problems that took the most care.
Everything specific to the restaurant (name, hours, menu, rules) lives in one business profile, so the product can be rebranded for a real restaurant. Bookings pass through one service with two interchangeable stores: PostgreSQL for a real restaurant, and a demonstration store that saves nothing.
Interface
Next.js App Router: the public site (home, menu, dish pages, booking, manage a booking) and the staff console, sharing one design system.
Booking service
Validates every request again on the server, applies rate limits keyed by a hash of the visitor's address, and logs outcome codes, never names, emails or phone numbers.
Stores
A PostgreSQL store that calls locked database functions with only the public key, and a demonstration store over fictional bookings. The configuration picks one and fails closed.
Database
PostgreSQL decides every booking: a lock per service date, every rule checked again inside it, unique idempotency keys and row-level security on guest data.
Technology
- In the public demo
- Next.js 16
- React 19
- TypeScript
- Tailwind CSS v4
- React Three Fiber
- GSAP
- Motion
- Zod
- Recharts
- Postgres schema (Supabase-ready)
- Vitest
- Playwright
Hard problems, and how I solved them
Never giving one table to two people
- Problem
- Two guests asking for the last table at the same moment must not both get it, and neither must a staff change made at the same time.
- Approach
- Every change that affects capacity takes a lock for its service date in PostgreSQL, then re-checks hours, closures, notice, party size, pacing and seating inside that lock before writing. One global lock order (settings, then dates, then the booking) removed a deadlock that the parallel-connection tests found.
One set of rules, drawn and enforced
- Problem
- The calendar a guest sees and the database that takes the booking must agree, or people are offered times they cannot have.
- Approach
- TypeScript draws the calendar and the time slots; a SQL function decides at write time. A parity test runs both over every slot and party size and requires identical answers.
Private links that stay private
- Problem
- Guests manage their booking without an account, so the link itself is the key.
- Approach
- The token is 256 random bits, shown once; only its SHA-256 digest is stored. It travels in the URL fragment, which browsers never send to servers, and the page removes it from the address bar.
A demonstration that cannot pretend
- Problem
- A public demo must show the real flow without storing anyone's details or implying a table is held.
- Approach
- In the demonstration, guest details are validated in the browser and never sent, and the result says that nothing was saved and no table is held. A half-configured deployment pauses booking instead of falling back to the demo.
The evidence
What can be checked, and what the public version does not do.
- Concurrency
- Tested on PostgreSQL 18 with parallel connections, including a 50-round, four-way race with no deadlock and no overbooking
- Automated tests
- 135 unit and component tests, 171+ browser tests at phone, tablet and desktop widths
- Accessibility
- axe on every public and console page at three widths; Lighthouse accessibility 100 on the home, menu, dish and booking pages
Figures from the project's own release record, written when it shipped. The public demonstration runs on fictional data.
Not in the public demo
- Real bookings stored in a restaurant's own Supabase database
- Confirmation and reminder emails
Explore the product
Running a restaurant or a café?
Saffron Table is what I would build for you: a menu that reads well on a phone, bookings that respect your real tables, hours and closures, and a console your staff can use on a busy night.