Skip to content

Cellar

Cellar is a site for a vineyard and winery with a cellar door: estate wineries with a tasting room, small family vineyards that sell their wine direct by the bottle, the case and through a wine club, and cideries or small distilleries with a tasting room. It is set in a cellar by candlelight: a wine-dark room with the faint courses of a brick vault behind every page, claret, candle gold and vine green, a wine-label serif and a glass pill nav. The demo is a fictional estate, Ravelin Hill, on one hillside in a made-up valley.

The first screen is one row of vines through one year, seen from a single spot. As you scroll, bare winter vines in frost and mist bud in spring, fill out and turn colour in summer, hang heavy with dark grapes beside a picking crate at golden hour, and stand at dusk with a glass of red on the upturned crate. A readout counts the season, the sugar in °Brix along a ripening curve and the bottles, and the five steps follow in a glass card. The headline, one line, Shop wines, Book a tasting and the rating stay over it from the first frame.

Cellar 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)
Wines (every wine, shipping, the age notice, questions) Pages → Wines page
A wine (a page each: story, grape and vintage, tasting notes, food pairing, vineyard and cellar facts, how to serve it, a bottle or a case, questions, photo) Content → Wines
Wine club (tiers, what members get, how a shipment goes, questions) Pages → Wine club page
Visit & tastings (the tastings, booking, map, hours, getting here, the terrace, message form, questions) Pages → Visit page
Events & weddings (the spaces, how it works, a venue visit, an enquiry form, questions) Pages → Events page
The estate (the story in chapters, the year in the vineyard, facts) Pages → Estate page
Gift vouchers Pages → Gifts page
Journal (harvest notes) Content → Journal (one post can be for club members only)
Name, phone, address, rating, cellar door hours, age notice, navigation, profiles Pages → Site settings
Buttons, labels, chat, emails, game texts Content → Interface texts
Privacy, terms of sale, shipping Pages → Legal

In headlines, words wrapped in *stars* are set in italic and filled with wine from the bottom up as they come into view, as in “One hillside, *one year* in every bottle”.

The hero pins five photos of the same row of vines, taken from exactly the same spot: winter, budburst, véraison, harvest and dusk. As you scroll, each is revealed into the last behind a soft seam of light: spring rises up the slope as the mist lifts, véraison opens out from the sun, the harvest light floods in from the low sun with dust turning in the beams, and dusk falls from the sky with the first stars and a glint on the glass. The readout counts to what you set for each step (the season’s name, the sugar in °Brix, the bottles) and draws the ripening 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 row in the middle and the sky quiet where the headline sits, then edit it four times so nothing but the season changes. Phones get a tall crop of the middle of each photo, so keep the row, the crate and the glass 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 harvest, the readout at the pick, the curve drawn, and the five steps listed under it.

Each wine has its grape, vintage and style (the colour of the wine in the small glass beside it), tasting notes, food pairing, vineyard and cellar facts, how to serve it, and the price of a bottle and of a case of six. Buy a bottle and Buy a case go through Stripe (a one-time Price id each, on every wine) or Lemon Squeezy (variant ids in the same fields). The club’s tiers take a recurring Price id (monthly, or every three months for a quarterly tier) and the gift vouchers a one-time one. Without keys the buttons explain how to switch payments on. Wines are published for search engines as schema.org Products with their offers.

Wine is age-restricted: the age notice in Site settings sits beside every Buy button and in the footer, and the terms of sale say who may buy and that deliveries are signed for with ID. Only sell where you hold the licence to ship wine, and turn on your payment provider’s age and address checks.

The visit page lists your tastings (length, price and what’s poured) and books them through Cal.com; the events page books venue visits the same way (its own Cal.com link, or the tasting one) and takes enquiries by email. Until Cal.com is connected, both show a preview of the calendar.

The visit page and the home section “Find us” show a map of the lanes round the winery, 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
Bottles, cases, the wine club, gift vouchers Wine pages, the club and gifts pages Stripe or Lemon Squeezy
Tastings and venue visits Visit and events pages, loads on click; a preview of the calendar until connected Cal.com
Cellar letters Home and journal pages, with a welcome email Supabase or Mailchimp
Event enquiries, messages, order emails Events and visit pages; to the email in Site settings Resend
AI chat Every page; answers about the wines, pairings, the club, tastings, events and visiting; never encourages anyone under age Claude or OpenAI
Members-only harvest notes Journal Clerk
Search ⌘K or the search button: wines, tastings, 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 year), The wines (cards whose glass fills as they rise), The estate (the winery comes up out of the dark as it scrolls in), Wine club (a vine-green band), Numbers (a cellar book with the hillside drawing itself behind it), Tastings, Journal, Cellar letters, 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 (claret: Shop and Book buttons, marks), the second (candle gold: stars, small capitals, the readout) and the third (vine green: the club 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 mist, sunlight, dusk and the wine in the glasses use the vineyard’s own 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 (limestone and ink) and the browser remembers it.

Cellar is ready for more languages: see Languages.

A small game, Stack the cellar: barrels come down on a hoist; move it (pointer, drag or the arrow keys) and drop them (tap, Space or Enter). They fall, roll and settle under real physics into the saddles between the barrels below; one dropped on the crown of another rolls off. Lose three off the end of the rack and the game is over. When a cellar’s barrels are down and still, each scores ten for every tier it rests on. Three cellars, with racks for 3, 4 and 5 barrels across. 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, wines, club tiers, tastings and the journal for AI assistants, in every language.
  • Pages carry JSON-LD: the business as a Winery (price range, hours), wines as Products with offers for a bottle and a case, club tiers, tastings, spaces and vouchers 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).