Build a Premium Booking Website with Claude Code
Build a one-of-a-kind booking website for a hair salon, barbershop or massage studio, with real appointments, booking emails and an owner dashboard.
Roadmap & resources
Requirements
Claude Desktop
Claude Code builds your website in the desktop app. A full build is a long session, so use a paid Claude plan with room to spare.
Download Claude Desktop
https://claude.ai/download
Node.js
Download the LTS release. Claude uses it to build and check your website.
Download Node.js
https://nodejs.org/en/download
GitHub account
Free. Your website's code lives here, and Hostinger deploys from it.
Create GitHub Account
https://github.com/signup
Supabase account
The free plan is enough. It allows two active projects — if you already have two, pause one you don't use. It stores your services, hours, bookings and your dashboard sign-in.
Create Supabase Account
https://supabase.link/r4gdwhi
Resend account
The free plan sends 100 emails a day and 3,000 a month. Each booking sends several: the code, the request received, your alert and the confirmation.
Create Resend Account
https://resend.com/signup
Your own domain
Your website lives on it, and booking emails are sent from it. A free address such as Gmail can't send them. Buying the domain with your Hostinger plan? Get the plan before Step 3.
Hostinger hosting
You need hosting in Step 5, when your website goes live.
Choose a Hostinger Plan
Use the Business (Unlimited) plan or a Cloud plan — only those run Node.js Web Apps: up to 5 on Business, 10 on Cloud.
Choose Your Booking Business
Choose Your Business Type
Your part. The Booking Engine is the same for all three — the design, words and sample services are made for the one you choose.
What kind of business is it?
Hair Salon
Cuts, colour and styling, with the engine's ready-made salon services.
Build a complete, premium booking website for **{{BUSINESS_NAME}}**, a hair salon in {{CITY}}, in this empty folder. The booking system is an installed package — the **Profit Studio Booking Engine**. It owns availability, bookings, booking emails and the owner's dashboard. You design and build the public website around it, and nothing the engine provides. ## 1. Set up 1. Scaffold a Vite + React + TypeScript app **in the current folder**: `npm create vite@latest . -- --template react-ts --no-interactive`, then `npm install`. create-vite stops without an error when the folder is not empty — a `.DS_Store` or a `.claude/` folder is enough — so check that `package.json` now exists. If it does not, scaffold into a new subfolder the same way, move all of its files (dotfiles included) up into this folder without overwriting or deleting anything already here, and remove the empty subfolder. Remove the template's demo content. 2. `npm install --save-exact @profitstudio/booking-engine@2.0.0-beta.1`, then `npx booking-engine init --preset hair-salon`. 3. Read `node_modules/@profitstudio/booking-engine/docs/BUILD-A-SITE.md` once: it has every file to wire, the booking API and the error table. Open `docs/INTEGRATION.md` only at a section it names, and nothing else in the package. 4. Wire the engine as BUILD-A-SITE §1 shows, with the engine's `hairSalonPreset` as `preview.preset`; in `preview.business` use `{{BUSINESS_NAME}}`, the time zone `{{TIMEZONE}}` and a plausible placeholder `contact` in {{CITY}}. The dashboard on `/admin/` mounts `<BookingAdmin engine={engine} siteUrl="/" content={siteContent} />` and gets only your brand accent (light and dark) in `src/admin-theme.css`. One public page (sections and an in-page booking flow), no client-side routes. No backend yet: no Supabase project, no `supabase` commands, no `.env.local` — the site runs in preview mode with a visible “Preview — sample data, bookings are not saved” banner. ## 2. Design — the creative part Make it feel like a real, distinctive hair salon, not a template: - **Style direction:** {{STYLE_DIRECTION}} - **Audience:** Local clients, mostly 25–55, booking cuts, color and styling — most of them on a phone, often in a hurry. - **Tone:** Confident, warm and expert. Never clinical, never luxury cliché. - **Sections:** - Hero with the salon's personality and one clear “Book an appointment” action - Services and prices loaded from the engine (duration, price, “from” prices), grouped sensibly — with the owner's optional service photos (cut, colour, styling work) shown in the site's own style when a service has one, and a complete typeset menu without them - The team / stylists — placeholder bios and names the owner will edit, clearly marked - The salon space and approach (what makes this salon different) - Testimonials — clearly marked as placeholder examples, never presented as real reviews - Opening hours (from the engine) and location / contact (from the engine's business contact) - The booking flow - Footer with a discreet “Staff login” link to /admin/ - **Booking experience:** - Service first, then the date and time together, then details, then the email code, then confirmation - A real month calendar with month and year navigation that shows each day's status from the engine — open, only a few times left, fully booked, closed or past — loaded with one getAvailability call per visible month (INTEGRATION.md §8 and the calendar helpers in §9); on phones a week strip with the same statuses and a “Month” view - Times grouped as Morning / Afternoon / Evening (a segmented switch with counts when there are many); the chosen time continues inline (“1:30 PM — Continue”), and a “soonest time” shortcut comes from data already loaded - Keep the chosen service, duration, price, date and time visible at every step — a summary beside the calendar on wide screens, a sticky bar on phones - The code step feels like part of the salon's flow: the masked address the code went to, one large code field, “Send a new code”, “Choose another time”, and a reminder to check spam - Designed states, not error dumps: times loading, times that could not load (Try again), a time just taken by someone else (fresh times), a fully booked day (the next available day), a closed day, and online booking switched off (contact details) - Pending wording: “Request received — we'll confirm your appointment shortly.” Confirmed wording: “You're booked.” - **Imagery:** Real, coherent photography is part of the design, not an extra: the hero, the salon space, details of the work (hands, tools, colour, texture) and the storefront, all in one photographic direction. Show hair and craft, never posed stock smiles. Never present a photographed person as one of the salon's stylists: sample team cards show work, not faces, and stay labelled as samples until the owner adds real portraits. Motifs (scissor, comb, hair-texture curves) frame the photos; they do not replace them. Service photos are the owner's (added per service in the dashboard): present them as part of the menu's design, never as stock fillers. - **Avoid:** - A generic SaaS landing page look, purple gradients, glassmorphism everywhere - Invented awards, certifications, review counts or ratings - Medical or clinical styling - Hard-coded services, prices or opening hours (they come from the engine) - **Words:** call customers “clients” and bookings “appointments”. How: - **Identity first.** A short `DESIGN.md`: the concept in one sentence, a display face with character and a readable text face (both self-hosted with `@fontsource`), colour tokens with every text/background pair at 4.5:1 or better, a type and spacing scale, how motion behaves. Every value lives in one CSS file of custom properties. Two businesses in this niche with different style directions must not look like the same layout recoloured. Plain CSS, no UI kit. - **Composition.** Varied rhythm — full-bleed moments, asymmetric grids, one focal point per screen; designed for phones first, from 320 px to wide screens; keyboard-accessible with visible focus; motion of 150–600 ms with a purpose, off under `prefers-reduced-motion`. - **Engine data, never hard-coded:** services, prices, opening hours, contact details and availability (BUILD-A-SITE §3). “Open now” lines come from `getOpeningTimes`. Show each service's optional `image` in your own style when it has one, and make the services section complete with none. - **Owner-editable copy.** Declare the texts, photos, logo, brand colours and heading style the owner may change in `src/content/site.ts` (BUILD-A-SITE §2), with your copy and photos as defaults, render the page from it as text, and bake it in with the Vite plugin. - **Photos.** Free, properly licensed photographs (Unsplash or Pexels licence), at most six, downloaded into `photos/source/` and recorded in `PHOTOS.md` — never hot-linked, never a photographed person presented as staff (sample team cards show the work and say “Sample photo”; content-list images cannot have defaults, so sample cards may be text-only or share one site image). Search terms that work: “hair salon interior”, “hairdresser cutting hair close up”, “hair colour bowl brush”, “salon chair mirror”. Prepare them with `npx booking-engine photos` (BUILD-A-SITE §5): AVIF + WebP at two or three widths, `width`/`height` on every `<img>`, fixed `aspect-ratio` frames with `object-fit: cover`, the hero eager with `fetchpriority="high"`, everything else lazy. When no fitting photo exists, design the frame without one. - **The booking flow** follows BUILD-A-SITE §4 with the calendar helpers: server slots only, the business time zone, the two steps (`requestBooking`, then the code and `verifyBooking`), every error in the table, contact details when online booking is off, success only after `verifyBooking` returned `ok: true`. On a phone: the step's name always visible, the choice and price in view, 44 px targets, the right input types, inline validation, a loading state on the control that waits, a code field that accepts a pasted code, focus on each new step's heading. - **Every “Book” button always answers.** Whatever the booking section is showing — loading, the flow, online booking off, no services, a calendar that failed to load — each Book/CTA button on the page scrolls to the booking section and moves focus to its heading, so the visitor sees that state's message and the shop's contact details. Never route a CTA only through the flow component (it is not on the page in those states), and never leave a click that does nothing. Check it with online booking off as well as on. ## 3. Work efficiently The viewer's Claude plan pays for every step, so keep the session lean: - Read BUILD-A-SITE.md once; don't re-read files you wrote or explore the package. - Photos: start from the leads above or spend at most about ten minutes searching; judge candidates on one contact sheet of small thumbnails, not one by one at full size. - Screenshots: about eight in all — desktop and a 390 px phone (device emulation), downscaled to at most 1280 px wide. Don't capture very long pages at full resolution. - Don't simulate the engine's error answers to see every state: build each state from the error table and check it in the code. - `npm run build` and `npx booking-engine doctor --dist dist` once midway and once at the end; afterwards fix and rerun only what failed. ## 4. Finish - `CLAUDE.md` in the project root: the engine's boundaries (from `BOOKING_ENGINE.md`), where the design lives, and that new booking features need a new engine version, never custom database code. - Done when: `npm run build` and `npx booking-engine doctor --dist dist` pass; every field in `src/content/site.ts` is used and the page looks as designed with the defaults; the key sections show real photos (`PHOTOS.md`, AVIF/WebP with dimensions; `DESIGN.md` matches the tokens); `npm run dev` serves `/` with the preview banner and a booking flow that reaches the code step and the honest preview message, and `/admin/` shows “Connect your backend”. Stop the dev server. - End with a short summary: what you built, the design choices, and anything in the engine's documentation that was unclear or wrong.
Barbershop
Cuts, fades, beard work and shaves, booked fast from a phone.
Build a complete, premium booking website for **{{BUSINESS_NAME}}**, a barbershop in {{CITY}}, in this empty folder. The booking system is an installed package — the **Profit Studio Booking Engine**. It owns availability, bookings, booking emails and the owner's dashboard. You design and build the public website around it, and nothing the engine provides. ## 1. Set up 1. Scaffold a Vite + React + TypeScript app **in the current folder**: `npm create vite@latest . -- --template react-ts --no-interactive`, then `npm install`. create-vite stops without an error when the folder is not empty — a `.DS_Store` or a `.claude/` folder is enough — so check that `package.json` now exists. If it does not, scaffold into a new subfolder the same way, move all of its files (dotfiles included) up into this folder without overwriting or deleting anything already here, and remove the empty subfolder. Remove the template's demo content. 2. `npm install --save-exact @profitstudio/booking-engine@2.0.0-beta.1`, then `npx booking-engine init`. 3. Read `node_modules/@profitstudio/booking-engine/docs/BUILD-A-SITE.md` once: it has every file to wire, the booking API and the error table. Open `docs/INTEGRATION.md` only at a section it names, and nothing else in the package. 4. Wire the engine as BUILD-A-SITE §1 shows, with this site's own preset as `preview.preset` — write it in `src/lib/preset.ts` and export it as `sitePreset` (BUILD-A-SITE §1) with the words “clients” and “appointments”; in `preview.business` use `{{BUSINESS_NAME}}`, the time zone `{{TIMEZONE}}` and a plausible placeholder `contact` in {{CITY}}. - Write `sitePreset` from this sample data (prices are US dollars: use the local currency at a plausible local price level; `id` "barbershop", `label` "Barbershop"): - Classic Cut · 30 min (+10 min buffer) · 40 · “Scissor or clipper cut, styled and finished with a neck shave.” - Skin Fade · 45 min (+10 min buffer) · 45 · “Tight fade blended to the skin, shaped on top, sharp line-up.” - Beard Trim & Line-up · 20 min (+5 min buffer) · 25 · “Shape, trim and a hot-towel line-up for the beard.” - Cut & Beard · 60 min (+10 min buffer) · 60 · “A full haircut and beard shape in one sitting.” - Hot Towel Shave · 40 min (+10 min buffer) · 45 · “Straight-razor shave with hot towels and a cool finish.” - Buzz Cut · 20 min (+5 min buffer) · 25 · “One length all over, cleaned up at the neck and ears.” - Kids Cut (under 12) · 30 min (+10 min buffer) · 28 · “A patient, quick cut for younger clients.” - Opening hours: Tuesday to Friday 10:00–19:00, Saturday 09:00–17:00; closed Sunday and Monday - Policies: 15-minute slot interval, at least 1 hour's notice, up to 30 days ahead, the owner confirms each request, a phone number is required; every other policy as in BUILD-A-SITE §1 The dashboard on `/admin/` mounts `<BookingAdmin engine={engine} siteUrl="/" content={siteContent} presets={[sitePreset]} />` and gets only your brand accent (light and dark) in `src/admin-theme.css`. One public page (sections and an in-page booking flow), no client-side routes. No backend yet: no Supabase project, no `supabase` commands, no `.env.local` — the site runs in preview mode with a visible “Preview — sample data, bookings are not saved” banner. ## 2. Design — the creative part Make it feel like a real, distinctive barbershop, not a template: - **Style direction:** {{STYLE_DIRECTION}} - **Audience:** Neighbourhood regulars and newcomers, mostly 18–50, who book a cut every three to five weeks — almost always on a phone, often the same day. - **Tone:** Easygoing, sharp and confident; plain-spoken and welcoming to everyone. No macho bravado, no “gentlemen's club” clichés. - **Sections:** - Hero with the shop's character and one clear “Book a chair” action, plus the soonest open time for the first service on the menu (named) when it is known - The menu: services, durations and prices loaded from the engine, readable at a glance like a board on the wall — with the owner's optional service photos (cuts, fades, beard work) shown in the site's own style when a service has one, and a complete menu without them - The barbers — placeholder names and short bios the owner will edit, clearly marked as samples (portraits come later from the owner) - The shop: the chairs, the music, the routine of a visit — what a first-timer can expect - Kind words — clearly marked as placeholder examples, never presented as real reviews - Opening hours (from the engine) and location / contact (from the engine's business contact), with directions - The booking flow - Footer with a discreet “Staff login” link to /admin/ - **Booking experience:** - Service first, then the date and time together, then details, then the email code, then confirmation — fast enough to finish standing in line for coffee - Make the soonest time obvious: a “Next open: today 4:15 PM” shortcut from data already loaded, and a week view that opens on today — on a closed day, say so and offer the next open time - Short services mean many times: group them Morning / Afternoon / Evening with counts, and keep the list scannable on a phone - There is one shared calendar: never offer “choose your barber” or show staff availability — the engine does not book per barber - Keep the chosen service, duration, price, date and time visible at every step — a summary beside the times on wide screens, a sticky bar on phones - The code step stays in the shop's voice: where the code went (masked), one large code field that accepts a pasted code, “Send a new code”, “Choose another time”, and a reminder to check spam - Designed states: times loading, times that could not load (Try again), a time just taken (fresh times), a fully booked day (the next open day), a closed day, and online booking switched off (call or message the shop) - Pending wording: “Request received — we'll confirm your chair shortly.” Confirmed wording: “You're booked. See you in the chair.” - **Imagery:** Real photography of the craft is part of the design: clippers and straight razors, the chair, the lather, the line-up, a door, window or street at dusk (no other shop's name, no barber pole, no identifiable people; if no such licensed photo exists, a designed frame such as a painted window sign) — one coherent photographic direction. Never present a photographed person as one of the shop's barbers: sample barber cards show tools or work, not faces, and stay labelled as samples until the owner adds real portraits. Graphic motifs (rules, numerals, signage lettering) frame the photos; they do not replace them. Service photos are the owner's (added per service in the dashboard): present them as part of the menu's design, never as stock fillers. - **Avoid:** - Barber-pole stripes, skulls, mustache icons and other stock barbershop clichés - A generic SaaS landing page look, purple gradients, glassmorphism - Invented awards, review counts, ratings or “best in town” claims - A “choose your barber” step or per-barber availability (not supported by the engine) - Hard-coded services, prices or opening hours (they come from the engine) - **Words:** call customers “clients” and bookings “appointments”. How: - **Identity first.** A short `DESIGN.md`: the concept in one sentence, a display face with character and a readable text face (both self-hosted with `@fontsource`), colour tokens with every text/background pair at 4.5:1 or better, a type and spacing scale, how motion behaves. Every value lives in one CSS file of custom properties. Two businesses in this niche with different style directions must not look like the same layout recoloured. Plain CSS, no UI kit. - **Composition.** Varied rhythm — full-bleed moments, asymmetric grids, one focal point per screen; designed for phones first, from 320 px to wide screens; keyboard-accessible with visible focus; motion of 150–600 ms with a purpose, off under `prefers-reduced-motion`. - **Engine data, never hard-coded:** services, prices, opening hours, contact details and availability (BUILD-A-SITE §3). “Open now” lines come from `getOpeningTimes`. Show each service's optional `image` in your own style when it has one, and make the services section complete with none. - **Owner-editable copy.** Declare the texts, photos, logo, brand colours and heading style the owner may change in `src/content/site.ts` (BUILD-A-SITE §2), with your copy and photos as defaults, render the page from it as text, and bake it in with the Vite plugin. - **Photos.** Free, properly licensed photographs (Unsplash or Pexels licence), at most six, downloaded into `photos/source/` and recorded in `PHOTOS.md` — never hot-linked, never a photographed person presented as staff (sample team cards show the work and say “Sample photo”; content-list images cannot have defaults, so sample cards may be text-only or share one site image). Free photos already checked for this niche, to start from (confirm the licence on each page; replace any): https://www.pexels.com/photo/barber-perfecting-a-fade-haircut-close-up-39559270/ (a fade being cut, close up (hero)); https://www.pexels.com/photo/vintage-barber-shop-interior-with-classic-chairs-37764947/ (a row of classic barber chairs); https://www.pexels.com/photo/customer-at-barber-16553361/ (a lathered shave in progress); https://www.pexels.com/photo/tools-at-barbershop-7697712/ (clippers and tools on a counter); https://www.pexels.com/photo/hairdresser-scissors-and-tools-10317439/ (scissors and combs); https://www.pexels.com/photo/gray-hand-tool-1319459/ (a brass shaving bowl and razors). Search terms that work: “barber fade close up”, “barbershop interior chairs”, “straight razor shave”, “barber tools clippers”. Prepare them with `npx booking-engine photos` (BUILD-A-SITE §5): AVIF + WebP at two or three widths, `width`/`height` on every `<img>`, fixed `aspect-ratio` frames with `object-fit: cover`, the hero eager with `fetchpriority="high"`, everything else lazy. When no fitting photo exists, design the frame without one. - **The booking flow** follows BUILD-A-SITE §4 with the calendar helpers: server slots only, the business time zone, the two steps (`requestBooking`, then the code and `verifyBooking`), every error in the table, contact details when online booking is off, success only after `verifyBooking` returned `ok: true`. On a phone: the step's name always visible, the choice and price in view, 44 px targets, the right input types, inline validation, a loading state on the control that waits, a code field that accepts a pasted code, focus on each new step's heading. - **Every “Book” button always answers.** Whatever the booking section is showing — loading, the flow, online booking off, no services, a calendar that failed to load — each Book/CTA button on the page scrolls to the booking section and moves focus to its heading, so the visitor sees that state's message and the shop's contact details. Never route a CTA only through the flow component (it is not on the page in those states), and never leave a click that does nothing. Check it with online booking off as well as on. ## 3. Work efficiently The viewer's Claude plan pays for every step, so keep the session lean: - Read BUILD-A-SITE.md once; don't re-read files you wrote or explore the package. - Photos: start from the leads above or spend at most about ten minutes searching; judge candidates on one contact sheet of small thumbnails, not one by one at full size. - Screenshots: about eight in all — desktop and a 390 px phone (device emulation), downscaled to at most 1280 px wide. Don't capture very long pages at full resolution. - Don't simulate the engine's error answers to see every state: build each state from the error table and check it in the code. - `npm run build` and `npx booking-engine doctor --dist dist` once midway and once at the end; afterwards fix and rerun only what failed. ## 4. Finish - `CLAUDE.md` in the project root: the engine's boundaries (from `BOOKING_ENGINE.md`), where the design lives, and that new booking features need a new engine version, never custom database code. - Done when: `npm run build` and `npx booking-engine doctor --dist dist` pass; every field in `src/content/site.ts` is used and the page looks as designed with the defaults; the key sections show real photos (`PHOTOS.md`, AVIF/WebP with dimensions; `DESIGN.md` matches the tokens); `npm run dev` serves `/` with the preview banner and a booking flow that reaches the code step and the honest preview message, and `/admin/` shows “Connect your backend”. Stop the dev server. - End with a short summary: what you built, the design choices, and anything in the engine's documentation that was unclear or wrong.
Massage & Wellness Studio
Massages and treatments, with a calm booking experience.
Build a complete, premium booking website for **{{BUSINESS_NAME}}**, a massage & wellness studio in {{CITY}}, in this empty folder. The booking system is an installed package — the **Profit Studio Booking Engine**. It owns availability, bookings, booking emails and the owner's dashboard. You design and build the public website around it, and nothing the engine provides. ## 1. Set up 1. Scaffold a Vite + React + TypeScript app **in the current folder**: `npm create vite@latest . -- --template react-ts --no-interactive`, then `npm install`. create-vite stops without an error when the folder is not empty — a `.DS_Store` or a `.claude/` folder is enough — so check that `package.json` now exists. If it does not, scaffold into a new subfolder the same way, move all of its files (dotfiles included) up into this folder without overwriting or deleting anything already here, and remove the empty subfolder. Remove the template's demo content. 2. `npm install --save-exact @profitstudio/booking-engine@2.0.0-beta.1`, then `npx booking-engine init`. 3. Read `node_modules/@profitstudio/booking-engine/docs/BUILD-A-SITE.md` once: it has every file to wire, the booking API and the error table. Open `docs/INTEGRATION.md` only at a section it names, and nothing else in the package. 4. Wire the engine as BUILD-A-SITE §1 shows, with this site's own preset as `preview.preset` — write it in `src/lib/preset.ts` and export it as `sitePreset` (BUILD-A-SITE §1) with the words “guests” and “sessions”; in `preview.business` use `{{BUSINESS_NAME}}`, the time zone `{{TIMEZONE}}` and a plausible placeholder `contact` in {{CITY}}. - Write `sitePreset` from this sample data (prices are US dollars: use the local currency at a plausible local price level; `id` "massage-studio", `label` "Massage & Wellness Studio"): - Swedish Massage — 60 min · 60 min (+15 min buffer) · 95 · “Long, flowing strokes to ease tension and settle the mind.” - Swedish Massage — 90 min · 90 min (+15 min buffer) · 135 · “The full-body Swedish session with time for slower, deeper work.” - Deep Tissue — 60 min · 60 min (+15 min buffer) · 105 · “Firm, focused pressure for tight shoulders, back and legs.” - Deep Tissue — 90 min · 90 min (+15 min buffer) · 150 · “Unhurried deep work on the areas you choose.” - Hot Stone — 75 min · 75 min (+20 min buffer) · 125 · “Warm basalt stones and long strokes to release and soften.” - Pregnancy Massage — 60 min · 60 min (+15 min buffer) · 100 · “Side-lying comfort and gentle pressure, from the second trimester.” - Reflexology — 45 min · 45 min (+15 min buffer) · 70 · “Pressure-point work on the feet in a reclined chair.” - Opening hours: Monday to Friday 10:00–20:00, Saturday 09:00–17:00, Sunday 10:00–16:00 - Policies: 15-minute slot interval, at least 4 hours' notice, up to 60 days ahead, the owner confirms each request, a phone number is required; every other policy as in BUILD-A-SITE §1 The dashboard on `/admin/` mounts `<BookingAdmin engine={engine} siteUrl="/" content={siteContent} presets={[sitePreset]} />` and gets only your brand accent (light and dark) in `src/admin-theme.css`. One public page (sections and an in-page booking flow), no client-side routes. No backend yet: no Supabase project, no `supabase` commands, no `.env.local` — the site runs in preview mode with a visible “Preview — sample data, bookings are not saved” banner. ## 2. Design — the creative part Make it feel like a real, distinctive massage & wellness studio, not a template: - **Style direction:** {{STYLE_DIRECTION}} - **Audience:** Adults 25–65 looking for relief from everyday tension or simply an hour of quiet — often booking in the evening on a phone, sometimes for the first time. - **Tone:** Calm, warm and reassuring; clear and practical. Never clinical, never mystical, and never promise health outcomes. - **Sections:** - Hero that slows the visitor down, with one clear “Book a session” action - Treatments loaded from the engine — each length is its own service (e.g. 60 and 90 minutes), shown as a calm, readable list with durations and prices — with the owner's optional service photos in the site's own style when a service has one, and complete without them - What to expect: arriving, the room, comfort and pressure, privacy — written for a first-timer - The therapists — placeholder names and short bios the owner will edit, clearly marked as samples - Questions (what to wear, arriving early, cancelling) as plain text the owner can edit - Opening hours (from the engine) and location / contact (from the engine's business contact) - The booking flow - Footer with a discreet “Staff login” link to /admin/ - **Booking experience:** - Treatment and length first, then the date and time together, then details, then the email code, then confirmation - A calm calendar: the next open days first, times grouped Morning / Afternoon / Evening; never a countdown or scarcity pressure - There is one shared calendar: never offer “choose your therapist” or show per-therapist availability - The note field is optional and invites preferences (pressure, areas to avoid) — never ask for medical history or health details in the form - Keep the chosen treatment, length, price, date and time visible at every step - The code step is gentle and clear: where the code went (masked), one large code field that accepts a pasted code, “Send a new code”, “Choose another time”, a reminder to check spam - Designed states: loading, could not load (Try again), a time just taken, a fully booked day (the next open day), a closed day, online booking switched off (call or email the studio) - Pending wording: “Request received — we'll confirm your session shortly.” Confirmed wording: “Your session is booked.” - **Imagery:** Quiet, tactile photography: hands at work, linen, warm stones, oil, soft light across a treatment room — one coherent direction, little colour, lots of air. Never present a photographed person as one of the studio's therapists: sample therapist cards show the room or hands, not faces, and stay labelled as samples. No before/after imagery. Service photos are the owner's (added per service in the dashboard): present them as part of the design, never as stock fillers. - **Avoid:** - Spa clichés: orchids, Buddha statues, stacked stones, lotus icons - Medical claims, diagnoses, “cures” or before/after promises - Countdown timers, scarcity pressure or pushy upsells - A “choose your therapist” step or per-therapist availability (not supported by the engine) - Hard-coded services, prices or opening hours (they come from the engine) - **Words:** call customers “guests” and bookings “sessions”. How: - **Identity first.** A short `DESIGN.md`: the concept in one sentence, a display face with character and a readable text face (both self-hosted with `@fontsource`), colour tokens with every text/background pair at 4.5:1 or better, a type and spacing scale, how motion behaves. Every value lives in one CSS file of custom properties. Two businesses in this niche with different style directions must not look like the same layout recoloured. Plain CSS, no UI kit. - **Composition.** Varied rhythm — full-bleed moments, asymmetric grids, one focal point per screen; designed for phones first, from 320 px to wide screens; keyboard-accessible with visible focus; motion of 150–600 ms with a purpose, off under `prefers-reduced-motion`. - **Engine data, never hard-coded:** services, prices, opening hours, contact details and availability (BUILD-A-SITE §3). “Open now” lines come from `getOpeningTimes`. Show each service's optional `image` in your own style when it has one, and make the services section complete with none. - **Owner-editable copy.** Declare the texts, photos, logo, brand colours and heading style the owner may change in `src/content/site.ts` (BUILD-A-SITE §2), with your copy and photos as defaults, render the page from it as text, and bake it in with the Vite plugin. - **Photos.** Free, properly licensed photographs (Unsplash or Pexels licence), at most six, downloaded into `photos/source/` and recorded in `PHOTOS.md` — never hot-linked, never a photographed person presented as staff (sample team cards show the work and say “Sample photo”; content-list images cannot have defaults, so sample cards may be text-only or share one site image). Free photos already checked for this niche, to start from (confirm the licence on each page; replace any): https://unsplash.com/photos/massage-on-leg-with-white-towel-yBLLzNcA6GU (hands at work over a white towel (hero)); https://www.pexels.com/photo/settee-by-the-window-6628520/ (a treatment table by a tall window); https://unsplash.com/photos/hands-massaging-a-persons-forearm-with-gentle-pressure-YBfzGCJjLuI (hands working on a forearm). Search terms that work: “massage hands towel”, “treatment room window”, “spa linen stones”. Prepare them with `npx booking-engine photos` (BUILD-A-SITE §5): AVIF + WebP at two or three widths, `width`/`height` on every `<img>`, fixed `aspect-ratio` frames with `object-fit: cover`, the hero eager with `fetchpriority="high"`, everything else lazy. When no fitting photo exists, design the frame without one. - **The booking flow** follows BUILD-A-SITE §4 with the calendar helpers: server slots only, the business time zone, the two steps (`requestBooking`, then the code and `verifyBooking`), every error in the table, contact details when online booking is off, success only after `verifyBooking` returned `ok: true`. On a phone: the step's name always visible, the choice and price in view, 44 px targets, the right input types, inline validation, a loading state on the control that waits, a code field that accepts a pasted code, focus on each new step's heading. - **Every “Book” button always answers.** Whatever the booking section is showing — loading, the flow, online booking off, no services, a calendar that failed to load — each Book/CTA button on the page scrolls to the booking section and moves focus to its heading, so the visitor sees that state's message and the shop's contact details. Never route a CTA only through the flow component (it is not on the page in those states), and never leave a click that does nothing. Check it with online booking off as well as on. ## 3. Work efficiently The viewer's Claude plan pays for every step, so keep the session lean: - Read BUILD-A-SITE.md once; don't re-read files you wrote or explore the package. - Photos: start from the leads above or spend at most about ten minutes searching; judge candidates on one contact sheet of small thumbnails, not one by one at full size. - Screenshots: about eight in all — desktop and a 390 px phone (device emulation), downscaled to at most 1280 px wide. Don't capture very long pages at full resolution. - Don't simulate the engine's error answers to see every state: build each state from the error table and check it in the code. - `npm run build` and `npx booking-engine doctor --dist dist` once midway and once at the end; afterwards fix and rerun only what failed. ## 4. Finish - `CLAUDE.md` in the project root: the engine's boundaries (from `BOOKING_ENGINE.md`), where the design lives, and that new booking features need a new engine version, never custom database code. - Done when: `npm run build` and `npx booking-engine doctor --dist dist` pass; every field in `src/content/site.ts` is used and the page looks as designed with the defaults; the key sections show real photos (`PHOTOS.md`, AVIF/WebP with dimensions; `DESIGN.md` matches the tokens); `npm run dev` serves `/` with the preview banner and a booking flow that reaches the code step and the honest preview message, and `/admin/` shows “Connect your backend”. Stop the dev server. - End with a short summary: what you built, the design choices, and anything in the engine's documentation that was unclear or wrong.
Customize and Copy Your Prompt
Your part.
Open Customize Prompt on the prompt above Enter your business name and city — the time zone starts as yours, so change it only if the business is somewhere else Pick a style direction — each one gives your website a different look Copy the prompt — you send it to Claude in Step 2
Warm editorial — espresso, copper and cream, an elegant serif and magazine-like layouts
Modern minimal — near-white and charcoal with one confident accent and a strict grid
Bold urban — deep black, concrete greys and one electric accent, with strong contrast
Soft natural — sage, sand and blush, rounded type, calm and airy
Heritage modern — bottle green, oxblood and brass on warm paper, classic but never theatrical
Industrial clean — black, steel and concrete greys with one signal colour and big numerals
Neighbourhood warm — cream, terracotta and ink, friendly type and relaxed spacing
Quiet Nordic — oat, stone and soft sage on warm white, wide margins and gentle motion
Warm earth — clay, terracotta and deep olive, rounded headlines and organic shapes
Night calm — deep ink blue and warm candle tones, dim and intimate
The prompt installs the Booking Engine at exactly version 2.0.0-beta.1, the version this guide was tested with. Don't change it to latest.
Generate Your Website with Claude Code
Open an Empty Folder
Your part.
Claude Desktop Code Local
Create a new, empty folder for your website, for example in Documents Start a Local session and choose that folder If Claude Desktop asks, trust the folder
Send Your Prompt
Claude Code's part — you approve its actions and wait.
Paste the prompt you copied in Step 1 and send it Click Allow when Claude asks to run a command Let it work — a full build is a long session, so keep it open
Creates the website project and installs the Booking Engine
Designs the site: fonts, colours, layout and words for your business
Finds free, licensed photos and prepares them for the web
Builds the booking flow and the owner dashboard page
Checks everything with npm run build and npx booking-engine doctor
If Claude stops because you reached a usage limit, wait until it resets, then type continue in the same session.
Look at Your Website
Claude Code's part, then yours — it starts the preview, you look.
Show Me My Website
Starts your website on your computer and gives you its address.
Start the website's dev server with npm run dev and open it in the Browser pane. Then give me its local address. Don't change any files.
Look at the preview in Claude Desktop's Browser pane, or open the address Claude gives you in your browser Check the design on a wide window and a narrow one Click a Book button: the booking flow opens and reaches the step that asks for an email code Open /admin/ — add it to the address, or ask Claude to open it: it shows Connect your backend
The banner at the top says Preview: this is sample data, and no booking is saved yet. Want changes? Ask Claude in the same session before you go on.
Connect Supabase and Resend
Create Your Supabase Project
Your part — in your Supabase account.
This step has three parts: Supabase, then email, then one prompt that connects everything. The values you enter are saved in this guide and reused in Step 5, so you type each one once.
Open Supabase Dashboard
https://supabase.com/dashboard
Click New project, choose a region close to your business and the Free plan Save the database password in your password manager — never send it to Claude When the project is ready, click Connect and paste the Project URL and the Publishable key below
Create Your Dashboard Login
Your part — in your Supabase account.
Supabase Authentication Users
Click Add user, then Create new user Enter your email and a strong password Leave Auto Confirm User ticked and create the user
Supabase Authentication Sign In / Providers
Turn off Allow new users to sign up and save — only you sign in to the dashboard
Sign In to the Supabase CLI
Your part — once, before you connect.
Click Terminal in your session's title bar Run the command below Approve the sign-in in the browser window that opens
npx supabase@2 login
If Claude asks you to link the project yourself, run the command it gives you here and type your database password into the Terminal — never into the chat.
Verify Your Sending Domain
Your part — in Resend and your domain's DNS.
Booking emails are sent from your own domain — the same one your website will use in Step 5. No domain yet? Buy one in Hostinger, then come back:
Hostinger Domains
Open Resend Domains
https://resend.com/domains
Click Add domain and enter the sending domain shown above Choose the region closest to your customers Keep the page with the DNS records open
Hostinger Domains DNS / Nameservers
Choose your domain and add each record Resend shows, with the same type and value Back in Resend, click Verify DNS records
Verification usually takes under 15 minutes, and can take up to 72 hours. Keep going with the next steps while you wait. Domain managed somewhere else? Add the same records there, and keep your existing email records.
Add Your Email Secrets to Supabase
Your part — the key goes into Supabase only, never into the chat.
In Resend, open API Keys and click Create API key Choose Sending access and your verified domain, then copy the key — Resend shows it only once In Supabase, open Edge Functions, then Secrets, and add these two
RESEND_API_KEY
Keep the Resend key secret
Paste it only into Supabase's Edge Function secrets. Never send it to Claude, never put it in a website file or a prompt, and never add it to Hostinger.
Ask Claude to Connect Everything
Claude Code's part — it stops whenever something is yours to do.
In the same Claude session, copy this prompt and send it:
Claude asks before each action — click Allow. It stops when it's your turn.
You're done when Claude says Connected. Booking emails start once Resend shows your domain as Verified — the test email in Step 4 confirms it.
Set Up Your Dashboard and Test a Booking
Open Your Dashboard
Claude Code's part, then yours — it starts the website, you sign in.
Start My Connected Website
Starts your website, now connected to Supabase, and gives you the addresses of the site and the dashboard.
Stop any dev server that is still running, so the new .env.local is read. Then start the website with npm run dev, open /admin/ in the Browser pane, and give me two addresses: the website and its /admin/ dashboard. Don't change any files.
Open the dashboard and sign in with the login you created in Step 3. Your website still runs on your computer, but everything here is already real: the dashboard, the bookings and the emails use your Supabase project, and your live website in Step 5 uses the same data.
Finish Setting Up
Your part — in the dashboard.
Choose where new-request alerts go first
Without an alerts address, nobody is told about new requests, and online booking can't be switched on.
Follow Finish setting up at the top of the dashboard — it walks you through your business details, services, opening hours and email In Email & online booking, set Send new-request alerts to your own inbox, then click Send test email and check it arrived
Before you turn bookings on, click each Book button on your website: it should jump to the booking section and show your contact details. A button that does nothing is a bug — ask Claude to fix it.
Turn on online booking
Book a Test Appointment
Your part — as a customer, then as the owner.
On your website, book a service with your own email address. Then tick each one as you see it happen:
The 6-digit code arrived, and the site said your request was received
The new-request alert reached your alerts address
You clicked Confirm request in the dashboard, and the confirmation email arrived
The same time is no longer offered on your website
No email? Check spam first, then open Email & online booking in the dashboard: it shows each email's status and what to fix.
Change Your Website's Words and Photos
Your part — in the dashboard, no code.
Open Website in the dashboard Change the main headline, or the main photo Click Preview to check it, then Publish Refresh your website — the change is there
Claude designed the site; the dashboard edits the words, photos and brand colours it made editable. For a new section or a different layout, ask Claude.
Deploy to Hostinger
Create a Private GitHub Repository
Your part — in your GitHub account.
Your website works on your computer. Now it goes online: GitHub keeps the code, and Hostinger builds and serves it — with the same Supabase data you just tested.
Create GitHub Repository
https://github.com/new
Name it after your business and choose Private Leave README, .gitignore and license off, so the repository is empty Click Create repository and copy its address
Push Your Website to GitHub
Claude Code's part — you approve the push.
Push My Website to GitHub
Checks that no secret is included, commits your website and pushes it to your new repository.
Put this website on GitHub at {{REPOSITORY_URL}}, on the main branch. Stop the dev server first. Before committing, check that .gitignore keeps node_modules, dist, .env, .env.local and supabase/.temp/ out of Git, and run npx booking-engine doctor. Show me the list of files you will commit and make sure no secret, key or password is in any of them. Then initialise Git if needed, commit everything with a clear message and push it to main. Never push to any other repository. If GitHub needs me to sign in, tell me exactly what to do, and never ask me to paste a password or token into the chat. When it's done, tell me the commit you pushed.
Your repository's address
https://github.com/your-name/your-booking-website
Create Your Web App
Your part — in Hostinger. No hosting yet? Get a plan in Requirements.
Hostinger Websites Create site Web App
If Hostinger offers a domain choice, pick your own domain — otherwise your app starts on a temporary domain Select Import Git Repository Authorize GitHub and select the repository Claude just pushed
Configure & Deploy
Your part — in Hostinger.
Keep the Vite settings Hostinger detects Choose Node.js 22 or newer Make sure the build command is npm run build and the output directory is dist
Before you deploy, find Environment variables under Review build settings, click Edit and add these two — your values from Step 3, ready to copy. They're built into the website, so changing one later needs a redeploy.
Public values only
Never add the Supabase Secret key or the Resend key here. Anything starting with VITE_ is visible in the browser, and your email secrets stay in Supabase.
Click Finish, then Deploy, and wait for the build to finish
If the build stops and names a variable, fix that variable's value in Hostinger and deploy again.
Add Your Live Address to Owner Alerts
Claude Code's part.
Verify Your Live Website
Your part.
Open your domain — the padlock shows and there is no Preview banner Check your services, prices and opening hours On your phone, book a test appointment and enter the code Open /admin/ on your live site, sign in and confirm the request Check that the alert and the confirmation email both arrived
From now on, change services, prices, hours and your website's words and photos in the dashboard — they're live at once. For design or code changes, ask Claude and approve the push: Hostinger rebuilds your site on every push to main. Search engines see a new page title or description after the next build.