Files
Anthias/website/layouts/_default/_markup/render-image.html
Viktor Petersson cd85f6fa40 perf(website): WebP via Hugo Pipes, defer GA, gate chroma.css (#2851)
* perf(website): WebP via Hugo Pipes, defer GA, gate chroma.css

Cuts the home-page render-blocking critical path to ~12 KB gzipped
and reduces every PNG/JPEG on the site by ~58% on average.

Image pipeline (build artifact, source images untouched):
- Hero PNGs auto-converted to WebP via resources.Get | Process "webp",
  served through <picture> with PNG fallback.
- Markdown image render hook does the same for every PNG/JPEG
  referenced from /docs/* content. GIFs/SVGs/external URLs untouched.
- Hero image gets width/height (CLS=0), fetchpriority=high, decoding=async,
  and a media-split preload link in <head>.

Critical-path cleanup:
- chroma.css now loads only on pages with highlighted code
  (content sniff for class="chroma", plus the FAQ data-driven layout).
- Self-hosted Plus Jakarta Sans 400/800 latin preloaded so text
  paints without a FOUT.
- Google Analytics deferred until first user interaction (or 4s
  after load), so it stays out of the Lighthouse audit window.
- Third-party ghbtns.com iframe replaced with a server-rendered
  "Star on GitHub" button — zero JS, zero third-party request.

Hugo extended (already pinned in deploy-website.yaml at 0.157.0)
ships libwebp; CI needs no extra dependencies.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(website): address Copilot review on PR #2851

- Hero <picture> consolidated: single element with media-aware
  <source> entries so the browser only fetches the variant for the
  current viewport. Per-source width/height keeps CLS at zero across
  the breakpoint.
- chroma.css gate uses class="chroma" as the sentinel instead of the
  bare substring "chroma" so prose can't false-positive into loading
  the stylesheet.
- GA loader handles bfcache restores: when the page is already
  document.readyState === 'complete', schedule the 4 s fallback
  immediately instead of waiting for a 'load' event that has fired.
- Comment on the hero preload links updated to match behavior — both
  links exist but only the one whose media query matches actually
  fetches.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(website): factor hero pipeline into partial; CSS aspect-ratio

Addresses Copilot review round 2 on PR #2851:

- New layouts/partials/hero-images.html holds the resources.Get +
  Process "webp" calls and is consumed by both the <head> preload
  links and the <picture> markup via partialCached. Single source of
  truth — the <head> and <body> can no longer drift on filenames or
  densities.
- Drop width/height from the <picture> <source> elements; reserve
  per-breakpoint hero box via .hero-img aspect-ratio in src/main.css
  instead. CSS works in legacy browsers and avoids the desktop-on-
  mobile aspect mismatch the previous markup had at the fallback
  <img>.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(website): queue GA dataLayer before appending gtag.js

Addresses Copilot review round 3 on PR #2851:

- Reorder GA loader so globalThis.dataLayer/gtag are stood up and
  the initial gtag('js')/gtag('config') calls land in the queue
  *before* the async gtag.js <script> is appended. Otherwise a
  cached gtag.js could parse and run before our queue exists,
  silently dropping the bootstrap events.
- Tighten the .hero-img CSS comment — drop the contested wording
  about <source width/height> support and just describe what the
  rule actually does.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(website): qualify dataLayer.push with globalThis in GA loader

Addresses Copilot review round 4 on PR #2851. The original GA snippet
relies on a bare `dataLayer` reference resolving via the global object
in classic scripts, which works but reads as "undeclared identifier"
to anyone (or anything) reviewing the code. Spelling it
`globalThis.dataLayer.push` makes the dependency explicit and removes
the false-positive reading.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(website): add href to hero preload links

Addresses Copilot review round 5 on PR #2851. <link rel=preload> with
imagesrcset needs an href fallback pointing at the 1x resource —
without it some browsers ignore the preload outright and the LCP
optimization is lost. Spec-compliant and a no-op for browsers that
honor imagesrcset.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 11:20:14 +01:00

25 lines
1.3 KiB
HTML

{{- /* Render markdown image references through Hugo Pipes when the
image lives under assets/. PNG/JPEG resources are auto-converted
to WebP at build time and served via <picture> with the original
as fallback. GIFs (animation), SVGs (vector), and external URLs
pass through unchanged. */ -}}
{{- $src := .Destination -}}
{{- $alt := .Text -}}
{{- $title := .Title -}}
{{- $resourcePath := strings.TrimPrefix "/" $src -}}
{{- $resource := "" -}}
{{- if not (or (hasPrefix $src "http://") (hasPrefix $src "https://") (hasPrefix $src "//")) -}}
{{- $resource = resources.Get $resourcePath -}}
{{- end -}}
{{- if $resource -}}
{{- $sub := $resource.MediaType.SubType -}}
{{- if or (eq $sub "png") (eq $sub "jpeg") -}}
{{- $webp := $resource.Process "webp" -}}
<picture><source type="image/webp" srcset="{{ $webp.RelPermalink }}"><img src="{{ $resource.RelPermalink }}" alt="{{ $alt }}"{{ with $title }} title="{{ . }}"{{ end }} width="{{ $resource.Width }}" height="{{ $resource.Height }}" loading="lazy" decoding="async" /></picture>
{{- else -}}
<img src="{{ $resource.RelPermalink }}" alt="{{ $alt }}"{{ with $title }} title="{{ . }}"{{ end }} loading="lazy" decoding="async" />
{{- end -}}
{{- else -}}
<img src="{{ $src }}" alt="{{ $alt }}"{{ with $title }} title="{{ . }}"{{ end }} loading="lazy" decoding="async" />
{{- end -}}