Skip to content

Ember

Ember is a site for campsites, glamping sites, caravan parks and cabin stays, set like a national-park field guide: map paper with topographic contour lines, photos mounted as numbered plates, trail-blaze colors and round passport stamps. It has your stays (each with its own page and a deposit to hold the dates), the camp with an illustrated map whose trail draws itself, field notes and a visit page with directions.

Ember 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)
Stays, camp, field notes, visit, 404 Pages → the page’s own entry
Stays (pitches, tents, cabins) Content → Stays
Field notes Content → Field notes
Camp name, email, phone, place, coordinates, season, navigation, header button, profiles Pages → Site settings
Buttons, labels, chat, search, emails, game texts Content → Interface texts
Privacy, terms Pages → Legal

Stays marked Featured come first, then by Order. Each stay has a kind (“Tent pitch”, “Cabin”), a price as shown and what it is per, how many it sleeps, its size, where it is on the map, what’s there, an optional stamp (Badge), a color for its trail blaze and a photo. Field notes pick a color too.

In headlines, words wrapped in *stars* get a painted trail-blaze underline, as in “Pitch your tent where the *lake* begins”.

The camp page (Pages → Camp page → Camp map) holds an illustrated map of your camp (3:2, without text) and its places: each has a name, a line of text, a color and a position in percent from the left and top of the picture. The places are numbered in their order, shown as pins on the map and listed in the legend beside it, and a dashed trail walks from pin to pin. In browsers with scroll-driven animations the trail draws itself and the pins pop up as the map scrolls into view; other browsers, and visitors who prefer reduced motion, see the whole trail. It needs no JavaScript. The home page shows the same map with its Camp map section.

The camp page also has your story with a photo, numbers and the camp code (house rules).

Every stay page has a Hold your dates button. With a Stripe Price id on the stay (a one-time price: the deposit), it takes the deposit with Stripe Checkout; the guest then confirms the dates by email. If you already use a booking system, put its link in the stay’s Booking link and the button opens it instead. Fully booked hides the button.

An illustrated map of the area with a pin (placed in percent), your address and arrival details. Add your latitude and longitude (Pages → Visit page → Map) and the page also offers directions (Google Maps, in a new tab) and a live OpenStreetMap, which loads only when the visitor opens it. Below: a contact form and booking activities (canoe hire, a guided walk…) with Cal.com.

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

Feature Where Service
Deposits Stay pages: a button per stay with a Stripe Price id (one-time) Stripe
Contact form Visit page; messages go to the email in Site settings Resend
Book an activity Visit page, loads on click Cal.com
Newsletter Home and field notes pages, with a welcome email Supabase
Ask the warden Chat on every page, answers from your stays, questions, camp map and code, and field notes Claude
Search ⌘K or the search button: stays, field notes, camp, visit built in, or Algolia
Analytics Every page; Google Analytics only after consent Plausible, Umami, Google Analytics

After a deposit is paid, the Stripe webhook (/api/stripe/webhook) emails the guest; add your own steps there, such as noting the booking in your calendar.

The home page is built from these sections, in any order: Hero (a photo plate with a season stamp and a strip of facts), Stays, Camp map, A day at camp (moments from morning to the campfire, under a strip of sky the sun crosses as you scroll), Numbers, Latest field notes, Newsletter sign-up and Closing call to action (a night panel with a campfire).

src/content/singletons/brand.json (Pages → Brand in Keystatic) holds three trail colors: the accent (buttons, the trail-blaze underline, markers) and a second and third color for markers, pins and panels (the newsletter is in the second, the camp code in the third). Text on a color is always ink, so each color is lightened until it is readable; a color that can’t get there stops the build with a message. The map paper, links and the night theme (the woods at night, with an ember glow) are derived from them. Brand also sets the fonts (text, headings, code) and the corners (square like a printed guide, or slightly rounded).

Ember is ready for more languages: see Languages.

A small game, Follow the stars: lost in the woods at night, watch the stars light up one after another, then follow them in the same order (click or tap them, or press 1–7). Each round adds a star; a wrong star ends the game and shows the way you came. 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/), including what makes the look.
  • /llms.txt lists your pages, stays and field notes for AI assistants, in every language.
  • Pages carry JSON-LD: the campground with its amenities, each stay with its price and occupancy, the booking questions and each note.
Terminal window
pnpm dev # local site, editor at /keystatic
pnpm build # production build
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).