Files
nexu-io-open-design/apps/landing-page/public/cta-bg.webp
lefarcen 101a42cb0d perf(landing): cut homepage LCP — defer JS + below-fold art, shrink backdrops, early locale redirect (#4987)
* perf(landing): load matter-js + cobe on demand, not inlined

The homepage inlined the full matter-js physics engine (~83KB) and the
cobe globe runtime (~13KB) into every document, even though both are
below-the-fold decorations. That shipped ~96KB of decorative JavaScript
in the critical HTML to every visitor — phones and reduced-motion
readers included — inflating the document the browser must download and
parse before the page settles.

Vendor both runtimes to public/enhancers/*.js (regenerated from
node_modules on every build/dev, so they always match the installed
versions) and inject each one via IntersectionObserver only when its
section nears the viewport. Reduced-motion readers skip the matter-js
download entirely and just get the server-rendered banner.

The site still ships zero Astro-bundled / ES-module JavaScript — these
are hand-vendored classic scripts injected by a runtime-created script
element, so the `Verify zero external JavaScript` gate stays green.

Homepage document: 358KB -> 264KB (brotli wire 94KB -> 51KB); the two
runtimes now load lazily, cached immutably per versioned URL.

* perf(landing): defer below-the-fold homepage art off the LCP path

The Cloudflare RUM network trace showed homepage LCP (~2.4s, the hero
webp) was starved of bandwidth: ~1.9MB of images — most of it below the
fold — was fetched in the first ~700ms and competed with the hero for
the connection. Culprits: two full-bleed section backdrops loaded via
CSS `::before` (lab-stage-art 454KB, cta-bg 335KB), the Labs dock
preloading ~500KB of preview stills on init, the contributor orbit
building ~350KB of avatars on init, and a few raw eager <img>.

Defer all of it until its section nears the viewport:

- precise-lazyload gains a `data-precise-bg` mode (adds `.precise-bg-in`,
  which the CSS uses to gate the `::before` background-image — you can't
  set a pseudo-element's background from JS) and a configurable
  `imgRootMargin`. The homepage mounts it at 600px instead of the catalog
  default 1500px so its heavy art doesn't join the initial burst.
- A shared `__whenNear(selector, cb)` helper gates the Labs dock
  (`enhanceLabSwitch`) and contributor orbit (`enhanceContributorOrbit`)
  init behind an IntersectionObserver.
- `cta-window` gets `loading="lazy"`; the near-fold hero product shot gets
  `fetchpriority="low"` so the hero-bg (the LCP element) wins the pipe.

Verified in a headless browser against the built site: initial requests
drop 50 -> 20, the ~1.76MB of below-fold art is withheld until scrolled
to (then loads on cue), CLS stays 0.001, and every enhancer still works
(section backdrops paint, Labs dock switches, falling-text physics runs
23/23, globe renders, 16 contributor avatars build).

* perf(landing): shrink oversized homepage backdrops to display size

The two full-bleed section backdrops shipped far more pixels than they
ever render: cta-bg was 2776×1554 (335KB) and lab-stage-art 2262×1358
(454KB), but both display at roughly 960px CSS — 1920px covers even a 2×
retina panel. Re-encoded at 1920px wide, q82 webp:

  cta-bg.webp        335KB → 174KB  (-48%)
  lab-stage-art.webp 454KB → 276KB  (-39%)

~340KB saved with no visible quality loss (verified at 2× device scale —
the painterly murals stay crisp). Only the pixel dimensions dropped; the
quality factor is high. The homepage's gated `background-image` URLs bump
their `?v=` so the immutable edge cache serves the new files immediately.

* perf(landing): redirect locale from <head> so the wrong-locale first load aborts

A non-English visitor hitting the English root ran the locale
auto-redirect from a <script> late in <body>, so the browser had already
streamed most of the document (and kicked off its head resources) before
bouncing to /zh/ etc. — a wasted near-full first load.

Move the auto-redirect decision to the top of <head> (first thing after
the viewport/theme-color meta) so it fires as the document starts
streaming and aborts the rest of the wrong-locale load. The switcher UI
wiring, which needs the DOM, now defers itself to DOMContentLoaded, so it
works whether the script runs in <head> (homepage) or late in <body>
(other layouts). All existing guards are untouched — canonical-only pages,
autoredirect-off pages, and already-localized roots still no-op, so
English and crawler traffic pays only a tiny inline read.

Verified: root / still redirects to /zh/ for a zh browser with no loop,
and all 11 language-switcher links bind correctly post-DOMContentLoaded.
2026-07-01 06:49:03 +00:00

170 KiB
1920x1075px