Skip to content

Char

Char is a site for a steakhouse or grill restaurant: steakhouses, grill rooms, smokehouses and BBQ joints, grill bars, asado and churrasco places. It is set in a dark dining room by firelight: warm soot with a faint oak grain and the glow of the hearth, ember red, brass and claret, a high-contrast serif and a glass pill nav. The home page opens on a full-bleed photo of the room with smoke drifting through a shaft of light and embers rising, the restaurant’s name arched over the kind like a sign on the window, the rating, one line, Book a table and Call side by side, and “Walk-ins welcome” with the address. As you scroll, a steak is seared step by step. The demo is a fictional restaurant, Ash & Oak.

Char 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)
Menu (sections, dishes, prices, weights, dietary marks, doneness scale, allergens) Pages → Menu page
Signature dishes (a page each: story, price and weight, how it’s cooked, pairing, questions, photo) Content → Signature dishes
Reserve (Cal.com booking, reservation request, phone, sittings, policies, questions) Pages → Reserve page
Private dining (rooms, set menus, what’s included, enquiry form, questions) Pages → Private dining page
Gift cards (cards to buy, how it works, questions) Pages → Gift cards page
Journal Content → Journal (one post can be members-only)
Visit (map, hours, getting here, message form, questions) Pages → Visit page
Name, phone, address, rating, opening hours, cuisine, navigation, 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 italic with a line of embers burning along under them, as in “Cooked over *oak*”.

The menu is one page made of sections. Each section has a layout: Dishes (a dish, a dotted leader, the price), Cuts (the weight beside the name, for the steaks from the hearth) or Drinks (on claret, for the wine list and cocktails). Each item can carry dietary marks (v, vg, gf, n, explained in the legend) and link to a signature dish’s own page. Under the menu sits the doneness scale (the temperatures your steaks reach at the table) and the allergen note. The menu is also published for search engines and assistants as a schema.org Menu.

The home section “The sear” pins a drawing of a steak on the grill while you scroll through four steps (Aged, Salt, Fire, Rested; write your own): it is salted, the grill marks burn in, it turns for the crosshatch, flips, moves to the board while the probe climbs to your temperature, and is sliced. Set the probe reading and the doneness label in the section. Without scroll-driven animations or with reduced motion, it shows the sliced steak and all four steps.

Book a table with Cal.com (it loads on click; until it’s connected the page shows a preview of the calendar with your times). For anything unusual, guests send a reservation request, which arrives in your inbox; the phone number sits beside it. Private dining enquiries arrive the same way. Gift cards are paid with Stripe (one-time Price ids) or Lemon Squeezy (variant ids); without keys the buttons explain how to switch them on.

The visit page and the home section “Find us” show a map of the streets round the restaurant, 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 your coordinates in map.config.json and run pnpm map -- fetch.

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

Feature Where Service
Book a table Reserve page, loads on click; a preview of the calendar until connected Cal.com
Gift cards Gift cards page (one-time prices) Stripe or Lemon Squeezy
Events by email Home and journal pages, with a welcome email Supabase or Mailchimp
Reservation requests, enquiries, messages Reserve, private dining and visit pages; to the email in Site settings Resend
AI chat Every page; answers about the menu, booking, private dining, hours and parking; never promises a dish is allergen-free Claude or OpenAI
Members-only recipe Journal Clerk
Search ⌘K or the search button: menu, dishes, pages, journal built in, or Algolia
Analytics Every page; Google Analytics only after consent Plausible, Umami, Google Analytics, PostHog
Photos Production builds Cloudinary (optional)

The home page is built from these sections, in any order: Hero, The sear, Signature dishes, Numbers (on a probe thermometer that fills with heat), Private dining (the room warms up as it scrolls in), Journal, Events by email (a claret band with embers drifting up), Find us (the map and the hours) and Closing call (the fire’s photo beside the headline, dark in both themes).

src/content/singletons/brand.json (Pages → Brand in Keystatic) holds three colors: the accent (ember red: Book buttons, marks), the second (brass: stars, small capitals, rules) and the third (claret: the wine list, badges, the events band). Buttons and claret are darkened until their white text is readable, brass is adjusted for each theme; a color that can’t get there stops the build with a message. Brand also sets the fonts and the corners. The steak and the grill use food colours, so they stay true whatever accent you pick. The site opens in its dark theme even when the visitor’s system is light; the toggle switches to the day (linen and ink) and the browser remembers it.

Char is ready for more languages: see Languages.

A small game, Resting: each table’s ticket asks for a steak at a doneness. Steaks of different thickness lie on a grill with a hot and a cool side. Pick one (tap it, or 1–4), flip it (F) so both sides get a crust, move it between the sides (M) and pull it onto the board (P or Enter). It keeps cooking while it rests, more after the hot side and more if it’s thick, so pull it early. A side left down over the heat too long burns; a side that never went down stays pale. Then it’s sliced: up to 100 points a steak. Three services of 2, 3 and 4 steaks; 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, dishes, the whole menu and the journal for AI assistants, in every language.
  • Pages carry JSON-LD: the restaurant as a Restaurant (cuisine, price range, hours, reservations) with its Menu on the menu page, signature dishes as MenuItems, set menus and gift cards as offers, 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).