Skip to content

Mint

Mint is a site for a dental practice: family dentists, dental clinics, orthodontists, hygienists and cosmetic or implant clinics. It is set edge to edge on a wall of fresh white tiles with mint grout, a sheen that slides across them as you scroll and bubbles of foam drifting up. On the home page a big grin runs right across the window and, as you scroll, a toothbrush with striped paste sweeps along the teeth: foam rises, the plaque goes, each tooth turns glossy white with a sparkle and the gums go a healthy pink, while a tube of toothpaste and a dental mirror float beside the headline. The visit page (and the home page) shows a map of the streets round the practice. The demo is a fictional practice, Brightwater Dental.

Mint 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.

Page Edit it in
Home Pages → Home page (a list of sections)
Treatments, team, journal, 404 Pages → the page’s own entry
Prices (first visit, price list, booking, deposits and gift cards, questions) Pages → Prices page
Visit (opening hours, emergencies, map, details, contact form, questions) Pages → Visit page
Treatments (summary, how long, price, steps, questions, photo) Content → Treatments
Team (role, qualifications, registration, languages, treatments, biography, portrait) Content → Team
Journal posts Content → Journal
Name, phone, address, registration statement, navigation, header button, profiles Pages → Site settings
Buttons, labels, chat, emails, game texts Content → Interface texts
Privacy, terms, complaints Pages → Legal

In headlines, words wrapped in *stars* get a swipe of fresh mint underneath and a little sparkle that twinkles, as in “Smile like you *mean it*”.

Each treatment has a line, a summary, how long it takes, the price as you’d show it (“£48”, “from £2,400”) with an optional note, the steps of how it goes, questions and a 3:2 photo. Its page shows who on the team does it: list the treatments by file name on each person. Team portraits are shown in round dental mirrors, so use a square photo with the face in the middle.

The prices page holds your first visit (its steps run along a strand of floss that pulls taut as you scroll, and the home page’s “Your first visit” section shows the same steps), the price list in groups, booking a check-up with Cal.com and the deposits and gift cards you take with Stripe: each option gets a one-time Price id. Without keys, the booking shows a preview of the calendar and the payment buttons explain how to switch them on.

The “Find us” map is drawn from OpenStreetMap in your colors: streets, green spaces, water and buildings round the practice, a pin with your name, your address and “Get directions” links to Google Maps, Apple Maps and OpenStreetMap. It is part of the page (no embed, no cookies, nothing loaded from another site) and follows the theme and your accent. To draw your own area, put the practice’s coordinates in map.config.json (center, and size in metres) and run pnpm map -- fetch: it downloads the area once and writes src/data/location-map.json. The map’s title and text are on the Visit page (and in the home page’s “Find us” section); the attribution link stays, as OpenStreetMap’s licence asks.

Everything works without keys; each feature switches on with its variables in .env.example.

Feature Where Service
Book a check-up Prices page, loads on click; a preview of the calendar until connected Cal.com
Deposits and gift cards Prices page (one-time Price ids) Stripe
Check-up reminders Home and journal pages, with a welcome email Supabase
Contact form Visit page; messages go to the email in Site settings Resend
AI chat On every page, answers about treatments, prices, the team and opening hours; never diagnoses Claude
Search ⌘K or the search button: treatments, team, journal, prices, visit built in, or Algolia
Analytics Every page; Google Analytics only after consent Plausible, Umami, Google Analytics

The home page is built from these sections, in any order: Brush up the smile (the opening), Treatments (cards with a stripe of toothpaste squeezed across the top), Your first visit (steps along the floss), The team (portraits in dental mirrors that tilt into place), Numbers, Emergency (a gum-pink band with the phone number), Journal, Check-up reminders, Find us (the map; its streets draw themselves and the pin drops in) and Closing call. With reduced motion, the smile stands clean and sparkling and nothing moves.

src/content/singletons/brand.json (Pages → Brand in Keystatic) holds three colors: the accent (deep mint: buttons, marks and the toothbrush), the second (gum pink) and the third (fresh mint: fills, the gums’ partner and the tile grout). Buttons are darkened until their white text is readable, the second and third are lightened until ink text on them is; a color that can’t get there stops the build with a message. Brand also sets the fonts (headings and text) and the corners. The site opens in the day theme even when the visitor’s system is dark; the toggle switches to the night, deep teal tiles under a soft lamp, and the browser remembers the choice.

Mint is ready for more languages: see Languages.

A small game, Brush up: plaque builds up on a row of teeth; scrub it away by dragging back and forth across a tooth, or pick a tooth with the arrow keys and scrub with the space bar. Three rounds of 15 seconds, the plaque coming faster each time; the best score is kept in the visitor’s browser.

  • AGENTS.md describes the project for coding assistants (Claude Code reads it through CLAUDE.md, Cursor through .cursor/rules/).
  • /llms.txt lists your pages, treatments, team and journal for AI assistants, in every language.
  • Pages carry JSON-LD: the practice as a Dentist, treatments as MedicalProcedures with their price, the team as Persons, your first visit as a HowTo, questions as a FAQPage, and each journal post as a BlogPosting.
Terminal window
pnpm dev # local site, editor at /keystatic
pnpm build # production build
pnpm map -- fetch # draw the map round map.config.json from OpenStreetMap
pnpm cms sanity schema|seed # 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).