4 Cloudflare Workers and a Kilt Shop
How a 1927 Edinburgh kilt shop went from paper notes and missed appointments to automated booking — powered by Cloudflare Workers, Google Calendar, and robust tests.
Recently my sister got married — congrats again, Floyd — so I needed a kilt. Luckily, Davison Menswear & Kilt Hire is a few doors down from me. Outside, the shops easy to spot: hand-painted marquee with logo, Est. 1927, and a previously broken url.
Thought I'd introduce myself while I got my threads.

The full hero viewport — archival photos bleed through as you scroll
The owner, Marty, hand-sews 12-yard kilts from memory. Generations of talent. He works alone in a showroom that hasn't changed much since his Fathers's day: notepad by the phone, paper diary, appointment slips.
The methods were proven, but dated. Calendar bookings misplaced, missed calls, staying late to rewrite orders. Marty was keen on upgrading - old site had been down for several years - so we sat down and talked through needs.
The Requirements
Dislikes and boundaries were my largest consideration for this project. Expecting a sole trading generational kilt maker to learn several new tools, after he's been working on paper for decades, is unrealistic. It was important tools felt familiar and intuative, while staying within his scope. Why build a feature that won't be used?
I'd already built Sicamon — a full Stripe CMS with digital cataloguing. Understandably, this was too much change. Not looking to become an online shop. I came up with a design that was modern, but familiar and wouldn't overwelm by changing his core practices: No online inventory, no card payments. Bank transfers. What he's always done.
Project runs on:
Workers, D1,
KV, Turnstile, Resend, GitHub Actions — and costs nothing to run.
Heritage was the identity I decided; made sense. A century in 2027 — I wanted a design to reflect. I was given a binder: Marketing photos from the 1920s through the 1950s (took ages to scan).

One of the original archive photos from the binder — the shopfront before Edinburgh's trams were pulled up
I scanned, and composited a site background. Every section uses a translucent white overlay so the archival photos bleed through as you scroll.

Archival photos composited into the site background — the binder became the brand
The Main Site
The user site is primarily a single page: header, map, services, tartan search,
reviews, a "Book a Fitting" button. Tailwind keeps
every layout consistent — utility classes enforce a design system, no magic
numbers. Typography scales fluidly with CSS clamp() — no breakpoint jumps, no
layout surprises.
Mostly minimal. The tartan search is a nice-to-have novelty — searchable directory of clan and district patterns - should help with SEO, though, not essential, but fun.

Searchable directory of clan and district tartans — a fun bonus for visitors
Primary user functionality is the booking system; ran via Cloudflare workers and existing Google Calendar. What happens when you click:

The booking form — Turnstile bot protection runs invisibly on submit
The Booking Pipeline
- Bot detection — Turnstile, invisible
- Input validation — malformed data rejected
- Closure check — reads
[Closed]calendar events - Slot availability — no double-booking
- Event creation — writes to Google Calendar
- Cancel link — signed JWT in the event
- Notifications — async emails to both customer and site owner, never blocks
Everything is secured with Cloudflare. Deterministic security was top of mind. Someone else books the slot mid-form? You're told immediately.
Closures are set by adding [Closed] — Christmas, holidays, any day off. The
worker updates automatically.



Booking confirmed — the customer sees this first, then both parties receive email alerts

Google Calendar is the database — already in use, the booking system reads and writes to it
Testing
This runs a sole trader's business. Every commit, every deploy — it has to work. Not "mostly work." Work.
Seven layers between a push and production
So there are seven layers between a push and production. Static audits catch hardcoded secrets and broken contracts before a test even runs. Worker tests mock every external dependency so internal logic can be validated deterministically — no network, no credentials. Live API tests then prove the real integrations work (Google Calendar, Resend, Turnstile). E2E and integration tests drive the full booking flow and admin lifecycle headless.
A pre-deploy check probes all four workers before anything ships. If something's degraded, the deploy stops. Once live, scheduled monitoring probes every six hours, and the calendar worker self-checks every sixty seconds.
It's a lot of ceremony for a booking form and an admin panel. But if a booking drops, it's not an alert on a dashboard — it's a customer standing outside a locked shop.
The Workers
Frontend Worker
The shop window. Serves every page a visitor lands on — hero, map, services, the tartan search widget, reviews, and the booking button. Static build, globally distributed. No server to babysit.
Calendar Worker
Looks after the booking system — a separate worker with its own credentials.
Google Calendar is the database, zero new infra,
already in use. Reads [Closed] events, checks slot availability, writes new
bookings, and generates cancel links using signed
JWTs in the event description — no database lookup needed. Turnstile over CAPTCHA on the booking form: free, invisible, no learning curve. Email fires asynchronously so the booking never blocks on a delivery failure.
Reviews Worker
Pulls Google Reviews on a schedule into KV. Stays current without input — fresh content, zero effort.

Live Google Reviews pulled on a schedule — fresh content with zero effort
The Admin Dashboard
The notepad replacement — its own Cloudflare Worker backed by D1. The same order in the diary, on a slip, and on a sales pad — three times, one customer. Now it's a page.

Full admin dashboard — 139 tests verify the whole system
Four statuses: In Progress, Ready, Delivered, Cancelled. Every transition logged immutably — who changed it, when, and whether the email went out. Group orders for wedding parties — groom, best man, ushers under one accordion. Measurements: kilt waist, seat, length, jacket chest, sleeve, back, height, build notes. All optional.
Emails fire on status changes via Resend. The receipt includes bank details — stored as Worker secrets, never in the codebase. On creation the customer gets payment instructions. When it's ready, a collection notice. Not a word typed. CSV export: current tax year, last year, or custom range. Clean totals at the bottom for the accountant.
Quick Add for reconciling past orders. Archived orders auto-purge after 30 days. No payments on-platform — bank transfer, cash, the till. Progress without overwhelm.
I'm a message away if a fix is needed. The binder is back on the shelf. And the marquee with qualitykilts.com is still there — now it works.
