Skip to content

Tumbler

Tumbler is a site for a locksmith: emergency lockouts, lock changes and upgrades, key cutting, safes, smart locks and security surveys for homes and small businesses. It is drawn edge to edge like an engineering drawing: drafting paper with a fine blueprint grid and key blanks dimensioned at the sides, machined brass, blued steel and a signal red kept for the emergency line, and a wide engraved typeface. On the home page a pin-tumbler lock in cross-section opens as you scroll: the key slides in, every pin rides up and down its cuts and settles at the shear line, the plug turns, the bolt draws back and the door swings open into warm light. The demo is a fictional business, Shearline Locksmiths.

Tumbler 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, jobs, guides, 404 Pages → the page’s own entry
Prices (call-out rate, packages, how a call-out works, booking, deposits and vouchers, questions) Pages → Prices page
Contact (quote form, areas, map, hours, details, questions) Pages → Contact page
Services (icon, nameplate colour, price from, how long, what’s included, typical jobs, questions, photo) Content → Services
Jobs (where, when, called, on site in, time, cost, photo, story) Content → Jobs
Guides (one can be for members only) Content → Guides
Name, phone, address, insurance, navigation, header button, profiles Pages → Site settings
Buttons, labels, chat, emails, game texts Content → Interface texts
Privacy, terms, guarantee Pages → Legal

In headlines, words wrapped in *stars* are set in brass with a fine double rule engraved under them, as in “Locked out? *Let’s get you in.*”.

The home page’s first section is the lock. Its texts are yours: the headline, the lead, two buttons (make the first one your emergency number as a tel: link and it turns signal red with a phone icon), the scroll hint, the label of the shear line and five captions, one for each step (the key goes in, the pins meet the shear line, the plug turns, the bolt draws back, the door opens). The drawing itself is in src/components/sections/Lock.astro: the key’s cuts, the pins and when each step happens are at the top, and the motion of every pin is worked out from the key’s profile when the site builds. With reduced motion the lock stands still with the key in and the pins at the shear line.

Each service has a nameplate in one of the three colours: brass, blued steel, or signal red (keep red for the emergency service). Jobs are shown as work orders: when the call came in, how soon you were on site, the time on site and the cost, with a “Done” stamp.

The prices page holds your call-out rate on a brass plate with its small print (evenings, nights, after the first half hour, parts), up to four packages (mark one as the popular one), how a call-out works as steps cut into a key (also on the home page), booking a visit with Cal.com, and deposits and gift vouchers you take with Stripe or Lemon Squeezy: 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 colours, with a pin on your workshop. It is part of the page (no embed, no cookies) and follows the theme and your accent. To draw your own, put your workshop’s coordinates in map.config.json and run pnpm map -- fetch. detail: "streets" (the default in Tumbler) draws the streets and buildings round one address; detail: "area" draws the main roads, railways, water and woods of a whole district, and Contact page → Map → Radius then adds a ring for the area you cover.

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
Deposits and gift vouchers Prices page (one-time prices) Stripe or Lemon Squeezy
Guides by email Home and guides pages, with a welcome email Supabase or Mailchimp
Members-only guides A guide marked members only shows its text after signing in Clerk
Quote form Contact page; messages go to the email in Site settings Resend
AI chat On every page, answers about services, prices, jobs and areas; gives the emergency number first and never explains how to open a lock that isn’t yours Claude or OpenAI
Search ⌘K or the search button: services, jobs, guides, prices, contact built in, or Algolia
Analytics Every page; Google Analytics only after consent Plausible, Umami, PostHog, Google Analytics
Photos Served from your Cloudinary account when it is set Cloudinary

The home page is built from these sections, in any order: Opening (the lock), Services (engraved plates), How a call-out works (steps cut into a key that slides in), Recent jobs (work orders), Numbers (on a caliper whose jaw slides along), The workshop on a map, Guides, Guides by email (a steel plate with a combination dial) and Closing call (a blued-steel band). With reduced motion nothing moves.

src/content/singletons/brand.json (Pages → Brand in Keystatic) holds three colors: the accent (machined brass: buttons, plates, the shear line, marks), the second (blued steel: dark fills) and the third (signal red: the emergency line). Brass is lightened until dark text on it is readable, steel and red are darkened until white 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 on the drafting paper even when the visitor’s system is dark; the toggle switches to blued steel, and the browser remembers the choice.

Tumbler is ready for more languages: see Languages.

A small game, Cut the key: a lock’s code is hidden. Set the depth of each cut on a key blank (tap a cut, then Deeper or Shallower, or the arrow keys) and try it: the lock only tells you how many pins reached the shear line, never which ones. Work out the code before the blanks run out. Three locks of three, four and five pins; a lock opened scores ten points for every blank left, and 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, services, jobs and guides for AI assistants, in every language.
  • Pages carry JSON-LD: the business as a Locksmith with the areas it serves and its packages, services as Services with their price, jobs as CreativeWorks, how a call-out works as a HowTo, questions as a FAQPage, and each guide 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).