Skip to content

Fern

Fern is a site for a veterinary clinic: wellness exams, vaccinations, dental care, surgery, urgent care and exotic pets. It also fits animal hospitals, mobile vets, cat clinics and exotic pet practices. It is gentle and green: the home page opens on a short film of a golden retriever and a tabby cat resting under an ancient oak in an enchanted meadow at sunrise, a fox and a deer in the ferns, with the headline in the bright sky on the right. Behind everything is pale fern paper in dappled morning light, with dandelion seeds by day and fireflies at night. The demo is a fictional clinic, Fernbrook, in Portland, Oregon.

Fern 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)
Services, happy tails, team, journal, 404 Pages → the page’s own entry
Pricing (wellness plans, small print, booking, the new patient exam and gift card, questions) Pages → Pricing page
Visit (opening hours, details, map, contact form, questions) Pages → Contact page
Services (category, price, time, who it is for, when to come, what we check, what happens, questions, photo) Content → Services
Happy tails (pet, species, breed, age, date, service, vet, story, photo) Content → Stories
Team (role, special interests, pets at home, experience, languages, services, portrait, biography) Content → Vets
Journal (guides; one can be for clients only) Content → Journal
Name, phone, address, currency, navigation, header button, profiles Pages → Site settings
Buttons, labels, species names, chat, emails, game texts Content → Interface texts
Privacy, terms, appointment policy Pages → Legal

In headlines, words wrapped in *stars* are set in italic in the accent (peach at night), as in “See you *at Fernbrook*”. The opening’s headline has its two lines in separate fields.

The opening plays a short muted film across the whole window (Pages → Home page → Opening → Film, a file in public/, e.g. /media/hero.mp4), with its first frame as the photo. The photo shows at once; the film is fetched after the page has loaded and fades in when it plays, so the page stays fast. With reduced motion or a data saver the photo stays. Keep the upper right of the picture bright and calm: the headline sits there on a veil of morning light, in the day’s ink in both themes.

To make your own: film a slow, gentle shot (or generate one), 10 to 20 seconds, then export it about 1280 pixels wide as H.264 MP4 without sound, ideally under 2 MB. A clip played forward and then backward loops without a cut. Replace the photo with its first frame.

Urgent care, Who’s visiting? and the life stages

Section titled “Urgent care, Who’s visiting? and the life stages”
  • Urgent care is a calm band right under the opening with a button that dials the clinic’s phone (Site settings → Phone).
  • Who’s visiting? (Pages → Home page) shows a round photo for each kind of animal you see. Pick one and its card opens: what to expect, what to bring to the first visit and the services it usually needs. It works without JavaScript.
  • Care through the years sets two to five life stages beside a fern frond that draws itself and unfurls its leaflets as it scrolls into view.

Each story has a page with the patient’s photo, species, breed, age, the care and the vet, and the story itself. On the home page and the stories page they look like instant photos pinned at an angle; the stories page filters by species.

The pricing page holds the monthly wellness plans, booking with Cal.com, a new patient exam paid ahead and a gift card. Each plan takes a monthly Stripe Price id (or a Lemon Squeezy subscription variant), the other options one-time ones. Without keys, the booking shows a preview of the calendar and the payment buttons explain how to switch them on.

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.

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

Feature Where Service
Book a visit Pricing page, loads on click; a preview of the calendar until connected Cal.com
Wellness plans, new patient exam, gift card Pricing page (monthly and one-time prices) Stripe or Lemon Squeezy
Newsletter 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, plans and visiting, never diagnoses and asks worried owners to call Claude or OpenAI
Clients-only guide A journal post marked “Members only” Clerk
Search ⌘K or the search button: services, happy tails, team, guides, pricing, 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

The home page is built from these sections, in any order: Opening (the film, the headline, the buttons and up to three numbers of proof), Urgent care, Who’s visiting?, Care through the years, Services, Wellness plans, Happy tails, Team (portraits with an ID tag), Reviews, Questions, Journal, Newsletter and Closing call. With reduced motion everything stands still.

src/content/singletons/brand.json (Pages → Brand in Keystatic) holds three colors: the accent (forest green: the button, links, marked words, and the evening itself), the second (fern: soft fills, the fronds and the paper by day) and the third (peach: morning light, the urgent button and badges). Buttons are deepened until their cream text is readable and links are adjusted until they read on the page; a color that can’t get there stops the build with a message. Brand also sets the corners and two fonts: one for headings (Lora, Fraunces, Playfair Display or Source Serif 4) and one for text (Nunito Sans, Figtree, DM Sans or Manrope). The site opens in its day theme; the toggle switches to the night, and the browser remembers the choice.

Fern is ready for more languages: see Languages.

A small game, Fetch: bounce a tennis ball off the paddle (mouse, finger or arrow keys) and knock loose the biscuits hidden in the hedge. Where the ball meets the paddle sets its angle. Three balls and three yards, each with more rows and a quicker ball; the most biscuits found is kept as the best score in the visitor’s browser.

  • AGENTS.md describes the project and its design rules for coding assistants (Claude Code reads it through CLAUDE.md, Cursor through .cursor/rules/).
  • /llms.txt lists your pages, services, happy tails, team and guides for AI assistants, in every language.
  • Pages carry JSON-LD: the clinic as a VeterinaryCare with its hours and plans, services as Services, happy tails as Articles, the team as Persons, questions as a FAQPage, and each guide as a BlogPosting.
Terminal window
npm run dev # local site, editor at /keystatic
npm run build # production build
npm run map -- fetch # draw the map round map.config.json from OpenStreetMap
npm 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).