Skip to content

Crema

Crema is a site for a specialty coffee roastery with a café: roasters with a bar, independent coffee shops, and roasters who sell beans online by the bag and by subscription, and to cafés and offices. It is set in a dark roastery: the brown of a roast with the faint grid of a roast-profile chart behind every page, roast copper, crema gold and green bean, a warm serif and a glass pill nav. The demo is a fictional roastery, Ostler, in an old granary on a canal wharf.

The first screen is the roast itself. As you scroll, a tray of green coffee in the roaster turns gold, cracks, darkens to a glossy medium roast and ends as an espresso in the same spot, with a probe counting the bean temperature, a roast curve drawing itself and the five steps in a glass card. The headline, one line, Shop beans, Visit the café and the rating stay over it from the first frame.

Crema 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)
Coffees (every coffee, shipping, subscription plans, questions) Pages → Coffees page
A coffee (a page each: story, origin, roast, tasting notes, brew guide, a bag or a subscription, questions, photo) Content → Coffees
The café (on the grinders, the menu with prices and dietary marks, allergens, hours, the room) Pages → Café page
Journal (brew guides and stories) Content → Journal (one post can be members-only)
Wholesale (what you supply, prices per kilo, how it works, a cupping call, an enquiry form, questions) Pages → Wholesale page
Visit (map, hours, getting here, message form, questions) Pages → Visit page
Name, phone, address, rating, opening hours, navigation, profiles Pages → Site settings
Buttons, labels, chat, emails, game texts Content → Interface texts
Privacy, terms, shipping and returns Pages → Legal

In headlines, words wrapped in *stars* are set in italic with a line of copper poured along under them, as in “Roasted on the wharf, *poured slow*”.

The hero pins five photos of the same roaster, taken from exactly the same spot: green beans, yellowing, first crack, the finished roast and the cup. As you scroll, each is revealed into the last behind a glowing seam: heat rises from the tray, first crack bursts out from the middle with chaff and smoke, the roast sweeps in from the lamp’s side, and the espresso clears out of the steam. The instrument counts to the readings you set for each step (bean temperature and roast time) and draws the curve through them; the five steps follow one at a time in the card.

Make your own five photos from one: shoot (or generate) the first frame with the beans low in the frame and the top dark and quiet, then edit it four times so nothing but the beans changes. Phones get a tall crop of the middle of each photo, so keep the beans and the cup in the centre. The first photo loads with the page and the others just after, so the page stays fast. With reduced motion, or in a browser without scroll-driven animations, the hero is a still frame: the roast, the readout at the drop, the curve drawn, and the five steps listed under it.

Each coffee has its country, tasting notes, roast (light to dark, shown as five beans), origin facts, the bag’s weight and price, an optional subscription price and a brew guide. Buy a bag and Subscribe go through Stripe (a one-time Price id for the bag, a monthly one for the subscription, on each coffee) or Lemon Squeezy (variant ids in the same fields). The coffees page adds subscription plans (monthly Price ids). Without keys the buttons explain how to switch payments on. Coffees are published for search engines as schema.org Products with their offers.

What’s on the grinders this week, then the menu in sections: each item with a description, a price and dietary marks (v, vg, gf, n, explained in the legend), the allergen note, the opening hours and the room. The menu is also published as a schema.org Menu.

The visit page and the home section “Find us” show a map of the streets round the roastery, 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
Bags and subscriptions Coffee pages and the coffees page Stripe or Lemon Squeezy
Cupping calls Wholesale page, loads on click; a preview of the calendar until connected Cal.com
Roast-day emails Home and journal pages, with a welcome email Supabase or Mailchimp
Wholesale enquiries, messages, order emails Wholesale and visit pages; to the email in Site settings Resend
AI chat Every page; answers about the coffees, brewing, postage, the café and wholesale; never promises a bake is allergen-free Claude or OpenAI
Members-only guide Journal Clerk
Search ⌘K or the search button: coffees, café menu, 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 roast), This week’s coffees, The café (the room develops like a print as it scrolls in), Subscriptions (a green-bean band), Numbers (a roast log with a curve that draws itself across the window), Journal, Roast-day emails, Find us (the map and the hours) and Closing call (a photo across the window, dark in both themes).

src/content/singletons/brand.json (Pages → Brand in Keystatic) holds three colors: the accent (roast copper: Shop buttons, marks, the curves), the second (crema gold: stars, small capitals, labels) and the third (green bean: the subscription band and badges). Buttons and the green fill are darkened until their white text is readable, gold 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 smoke, steam and beans use coffee 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 (kraft paper and ink) and the browser remembers it.

Crema is ready for more languages: see Languages.

A small game, Bean by bean, a nonogram: the numbers beside each row and above each column are the runs of beans in that line, in order. Fill the tray (tap or click a cell, or arrows and Space) until the picture appears, and mark the cells you know are empty (right-click, a long press, X or the Mark switch). A bean where there’s none cracks and costs 20 points. Three trays, 5×5, 7×7 and 10×10, up to 100 points each; every picture has a single solution you can reach line by line. 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, coffees, plans, the café menu and the journal for AI assistants, in every language.
  • Pages carry JSON-LD: the business as a CafeOrCoffeeShop (price range, hours) with its Menu on the café page, coffees as Products with offers, plans and wholesale prices 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).