mirror of
https://github.com/nexu-io/open-design.git
synced 2026-06-01 03:14:35 +07:00
* perf(landing): edge-cache HTML and precise-load thumbnails Without `public/_headers` Cloudflare Pages serves every HTML with `cf-cache-status: DYNAMIC` so each request roundtrips to the Pages origin — observed TTFB 660–900ms from Seattle, worse from Asia. With `s-maxage=3600, stale-while-revalidate=86400` HTML stays cached at the edge between deploys (CF Pages auto-purges on every deploy so freshness is unchanged in practice), and `_astro/` hash bundles flip to `immutable` so the existing 4h+must-revalidate roundtrips go away. For thumbnails, native `loading="lazy"` is browser-decided — Chrome over-prefetches (1250–3000px), Safari fires near in-viewport. A new `<LazyImg>` Astro component and global IntersectionObserver (rootMargin 300px for images, 600px for videos) replaces all 10 site-wide `loading="lazy"` usages with precise control. Above-the-fold slots (first 4 rows, detail-page hero previews) opt into `eager` or `priority` to skip the IO roundtrip. Homepage hero LCP gets `<link rel="preload" imagesrcset>`, a 4-step `srcset` (768/1280/1920/2560) plus `fetchpriority="high"` so retina devices stop repainting from the 1024-only variant — was the P99 long tail. Verified: `pnpm guard` 6/6, `pnpm typecheck` 0 errors, `pnpm build` 865 pages 28s, generated `out/index.html` contains the preload link and 15 `data-precise-src` thumbnails, `out/plugins/index.html` has 95 precise-loaded thumbnails plus the IO script. * perf(landing): logo to webp + parallelize Google Fonts load Two HAR-validated wins on top of the edge-cache / precise-load commit: logo: 500x500 192KB PNG → 200x200 7.5KB WebP. Footer/header actually render at 36x36, so the source is 5x larger than necessary at the display size and ships RGBA PNG bytes for what reads as a flat graphic. WebP at q=85 keeps the gradient ring crisp at every DPR we care about. fonts: globals.css used `@import url(...)` for Google Fonts, which serialized HTML → CSS → fonts.googleapis.com/css2 → fonts.gstatic.com/ woff2. HAR measured 953ms for the fonts CSS plus 400–800ms per woff2 × 4 — close to 3s before text could render in the intended family, even with display=swap. Moving to `<link>` + `<link rel=preconnect>` in each page's <head> lets the fonts CSS fetch race the HTML body parse, and warms the TLS handshake to gstatic.com so woff2 requests don't pay DNS+TLS at request time. A shared `font-stylesheet.astro` keeps the four-family URL canonical across all five entry points (index, sub-page-layout, plugins/index, plugins/[slug], blog/index, blog/[slug]). og.astro already had this treatment.
96 lines
2.6 KiB
Text
96 lines
2.6 KiB
Text
---
|
||
/*
|
||
* <LazyImg> — the only image primitive used for non-LCP thumbnails on this
|
||
* site. Replaces every previous `<img loading="lazy">` site-wide.
|
||
*
|
||
* Modes:
|
||
* - default (precise): outputs `<img data-precise-src>` with a 120-byte
|
||
* transparent SVG as the initial `src`. The global IntersectionObserver
|
||
* in `precise-lazyload.astro` swaps `data-precise-src` → `src` once the
|
||
* element enters viewport ± 300px. Use this for *every* thumbnail
|
||
* below the fold. Net effect vs. `loading="lazy"`: Chrome stops
|
||
* over-prefetching (default rootMargin 1250–3000px), Safari stops
|
||
* under-prefetching (essentially in-viewport), and CLS is unaffected
|
||
* because callers wrap us in an aspect-ratio container.
|
||
*
|
||
* - priority: outputs `<img src loading="eager" fetchpriority="high">`
|
||
* for the rare thumbnail that's known to be above the fold AND on the
|
||
* LCP candidate path. Use sparingly — the hero image on `/` is the
|
||
* canonical case (and is hand-written, not via this component).
|
||
*
|
||
* - eager (default for above-the-fold non-LCP): outputs `<img src>` with
|
||
* no lazy behavior. Faster than precise mode for the first row of a
|
||
* visible grid; doesn't compete with the LCP for fetchpriority.
|
||
*
|
||
* Why a component and not a helper function:
|
||
* `srcset` + `sizes` are awkward to inline in JSX/Astro; a component
|
||
* gives us one place to type-check and apply width/height (for CLS).
|
||
*/
|
||
import { PRECISE_LAZY_PLACEHOLDER } from '../image-assets';
|
||
|
||
export interface Props {
|
||
src: string;
|
||
alt: string;
|
||
/**
|
||
* 'precise' (default): wait until viewport ± 300px.
|
||
* 'eager': load immediately, normal priority.
|
||
* 'priority': load immediately, fetchpriority="high".
|
||
*/
|
||
loading?: 'precise' | 'eager' | 'priority';
|
||
srcset?: string;
|
||
sizes?: string;
|
||
width?: number | string;
|
||
height?: number | string;
|
||
class?: string;
|
||
}
|
||
|
||
const {
|
||
src,
|
||
alt,
|
||
loading = 'precise',
|
||
srcset,
|
||
sizes,
|
||
width,
|
||
height,
|
||
class: className,
|
||
} = Astro.props;
|
||
---
|
||
|
||
{loading === 'precise' ? (
|
||
<img
|
||
src={PRECISE_LAZY_PLACEHOLDER}
|
||
data-precise-src={src}
|
||
data-precise-srcset={srcset}
|
||
sizes={sizes}
|
||
alt={alt}
|
||
width={width}
|
||
height={height}
|
||
class={className}
|
||
decoding="async"
|
||
/>
|
||
) : loading === 'priority' ? (
|
||
<img
|
||
src={src}
|
||
srcset={srcset}
|
||
sizes={sizes}
|
||
alt={alt}
|
||
width={width}
|
||
height={height}
|
||
class={className}
|
||
loading="eager"
|
||
fetchpriority="high"
|
||
decoding="async"
|
||
/>
|
||
) : (
|
||
<img
|
||
src={src}
|
||
srcset={srcset}
|
||
sizes={sizes}
|
||
alt={alt}
|
||
width={width}
|
||
height={height}
|
||
class={className}
|
||
loading="eager"
|
||
decoding="async"
|
||
/>
|
||
)}
|