Skip to content

perf(docsite): load the /themes showcase photos through the image optimizer, ahead of a theme switch - #11369

Draft
nynexman4464 wants to merge 1 commit into
mainfrom
nynexman4464/perf/theme-showcase-images
Draft

nynexman4464 wants to merge 1 commit into
mainfrom
nynexman4464/perf/theme-showcase-images

Conversation

@nynexman4464

@nynexman4464 nynexman4464 commented Nov 12, 2026 •

Copy link
Copy Markdown
Contributor

Primary intent

Make a theme switch on /themes show its product photos at once. This is a docsite performance change: it restores no broken behavior and adds no capability, so the other templates do not fit.

User or maintainer impact

Anyone who picks a theme on /themes. The showcase swaps in six product photos for the theme. Each one is a PNG of 0.5 to 1.2 MB under public/images/<theme>/, and the page requests it only when the theme renders. The product cards stay empty until the files arrive.

Headless Chromium at 1512×882, device scale 2. "Before" is the live site, "after" is a local production build (next start) of this branch. Times run from the click on a theme to the moment the three visible photos are complete.

Before After, photos warmed in idle time After, data-saving mode (no warm-up)
Bytes requested by one theme switch 3.7 to 6.7 MB of PNG 0 68 to 182 KB of WebP
Photos shown, no throttling 0.39 to 0.86 s 0.15 to 0.25 s not measured
Photos shown, 20 Mbps / 40 ms 1.7 to 3.0 s 0.14 to 0.21 s 0.23 to 0.34 s

The warm-up requests the 30 photos of the five themes with bespoke content once, in idle time: 591 KB in all. That is less than one photo costs today.

Problem and solution fit

  1. Why are the product cards empty after a theme switch? The six photos of the theme are not requested until it renders, and they are 3.7 to 6.7 MB.
  2. Why are they that large? They are served as the PNG files under public/images/<theme>/, 700 to 1029 pixels wide, for a card that shows them about 280 CSS pixels wide.
  3. Why did perf(docsite): serve template preview images through the image optimizer #11320 not cover them? Its loader rewrites the template fixtures under /template-assets/. These photos are docsite content that themeShowcaseContent.ts passes to the showcase template through its images prop.

Every delta maps to that:

  • themeShowcaseContent.ts names each photo by its image optimizer URL at 828 pixels wide. The optimizer does not enlarge, so the 700-pixel photos keep their pixel size, and the Matcha photos (1029 pixels) come back 828 wide.
  • src/lib/optimizedImageUrl.mjs writes the optimizer URL. The template image loader from perf(docsite): serve template preview images through the image optimizer #11320 now calls it too, so the URL form has one definition. templateImageUrl keeps its width parameter from perf(docsite): render the landing templates card previews at build time #11292 and gives the same output as before, so the landing preview generator's URLs do not change.
  • themeShowcaseWarmup.ts requests every bespoke photo once the page is idle and returns the images. ThemePackagePage holds them in a ref while it is mounted, so a theme switch finds them in the browser's cache and sends no request. It does nothing when the browser reports a data-saving mode or a 2G link, and it cancels a warm-up that has not started when the page unmounts.

Not changed: the Neutral defaults (template fixtures, already optimized by #11320), the theme picker thumbnails, and the unused @2x files.

Authority

  • spec:AST-033 governs the docsite. It has no requirement on how /themes loads its showcase photos, and this changes no behavior it describes: the same photos show for the same theme.
  • spec:AST-028 FR4 to FR6 govern template fixtures under /template-assets/. These photos are not template fixtures. The showcase template and its fixture references are not edited.
  • architecture:react-component-runtime INV3 and INV5: the one new Effect names the system it syncs (the browser's image cache), and its cleanup cancels the pending warm-up and releases the held images.

Change and evidence

  • A theme switch after the warm-up sends no photo request in any of the five themes, and the three visible photos are complete 0.14 to 0.25 s after the click. That time is the showcase's re-render.
  • With navigator.connection.saveData set, the page requests no photo ahead of time, and a switch requests the theme's six photos through the optimizer (68 to 182 KB).
  • Every photo response is image/webp with status 200. The photos are 700 or 704 pixels wide as before, and 828 for Matcha.
  • A screenshot of the Stone showcase shows the same three photos as the live site.
  • The docsite build passes, and the template image loader's tests pass unchanged. After the rebase onto perf(docsite): render the landing templates card previews at build time #11292, the landing preview generator still writes its photo URLs at 640 pixels wide.

Preview deployment of d237024a36, the same measurement, the five themes in each row. That commit is this change before its rebase onto #11292; the rebase only resolved templateImageUrl, and the same measurement on the rebased head follows.

Case Photos requested in idle time Requests after a theme click Three visible photos complete after the click
No throttling (two runs) 30, 596 KB, image/webp, status 200 0 0.13 to 0.19 s
20 Mbps / 40 ms 30, 595 KB 0 0.13 to 0.23 s
20 Mbps / 40 ms, data-saving mode 0 6 per theme, 69 to 183 KB 0.25 to 0.33 s

Scope

Components: docsite /themes showcase content, docsite ThemePackagePage, docsite template image loader

  • This PR has one primary intent.
  • Independently removable deltas were removed or split.
  • Public text and artifacts contain no internal Meta context.

Testing

pnpm -F @astryxdesign/docsite exec vitest run src/__tests__/theme-showcase-photos.test.ts src/__tests__/theme-showcase-warmup.test.ts src/__tests__/template-image-loader.test.ts src/__tests__/landing-template-previews.test.ts
pnpm -F @astryxdesign/docsite typecheck
pnpm -F @astryxdesign/docsite build
pnpm review:pre

theme-showcase-photos.test.ts checks that each of the 30 photos is an optimizer URL for a file that public serves, at a width and quality the optimizer allows, and that the warm-up list holds each photo once. theme-showcase-warmup.test.ts checks that the warm-up waits for idle time, requests every photo, can be cancelled, does nothing in a data-saving mode or on 2G, and falls back to a timer without requestIdleCallback. pnpm review:pre reports 0 findings.

Not tested: Safari and Firefox.

@vercel

vercel Bot commented Nov 12, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
astryx Building Building Preview Nov 12, 2026 7:56am UTC

Request Review

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Meta Open Source bot. label Nov 12, 2026
@github-actions github-actions Bot added the needs:design-review Affects visuals — Design should review label Nov 12, 2026
@astracat-bot

astracat-bot Bot commented Nov 12, 2026 •

Copy link
Copy Markdown
Contributor

AI review status for this pull request.

Review status Updated
🔄 In progress (for maintainers only) Nov 12, 2026, 7:56 AM UTC

@github-actions

github-actions Bot commented Nov 12, 2026 •

Copy link
Copy Markdown
Contributor

PR Analysis Report

Current run: The current CI run concluded skipped. Current analysis is unavailable.


Generated by PR Enrichment workflow | View current CI run

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes requested: one fix is needed before approval. The rest of the change matches its description: the photo URLs, the shared URL helper (the template loader's output is unchanged), the data-saving and 2G checks, and the unmount cleanup.

  • apps/docsite/src/components/themeShowcaseWarmup.ts lines 54-59 re-implement whenIdle from apps/docsite/src/lib/searchWarmup.ts, so please build the cancel from whenIdle(load, …) and delete the local copy, which keeps the docsite on one idle scheduler (the hero reel's photo warm-up in HeroThemeReel.tsx could share the same helper).

Non-blocking: the header of themeShowcaseWarmup.ts says the 30 photos are about 0.7 MB in all, while the description and commit message say about 0.6 MB.

Claims and files checked
  • Claim: the showcase PNGs are 0.5 to 1.2 MB each and 700 to 1029 px wide. Verified: 559 KB to 1.19 MB; 700, 704 and 1029 px.
  • Claim: one theme switch requests 3.7 to 6.7 MB of PNG today. Verified approximately: the six files total 3.8 to 6.8 MB per theme.
  • Claim: each photo is named by an optimizer URL at 828 px, so 700 px photos keep their size and Matcha comes back 828 wide. Verified in code (themeShowcaseContent.ts lines 28-32); the served widths were not observed.
  • Claim: one function writes the optimizer URL and templateImageUrl gives the same output. Verified: optimizedImageUrl.mjs lines 24-26 and template-image-loader.mjs lines 38-40 produce the same string as before.
  • Claim: the warm-up requests every bespoke photo once in idle time and the page holds the images while mounted. Verified: themeShowcaseWarmup.ts lines 46-59 and ThemePackagePage.tsx lines 459-467.
  • Claim: the warm-up does nothing in a data-saving mode or on 2G. Verified: themeShowcaseWarmup.ts lines 22-28 and the new test.
  • Claim: unmounting cancels a warm-up that has not started. Verified: ThemePackagePage.tsx lines 463-466.
  • Claim: the warm-up is 591 KB in all and a data-saving switch costs 68 to 182 KB. Consistent: a quality-75 WebP re-encode at 828 px totals about 588 KiB, 70 to 187 KB per theme.
  • Claim: the warm-up costs less than one photo does today. Mostly true: it is below 28 of the 30 PNGs; two Gothic photos are 559 KB and 591 KB.
  • Claim: switch timings, zero photo requests after the warm-up, and WebP responses with status 200. Not reproduced.
  • Claim: the Neutral defaults, picker thumbnails and @2x files are unchanged. Verified: none is referenced by the diff.
  • Claim: the docsite spec has no rule on loading these photos, and the template fixture rules do not cover them. Verified.
  • Claim: the new Effect names the system it syncs and cleans up. Verified; the cited runtime record covers the component packages, not the docsite.
  • Claim: the docsite build, typecheck, tests and pre-review check pass. Not reproduced; CI has not run on this draft head.
  • File apps/docsite/src/components/themeShowcaseWarmup.ts: blocking finding above, plus the size note.
  • File apps/docsite/src/components/ThemePackagePage.tsx: clear.
  • File apps/docsite/src/components/themeShowcaseContent.ts: clear.
  • File apps/docsite/src/lib/optimizedImageUrl.mjs: clear.
  • File apps/docsite/scripts/template-image-loader.mjs: clear.
  • File apps/docsite/src/__tests__/theme-showcase-photos.test.ts: clear.
  • File apps/docsite/src/__tests__/theme-showcase-warmup.test.ts: clear; it would need its timer-fallback case updated with the scheduler change.

Automated review (maintainer link)

Comment on lines +54 to +59
if (typeof window.requestIdleCallback === 'function') {
const handle = window.requestIdleCallback(load);
return {images, cancel: () => window.cancelIdleCallback(handle)};
}
const handle = window.setTimeout(load, 200);
return {images, cancel: () => window.clearTimeout(handle)};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These lines re-implement whenIdle from apps/docsite/src/lib/searchWarmup.ts (idle callback with its cancel, else a timer with its cancel), which the search warm-up already uses. Please build the cancel from it and drop the local branches, for example return {images, cancel: whenIdle(load, 200)};. Note that whenIdle also caps the idle wait at twice the fallback delay, so pick the fallback with that in mind.

…imizer, ahead of a theme switch

A theme switch on /themes swaps in six product photos. Each one is a PNG
of 0.5 to 1.2 MB under `public/images/<theme>/`, and the page requests
it only when the theme renders, so the product cards stay empty while
3.7 to 6.7 MB arrive.

The photos now load through the image optimizer, as the template
fixtures do. One function writes the optimizer URL for both. The page
also requests every theme's photos in idle time, about 0.6 MB in all,
and holds them, so a theme switch needs no request. It skips that for a
visitor in a data-saving mode or on a 2G link.
@nynexman4464
nynexman4464 force-pushed the nynexman4464/perf/theme-showcase-images branch from d237024 to 9420780 Compare November 12, 2026 07:56

This branch was successfully deployed

1 active (outdated) deployment
Preview — d237024a Deployed Nov 12, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Meta Open Source bot. needs:design-review Affects visuals — Design should review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant