Velvet
Velvet is a site for an aesthetics clinic or med spa: skin treatments, facials and peels, laser and light, and injectables by licensed practitioners. It also fits skin clinics and beauty salons with treatment rooms. It is set on black-plum velvet with champagne gold and a high-contrast serif whose italic is the second voice of every headline. The home page opens on a full-bleed photo of a candle-lit treatment room, the clinic’s name set on an arch, a rating, a two-line headline and Book and Call; under it the silk horizon, a vast arc of light with a ribbon of silk lying along it, rises and ripples as you scroll. The demo is a fictional clinic, Maison Velour.
Velvet 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) |
| Treatments, results, practitioners, journal, 404 | Pages → the page’s own entry |
| Prices (the treatment menu, memberships, booking, deposit and gift cards, questions) | Pages → Prices page |
| Visit (opening hours, details, map, contact form, questions) | Pages → Visit page |
| Treatments (price from, how long, downtime, sessions, who it suits, when it isn’t for you, what happens, aftercare, questions, photo) | Content → Treatments |
| Results (treatment, practitioner, sessions, time between photos, consent note, before and after photos, story) | Content → Results |
| Practitioners (role, qualifications, registration, experience, languages, treatments, portrait, biography) | Content → Practitioners |
| Journal (skin guides; one can be for members only) | Content → Journal |
| Name, phone, address, registration note, navigation, header button, profiles | Pages → Site settings |
| Buttons, labels, chat, emails, game texts | Content → Interface texts |
| Privacy, terms, medical information | Pages → Legal |
In headlines, words wrapped in *stars* are set in gold italic, as in “Six treatments, *done
beautifully*”. The opening’s headline has its two lines in separate fields.
Careful with medical claims
Section titled “Careful with medical claims”The demo never promises a result: every result says that results vary and carries its consent note, and medical treatments (mark them with “Consultation first”) say they start with a consultation with a prescriber. Keep it that way with your own content, use only your own clients’ photos with their written consent, and check what your regulator allows you to advertise. The AI chat’s instructions (Content → Interface texts → AI chat) forbid medical advice and doses and send people to a consultation.
Treatments and the menu
Section titled “Treatments and the menu”Each treatment has a category; the treatment menu (on the home page and the prices page) groups them by it, like the carte of a grand hotel, and a gold hairline draws itself from each name to its price. Prices on the menu come from the treatments, so you change them in one place.
Results: before and after
Section titled “Results: before and after”Each result has a before and an after photo of the same view in the same light. On the cards (home page, results page, a treatment’s page) the after photo is drawn across behind a soft edge as the card scrolls past; on the result’s own page a slider lets visitors compare them (drag, or the arrow keys).
Prices, memberships, booking and payments
Section titled “Prices, memberships, booking and payments”The prices page holds the treatment menu, up to four monthly memberships (mark one as the most popular), booking a consultation with Cal.com, and a consultation deposit and gift cards. Memberships take a monthly Stripe Price id (or a Lemon Squeezy subscription variant), the deposit and gift cards a one-time one. 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 clinic, 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 clinic’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 consultation | Prices page, loads on click; a preview of the calendar until connected | Cal.com |
| Memberships, deposit and gift cards | Prices page (monthly and one-time prices) | Stripe or Lemon Squeezy |
| Skin notes 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 treatments, prices, practitioners and visiting, never gives medical advice | Claude or OpenAI |
| Members-only guide | A journal post marked “Members only” | Clerk |
| Search | ⌘K or the search button: treatments, results, practitioners, guides, 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 (the photo, the arch, the silk horizon and the treatments gliding by), Treatment menu, Practitioners (arched portraits revealed by a sweep of light), Results, Your visit (steps on a gold thread that draws itself), Numbers, Membership, Journal, Skin notes by email and Closing call (a photo in an arched doorway with a lamp’s glow). With reduced motion everything stands still.
Colors and brand
Section titled “Colors and brand”src/content/singletons/brand.json (Pages → Brand in Keystatic) holds three colors: the accent
(champagne gold: buttons, hairlines, the silk light), the second (dusty rose: soft fills) and the
third (plum: the hue of the velvet behind every page and of the deep bands). Buttons are lightened
until their dark text is readable, links are adjusted until they read on the velvet; 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 night theme whatever the visitor’s system prefers; the toggle switches to the day, ivory silk, and
the browser remembers the choice.
Languages
Section titled “Languages”Velvet is ready for more languages: see Languages.
The 404 page
Section titled “The 404 page”A small game, Tangled silk: threads of gold silk join pearls and cross each other. Drag the pearls (or Tab to one and move it with the arrow keys) until no two threads cross; crossing threads show dashed in rose. Three skeins of 6, 8 and 10 pearls, a minute each; points are 40 plus the seconds left for each skein, and 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, treatments, results, practitioners and guides for AI assistants, in every language.- Pages carry JSON-LD: the clinic as a
MedicalBusinessandBeautySalonwith its opening hours, treatments asMedicalProcedures with their price, treatments and memberships as offers, practitioners asPersons, results asCreativeWorks, questions as aFAQPage, and each guide 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).