Skip to content
VibeFather
Join

About this template

A two-page website for an independent salon and barbershop: a home page with services, prices and durations, stylist profiles, a work gallery and reviews, and a dedicated booking page with a four-step appointment flow. The default content follows an invented six-chair shop in Chicago with four stylists, but the structure suits any salon, barbershop or grooming studio: change the brand, city, street and founding year and every section still holds. The home page opens on a full-bleed photograph of the room — deep green walls, brass-framed mirrors, leather chairs — then a price list grouped into cuts, color, beard and treatments with durations, four stylist cards each with their chair, specialties and working days, a gallery of finished work shot from behind and tool still lifes (no faces anywhere), three customer reviews and a marquee of short pull quotes. The booking page walks through service, stylist, day and time slot, and name, with client-side validation, then shows a confirmation card with a reference number, saved in the browser — no backend, no accounts, nothing submitted. The palette is forest green, warm cream and brass; Bodoni Moda sets the headlines in high-contrast fashion serif and Karla the body, both self-hosted. Closed weekdays disable their dates in the picker, and each stylist's working days filter the calendar. To adapt it, swap the variables and rewrite the copy in one content file; services, stylists, hours and slots all read from it.

The template prompt

The live demo was built from exactly this text — nothing added.

# Hair Salon and Barbershop Website

Build this website exactly as described below. Replace the [VARIABLES] with your own values; keep everything else.

## Brief
A two-page website for [BRAND], an independent salon and barbershop on [STREET] in [CITY], open since [FOUNDED] with four stylists and six chairs. It must feel like a real neighborhood shop: full-bleed photography of the room and the tools (never faces), a price list with durations, stylist profiles with chairs and specialties, a gallery of finished work, reviews, opening hours with a live open-now indicator, and a four-step booking flow — service, stylist, day and time, name — that ends in a client-side confirmation card with a reference number. Pages: index.html (home), booking.html (book a chair). Everything runs in the browser; nothing is sent anywhere.

- Audience: Locals within a ten-minute walk deciding between three shops on the same street, plus lapsed regulars checking whether their stylist still works Sundays. Adults of every hair type; men coming for fades and shaves, women for color and long-hair cuts, parents booking kids' cuts. Phone-first, wary of phone-call bookings.
- Goal: Fill the chairs. Within ten seconds a visitor should know what a signature cut costs, who cuts hair there, whether the shop is open right now, and be one tap from a booked slot.
- Tone: Confident, warm and unpretentious, like a stylist who has heard every hair confession. Short sentences, concrete numbers (minutes, dollars, chairs), no beauty-industry buzzwords, no exclamation marks.

## Stack
- Framework: Vite 5 + React 18 + TypeScript
- Styling: Tailwind CSS 3.4
- Libraries: react (MIT), react-dom (MIT), motion (motion.dev, import from 'motion/react') (MIT), lucide-react (ISC), tailwindcss (MIT), postcss (MIT), autoprefixer (MIT), vite (MIT), @vitejs/plugin-react (MIT), typescript (Apache-2.0), @fontsource-variable/bodoni-moda (OFL-1.1), @fontsource/karla (OFL-1.1)
- Build: `npm run build` → static files in `dist/`. No backend, no server code, no external APIs.
- Setup:
  - Scaffold with npm create vite@latest using the react-ts template. Two real HTML pages at the project root: index.html (entry src/main-home.tsx) and booking.html (entry src/main-booking.tsx).
  - vite.config.ts: base './', plugins [react()], build { outDir 'dist', sourcemap false, assetsInlineLimit 0, rollupOptions { input { index: resolve(__dirname, 'index.html'), booking: resolve(__dirname, 'booking.html') } } }. Do not copy src/ into dist/.
  - Tailwind 3.4 through PostCSS (tailwindcss + postcss + autoprefixer), content ['./index.html', './booking.html', './src/**/*.{ts,tsx}']. In tailwind.config.ts add every color under Design to theme.extend.colors with kebab-case keys (cream, oat, card, forest, forest-soft, ink, muted, rule, brass, brass-deep, brass-light, sage) and fontFamily display ['Bodoni Moda Variable', 'Georgia', 'serif'], sans ['Karla', 'system-ui', 'sans-serif']. Set borderRadius DEFAULT to 10px and add maxWidth { container: '1200px' }.
  - In src/fonts.ts import '@fontsource-variable/bodoni-moda' and '@fontsource/karla/400.css', '@fontsource/karla/500.css', '@fontsource/karla/700.css'; import src/fonts.ts and src/index.css from both entry files.
  - File layout: src/content.ts (every string, number, service, stylist, hour, review, gallery item and FAQ answer for both pages, typed), src/lib/brand.ts (brandSlug = [BRAND] lowercased with every run of non-alphanumeric characters replaced by '-', brandInitials = first letter of each word of [BRAND] uppercased, emailDomain = brandSlug without hyphens + '.com'), src/lib/storage.ts (safe localStorage get/set wrapped in try/catch, keys prefixed with brandSlug + ':'), src/lib/useOpenNow.ts (open-now from the hours table and the client clock), src/lib/slots.ts (builds the next 14 bookable days from the hours table and a stylist's working days, plus 30-minute time slots for a service's duration within opening hours).
  - State: src/store/booking.ts is a tiny external store (useSyncExternalStore) holding the draft { serviceId, stylistId, date, time, name, phone } and the confirmed booking, persisted to localStorage key brandSlug + ':booking'. The confirmation card survives reloads; 'Make another booking' clears it.
  - Components in src/components/: Header.tsx, MobileMenu.tsx, Reveal.tsx (Motion whileInView wrapper), Eyebrow.tsx, Badge.tsx (duration and price pills), Footer.tsx, CtaBand.tsx; home: Hero.tsx, Services.tsx (ServiceGroup.tsx, ServiceRow.tsx), Stylists.tsx (StylistCard.tsx), Gallery.tsx (Lightbox.tsx), Reviews.tsx, Marquee.tsx, HoursBlock.tsx (with OpenNowPill.tsx); booking: BookingMasthead.tsx, Stepper.tsx, steps/ (ServiceStep.tsx, StylistStep.tsx, TimeStep.tsx, DetailsStep.tsx), Confirmation.tsx, Policies.tsx. src/pages/Home.tsx and Booking.tsx compose them; both pages render Header and Footer.
  - Before the first build, download every image listed under Assets into public/images/ with curl -L -o using the exact file names given. The build itself never fetches anything.
  - public/favicon.svg as described under Assets. Both HTML files: lang en, <meta name='theme-color' content='#F7F2E7'>, <link rel='icon' href='./favicon.svg' type='image/svg+xml'>. Titles: index.html '<title>[BRAND] — Salon and barbershop on [STREET], [CITY]</title>', booking.html '<title>Book a chair — [BRAND]</title>'. booking.html must render a usable default when opened directly (step 1 active).
  - Write ASSETS.md at the project root as described under Assets.
  - dist/ holds the two HTML files, hashed JS/CSS, the woff2 fonts, favicon.svg and the six images. Keep it under 10 MB.

## Variables
- [BRAND] — Salon name (default: "The Juniper Chair"). Invented salon name. Used in the wordmark, titles, footer, email (hello@ + brandSlug without hyphens + .com), localStorage keys and booking references (initials, e.g. TJC-0247). Never a real salon or chain.
- [CITY] — City (default: "Chicago, Illinois"). Where the shop is. Used in the hero eyebrow, the getting-here line and the footer. The street stays invented-sounding; only the city may be real.
- [STREET] — Street (default: "Rowan Street"). The invented street the shop sits on ('118 Rowan Street'). Drives the address, the hero eyebrow and the footer. Keep it common English words, not a real address.
- [FOUNDED] — Year opened (default: "2019"). Four-digit year. Drives the hero eyebrow, the wordmark line 'Est. [FOUNDED]', the story line and the first review's 'regular since' year.
- [CREDIT] — keep a small "Template by VibeFather" link to https://vibefather.ai/t/hair-salon-and-barbershop-website in the footer (default: yes; delete this line to remove it)

The copy under Pages & Sections is written for the default values. If your values differ, rewrite every headline, paragraph, label and image for them — same length, tone and structure. Layout, colors, type and motion stay as described.
Keep all copy, image paths and links in one content file (for example `src/content.ts`), keyed by section, so the content can be swapped later without touching components.

## Design
### Colors
- Cream `#F7F2E7` — Page background on both pages, input fill.
- Oat `#EFE7D5` — Alternate section background (Services, Gallery), hover fill for rows and chips, image wells.
- Card `#FFFEF9` — Card surfaces (stylist cards, review cards, confirmation), light modal background.
- Forest `#24382B` — Hero overlay base, Marquee band, footer, confirmation header, photo overlays (as rgba(36,56,43,x)).
- Forest Soft `#2F4A39` — Hover surface on Forest, slot chip selected fill, deep section borders.
- Ink `#23201A` — Headlines and body text on light backgrounds, primary outline buttons, icon color.
- Muted `#7C7466` — Secondary text, captions, eyebrows, disabled days and slots (at 45% opacity).
- Rule `#DDD2BC` — 1px hairlines, card borders, input borders, dividers.
- Brass `#A87B2F` — The one accent: primary buttons, prices, duration pills, selected step dots, focus ring, marquee text on Forest.
- Brass Deep `#8C6624` — Primary button hover and pressed state.
- Brass Light `#D9B36A` — Brass replacement for text and lines on Forest backgrounds (prices, active borders).
- Sage `#7C8B6F` — Quiet second color: open-now dot and text, 'walk-ins welcome' tag, booking success check.

### Typography
- Display: H1, H2, H3, service names, stylist names, prices in large sizes, the wordmark, booking reference: Bodoni Moda (OFL), weights 500/600
- Text and UI: body, eyebrows, buttons, nav, form fields, review attributions, numbers (font-variant-numeric tabular-nums for prices, durations and hours): Karla (OFL), weights 400/500/700

### Layout & motion
- Spacing: 8px base unit. Container max-width 1200px, centered, side padding 48px desktop, 32px tablet, 20px under 640px. 12-column grid, column gap 24px. Section vertical padding 112px desktop, 80px under 1024px, 64px under 640px. Type scale: H1 clamp(44px, 6.2vw, 92px) Bodoni Moda 500, line-height 1.02, letter-spacing -0.01em; H2 clamp(32px, 4vw, 56px) Bodoni 500, line-height 1.06; H3 25px Bodoni 600, line-height 1.2; italic phrases in headings use Bodoni 500 italic (marked with *asterisks* in content.ts and rendered as <em>); lead paragraph Karla 400 18px / 1.65 Ink at 85% opacity; body Karla 400 16px / 1.7 (max-width 62ch); eyebrow Karla 700 12px uppercase letter-spacing 0.16em Muted, preceded by a 24px x 1px Brass line with 12px gap; caption Karla 400 13px Muted. Buttons: height 50px, padding 0 26px, Karla 700 14px uppercase letter-spacing 0.1em. Primary: Brass fill, Card text. Secondary: 1px Ink border, Ink text, transparent (on photos: 1px Card border, Card text). Small buttons 38px high, padding 0 16px, 12px text. Inputs: height 50px, 1px Rule border, Card fill, 16px Karla, focus border Brass. Focus ring everywhere: 2px Brass outline with 3px offset.
- Radius: 10px on cards, buttons, inputs, chips, photos and the confirmation. 999px only for duration and price pills, the open-now dot, step dots and slot chips.
- Motion: Easing: --ease-out cubic-bezier(0.22, 0.61, 0.36, 1) for reveals and steps; --ease-io cubic-bezier(0.65, 0, 0.35, 1) for accordions, tab and step switches. Nothing bounces.
- Motion: Reveal: blocks wrapped in <Reveal> start at opacity 0, translateY(18px) and animate to rest over 700ms --ease-out when 15% visible (Motion whileInView, viewport once). Siblings in a list stagger by 80ms (index prop).
- Motion: Hero photo: on load the image scales from 1.07 to 1.0 over 2200ms --ease-out; while scrolling, the image translates 0 to 10% of its height as the hero scrolls out (Motion useScroll on the hero, offset ['start start', 'end start']), and the headline block fades to opacity 0.25 and moves -32px over the same range. H1 lines rise from translateY(26px) and opacity 0 with 120ms stagger, starting 150ms after load.
- Motion: Marquee: one continuous strip of pull quotes on Forest scrolling left at a constant 40px/s, pausing on hover, duplicated content for a seamless loop (CSS animation; disabled under reduced motion).
- Motion: Service rows: hover fills Oat at 60% and slides a 24px x 2px Brass rule in from the left over 250ms; the 'Book' affordance fades in at the right edge.
- Motion: Stylist cards: on hover the photo scales to 1.04 over 700ms --ease-out and the card border turns Ink; the 'next free slot' pill crossfades to 'Book with [name]' linking to ./booking.html?stylist=<id> (the booking page preselects that stylist; opened without a query it starts unselected — never an error).
- Motion: Gallery: photos rise 12px and lose their Oat frame on hover; clicking opens the Lightbox (Forest overlay at 85%, photo at max 80vh, caption in Card, X closes, left/right arrows navigate, Escape closes) with a 240ms fade and 16px scale settle.
- Motion: Booking steps: step changes crossfade with a 20px horizontal slide, 300ms --ease-io (AnimatePresence mode 'wait'). Selecting a service, stylist, day or slot fills the chip over 150ms; the sticky summary price counts to the new total over 400ms. The confirmation card flips in (rotateX -8deg to 0, opacity 0 to 1, 420ms --ease-out) and the reference number types character by character over 700ms.
- Motion: Hours: the open-now dot pulses (scale 1 to 1.25, 1600ms loop) only while open.
- Motion: Header: transparent over the photo hero with Card text; after 40px of scroll it gets Cream at 94% opacity, backdrop-filter blur(10px), Ink text and a 1px Rule bottom border, 250ms transition. On booking.html it is solid from the start.
- Motion: Links: .link-draw grows a 1px currentColor underline from 0% to 100% width over 300ms --ease-out on hover and focus-visible.
- Motion: prefers-reduced-motion: reduce — disable the hero scale and parallax, H1 rise, marquee (show a static centered quote), count-ups, type-in and layout animations (render at rest). Color and border hover changes stay. Use CSS @media in index.css plus Motion's useReducedMotion in components.
- Imagery: Real photography only, warm and natural, all six made for this template in one consistent style: deep green walls, warm cream light, brass details, leather and wood, documentary editorial look, fine grain. Never a face: interiors, tools, chairs, backs of heads. No readable text, logos or branded tools anywhere. Every photo uses object-fit cover with the object-position given under Assets, 10px corners, no shadow; photos on Cream get a 1px Rule outline. Captions below photos in Karla 13px Muted. Drawn in code: the favicon, the wordmark (type only), duration and price pills, the confirmation card layout, dividers and the stepper. Icons: lucide-react only, stroke width 1.5.

## Pages & Sections
### Home (`index.html`)
1. **Content file and copy rules** — All copy below is written for the default values ([BRAND] = The Juniper Chair, [CITY] = Chicago, Illinois, [STREET] = Rowan Street, [FOUNDED] = 2019). Put every string, number and list in src/content.ts and render from it; both pages share one content file. If any variable value differs, rewrite every headline, service, price, stylist bio, review, hour, policy and FAQ answer so it is specific and believable for the new shop, keeping the same structure, counts and lengths. All people, services, streets and figures are invented. Shop facts used throughout: address 118 [STREET], [CITY]; phone (312) 555-0166; email hello@ + emailDomain ([email protected]); 6 chairs, 4 stylists; walk-ins welcome when a chair is free, bookings skip the wait; kids under 12 welcome before 4pm. Hours (client clock): Wed–Fri 9:00–19:00, Sat 9:00–17:00, Sun 10:00–15:00, Mon–Tue closed. Prices: cuts from $26, color from $55, shaves from $38. Stylist working days: Nadia Tue–Sat (note: the shop is closed Tue, so Nadia effectively works Wed–Sat — keep her listed days within opening days), Marcus Wed–Sun, June Wed–Sun, Theo Thu–Sun.
   Layout: n/a
   Motion: n/a
2. **Header (all pages)** — Left: wordmark '[BRAND]' (Bodoni 600, 21px, letter-spacing 0.04em) with a second line 'Salon & barbershop · Est. [FOUNDED]' (Karla 700 10px uppercase letter-spacing 0.22em), linking to ./index.html. Center nav (Karla 500 14px): 'Services' → ./index.html#services, 'Stylists' → ./index.html#stylists, 'Gallery' → ./index.html#gallery, 'Policies' → ./booking.html#policies. Current page link has a 1px underline. Right: primary button 'Book a chair' → ./booking.html (always visible) and the hamburger (Lucide Menu 22px, under 820px) opening MobileMenu: full-screen Oat panel, links at Bodoni 500 30px, the book button at the bottom, close icon top right.
   Layout: Fixed top, height 76px, container-aligned. Over the home hero only it starts transparent with Card text. Mobile menu covers the viewport with a 300ms slide from the right.
3. **Hero** — Background photo hero-salon.webp. Eyebrow (Card, line in Card at 60%): '[STREET] · [CITY] · Est. [FOUNDED]'. H1 in two lines: 'Good hair' / '*is a habit.*' Lead (Card at 88%, max-width 560px): 'Four stylists, six chairs and zero attitude at [BRAND]. Fades, color, long hair and hot-towel shaves on [STREET] since [FOUNDED] — booked online in under a minute or walked into when the sign says so.' Buttons: primary 'Book a chair' → ./booking.html (Lucide CalendarDays 16px), secondary on-photo 'See services & prices' → ./index.html#services. Bottom ledger of four items, each a big value (Bodoni 500 30px Card) over a label (Karla 700 12px uppercase letter-spacing 0.12em Card at 70%): '6 chairs' / 'Walk-ins when free'; 'From $26' / 'Kids'' cuts'; '45 min' / 'Signature cut'; 'Wed–Sun' / 'Closed Mon & Tue'. The hero text sits on a darkened overlay strong enough for Bone copy over the mirror scene; an open-now pill with the next free slot ('Open now - next chair 3:30') sits beside the CTA; on mobile the hero pads down by the header height so the H1 never overlaps the wordmark, and the header CTA never wraps.
   Layout: Height 92svh, min-height 640px, max-height 980px. Image object-fit cover, object-position 50% 58%. Overlay linear-gradient(180deg, rgba(36,56,43,0.52) 0%, rgba(36,56,43,0.14) 35%, rgba(36,56,43,0.28) 60%, rgba(36,56,43,0.82) 100%). Content block anchored bottom-left inside the container, 120px above the ledger, max-width 780px. Ledger: absolute bottom 0, full container width, a 1px Card/25% top border, 4 equal columns with 1px Card/25% vertical dividers, padding 24px 0 32px. Under 768px: H1 at 44px, ledger becomes a 2x2 grid, buttons stack full-width.
4. **Services** — Section id='services'. Eyebrow 'The price list' and H2 'Every service, timed and priced. No surprises at the till.' Four groups in a 2x2 grid, each a Card with a group title and rows (name Karla 700 16px, one-line description Karla 15px Muted, duration pill '45 min' and Bodoni 600 20px Brass price at the right): Group 'Cuts & fades': 'Signature Cut' — 'Consultation, cut, wash and style' — 45 min — $38; 'Skin Fade' — 'Clippers to the skin, blended by hand' — 40 min — $36; 'Cut & Style, long hair' — 'Long-hair cut with a proper blow-dry' — 60 min — $52; 'Kids under 12' — 'First-cut photos encouraged' — 30 min — $26. Group 'Color': 'Root Touch-up' — 'Regrowth matched to the existing shade' — 60 min — $68; 'Full Color' — 'All-over single process' — 90 min — $110; 'Balayage' — 'Hand-painted, two sessions for big changes' — 150 min — $160; 'Gloss & Tone' — 'Shine and color correction between appointments' — 45 min — $55. Group 'Beard & shave': 'Beard Trim & Line-up' — 'Shape, edges, oil finish' — 25 min — $22; 'Hot-Towel Shave' — 'Straight razor, two lathers, one hot towel too many' — 40 min — $38; 'Beard Sculpt & Oil' — 'Full reshape with a treatment' — 35 min — $30. Group 'Treatments': 'Scalp Massage & Wash' — 'Fifteen minutes of quiet' — 25 min — $28; 'Deep Conditioning' — 'Repair mask under gentle heat' — 30 min — $32. Under the grid, one centered line (Karla 15px Muted): 'All prices include tax. Oat milk is for the coffee place next door; we have tea and water.'
   Layout: Cream background. Header block left-aligned max-width 640px. 2x2 grid of group cards with 24px gaps, cards 10px radius, 1px Rule border, 28px padding; rows separated by 1px Rule dividers with 14px vertical padding. Row hover: 'Book' pill fades in at the right edge linking to ./booking.html?service=<id>. Under 900px the grid becomes one column.
5. **Stylists** — Section id='stylists'. Eyebrow 'The chairs' and H2 'Pick a chair, keep a stylist.' Four cards, each with a working portrait (4:3) of that stylist — an AI-generated fictional person, mid-cut, scissors in hand, warm shop light: 1) stylist-nadia.webp, alt 'Nadia coloring hair, a fictional stylist' — Nadia Okafor — 'Color Lead, Chair 1' — 'Twelve years of color, the last four at [BRAND]. Balayage, color correction and the patience to talk you out of a decision you''ll regret.' — pills 'Balayage · Corrections · Gloss' — 'Works Wed–Sat' — 'Next: Wed 9:00am'. 2) stylist-marcus.webp, alt 'Marcus fading a haircut, a fictional stylist' — Marcus Reyes — 'Fade Specialist, Chair 2' — 'Skin fades sharp enough to schedule around. Also the shop''s fastest beard line-up and slowest talker.' — pills 'Skin fades · Beard · Kids'' cuts' — 'Works Wed–Sun' — 'Next: Wed 9:00am'. 3) stylist-june.webp, alt 'June braiding long hair, a fictional stylist' — June Park — 'Long Hair & Styling, Chair 3' — 'Long-hair cuts, braids and updos that survive a whole wedding day. Believes in trims before splits become stories.' — pills 'Long hair · Braids · Updos' — 'Works Wed–Sun' — 'Next: Wed 10:30am'. 4) stylist-theo.webp, alt 'Theo at the hot-towel station, a fictional barber' — Theo Brandt — 'Classic Barber, Chair 4' — 'Scissor cuts and hot-towel shaves, learned the trade the slow way. Keeps the kettle for the towels, not the tea.' — pills 'Scissor cuts · Shaves · Grey blending' — 'Works Thu–Sun' — 'Next: Thu 9:00am'. Every card links 'Book with [name]' → ./booking.html?stylist=<id>. Stylist cards use craft shots (no faces, no hands): Nadia - scissors mid-snip behind freshly cut copper waves; Marcus - clippers, brass comb and folded cloth on walnut; June - tint brush, amber-dye bowl and foil on a chair arm; Theo - straight razor on a strop beside a steamer. Consistent 3:4 framing, card heights match.
   Layout: Oat background. Four equal columns (24px gap) on desktop, 2x2 under 1024px, one column under 640px. Card: working portrait top (aspect 4:3, 10px radius, 1px Rule outline), name Bodoni 600 24px, role line Karla 700 12px uppercase Brass, bio Karla 15px / 1.6, pills, working days and next-free line, full-width small secondary button.
6. **Gallery** — Section id='gallery'. Eyebrow 'Recent work' and H2 'The backs of heads we''re proud of.' Six-photo mosaic with captions: gallery-fade.webp 'Skin fade, three weeks in — still holding'; stylist-june.webp (reused) 'June mid-braid, wedding #14 of the season'; gallery-fade.webp (reused, different crop via object-position) 'Low fade with a soft part'; tools-color.webp (reused) 'Nadia''s Tuesday kit'; shave-tools.webp (reused) 'Theo''s tools, sharpened weekly'; hero-salon.webp (reused) 'The room, Saturday 9am'. Caption under each in Karla 13px Muted. The masonry grid aligns flush with even gaps; the fade shot is a back-of-head nape-line close-up, no mirror face visible; no photo repeats one used in the stylist cards; all gallery shots share the deep-green-and-brass grade.
   Layout: Cream background. 6-column mosaic on desktop: items 1 and 4 span 2 rows (aspect 3:4), the others aspect 1:1, 12px gaps, 10px radius. Under 900px: 2 columns with aspect 1:1 (no tall items). Clicking opens the Lightbox (shared with the strip rules in Motion).
7. **Marquee** — Forest band, one continuous scrolling strip of short pull quotes in Bodoni 500 italic 22px Brass Light, separated by 4-point stars (drawn as SVG): '"Best fade in three blocks."' · '"Nadia is a color genius."' · '"Booked at midnight, cut at nine."' · '"The shave is a religious experience."' · '"They remember how you take your tea."' · '"Zero small talk, if you ask nicely."'
   Layout: Full-width band, vertical padding 40px, overflow hidden, content duplicated for a seamless loop. 1px Brass Light top and bottom borders at 30% opacity.
8. **Reviews** — Eyebrow 'Word of mouth' and H2 'The regulars, quoted.' Three quote cards on Card: 1) '"Marcus spent forty minutes on a fade most shops rush in ten. Worth every dollar, and the queue agrees."' — Devon R., regular since 2021. 2) '"Nadia fixed a box-dye disaster over two sessions and never once made me feel bad about it. That''s the whole review."' — Priya S., balayage convert. 3) '"Booked online at 11pm for a Saturday slot, walked in, sat down, no phone calls, no fuss. Why doesn''t everywhere work like this?"' — Tom W., cut every three weeks. Each card: large opening quote mark (Bodoni 600 48px Brass), quote in Bodoni 500 21px / 1.4, attribution in Karla 600 13px Muted.
   Layout: Oat background. Three equal columns, 24px gap, cards 10px radius with 1px Rule border, 32px padding, middle card offset 24px down on desktop. Under 860px: stacked, no offset.
9. **Hours and visit** — Left: 'This week' card (Card) with OpenNowPill and the hours table: Wed–Fri 9:00–19:00 / Sat 9:00–17:00 / Sun 10:00–15:00 / Mon–Tue Closed, current day row bold with a 2px Brass left border; caption 'Walk-ins welcome whenever a chair is free; bookings skip the queue.' Right: 'Getting here' three rows with Lucide icons: TrainFront 'Transit' — 'Two blocks from the Mulberry stop; the Green Line drops you closer if the weather disagrees.'; Bike 'Bike' — 'Racks outside the flower shop next door.'; Car 'Car' — 'Metered on [STREET] until 6pm; the alley is for deliveries and dramatic exits.' Below both, the CtaBand on Forest: H2 in Card 'The chair is warmer than the weather.' with primary button 'Book a chair' → ./booking.html and caption Card at 55% 'Open Wed–Sun · From $22 · Under a minute to book'.
   Layout: Cream background. Hours card 5 cols, transport rows 6 cols offset 1 with 24px gaps, icons in 44px Oat rounded squares. CtaBand full-width below with vertical padding 96px, centered stack.
10. **Footer (all pages)** — Forest background. Four columns: 1) wordmark '[BRAND]' (Bodoni 600 22px Card) over 'Salon & barbershop · [STREET], [CITY]'. 2) 'Find us': address '118 [STREET]', '[CITY]', phone (312) 555-0166, hello@ + emailDomain. 3) 'Hours': the hours table condensed, caption 'Holiday hours taped to the door, in brass pen.' 4) 'Around the shop': 'Services' → ./index.html#services, 'Stylists' → ./index.html#stylists, 'Book a chair' → ./booking.html, 'Policies' → ./booking.html#policies. Bottom line under a 1px Card/15% rule: '© [BRAND]. Cut well, be kind.' Text in Card at 80%, links Card with .link-draw.
   Layout: Vertical padding 72px, 4 equal columns, bottom line 40px below the rule. Under 768px: two columns, then stacked.

### Book a chair (`booking.html`)
1. **Masthead** — Solid header from the start. Eyebrow 'Book a chair', H1 'Four questions, one chair.' Lead (max-width 620px): 'Pick a service, a stylist, a slot and a name. Nothing is sent anywhere — your booking lives in this browser, and the reference card at the end is what you show at the door.'
   Layout: Cream background, container-aligned, vertical padding 136px top (header height) + 48px. H1 max-width 720px. Masthead bottom border 1px Rule, 64px below text.
2. **Booking flow** — Step 1 'Service' — the four groups as collapsible cards (all expanded on desktop, accordions on mobile) with rows: name, duration pill, Brass price, radio selection filling the row Oat with a Brass left rule. Step 2 'Stylist' — four compact cards (small 56px round portrait, name, role, pills, working days) plus a 'First available' card at the front ('We pick whoever is free — usually the right call for cuts'). Step 3 'Day & time' — a horizontal strip of the next 14 bookable days (closed weekdays and the chosen stylist's off-days disabled at 45% opacity with a caption explaining why); below it 30-minute time slots for the chosen day from opening to last slot that still fits the service duration, occupied slots (a fixed seeded pattern from the date, stable across reloads) shown struck through and unclickable. Step 4 'Your details' — name input (required), phone input (optional, placeholder 'Only if you want a reminder text'), a checkbox 'I''ve read the 24-hour cancellation policy' (required). Sticky summary card on the right at every step: service name, duration, stylist, day and time, Bodoni 32px total price (tabular-nums), primary 'Confirm booking' button (disabled until step 4 is valid), caption 'Nothing is charged — pay at the chair.' On mobile the stepper shows step numbers with only the active label (or scrolls horizontally); no label is clipped at the edge; the header stays one row.
   Layout: Oat background. Two columns: the current step (7 cols) and the sticky summary (4 cols, offset 1, sticky top 100px). Stepper above: four numbered dots with labels connected by 1px Rule lines, done steps filled Sage, current Brass. Step changes slide and crossfade (Motion). Query params preselect: ?service=<id>, ?stylist=<id>. Under 900px: single column, summary as a fixed bottom bar (service · slot · price · Confirm button).
3. **Confirmation** — Replaces the flow when confirmed. Card centered at max-width 560px: Forest header strip with '[BRAND]' in Card (Bodoni 600 20px) and the reference number (brandInitials + '-' + four digits, e.g. 'TJC-0247') typing in character by character in Brass Light; body on Card: 'Booked' check circle (Sage, stroke draws), 'for [NAME]' in Bodoni 30px, detail rows (Service + duration · Stylist + chair · Day · Time · Price), a dashed-border section 'Show this at the door' with a code-drawn barcode (random-height Ink bars from a seeded hash of the reference), the line 'Pay at the chair · Running late? Call (312) 555-0166', buttons 'Make another booking' (clears the stored booking) and 'Back to the shop' → ./index.html. Caption below the card: 'Saved in this browser — take a screenshot if you''re the careful kind.' The confirmation persists in localStorage.
   Layout: Oat background, centered stack. Card 10px radius, 1px Rule border, overflow hidden for the header strip. Barcode 100% width x 56px. Buttons side by side, stacked under 480px.
4. **Policies and FAQ** — Section id='policies'. Eyebrow 'The fine print, in large print' and H2 'Policies & questions.' Four accordions: 1) 'Cancellations' — 'Free until 24 hours before. Inside 24 hours we ask for half the service price — the chair sat empty, and the chair knows.' 2) 'Walk-ins' — 'Always welcome when a chair is free; bookings skip the wait. Saturdays the wait starts at the door around 10, so book.' 3) 'Kids & first cuts' — 'Under-12s before 4pm, Wednesday to Friday. First-cut certificates are real and frankly lovely.' 4) 'Products & allergies' — 'We use two product lines, both nut-free — tell your stylist about allergies, dye sensitivities and anything else; patch tests are free and take 15 minutes.'
   Layout: Cream background, accordion rows with 1px Rule dividers, question in Bodoni 600 20px, answer Karla 16px / 1.7 max-width 62ch, Plus icon right. Container max-width 760px. Above the Footer.

## Assets
- image: Template photos made for this design (free to redistribute with the template). Download each with curl -L -o into public/images/: https://media.vibefather.ai/assets/56a9ef1d-16e1-4f84-b6f9-abc889e23747/hero-salon.webp → public/images/hero-salon.webp (Home hero, Gallery; object-position 50% 58%; alt 'Salon interior with deep green walls, brass-framed mirrors and leather chairs'); https://media.vibefather.ai/assets/56a9ef1d-16e1-4f84-b6f9-abc889e23747/stylist-nadia.webp → public/images/stylist-nadia.webp (Stylist card Nadia, Gallery; alt 'Nadia coloring hair, a fictional stylist'); https://media.vibefather.ai/assets/56a9ef1d-16e1-4f84-b6f9-abc889e23747/stylist-marcus.webp → public/images/stylist-marcus.webp (Stylist card Marcus, Gallery; alt 'Marcus fading a haircut, a fictional stylist'); https://media.vibefather.ai/assets/56a9ef1d-16e1-4f84-b6f9-abc889e23747/stylist-june.webp → public/images/stylist-june.webp (Stylist card June, Gallery; alt 'June braiding long hair, a fictional stylist'); https://media.vibefather.ai/assets/56a9ef1d-16e1-4f84-b6f9-abc889e23747/stylist-theo.webp → public/images/stylist-theo.webp (Stylist card Theo, Gallery; alt 'Theo at the hot-towel station, a fictional barber'); https://media.vibefather.ai/assets/56a9ef1d-16e1-4f84-b6f9-abc889e23747/gallery-fade.webp → public/images/gallery-fade.webp (Gallery; alt 'Back of a head with a crisp skin fade, three weeks grown'). Six files in total.
- image: Write ASSETS.md at the project root with a table: path, source URL, author, license — one row for each photo (source 'media.vibefather.ai, made for this template', author 'VibeFather', license 'Distributed with the template'), plus rows for @fontsource-variable/bodoni-moda and @fontsource/karla (OFL-1.1) and lucide-react (ISC). Never replace photos with stock sites (Unsplash, Pexels, Pixabay) or images with faces, hands or readable branding.
- icon: lucide-react only: Scissors, CalendarDays, Menu, X, Plus, Check, ArrowRight, ArrowLeft, MapPin, Phone, Mail, Clock, TrainFront, Bike, Car, Star, Sparkles, User, CreditCard. Stroke width 1.5, 16–24px, color inherits. public/favicon.svg hand-written: viewBox 0 0 32 32, a 32x32 rect rx 8 filled #24382B, a Card (#FFFEF9) pair of scissors glyph drawn with two circles and two crossing lines. No other logo: the salon name is always set as type.
- font: Install @fontsource-variable/bodoni-moda and @fontsource/karla and import them in src/fonts.ts as listed in Setup. Vite bundles the woff2 files into dist/.
- image: Drawn in code: duration and price pills, the open-now dot, the stepper dots, the confirmation barcode, the check circle, the marquee star separators, dividers and the favicon. No other bitmaps, no illustrations, no map tiles.

## Don'ts
- No backend, no form submission, no fetch to any API: the booking, its confirmation and every choice live in the browser only (localStorage); nothing is charged or sent.
- Every internal link points to a file name (./index.html, ./booking.html, ./booking.html#policies, ./index.html#services) — never to a bare directory.
- Self-host everything: fonts through Fontsource, icons as npm packages or inline SVG, images downloaded into public/images/. No CDN <script> or <link> tags, no Google Fonts, no analytics.
- Production build: minified, no source maps, no src/ copied into dist/, single command npm run build, output in dist/ under 10 MB.
- No real salon or chain names, no real addresses, no map tiles or embeds, the stylist portraits are AI-generated fictional people used as placeholders; elsewhere no faces, hands or readable branding.
- No dates or years anywhere except 'Est. [FOUNDED]' and review attributions; the site never shows today's date.
- Never mention VibeFather or any other brand inside the site except the optional credit footer link; all copy is invented US English.
- Respect prefers-reduced-motion everywhere: disable parallax, marquee, count-ups, type-in and layout animations; keep color/border hovers.
- The site must work at 1440x900 desktop and 390x844 mobile with zero console errors; the booking flow works with keyboard only, every step has a visible focus ring, and booking.html opened directly starts at step 1 with nothing preselected unless a query param says so.
- Keep copy concrete: no lorem, no filler, no superlatives without evidence; prices, durations and hours always come from src/content.ts.

---
— Prompt by VibeFather · https://vibefather.ai/t/hair-salon-and-barbershop-website · CC BY-NC 4.0 (non-commercial; commercial use needs a VibeFather Pro license) · Request removal: https://vibefather.ai/takedown

Free for personal projects under CC BY-NC 4.0. Commercial use comes with Pro.

DESIGN.md

The design system on its own — colors, type, spacing and motion. Apply this look to a project you already have.

---
name: "Hair Salon and Barbershop Website"
colors:
  cream: "#F7F2E7"
  oat: "#EFE7D5"
  card: "#FFFEF9"
  forest: "#24382B"
  forest_soft: "#2F4A39"
  ink: "#23201A"
  muted: "#7C7466"
  rule: "#DDD2BC"
  brass: "#A87B2F"
  brass_deep: "#8C6624"
  brass_light: "#D9B36A"
  sage: "#7C8B6F"
typography:
  display_h1_h2_h3_service_names_stylist_names_prices_in_large_sizes_the_wordmark_booking_reference: { family: "Bodoni Moda", weights: [500, 600] }
  text_and_ui_body_eyebrows_buttons_nav_form_fields_review_attributions_numbers_font_variant_numeric_tabular_nums_for_prices_durations_and_hours: { family: "Karla", weights: [400, 500, 700] }
spacing: "8px base unit. Container max-width 1200px, centered, side padding 48px desktop, 32px tablet, 20px under 640px. 12-column grid, column gap 24px. Section vertical padding 112px desktop, 80px under 1024px, 64px under 640px. Type scale: H1 clamp(44px, 6.2vw, 92px) Bodoni Moda 500, line-height 1.02, letter-spacing -0.01em; H2 clamp(32px, 4vw, 56px) Bodoni 500, line-height 1.06; H3 25px Bodoni 600, line-height 1.2; italic phrases in headings use Bodoni 500 italic (marked with *asterisks* in content.ts and rendered as <em>); lead paragraph Karla 400 18px / 1.65 Ink at 85% opacity; body Karla 400 16px / 1.7 (max-width 62ch); eyebrow Karla 700 12px uppercase letter-spacing 0.16em Muted, preceded by a 24px x 1px Brass line with 12px gap; caption Karla 400 13px Muted. Buttons: height 50px, padding 0 26px, Karla 700 14px uppercase letter-spacing 0.1em. Primary: Brass fill, Card text. Secondary: 1px Ink border, Ink text, transparent (on photos: 1px Card border, Card text). Small buttons 38px high, padding 0 16px, 12px text. Inputs: height 50px, 1px Rule border, Card fill, 16px Karla, focus border Brass. Focus ring everywhere: 2px Brass outline with 3px offset."
radius: "10px on cards, buttons, inputs, chips, photos and the confirmation. 999px only for duration and price pills, the open-now dot, step dots and slot chips."
---

# Hair Salon and Barbershop Website — Design System

Apply this design system to an existing project. It describes the look and feel only; for the full site, use prompt.md.

## Colors
- **Cream** `#F7F2E7` — Page background on both pages, input fill.
- **Oat** `#EFE7D5` — Alternate section background (Services, Gallery), hover fill for rows and chips, image wells.
- **Card** `#FFFEF9` — Card surfaces (stylist cards, review cards, confirmation), light modal background.
- **Forest** `#24382B` — Hero overlay base, Marquee band, footer, confirmation header, photo overlays (as rgba(36,56,43,x)).
- **Forest Soft** `#2F4A39` — Hover surface on Forest, slot chip selected fill, deep section borders.
- **Ink** `#23201A` — Headlines and body text on light backgrounds, primary outline buttons, icon color.
- **Muted** `#7C7466` — Secondary text, captions, eyebrows, disabled days and slots (at 45% opacity).
- **Rule** `#DDD2BC` — 1px hairlines, card borders, input borders, dividers.
- **Brass** `#A87B2F` — The one accent: primary buttons, prices, duration pills, selected step dots, focus ring, marquee text on Forest.
- **Brass Deep** `#8C6624` — Primary button hover and pressed state.
- **Brass Light** `#D9B36A` — Brass replacement for text and lines on Forest backgrounds (prices, active borders).
- **Sage** `#7C8B6F` — Quiet second color: open-now dot and text, 'walk-ins welcome' tag, booking success check.

## Typography
- **Display: H1, H2, H3, service names, stylist names, prices in large sizes, the wordmark, booking reference** — Bodoni Moda (OFL), weights 500/600. Self-host the font files.
- **Text and UI: body, eyebrows, buttons, nav, form fields, review attributions, numbers (font-variant-numeric tabular-nums for prices, durations and hours)** — Karla (OFL), weights 400/500/700. Self-host the font files.

## Spacing & shape
- Spacing: 8px base unit. Container max-width 1200px, centered, side padding 48px desktop, 32px tablet, 20px under 640px. 12-column grid, column gap 24px. Section vertical padding 112px desktop, 80px under 1024px, 64px under 640px. Type scale: H1 clamp(44px, 6.2vw, 92px) Bodoni Moda 500, line-height 1.02, letter-spacing -0.01em; H2 clamp(32px, 4vw, 56px) Bodoni 500, line-height 1.06; H3 25px Bodoni 600, line-height 1.2; italic phrases in headings use Bodoni 500 italic (marked with *asterisks* in content.ts and rendered as <em>); lead paragraph Karla 400 18px / 1.65 Ink at 85% opacity; body Karla 400 16px / 1.7 (max-width 62ch); eyebrow Karla 700 12px uppercase letter-spacing 0.16em Muted, preceded by a 24px x 1px Brass line with 12px gap; caption Karla 400 13px Muted. Buttons: height 50px, padding 0 26px, Karla 700 14px uppercase letter-spacing 0.1em. Primary: Brass fill, Card text. Secondary: 1px Ink border, Ink text, transparent (on photos: 1px Card border, Card text). Small buttons 38px high, padding 0 16px, 12px text. Inputs: height 50px, 1px Rule border, Card fill, 16px Karla, focus border Brass. Focus ring everywhere: 2px Brass outline with 3px offset.
- Radius: 10px on cards, buttons, inputs, chips, photos and the confirmation. 999px only for duration and price pills, the open-now dot, step dots and slot chips.

## Motion
- Easing: --ease-out cubic-bezier(0.22, 0.61, 0.36, 1) for reveals and steps; --ease-io cubic-bezier(0.65, 0, 0.35, 1) for accordions, tab and step switches. Nothing bounces.
- Reveal: blocks wrapped in <Reveal> start at opacity 0, translateY(18px) and animate to rest over 700ms --ease-out when 15% visible (Motion whileInView, viewport once). Siblings in a list stagger by 80ms (index prop).
- Hero photo: on load the image scales from 1.07 to 1.0 over 2200ms --ease-out; while scrolling, the image translates 0 to 10% of its height as the hero scrolls out (Motion useScroll on the hero, offset ['start start', 'end start']), and the headline block fades to opacity 0.25 and moves -32px over the same range. H1 lines rise from translateY(26px) and opacity 0 with 120ms stagger, starting 150ms after load.
- Marquee: one continuous strip of pull quotes on Forest scrolling left at a constant 40px/s, pausing on hover, duplicated content for a seamless loop (CSS animation; disabled under reduced motion).
- Service rows: hover fills Oat at 60% and slides a 24px x 2px Brass rule in from the left over 250ms; the 'Book' affordance fades in at the right edge.
- Stylist cards: on hover the photo scales to 1.04 over 700ms --ease-out and the card border turns Ink; the 'next free slot' pill crossfades to 'Book with [name]' linking to ./booking.html?stylist=<id> (the booking page preselects that stylist; opened without a query it starts unselected — never an error).
- Gallery: photos rise 12px and lose their Oat frame on hover; clicking opens the Lightbox (Forest overlay at 85%, photo at max 80vh, caption in Card, X closes, left/right arrows navigate, Escape closes) with a 240ms fade and 16px scale settle.
- Booking steps: step changes crossfade with a 20px horizontal slide, 300ms --ease-io (AnimatePresence mode 'wait'). Selecting a service, stylist, day or slot fills the chip over 150ms; the sticky summary price counts to the new total over 400ms. The confirmation card flips in (rotateX -8deg to 0, opacity 0 to 1, 420ms --ease-out) and the reference number types character by character over 700ms.
- Hours: the open-now dot pulses (scale 1 to 1.25, 1600ms loop) only while open.
- Header: transparent over the photo hero with Card text; after 40px of scroll it gets Cream at 94% opacity, backdrop-filter blur(10px), Ink text and a 1px Rule bottom border, 250ms transition. On booking.html it is solid from the start.
- Links: .link-draw grows a 1px currentColor underline from 0% to 100% width over 300ms --ease-out on hover and focus-visible.
- prefers-reduced-motion: reduce — disable the hero scale and parallax, H1 rise, marquee (show a static centered quote), count-ups, type-in and layout animations (render at rest). Color and border hover changes stay. Use CSS @media in index.css plus Motion's useReducedMotion in components.

## Imagery
Real photography only, warm and natural, all six made for this template in one consistent style: deep green walls, warm cream light, brass details, leather and wood, documentary editorial look, fine grain. Never a face: interiors, tools, chairs, backs of heads. No readable text, logos or branded tools anywhere. Every photo uses object-fit cover with the object-position given under Assets, 10px corners, no shadow; photos on Cream get a 1px Rule outline. Captions below photos in Karla 13px Muted. Drawn in code: the favicon, the wordmark (type only), duration and price pills, the confirmation card layout, dividers and the stepper. Icons: lucide-react only, stroke width 1.5.

## Tone
Confident, warm and unpretentious, like a stylist who has heard every hair confession. Short sentences, concrete numbers (minutes, dollars, chairs), no beauty-industry buzzwords, no exclamation marks.

---
— Prompt by VibeFather · https://vibefather.ai/t/hair-salon-and-barbershop-website · CC BY-NC 4.0 (non-commercial; commercial use needs a VibeFather Pro license) · Request removal: https://vibefather.ai/takedown