Tress
Tress is a site for a hair salon or studio: cuts, color and balayage, blow-dries, treatments and bridal hair. It also fits independent stylists, colorists and curly-hair specialists. It is soft and full of morning light: cream paper with patches of sunlight and the shadow of leaves, rosewood, sage and warm peach, tall arches like the studio’s window, and a soft serif whose italic is the second voice of every headline. The home page opens on a woman by a sunny window whose hair goes from freshly washed to blow-dried to soft, glossy waves as you scroll. The demo is a fictional studio, Wren & Willow, in Larkfield, Vermont.
Tress comes in two variants, chosen when you create the site: files/Sanity (static pages, any host)
and EmDash (--cms emdash, rendered on request on Cloudflare). The pages are the
same in both.
Pages and their content
Section titled “Pages and their content”| Page | Edit it in |
|---|---|
| Home | Pages → Home page (a list of sections) |
| Services, lookbook, stylists, journal, 404 | Pages → the page’s own entry |
| Prices (the menu and its switch, booking, gift cards and the bridal deposit, questions) | Pages → Prices page |
| Visit (opening hours, details, map, contact form, questions) | Pages → Visit page |
| Services (how long, upkeep, patch test, price menu lines, lovely for, what happens, aftercare, questions, photo) | Content → Services |
| Lookbook (service, stylist, time in the chair, upkeep, how to ask for it, photo, story) | Content → Lookbook |
| Stylists (level, role, what they love doing, experience, languages, services, portrait, biography) | Content → Stylists |
| Journal (hair-care notes; one can be for members only) | Content → Journal |
| Name, phone, address, currency, navigation, header button, profiles | Pages → Site settings |
| Buttons, labels, chat, emails, game texts | Content → Interface texts |
| Privacy, terms, booking policy | Pages → Legal |
In headlines, words wrapped in *stars* are set in rosewood italic, as in “Six ways to a *good hair
day*”. The opening’s headline has its two lines in separate fields.
The price menu
Section titled “The price menu”Salon prices depend on two things, so the menu has two switches: the hair’s length (short, shoulder, long) and the stylist (stylist, senior, creative director). Pick either and every price on the menu changes; the choice stays as the visitor moves between pages. It works without JavaScript (radio buttons and CSS).
- Each service has one or more lines on the menu (Content → Services → Price menu), each with a price for short, shoulder and long hair. Type numbers only; the currency symbol comes from Site settings.
- A line marked “Priced by level” adds the level’s amount, set once in Pages → Prices page → Price menu → Levels (e.g. senior +$15, creative director +$30). Lines that cost the same with anyone (a blow-dry, a bang trim) leave it off.
- Each stylist has a level, so you can see whose prices are whose; cards show the lowest price of a service (“From $70”), worked out for you.
Color services are marked “Patch test first”: their page says a free patch test is needed 48 hours before. The home page’s color section shows a fan of hair swatches that opens as you scroll; the shades and their names are in the section itself (Pages → Home page → Color shades). The AI chat never promises how a color will turn out and points people to the patch test.
Lookbook
Section titled “Lookbook”Each look has a tall photo, the service and the stylist, time in the chair, upkeep and “how to ask for it”: short phrases a client can bring to their consultation. Only share looks of your own clients, with their permission.
Booking and payments
Section titled “Booking and payments”The prices page books visits with Cal.com and sells gift cards and a bridal trial deposit as one-time payments (a Stripe Price id or a Lemon Squeezy variant on each). Without keys, the booking shows a preview of the calendar and the payment buttons explain how to switch them on.
The map
Section titled “The map”The visit page shows the streets round the studio, drawn from
OpenStreetMap in your colors, with a pin, your address and
directions links. It is part of the page (no embed, no cookies) and follows the theme and your accent.
To draw your own, put the studio’s coordinates in map.config.json and run npm run map -- fetch.
Integrations
Section titled “Integrations”Everything works without keys; each feature switches on with its variables in .env.example.
| Feature | Where | Service |
|---|---|---|
| Book a visit | Prices page, loads on click; a preview of the calendar until connected | Cal.com |
| Gift cards and the bridal deposit | Prices page (one-time prices) | Stripe or Lemon Squeezy |
| Hair-care letters by email | Home and journal pages, with a welcome email | Supabase or Mailchimp |
| Contact form | Visit page; messages go to the email in Site settings | Resend |
| AI chat | Every page; answers about services, prices, stylists and visiting | Claude or OpenAI |
| Members-only note | A journal post marked “Members only” | Clerk |
| Search | ⌘K or the search button: services, looks, stylists, notes, prices, visit | built in, or Algolia |
| Analytics | Every page; Google Analytics only after consent | Plausible, Umami, Google Analytics, PostHog |
| Photos | Served by Cloudinary when it is set | Cloudinary |
Sections
Section titled “Sections”The home page is built from these sections, in any order: Opening (three photos of one blow-dry, played as you scroll, with the three moments named along the bottom), Services (cards in tall arches), Color shades (the swatch fan), Your visit (a photo that changes as the steps scroll by), Lookbook, Stylists (portraits that come into focus), Notes from the chair (reviews), Journal, Hair-care letters and Closing call. With reduced motion everything stands still.
For the opening, use three photos of the same shot (same framing and light), a moment apart; the first is the page’s first image and should keep the middle calm behind the text.
Colors and brand
Section titled “Colors and brand”src/content/singletons/brand.json (Pages → Brand in Keystatic) holds three colors: the accent
(rosewood: buttons, links, marks), the second (sage: soft fills) and the third (warm peach:
the sunlight behind every page and the glows). Buttons are deepened until their cream text is
readable and links are adjusted until they read on the paper; a color that can’t get there stops the
build with a message. Brand also sets the fonts and the corners. The site opens in its day theme
whatever the visitor’s system prefers; the toggle switches to the evening, a warm mauve dusk, and the
browser remembers the choice.
Languages
Section titled “Languages”Tress is ready for more languages: see Languages.
The 404 page
Section titled “The 404 page”A small game, Comb through: hold (the board, the button or Space) to sweep a comb down a long strand of hair; let go and it glides on slowly. Knots drift up and down the strand: pass them gently or the comb snags. Comb as many strands as you can in 45 seconds; each strand has more knots, and three snags end it. The best score is kept in the visitor’s browser.
AI assistants and search engines
Section titled “AI assistants and search engines”AGENTS.mddescribes the project for coding assistants (Claude Code reads it throughCLAUDE.md, Cursor through.cursor/rules/)./llms.txtlists your pages, services with their prices, looks, stylists and notes for AI assistants, in every language.- Pages carry JSON-LD: the studio as a
HairSalonwith its opening hours, services asServices with their lowest price, services and gift cards as offers, stylists asPersons, looks asCreativeWorks, questions as aFAQPage, and each note as aBlogPosting.
Commands
Section titled “Commands”npm run dev # local site, editor at /keystaticnpm run build # production buildnpm run map -- fetch # draw the map round map.config.json from OpenStreetMapnpm run cms -- sanity schema # move content to Sanity (see Content → Sanity)In the EmDash variant: npm run dev, npm run deploy (Cloudflare) and npm run emdash:schema
(after changing the content model).
