Skip to content

perf(navigator): SSR the chrome to kill the mount flash on /tutorial-navigator - #1974

Merged
jung-thomas merged 1 commit into
DEVfrom
worktree-navigator-ssr-chrome
Aug 22, 2026
Merged

jung-thomas merged 1 commit into
DEVfrom
worktree-navigator-ssr-chrome

Conversation

@jung-thomas

Copy link
Copy Markdown
Contributor

Problem

On /tutorial-navigator, only the 24-card grid was server-rendered. TutorialNavigator.vue renders the full chrome — hero + search + open filter panel + toolbar — above the grid on mount, shoving the first card down 672px (desktop) / 1434px (mobile). That jump is the flash Tom reported.

#1850 (synchronous mount) and #1688 (UI5 chrome cloak) are both live and don't cover this — the SSR preview only mirrored the grid, never the chrome. Verified on DEV: SSR first-card top 120px → mounted 792px (desktop) / 1554px (mobile).

Fix (option 1a)

  • list.html SSRs the same chrome (mirroring the Vue markup + class names) so the grid sits at its final offset from first paint. Toolbar counts computed from browse.json (real totals for crawlers); Topic/Product facet lists render empty (populated at runtime from /build/navigator), Experience/Type are static real values.
  • The chrome's layout CSS lived in the component's <style scoped> block (injected only when the island JS runs, after first paint), so it's added to a render-blocking @navigator-chrome region in home.css — loaded on this page and kept loaded post-mount, so the mounted Vue tree (same classes) lands identically. Prefixed under .tutorial-navigator so nothing leaks onto the homepage (which also loads home.css). Empty SSR facet lists reserve the runtime lists' capped height via min-height.

Verification

  • RED: the new test/e2e/navigator-no-mount-shift.test.js fails on DEV today — SSR-vs-mounted first-card delta 672px/1434px (tolerance 8px).
  • GREEN (local Hugo build): SSR first-card top now 792px desktop / 1554px mobile — exact match to DEV mounted offsets. Screenshots confirm the pre-mount paint is fully styled at both widths (no jump, no flash-of-unstyled).

Caveats / follow-ups (why draft)

  1. Mounted zero-shift not yet observed live — the local loop can't mount Vue (no CAP backend / island build in the worktree). It's proven by construction (identical classes + render-blocking CSS) and by the SSR offset matching the measured production mounted offset. The committed e2e is the gate and runs post-DEV-deploy — needs a DEV deploy to flip green.
  2. Scoped chrome CSS kept as a byte-identical duplicate in TutorialNavigator.vue rather than deleted. Removing it (the true "relocate", single source of truth) changes the mounted view's CSS source, which can't be verified without a mounted build — should land together with the mounted DEV check. As-is, mounted rendering is unchanged (zero risk).

🤖 Generated with Claude Code

…unt shift

The /tutorial-navigator page SSR'd only the 24-card grid; TutorialNavigator.vue
rendered the full chrome (hero + search + open filter panel + toolbar) ABOVE it
on mount, shoving the first card down 672px (desktop) / 1434px (mobile) — the
visible flash Tom reported. #1850 (sync mount) and #1688 (UI5 cloak) never
covered this; the SSR preview only mirrored the grid, not the chrome.

Fix (option 1a): SSR the same chrome in list.html (mirroring the Vue markup +
class names) so the grid sits at its final offset from first paint. The chrome's
layout CSS lived in the component's <style scoped> block (injected only when the
island JS runs, after first paint), so it is relocated to the render-blocking
@navigator-chrome region in home.css — loaded on this page and kept loaded
post-mount, so the mounted Vue tree (same classes) lands identically. Empty SSR
Topic/Product lists reserve the runtime lists' capped height via min-height.

Verified on a local Hugo build: SSR first-card top now 792px (desktop) / 1554px
(mobile), matching the DEV mounted offsets exactly (was 120px both). Adds a
post-deploy e2e (navigator-no-mount-shift) asserting SSR==mounted first-card top
at both widths; it fails on DEV today (delta 672/1434 vs 8px tolerance).

Note: scoped chrome rules kept as a byte-identical duplicate for now (mounted
render unchanged, zero risk); removing them is a mechanical cleanup to land with
a mounted DEV check.
@jung-thomas
jung-thomas marked this pull request as ready for review August 22, 2026 14:25
@jung-thomas
jung-thomas merged commit 693a4f8 into DEV Aug 22, 2026
3 of 4 checks passed
@jung-thomas
jung-thomas deleted the worktree-navigator-ssr-chrome branch August 22, 2026 14:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant