Colophon
A full technical accounting of this website. Nobody requested this page. I wrote it anyway, which tells you most of what you need to know about the project.
- Dependencies
- 0
- Build step
- None
- CSS
- 39KB
- JavaScript
- 452 lines
What it’s made of
Seven HTML files sharing one stylesheet and one script. That is the entire site.
- No framework. No React, no Vue, no Svelte, no Astro, no Next. The DOM is quite good now.
- No build step. No bundler, no transpiler, no
node_modules. What is in the repository is exactly what is served. - No dependencies, and no external requests at all. The fonts are self-hosted, so the site never contacts another server — not even for a typeface.
- No tracking. No analytics, no cookies, no pixels, no consent banner — because there is nothing to consent to.
Typography
Fraunces for display — a variable serif with optical-size, SOFT and WONK axes, which means the large headings are genuinely a different shape from the small ones rather than the same letterforms scaled up. The italic runs a higher SOFT value so it reads warmer.
Inter for body text, JetBrains Mono for labels and small caps. Every size is a clamp(), so type scales fluidly instead of stepping at breakpoints.
All four files are self-hosted and subset: reduced to Latin plus the punctuation this site actually uses, then instanced down to only the axis ranges the stylesheet requests — Fraunces to wght 300–700 and opsz 14–144, Inter to wght 400–600. That cut the payload by roughly a third while keeping every axis live, including SOFT and WONK, which is the whole reason for using Fraunces at all.
Motion
The brief was “animated but not janky,” which is a real engineering constraint, so:
- Only
transformandopacityare animated. Those are the two properties the compositor can handle without re-running layout or paint. Nothing animateswidth,toporheight— except the timeline fill, which is a single element with no siblings to reflow. - One scroll listener for the whole site, marked
passive, rAF-throttled, and gated behind atickingflag so it can never queue more than one frame of work. - Reveals, counters and the gauge share one pending-set sweep driven by that same loop. An IntersectionObserver was the first approach and it was wrong: it drops entries during a fast flick or an instant anchor jump, which strands a revealed element at
opacity: 0— a permanently blank section. The sweep testsrect.top < viewport × 0.91, true both while entering and once scrolled past, so nothing can be skipped. Elements leave the set the moment they fire, so per-frame cost collapses to zero within the first screenful. - Pointer effects are rAF-gated too. The cursor spotlight lerps toward the pointer and cancels its own animation frame once it arrives, so an idle mouse costs nothing.
- The particle field caps device pixel ratio at 2, scales its count to the viewport area, skips link-drawing below 720px, and stops entirely when the tab is hidden.
- Everything is disabled under
prefers-reduced-motion. Not reduced — disabled. Reveals resolve instantly, the canvas never initialises, the grain and spotlight are removed from the document.
Accessibility
- Skip link, landmark elements, and a visible focus ring on every interactive element.
- The mobile menu button carries
aria-expandedandaria-controls. - Decorative layers — gradient blobs, grain, spotlight and both canvases — are all
aria-hidden. - Content is fully readable with JavaScript disabled. Every JS-driven hidden state is gated behind a
.jsclass set inline in the head, and the statistics carry their real values in the markup, which the script zeroes before animating. - Both themes are contrast-checked, and the theme choice persists in
localStorageinside atry/catch, so private browsing doesn’t throw.
Licensing
All three typefaces are under the SIL Open Font License 1.1, and the full licence for each family ships alongside the fonts in assets/fonts/. Subsetting a font makes it a “Modified Version” under the OFL, so what changed is documented there too: character set and axis ranges only, no outlines touched.
None of the three carry a Reserved Font Name, which is what makes it legitimate for the trimmed files to keep calling themselves Fraunces, Inter and JetBrains Mono. The subsetting tool quietly strips the licence fields out of the font binaries, so shipping the licence text next to them is not a formality — without it, redistributing these files would breach the terms.
Three bugs worth admitting
All three were found by driving the site in a real browser rather than by looking at it, and all three would have shipped invisibly:
- The forty-pixel margin. Browsers give
<figure>and<blockquote>a40pxside margin by default. Never reset, it pushed the testimonial grid 64 pixels past the viewport at every mobile width — andoverflow-x: hiddenon the body was hiding the evidence. - The blank page. With JavaScript disabled, every scroll-reveal section sat at
opacity: 0permanently. The page was, functionally, empty — while this very colophon claimed it degraded gracefully. - The invisible overlap. Tall hero content landed on top of the scroll cue at common laptop heights. The fix needed
.hero .scroll-cuerather than.scroll-cue, because the element’s owndisplay: flexis declared later in the file and was winning on source order.
Credit where it’s due
Written by Claude Opus, in one sitting, at the request of a daughter whose father had recently and repeatedly mentioned that he built his website entirely with a large language model.
His site is genuinely good, and this should be said plainly rather than buried in a joke: it is fast, it is clear, it has no wasted bytes, and it does exactly the job a real business needs it to do. Measured as what it is, it is the better website of the two.
This one just has a rotating wax seal, a constellation field, a confetti cannon and a Konami code.
Love you, Dad.