Skip to content

Kelvin

Kelvin is a site for a local air conditioning contractor: AC and HVAC installers, heat pump and mini-split fitters, ventilation and refrigeration contractors, one-van cooling businesses. It is set edge to edge on a weather map’s isotherms, the wavy temperature contours that cool from sun amber to cool blue as you scroll, with cool blue, sun amber and frost mint and a wide display face for headings. On the home page a cutaway of a desert home runs across the window and, as you scroll, it cools room by room while it stays hot outside. The demo is a fictional business in Mesa, Arizona, Saguaro Air.

Kelvin 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)
Services, installs, tips, 404 Pages → the page’s own entry
Prices (service call, plans, how an install works, booking, tune-ups and deposits, questions) Pages → Prices page
Contact (quote form, areas, map and its ring, hours, details, questions) Pages → Contact page
Services (icon, price from, how long, what’s included, common jobs, questions, photo) Content → Services
Installs (where, when, the system, time, cost, before and after photos, story) Content → Jobs
Tips Content → Tips
Name, phone, address, licence, navigation, header button, profiles Pages → Site settings
Buttons, labels, chat, emails, game texts Content → Interface texts
Privacy, terms, warranty Pages → Legal

In headlines, words wrapped in *stars* are painted with the cool gradient, as in “Outside 112°. *Inside 72°.*”.

The home page opens with the headline, a line and two buttons (one can be a tel: link) above a cutaway of a home. Set the temperature outside (it stays put), the inside temperature it starts from and the one it settles at, the unit (°F or °C) and the labels. As you scroll, the inside readout counts down from one to the other while the rooms cool. With reduced motion the home is shown cool and nothing moves.

Each install has a before and an after photo of the same view (the old unit and the new one). On the cards the after photo wipes in as the card scrolls past; on the install’s own page a slider lets visitors compare them (drag, or the arrow keys). Take both photos from the same spot.

The prices page holds your service call on a thermostat dial with its small print, up to four plans (mark one as the popular one), how an install works along a duct (also on the home page), booking a visit with Cal.com, and tune-ups and deposits you take with Stripe: each one gets a one-time Price id. Without keys, the booking shows a preview of the calendar and the payment buttons explain how to switch them on.

The map is drawn from OpenStreetMap in your colors, with a pin on your base and a dashed ring for the area you cover (Contact page → Map → Radius, in km; 0 hides it). It is part of the page (no embed, no cookies) and follows the theme and your accent. To draw your own area, put your base’s coordinates in map.config.json and run pnpm map -- fetch. detail: "area" draws the main roads, railways, water and woods of a district; in a big city, roads (e.g. ["motorway", "trunk", "primary", "secondary"]) keeps the map light.

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

Feature Where Service
Book a visit Prices page, loads on click; a preview of the calendar until connected Cal.com
Tune-ups and deposits Prices page (one-time Price ids) Stripe
Tips by email Home and tips pages, with a welcome email Supabase
Quote form Contact page; messages go to the email in Site settings Resend
AI chat On every page, answers about services, prices, plans and areas; never tells anyone to handle refrigerant, electrics or gas, and gives the emergency number Claude
Search ⌘K or the search button: services, installs, tips, prices, contact built in, or Algolia
Analytics Every page; Google Analytics only after consent Plausible, Umami, Google Analytics

The home page is built from these sections, in any order: The cooldown (the opening), Services (cards that open like louvres), Recent installs (before and after), How an install works (steps along a duct that fills with cool air), Numbers (on thermostat displays), The area covered (the map), Tips, Tips by email and the Closing call (a night band with a 24/7 light).

src/content/singletons/brand.json (Pages → Brand in Keystatic) holds three colors: the accent (cool blue: buttons, marks, the cold end of the gradient), the second (sun amber: the heat, the outside readout) and the third (frost mint). Buttons are darkened until their white text is readable, the second and third are lightened until ink text on them is; a color that can’t get there stops the build with a message. Brand also sets the fonts and the corners. The site opens in the day theme even when the visitor’s system is dark; the toggle switches to the night, a deep night sky, and the browser remembers the choice.

Kelvin is ready for more languages: see Languages.

A small game, Keep it 72: four rooms heat up in the sun, each at its own pace. Point the one cooling zone at a room (tap it, or keys 1 to 4) and keep every room below 85° for as long as you can; the sun gets stronger as time goes on. The best time 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, services, installs and tips for AI assistants, in every language.
  • Pages carry JSON-LD: the business as a HomeAndConstructionBusiness with the areas it serves and its plans, services as Services with their price, installs as CreativeWorks, how an install works as a HowTo, questions as a FAQPage, and each tip 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).