Don Joyce sells houses around Lac-Brome, in the Eastern Townships. He needed a site that would show up when someone in Knowlton searched for a broker, work on a phone with two bars of signal, and read the same in French as in English. He didn’t need a web application.
So there’s no framework, no build step and no JavaScript. Sixteen hand-written HTML pages, one stylesheet, deployed as static files. The whole site is 1.4 MB including every image and both self-hosted fonts.
No JavaScript
The site has thirty-two <script> tags and none of them execute. They’re all application/ld+json, which is structured data for search engines rather than code. The Content Security Policy says so out loud:
Content-Security-Policy: default-src 'self'; script-src 'none'; object-src 'none';
base-uri 'none'; form-action 'none'; frame-ancestors 'self'
script-src 'none' means the browser refuses to run script on this origin at all. That’s not a hardening measure bolted on at the end. It’s a design constraint asserted at the boundary: if a dependency, an analytics vendor or a future me tries to add a tracker, the page breaks loudly instead of quietly shipping it to visitors.
What it buys is everything a small business site actually wants. Total blocking time is 0 ms because there’s nothing to block on. There is no dependency tree to audit, no supply chain to inherit, and nothing that goes stale between now and the next time anyone touches it.
The rest of the header set is the ordinary list, and it’s ordinary on purpose: HSTS for a year, nosniff, SAMEORIGIN framing, a referrer policy, and a permissions policy that turns off geolocation, camera, microphone and cohort tracking rather than leaving them available to a page that will never ask for them.
English and French
Every page exists as /en/ and /fr/, and the French URLs are French: analyse-de-marche, not market-analysis-fr. Each page declares all three hreflang alternates including x-default, so Google serves a Quebec searcher the French page and doesn’t treat the pair as duplicate content.
This is the part clients most often get sold badly. A translation widget bolted onto an English site produces one indexable page and a machine translation that a French speaker in Quebec can identify in about four words. Two native pages produce two indexable pages, in a market where a good share of the searches are in French.
Structured data
Search engines can read prose, but they’ll act on markup. Every page carries JSON-LD describing what it is:
| Type | What it does |
|---|---|
RealEstateAgent, Person, Organization | Identifies the broker, the brokerage and their relationship |
PostalAddress | Feeds the local pack, where “real estate broker near me” is answered |
FAQPage with eleven Q&A pairs | Eligible to render as expandable answers directly in results |
Article on seven pages | Marks the market-analysis writing as editorial, not sales copy |
BreadcrumbList on seven pages | Puts a readable path in the result instead of a raw URL |
Place twenty times | Names the specific towns served, which is how “Sutton” or “Bolton-Est” gets matched |
There’s also an llms.txt at the root, which is a plain-language summary of who Don is and what the site covers, aimed at language-model crawlers rather than search engines. It’s a young convention and it may not amount to anything. It’s also a text file that took an hour, on a site where a growing share of “who’s a broker in Knowlton” questions now get asked of a chatbot instead of a search bar.
Performance
Lighthouse 13.4.1, run against production on 2 September 2026:
| Mobile | Desktop | |
|---|---|---|
| Performance | 99 | 100 |
| Accessibility | 100 | 100 |
| Best Practices | 100 | 100 |
| SEO | 100 | 100 |
| Largest Contentful Paint | 1.4 s | 0.3 s |
| Total Blocking Time | 0 ms | 0 ms |
| Cumulative Layout Shift | 0 | 0.006 |
Perfect scores on a sixteen-page static site aren’t an achievement to boast about. Static HTML with no scripts starts near the ceiling and the work is not falling off it: subset the fonts and self-host them, size every image, reserve layout space so nothing shifts, and decline the things that would cost more than they return.
Accessibility
The site scores 100. Getting there took one fix; the part worth writing down is what the score didn’t cover.
Lighthouse reported 95, from a single failing contrast rule on the homepage that missed the WCAG AA threshold of 4.5:1 by 0.03. Fixing that one rule would have produced the 100, and 100 is what most people ship.
Instead I read every color declaration in the stylesheet against its background by hand, and found three more failures of the same kind that Lighthouse structurally cannot see. One was on a page the run didn’t scan. Two were in :hover states, which the audit engine never evaluates because it only ever measures the resting page.
The worst of them was the footer link hover: brand red on the navy footer at 2.87:1, effectively invisible, and it would have survived every automated run forever.
| Rule | Was | Now | Found by |
|---|---|---|---|
.reference-card__translation | 2.85 | 5.10 | Lighthouse |
.intro__cta | 4.47 | 5.58 | Lighthouse |
.market-table .is-down | 4.47 | 5.58 | Hand audit |
.faq__answer a:hover | 4.47 | 5.58 | Hand audit |
.footer__section a:hover | 2.87 | 6.02 | Hand audit |
A green audit means the checks that ran, passed. Automated tools measure the resting page, so hover, focus and active states go unmeasured, and any page the crawl didn’t reach goes unmeasured too.
Caching
Images and fonts are served immutable with a one-year lifetime, HTML with must-revalidate so a correction to a listing or a phone number goes live immediately. That split is what makes a repeat visit load instantly on cellular while keeping the operator’s ability to fix a typo now rather than after a cache expires.
The tradeoff is documented in the header file itself, because it’s the kind of thing that bites whoever touches it next:
because these are immutable for a year, a REPLACEMENT image must ship under a NEW filename or returning visitors will keep the old one indefinitely
Print materials point back to the site through QR codes, generated separately for the English and French market pages so a scan lands in the language the card was printed in. Redirects are checked by a script rather than by hand, because thirteen rules is already more than anyone reliably remembers.
Scope
This is a small site. It has no database, no listings feed, no search, no user accounts, and it deliberately doesn’t need them. Royal LePage already runs the inventory system, and rebuilding that badly would have made the site worse.
The work went into details a visitor never sees itemised: the contrast ratio no automated tool would have caught, the cache header with its own failure mode written down beside it, French URLs that are actually in French. A broker’s site has to load on a phone, on the first visit, for someone deciding whether to trust him with the largest transaction of their life.