Riva
Riva is a site for a lakeside hotel: rooms and suites named by their view, a restaurant, a spa and experiences, places around the lake, stays held with a deposit paid online, gift vouchers and a call with the concierge. It also fits seaside hotels, grand hotels, historic villas, boutique resorts and guesthouses by the water. It is Belle Époque and luminous: the home page opens on a short film of an ivory villa on the lake at golden hour, a wooden runabout bobbing at the jetty among lemon trees and lavender, with the headline on the calm water on the left. Behind everything is sunlit ivory paper with slow veils of reflected lake light by day, and deep lake water under a moon at night, with glints of light on the ripples. The demo is a fictional hotel, Villa Lucenti, in Varenna on Lake Como.
Riva 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.
Pages and their content
Section titled “Pages and their content”| Page | Edit it in |
|---|---|
| Home | Pages → Home page (a list of sections) |
| Rooms, experiences, the lake, journal, 404 | Pages → the page’s own entry |
| Stays (packages with a deposit, small print, call with the concierge, gift vouchers, questions) | Pages → Stays page |
| Visit (hours, getting here, map, contact form, questions) | Pages → Contact page |
| Rooms (view, price from a night, size, beds, guests, what’s in the room, what every stay includes, questions, photo, story) | Content → Rooms |
| Experiences (kind, when, where, price, highlights, story, photo) | Content → Experiences |
| Places (kind, minutes by boat and on foot, what not to miss, the concierge’s tip, story, photo) | Content → Places |
| Journal (letters; one can be for guests only) | Content → Journal |
| Name, phone, address, currency, navigation, header button, profiles | Pages → Site settings |
| Buttons, labels, chat, emails, game texts | Content → Interface texts |
| Privacy, terms, booking and cancellation | Pages → Legal |
In headlines, words wrapped in *stars* are set in italic in the accent color (brass at night), as in
“Every room is *a window*”. The opening’s headline has its two lines in separate fields.
The film in the opening
Section titled “The film in the opening”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 a moment 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 left of the picture calm (water or
sky): the headline sits there on a veil of light.
To make your own: film a slow, steady shot of your hotel (or generate one), 5 to 20 seconds, then export it about 1280 pixels wide as H.264 MP4 without sound, ideally under 3 MB. A clip played forward and then backward loops without a cut. Replace the photo with its first frame.
Choose your view and days at the lake
Section titled “Choose your view and days at the lake”- Choose your view (Pages → Home page) shows one arched window traced in brass and tabs for the views (Lake, Terrace, Garden, Panorama). Each view has a line, a room (by its file name) and a photo through the window; picking a tab cross-fades the view and shows its room beside it with its size, guests and price. Photograph the same window with the same framing each time (or edit one photo), so only the world outside seems to change. It works without JavaScript and with the keyboard.
- Days at the lake lists moods (Slow, Active, Romantic, With children); each has a line and its day, one step per line as the time, a dash and what (“9:30 – Breakfast on the terrace”). Picking a mood shows its day as a small timetable.
Rooms, stays and places
Section titled “Rooms, stays and places”Each room has a page with its photo in an arched window, the view, size, beds, guests and the price from a night, what is in the room, what every stay includes, its story, the other rooms, the experiences and questions. The stays page and the home page show packages as cards: each holds the dates with a deposit paid online through a one-time Stripe Price id (or a Lemon Squeezy variant), and gift vouchers sit below. Places around the lake appear as ferry tickets with the minutes by boat or on foot; each has a page with what not to miss and the concierge’s tip.
The map
Section titled “The map”The visit page shows the streets round your hotel, 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 coordinates in map.config.json and run npm run map -- fetch.
Integrations
Section titled “Integrations”Everything works without keys; each feature switches on with its variables in .env.example.
| Feature | Where | Service |
|---|---|---|
| Call with the concierge | Stays page, loads on click; a preview of the calendar until connected | Cal.com |
| Stay deposits and gift vouchers | Stays page (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; suggests rooms and places, never confirms availability | Claude or OpenAI |
| Guests-only letter | A journal post marked “Members only” | Clerk |
| Search | ⌘K or the search button: rooms, experiences, places, letters, stays, 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 |
Sections
Section titled “Sections”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), Choose your view, Rooms (arched windows), Days at the lake, Experiences, Places (ferry tickets), Stays and vouchers, Reviews, Questions, Journal, Newsletter and Closing call. With reduced motion everything stands still.
Colors and brand
Section titled “Colors and brand”src/content/singletons/brand.json (Pages → Brand in Keystatic) holds three colors: the accent
(lake blue: the button, links, marked words), the second (mist blue: the water light, soft fills and
the hue of the night) and the third (brass: fine lines such as the window frame, eyebrows and the
marks at night). Buttons are deepened until their ivory 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 (DM Serif Display, Playfair Display, Cormorant Garamond or Bodoni
Moda) and one for text (Jost, Manrope, DM Sans or Hanken Grotesk). The site opens in its day theme; the
toggle switches to the night, and the browser remembers the choice.
Languages
Section titled “Languages”Riva is ready for more languages: see Languages.
The 404 page
Section titled “The 404 page”A small game, Golden hour: the sun is setting and its light spreads across a lake drawn in patches of five colours, starting from the villa’s corner. Pick a colour (tap or click it, or press 1 to 5) to pour the light into it: every patch of that colour touching the lit water joins it. Light the whole lake before the moves run out. Three lakes, each bigger; points for lighting a lake and for every move left. The best total is kept in the visitor’s browser.
AI assistants and search engines
Section titled “AI assistants and search engines”AGENTS.mddescribes the project and its design rules for coding assistants (Claude Code reads it throughCLAUDE.md, Cursor through.cursor/rules/)./llms.txtlists your pages, rooms, experiences, places and letters for AI assistants, in every language.- Pages carry JSON-LD: the hotel as a
Hotelwith its hours, rooms asHotelRooms with their price a night, experiences and places asTouristAttractions, questions as aFAQPage, and each letter as aBlogPosting.
Commands
Section titled “Commands”npm run dev # local site, editor at /keystaticnpm run build # production buildnpm run map -- fetch # draw the map round map.config.json from OpenStreetMapnpm 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).
