Design guide · drawn entirely in code
01Concept & art direction
Corniche 2400 is a fictional ski hotel on a ridge at 2,400 metres — above the valley fog, not below the summit. The whole site is organised by altitude: the hero is the summit, the rooms sit at 2,400 m, the journey climbs from the valley at 1,447 m, and a live altitude rail on the left descends as you scroll. Every visual is code — layered SVG for the panorama, canvas for the snow — because a hand-drawn mountain says "boutique" where a stock photo says "template".
02Palette & type
Sleep above the weather.
Archivo · variable width 62–125
Display and UI. Stretched to width 116–122 for headlines it reads like Swiss resort signage — crisp, modernist, cold-air clear. One family, many voices, via the wdth axis.
The valley makes its own weather.
Vollkorn · 400 / 500
The chalet voice. Warm, sturdy old-style serif for stories, room descriptions and anything spoken by the hotel rather than the mountain.
2 400 m · 16:40
Sometype Mono · 400–600
The instrument voice. Altitudes, timetables, prices, labels — anything you would find engraved on a lift station sign.
03Techniques
The hero is one inline SVG with seven stacked groups — sun, two mountain ranges, a cloud sea behind the hotel ridge, the ridge and hotel, a second cloud sea in front of the ridge, and the foreground where you stand. Each group carries a data-depth factor; on scroll, one rAF-throttled handler translates each layer down by scrollY × depth, so far layers lag and the mountain separates into planes. Putting one cloud layer in front of the ridge is what sells "above the clouds".
<!-- back clouds → ridge+hotel → front clouds: the sandwich is the trick --> <g class="layer" data-depth="0.42"> …back cloud sea… </g> <g class="layer" data-depth="0.30"> …ridge, hotel, cable car… </g> <g class="layer" data-depth="0.24"> …front cloud sea… </g> // one scroll handler, throttled with requestAnimationFrame layer.style.transform = "translate3d(0," + (scrollY * depth).toFixed(1) + "px,0)";
Snow is a plain 2D-canvas particle system — ~140 flakes with individual fall speed and a sine-wave sway — that only draws while the hero is on screen. Clouds drift on infinite alternating CSS keyframes. The cable-car cabin rides its own SVG path via getPointAtLength with a smoothstepped ping-pong, an 18-second round trip. All of it checks prefers-reduced-motion first: with motion reduced, the clouds hold still, the snow never starts, and the cabin parks a third of the way down the cable.
// the cabin commutes along the cable path var t = pingPong(now / 18000); // 0→1→0 t = t * t * (3 - 2 * t); // smoothstep easing var pt = cable.getPointAtLength(t * cable.getTotalLength()); cabin.setAttribute("transform", "translate(" + pt.x + " " + (pt.y + 6) + ")");
The room cards are lift tickets: a dashed perforation, two punched holes cut with pseudo-elements the exact colour of the section behind them, and a stub that slides 5 px along the perforation on hover — as if about to tear.
04Iteration
Correctness and composition: the first screenshots showed the hotel oversized, dead-centre, and colliding with the headline on desktop and mobile. Redrew the ridge with a lower plateau, rescaled the hotel to two-thirds size and moved it right of the text column, and resized the cable car to match — the hero finally read as headline, mountain, booking bar, each owning its own band.
Elevation: gave the ridge its namesake — a drifted snow cornice along the whole top edge with a wind-lip overhanging the drop; darkened the far range so its summits stopped dissolving into the sky; carved two first-tracks ski lines into the foreground slope; and made the stat band count up to its readings like instruments settling.
Taste: removed the embossed shadow from the ticket punch holes so they read as clean die-cuts, re-verified the reduced-motion path end to end — clouds hold, snow never starts, cabin parks — and confirmed both pages ship with zero console errors and no horizontal overflow at 1440 and 390 px.
05Do this yourself
data-depth on each group. Get the overlap right (something must pass in front of your subject) before styling anything else.prefers-reduced-motion and keyboard focus last — the calm version of the page should still feel finished, not switched off.