meridian.intlo.com · front-end build
Meridian

An invented product, built to real standards

A dark editorial marketing site for an observability console that doesn’t exist — recreated from a reference design as a maintainable Next.js codebase rather than a static dump, and held to the constraints a real property would be held to.

Meridian is fiction. There is no Meridian company, no customers, and no incidents behind the figures the site displays — the copy, the studies and the dashboards are written content, the way a set is built. What follows describes the engineering, and every number in it was read off the repository.

3
runtime dependencies
0
webfonts, analytics or third-party requests
13
prerendered pages
848 KB
the entire intro film
01 · The brief

Recreate it, don’t re-host it

The starting point was a reference landing page and a set of screenshots. The rule was that none of it could be served as-is: no iframe, no copied bundles, no shipping the original HTML. The page had to be rebuilt from its public presentation as a codebase somebody else could pick up.

What the repository holds
SurfaceCountNotes
Landing page1Hero dashboard, seismic tape, pricing, comparison
Customer studies3Basin, Compass, Tideline — written as part of the fiction
Blog5Index plus five prerendered posts
Legal2Terms and privacy, sharing one page shell
Components25All under components/, none deeper than one level
CSS1,173 linesTwo plain stylesheets, 32 custom properties

The self-imposed constraint that shaped everything else: three runtime dependencies — Next, React and React DOM. No UI kit, no animation library, no icon package, no webfont. Each of those would have been the easy answer to something on the page, and declining them is most of what makes the build interesting.

02 · The intro film

Five ways a ten-second video refuses to play

The site opens on a short film that hands off to the dashboard when it ends. Almost none of the difficulty was in the playback — it was in the browser’s autoplay rules and in the gap between server-rendered markup and the moment React takes over.

  1. Keep muted static

    Autoplay eligibility is decided from the markup. Binding muted to React state loses it — the attribute has to be literal in JSX, and sound gets toggled imperatively on the element afterwards.

  2. Don't listen for playing

    The first stall guard waited for a playing event. Server-rendered autoPlay starts the video before React attaches the listener, so the event had already fired and the guard killed the film at 4.8 s.

  3. Check progress instead

    The rewritten guard reads currentTime when its timeout expires. It cannot miss an event it never subscribes to, and it still bails on a genuinely stalled load.

  4. Hand off on an event

    The film dispatches meridian:intro-done and the dashboard boot sequence starts from that, not from a matching setTimeout that would drift out of sync the moment the clip changed length.

  5. Show it once

    Completion writes a sessionStorage key, and a repeat visit takes a fast path that never mounts the film — the dashboard boots in 1.5 s instead of 11.

The second and third steps are the same bug seen twice. An event listener attached in useEffect is attached late — anything the browser did between HTML parse and hydration has already happened, unobserved. Guards that poll state survive that window; guards that wait for an event do not.

03 · Paying for the film

Two thirds off, measured rather than guessed

A 2.35 MB autoplaying video is a real cost on the first paint of a page whose whole argument is restraint. Six encodes were produced — H.264 across CRF 24 to 30, and VP9 at two quality levels — and each was scored against the source rather than eyeballed.

Before
2.35 MB on every first visit
After
848 KB — 64.8% smaller
Cost
SSIM 0.985 against the source
The candidates that decided it
EncodeSizeVerdict
Source2.35 MBAs delivered
H.264 · CRF 28848 KBShipped — SSIM 0.985 against source
VP9Smaller at equal quality, but a second asset to serve

SSIM was the tiebreak because the alternative — deciding by eye at 1× speed — reliably prefers the largest file. A structural similarity of 0.985 says the difference between the shipped encode and the source is compression noise, not lost detail, and a pixel-difference pass confirmed the two were still frame-aligned.

04 · Motion

Animated throughout, and switchable off

The page counts figures up, grows a bar chart on load, boots a dashboard in sequence and drives a seismograph from scroll position. All of it has a documented still state.

One hook, five components

Five components read a shared useReducedMotion hook and return their static arrangement when it is set — the count-up figures, the growing bar chart, the scroll-driven seismic tape, the dispatch list and the intro film.

CSS carries its own contract

Five prefers-reduced-motion blocks across the two stylesheets cover the animation the hook can’t reach, including the scanning gradient on the footer credit.

Scroll drives, rAF throttles

The seismic section maps scroll progress onto a drift figure and a horizontal offset, coalescing every scroll and resize into a single requestAnimationFrame rather than writing style on each event.

No animation library

Reveals, count-ups, the boot sequence and the seismograph are hand-rolled against the platform. The dependency list stayed at three because nothing here needed a fourth.

05 · Stack

What it’s built on

Layer by layer, and the reason for each
LayerChoiceWhy
FrameworkNext.js 16 · React 19App Router; every page prerenders
LanguageTypeScript, strictNo any, and no suppressed errors
StylingPlain CSSCustom properties as tokens — no framework
TypeSystem stacksNo webfont request on any route
GraphicsInline SVGCharts and marks authored in the markup
HostingVercelPush to main deploys meridian.intlo.com

Every page prerenders and the site makes no request to a third party at runtime — no font host, no analytics beacon, no embed. A visitor loading Meridian talks to exactly one origin.