[ Parable build guide ]
How Beaufort was built
A charter house, drawn as a chart. The signature is an inline-SVG sea chart — isobaths, soundings, a rhumb-line network and a real compass rose — whose needle rotates to track scroll progress, so your heading changes as you sail down the page. No canvas, no libraries, no image files.
The idea
A boutique yacht charter sells one thing above the boats: trust in the people who navigate them. So the hero isn't a drone shot of a yacht — it's the thing the crew actually reads. A cartographic chart, rendered in vector so its lines stay hair-thin at any size, with the one flourish a photo can never give you: a compass that responds. Scroll down and the needle swings, as if the boat were slowly changing course. The romance of the brand is the navigation, so the navigation is the interface.
The stack
Deliberately plain. One index.html, one styles.css, one
main.js. No framework, no bundler, no CDN — the chart is hand-authored SVG right in
the markup, so there are zero runtime dependencies and nothing to build. Type is Spectral (a
lyrical text serif for the display voice), Karla (a warm grotesque for body), and IBM Plex Mono
for everything a navigator would set in a fixed pitch: soundings, bearings, distances, coordinates.
Signature technique — the scroll-driven compass
The compass rose is authored once in SVG — outer ring, an eight-point star, two-tone cardinal
points, and a needle isolated in its own <g id="roseNeedle">. On scroll we map
page progress to a bearing and rotate the needle group about the rose's centre. One fixed tell-tale
badge, bottom-left, mirrors the same bearing in degrees so the heading is legible even after the
chart scrolls away.
const START = 20, SWEEP = 300; // sweep 20° → 320° over the page
const update = () => {
const max = document.documentElement.scrollHeight - innerHeight;
const p = clamp(scrollY / max, 0, 1);
const deg = START + p * SWEEP;
needle.setAttribute('transform', `rotate(${deg} 468 158)`); // rose centre
ttDeg.textContent = String(Math.round(deg % 360)).padStart(3,'0') + '°';
};
addEventListener('scroll', () => requestAnimationFrame(update), { passive: true });
The charter route is a dashed <path> revealed on load by animating a
clipPath rectangle from scaleX(0) to scaleX(1) — so the
course appears to be laid down across the water — while a faint gradient band sweeps the sea as a
slow shimmer. Both are pure CSS; only the needle needs JavaScript.
Details that matter
- Cheap by construction. There's no render loop — the needle only moves in a
scroll handler, and that handler is throttled to one
requestAnimationFrameper frame. The shimmer and route-draw are GPU-friendly CSS animations. - Reduced motion is honest. Under
prefers-reduced-motionthe chart is a single, fully-drawn still: the needle sits at a fixed 042°, the route is already complete, the shimmer is off and the tell-tale is hidden. No scroll listener is attached. - Content is never hidden without JS. The hero rise, the reveals and the
route-draw are gated behind a
.jsclass set synchronously in<head>. Kill JavaScript and the whole chart and page render, fully legible. - Real cartography, not decoration. Soundings are in fathoms, the scale bar is in nautical miles, tidal diamonds carry letters, and the cartouche names the chart — the detail is what sells the fiction.
- Two themes, one flip. Night-passage navy and paper-chart cream are the same
tokens inverted on
:root[data-theme]; the chart, the sail plans and the whole page re-tint from one switch, persisted tolocalStorage.
The enquiry form is a demo — it validates and confirms in-place but sends nothing. Wire it to your own endpoint (Formspree, a serverless function, an email service) before taking real bookings.
Ship it on GitHub Pages
Nothing to build. Push the folder and point Pages at the root.
gh repo create bswxyz/beaufort --public --source . --push
gh api --method POST /repos/bswxyz/beaufort/pages -f 'source[branch]=main' -f 'source[path]=/'
Relative paths and a .nojekyll file mean it serves from the project subpath without
a single config change — the whole point of a static build.