Skip to content

Sillage

Sillage is a site for a perfume house: house fragrances with their notes, a discovery set, blending workshops, bespoke fragrances and consultations at the boutique. It also fits candle makers, niche fragrance shops, soap and skincare ateliers, tea houses and other makers of things you smell or taste. It is soft, luminous and a little magical: the home page opens on a short film of a crystal bottle on a mossy stone in a mirror-still pool at sunrise, a ribbon of rose-gold mist curling from it among giant irises and roses, with the headline on the calm left. Behind everything is champagne paper with slow veils of lilac and blush mist by day and a velvet evening at night, with fine gold motes rising. The demo is a fictional perfume house, Ondine, in the French Quarter of New Orleans.

Sillage 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)
Fragrances, ingredients, perfumers, journal, 404 Pages → the page’s own entry
Prices (sets and workshops, small print, consultations, gift cards, questions) Pages → Pricing page
Boutique (hours, details, map, contact form, questions) Pages → Contact page
Fragrances (family, price, size, top, heart and base notes, how it wears, what to wear it for, questions, photo, story) Content → Fragrances
Ingredients (origin, family, what is used, harvest, what it smells like, story, photo) Content → Ingredients
Perfumers (role, known for, training, experience, languages, fragrances, portrait, biography) Content → Perfumers
Journal (guides; one can be for customers 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, returns and samples Pages → Legal

In headlines, words wrapped in *stars* are set in italic in the accent color (champagne gold at night), as in “One fragrance, *three acts*”. The opening’s headline has its two lines in separate fields.

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 soft and calm: the headline sits there on a veil of light, in the day’s ink in both themes.

To make your own: film a slow shot of your bottle (or generate one), 5 to 20 seconds, then export it about 1280 pixels wide as H.264 MP4 without sound, ideally under 2 MB. A clip played forward and then backward loops without a cut. Replace the photo with its first frame.

  • The trail (Pages → Home page) follows one fragrance (by its file name) through three stages: the top, heart and base notes. Each stage has a time (“The first 15 minutes”), a title and a line; the notes themselves come from the fragrance, so they stay in step when you change it. A gold scent trail draws itself down through the stages.
  • The scent finder lists moods (“Fresh after rain”, “Warm and glowing”); each points at a fragrance by file name and says why in a line. Picking a mood shows its fragrance with a button to its page. It works without JavaScript and with the keyboard.

Each fragrance has a page with its bottle in an arch, family, size, price and how it wears, its notes as a pyramid (top, heart, base), its story, what to wear it for, the ingredients inside it, the perfumers who made it and questions. A fragrance lists an ingredient when the ingredient’s first word appears in its notes (“Orris root” and “Orris butter”); the ingredient’s page lists the fragrances it is found in. The prices page and the home page show the sets and workshops as cards; each pays online with a one-time Stripe Price id (or a Lemon Squeezy variant), and gift cards sit below.

The boutique page shows the streets round your shop, 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.

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

Feature Where Service
Book a consultation Prices page, loads on click; a preview of the calendar until connected Cal.com
Sets, workshops and gift cards Prices page (one-time prices) Stripe or Lemon Squeezy
Newsletter Home and journal pages, with a welcome email Supabase or Mailchimp
Contact form Boutique page; messages go to the email in Site settings Resend
AI chat Every page; suggests fragrances from what a visitor likes, makes no health or allergy claims Claude or OpenAI
Customers-only guide A journal post marked “Members only” Clerk
Search ⌘K or the search button: fragrances, ingredients, perfumers, guides, prices, boutique 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

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), The trail, Fragrances (bottles in arched windows), Scent finder, Sets and workshops, Ingredients (botanical plates), Perfumers (portraits with a blotter strip), Reviews, Questions, Journal, Newsletter and Closing call. With reduced motion everything stands still.

src/content/singletons/brand.json (Pages → Brand in Keystatic) holds three colors: the accent (plum: the button, links, marked words), the second (lilac: the mist, soft fills and the hue of the night) and the third (champagne gold: the trail, the drops and eyebrows). Buttons are deepened until their cream 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 (Bodoni Moda, Playfair Display, Cormorant Garamond or Libre Bodoni) and one for text (Hanken Grotesk, Manrope, DM Sans or Jost). The site opens in its day theme; the toggle switches to the night, and the browser remembers the choice.

Sillage is ready for more languages: see Languages.

A small game, Follow the trail: somewhere in a garden a perfume bottle lies open and a breeze carries its scent downwind. Sniff squares (tap, click, or the arrow keys and Enter) to read how strong the scent is there, from 0 to 9, and find the bottle before the sniffs run out. Five trails, each with a new breeze and fewer sniffs; points for finding the bottle and for every sniff left. The best total is kept in the visitor’s browser.

  • AGENTS.md describes the project and its design rules for coding assistants (Claude Code reads it through CLAUDE.md, Cursor through .cursor/rules/).
  • /llms.txt lists your pages, fragrances, ingredients, perfumers and guides for AI assistants, in every language.
  • Pages carry JSON-LD: the house as a LocalBusiness with its hours and prices, fragrances as Products, ingredients as Articles, perfumers as Persons, questions as a FAQPage, and each guide as a BlogPosting.
Terminal window
npm run dev # local site, editor at /keystatic
npm run build # production build
npm run map -- fetch # draw the map round map.config.json from OpenStreetMap
npm 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).