Back to Projects

Brand Website & Booking Flow Design · 2026

Self-Initiated Concept Project

MARGIN Hair Salon

A fictional appointment-only hair salon in Kaohsiung, taken from brand positioning, spec documents and a design system all the way to a website where the five-step booking flow actually works.

Role

Product Spec · UI/UX Design · Design System · Implementation Review

Impact

Turned the brand idea of "margin" into rules a layout can be checked against, and built an in-house booking flow that designs for three failure states, not only the happy path.

Tools

Claude Design · Claude Code · Gemini · Nuxt 4 · Tailwind CSS 4 · Google Calendar API · Notion API · Cloudflare Workers

MARGIN Hair Salon Website Preview

Design Highlights

  • Design System: broke "margin" into four layout principles — black and white plus one accent, zero corner radius, and at most one primary button per section
  • Interaction States: beyond the five-step booking, designed three edge states — no room that day, slot taken by someone else, and submission failed
  • Decision Log: recorded 12 design decisions in the PRD, each stating what was given up and how the cost is covered

Positioning

A fictional salon, taken from spec documents to a site you can actually book on

This is a self-initiated concept project: the brand, address, stylists and imagery are all fictional, and the site is set to stay out of search engines. I wrote the PRD, site-wide copy and SEO spec, and designed the high-fidelity mockups and the MARGIN design system with Claude Design. The site code was written by Claude Code; my part was defining requirements, reviewing and accepting the work, making the call whenever the spec and the build disagreed, and writing those calls back into the decision log.

Problem & Goal

Salon websites break at three points on the way to a booking

Salon customers make almost the whole decision on their phones: browse the work, check the stylist, check the price, book. Most salon sites stall along the way — the portfolio looks like five different shops, booking means calling or messaging on LINE, and the big photos never show who will actually do your hair. So the site has a single conversion goal: bookings. The main audience is people looking for a new stylist, and what they fear most is the upsell, the surprise surcharge, and ending up with someone whose work they have never seen.

Key Decisions

Every decision records what it gave up

  • Built booking in-house instead of embedding a widget: embedding takes half a day and building takes about two weeks, but booking is the site's only conversion — its most important page should not wear someone else's design.
  • Published every price: it speaks directly to the fear of surprise surcharges. The cost is losing quote flexibility, so every price is shown as "from" with the surcharge conditions spelled out.
  • Removed the breadcrumb and the header booking button: the site is only two levels deep and every page already ends with a booking block, so a header button would put two primary buttons on one screen. The cost is one fewer way in, covered by a persistent booking bar on mobile — with the rollback condition set in advance: if booking click-through falls below 8%, the header button comes back first.
  • No before-and-after: there were no real "before" photos, and pairing unrelated pictures would be false advertising in this industry. Bleach labels and colour codes on the work pages, plus a clear list of hair conditions the salon will not take, carry that job instead.

Key Screens

A brand site that says one thing per screen

The home page, portfolio and stylist pages all follow the same margin rules. The accent colour marks state only, never actions — if buttons were red too, the signal would stop meaning anything.

MARGIN portfolio page: a large two-line heading, a short intro with the work count in red, a single row of service, stylist and sort filters, and a three-column grid of hair photos.
Portfolio: the nav underline and the work count are the only red on the page, and both mark current state. Filters sit in a single row, and the grid stops at three columns on desktop rather than trading margin for more thumbnails.
Stylist page for Yuki: a wide salon photo with the stylist working on the right, and her name set large in white over the photo's empty left side.
Stylist page: the most important page on the site, showing customers who will serve them before they book. The hero photo keeps its left side empty for the name, and the contrast of the white type was measured photo by photo.

Interaction Design Case

Designing for when booking goes wrong, not only the happy path

Pick a stylist, pick services, pick a time, fill in details, confirm. Availability comes from Google Calendar: "one customer at a time" maps exactly to "one calendar per stylist", so the salon has no new back office to learn. The cost is that booking is not atomic — a slot can be taken between checking availability and submitting — so the flow has to design for that moment.

  • The summary panel enlarges the value just chosen and labels undecided fields "not selected yet"; when the button is disabled, the reason is written right below it.
  • No room that day: the date turns the accent colour and three alternative slots are offered on the spot.
  • Slot taken by someone else: a dialog explains what happened and offers the three remaining openings on the same day, with the option to pick a time yourself.
  • Submission failed: a dark notice appears under the nav, everything entered is kept so the customer can simply resend, and a phone number is there as a fallback.
  • The build also fixed a flaw in the prototype: field errors were checked on "Next", but "Next" stays disabled until the form is complete, so the designed error state could never appear. Validating on blur made it reachable.
Booking step one: a five-step progress bar, four stylist cards with the selected one outlined in black, and a summary panel on the right showing the chosen stylist while service and time read not selected yet.
Step one: the chosen stylist gets a black outline, and the summary on the right enlarges that name while services and time still read "not selected yet".
Booking step three for 12 September: the date is filled red in the calendar, a red-bordered No Room That Day panel lists three other dates and times, and the summary shows the time in red as no availability with the Next button disabled.
No room that day: the date and the time in the summary both turn red, and instead of making customers try day after day, the three nearest openings are offered right away. "Next" stays disabled, with the reason written under it.
Slot Taken dialog over the confirmation step: the heading says the 17:00 slot on 6 September was just taken, followed by three other times that day, a Submit With New Time button and a Pick Another Time link.
Slot taken: the calendar backend cannot hold a slot atomically, and this dialog is the designed answer to that trade-off. Same-day openings come first, because a different time is easier to accept than a different day.
Confirmation step with a black Submit Failed bar under the navigation: it says the connection dropped and the booking was not made, the details are still there, and it offers a link to call the salon.
Submission failed: nothing is cleared, so the customer only needs to press submit again — and if that still fails, the phone number is right in the notice.

Legibility & Imagery

Measuring the white type on every photo

Once AI-generated interior photos went in, the white walls swallowed the white headlines. Each photo was cropped to its slot's aspect ratio, overlaid with the gradient, and measured at the brightest area behind the headline. The stylist page dropped to 1.6:1 at 901px wide and the closing footer block to 1.7:1 — both under the 3:1 AA threshold for large text.

  • After retuning the gradients, the lowest contrast anywhere on the site rose to 3.4:1.
  • On mobile the stylist page switches to the photo's native 16:9, rather than darkening the whole image just to hold the text.
  • With no booking button in the header, a persistent bar at the bottom of the mobile screen keeps the way in, with the phone number as the second option.
Mobile home page: the MARGIN logo and menu icon, a salon photo with a round mirror and chair under a white two-line headline, and a fixed bar at the bottom with a Book Online button and the phone number.
Mobile home: a persistent booking bar, with the phone number as the secondary option.
Mobile booking page: a large two-line heading, the five-step progress bar wrapped onto two rows with step one in red, and the first stylist card below.
Mobile booking: the step bar wraps to two rows, with the current step marked in the accent colour.
Mobile stylist page for Yuki: a 16:9 salon photo with her name in large white type on the left, her quote as a serif heading below, a short bio, and the fixed booking bar at the bottom.
Mobile stylist page: the photo keeps its native 16:9 and the name sits over its empty left side, without darkening the whole image.
Design Checks

This is a concept project with no real operating data. These are the checks I ran on my own design: whether the rules became specs that can be signed off, whether components map to the design system, and whether type over photos reaches readable contrast.

12 decisions
Design decisions logged with their cost
23 components
Components mapped to the design system
3.4:1
Lowest contrast of white type over photos

Reflection

Before real operations: real photography and real validation

All imagery is placeholder for now, and the work photos are stock images from Unsplash. Going live would need real photography with consent, and several decisions that traded one cost for another can only be judged once real data comes in.

  • Once real photos exist, bring back before-and-after first to reassure high-spend customers.
  • Test the header-button decision against booking click-through, and restore the button if it falls below 8%.
  • Add month switching and SMS notifications, and replace the in-memory rate limit with Cloudflare's rate limiting.