← Fernweh THE GUIDE · HOW IT WAS BUILT
Design showcase · build notes

A landscape that lags behind you, on purpose.

Fernweh is a booking site for cabins that don’t exist, built to move the way its product feels: slow, layered, a little far away. These are the working notes — the idea, the stack, and the two techniques that carry it.

A timber cabin with a warmly lit glass gable wall on a forested hillside at dusk, mist pooling in the pine valleys behind it.
The one photograph on the site. Everything else — ridges, mist, stars, cabins — is SVG and CSS.

The idea

Slow-travel brands usually oversell with photography. Fernweh undersells on purpose: one misty photo, one whispered line (“The signal fades. You won’t miss it.”), and then a world drawn from layered ridge silhouettes that drift as you scroll. The product’s promise is the absence of things — wifi, signal, hurry — so the design’s job was restraint: minimal chrome, a nav that stays out of the way, and “no wifi” typeset as an amenity, not a warning.

The interaction patterns are borrowed from shipped booking products (researched on Mobbin): Airbnb’s price-first booking card with visible arithmetic and its “you won’t be charged yet” reassurance line, Booking.com’s name-→-distance tables, Tripadvisor’s availability-as-a-calm-fact. Each one got de-alarmed and rewritten in Fernweh’s voice.

The stack — why Astro

Signature technique — the long lazy parallax

The backdrop is a position:fixed stack: a sky gradient, a box-shadow starfield, five SVG ridge silhouettes and two blurred mist bands. Depth comes from two cheap tricks — atmospheric perspective (farther ridges are paler and more transparent) and differential motion (farther ridges move less):

<svg class="ridge r5 px" data-amp="22"  ...>  <!-- farthest, palest -->
<svg class="ridge r3 px" data-amp="68"  ...>
<svg class="ridge r1 px" data-amp="140" ...>  <!-- nearest, darkest -->

Instead of multiplying scrollY by a rate (which drifts unbounded on a long page), each layer’s data-amp is the total distance it may rise across the whole document. Scroll progress eases it there:

const p = Math.min(1, scrollY / (docHeight - innerHeight));
for (const el of layers)
  el.style.transform =
    `translate3d(0, ${-p * el.dataset.amp}px, 0)`;

One rAF-throttled scroll handler paints every layer plus a slower-than-scroll drift on the hero photograph. Each layer is anchored with a negative bottom equal to its amplitude, so no seam ever shows. The mist bands add a second, time-based motion — a 34–46s CSS translateX loop — so the landscape breathes even when you stop scrolling. And because the whole thing reads through the transparent “slow manifesto” section, the ridges become the page’s quietest moment instead of a background you scroll past.

Reduced motion: if prefers-reduced-motion is set, the scroll listener is never attached, every CSS animation is killed with one media query, and the layers hold their designed positions. The site is fully legible as a still image.

Details that matter

Ship it on GitHub Pages

Astro builds into docs/ so Pages can serve the main branch directly — no Actions workflow, no second branch. Three lines of config do all the path work:

// astro.config.mjs
export default defineConfig({
  site: 'https://bswxyz.github.io',
  base: '/fernweh-cabins',
  outDir: './docs',
});

Internal links use import.meta.env.BASE_URL so nothing 404s under the /fernweh-cabins/ prefix, and an empty .nojekyll in public/ keeps Pages from mangling the output. Then:

npm run build
git add -A && git commit
gh repo create bswxyz/fernweh-cabins --public --source . --push
gh api --method POST /repos/bswxyz/fernweh-cabins/pages \
  -f 'source[branch]=main' -f 'source[path]=/docs'
# live at https://bswxyz.github.io/fernweh-cabins/ in ~a minute

Fernweh is a design-showcase concept — the cabins are fictional, the booking card is a teaser, and the only photograph is AI-generated. See the repository README for the full demo-vs-real map.

← Back to Fernweh