Skip to content

Skip off-screen merch cards from raster with content-visibility - #20050

Closed
ianmatson wants to merge 2 commits into
masterfrom
posthog/merch-grid-content-visibility
Closed

Skip off-screen merch cards from raster with content-visibility#20050
ianmatson wants to merge 2 commits into
masterfrom
posthog/merch-grid-content-visibility

Conversation

@ianmatson

Copy link
Copy Markdown
Contributor

Changes

The merch grid renders 52 product cards. Each card image loads lazily, and each srcset offers a 500w candidate. A phone is narrower than the 500px breakpoint in the sizes attribute, so the browser selects the 500w candidate for every card.

Lazy loading usually keeps most of those bitmaps off the heap. A pinch-zoom defeats it. The visual viewport expands to the full page, so every lazy image becomes eligible to decode at the same time, and WebKit re-rasterizes the page tiles at the new scale at the same moment. On iOS the tab can then exceed its memory limit, and Safari kills it. The user sees "A problem repeatedly occurred".

This change adds content-visibility: auto and contain-intrinsic-size: auto 300px to the product card. The browser then skips layout, paint and raster for off-screen cards at any zoom level, which caps the memory the grid can hold.

content-visibility is available in Safari 18 and later, and in Chrome. Older browsers ignore the property and render the card as before. The auto keyword in contain-intrinsic-size makes the browser remember the real card size after the first render, so the scroll position stays stable.

Refs #17108. That report is about the product modal, not the grid, so this change alone is not expected to close it. It lowers the memory the page holds while the modal is open.

Screenshots

Not included. This PR is blocked until someone adds them.

I cannot render /merch in this environment. gatsby/sourceNodes.ts skips Shopify sourcing when SHOPIFY_APP_PASSWORD is absent, so shopifyCollection has no nodes and src/pages/merch.tsx returns null. That secret is not in .env.development, and I did not go looking for it.

Please add before/after images for a narrow window and a wide window, in light and dark mode, or tell me how to source the merch data locally.

Before you open a PR

  • pnpm format: run on the changed file. Prettier made no further changes.
  • Dev server: started with pnpm start. I could not complete the page check, for the reason above.
  • No page moved, so this PR needs no redirect and no pnpm test-redirects run.

Checklist

  • I've read the docs and/or content style guides.
  • Words are spelled using American English
  • Use relative URLs for internal links
  • I've checked the pages added or changed in the Vercel preview build
  • If I moved a page, I added a redirect in vercel.json (no page moved)

Created with PostHog from a Slack thread

The merch grid renders 52 product cards, each with a lazily loaded image. A pinch-zoom expands the visual viewport, so every lazy image becomes eligible to decode at the same time while WebKit also re-rasterizes tiles at the new scale.

content-visibility: auto keeps off-screen cards out of layout, paint and raster at any zoom level, which caps the memory the grid can use.

Generated-By: PostHog Desktop
Task-Id: 7e192c61-25db-41a6-a34e-dadc41c8ab52
@ianmatson ianmatson self-assigned this Sep 9, 2026
@github-actions github-actions Bot added the website About the website (beyond just landing pages) label Sep 9, 2026
@github-actions

github-actions Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Deploy preview

Status Details Updated (UTC)
🟢 Ready View preview Sep 09, 2026 09:52PM

@github-actions

github-actions Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Bundle report

Total JS (gzip)

8.86 MiB (+0.0 KiB / +0.0%)

Eager graph (modules shipped in each entrypoint's initial chunks)

Entrypoint Eager size Budget Modules
app 18.47 MiB (+2.6 KiB / +0.0%) report-only 2054
Largest modules in the app closure
Module Size
./src/data/mcp-tools.json 1119.1 KiB
css ./node_modules/.pnpm/css-loader@5.2.7_webpack@5.101.3/node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[8].oneOf[1].use[1]!./node_modules/.pnpm/postcss-loader@4.3.0_postcss@8.5.6_webpack@5.101.3/node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[8].oneOf[1].use[2]!./src/styles/global.css 761.2 KiB
./src/components/Stickers/Stickers.tsx 696.4 KiB
./node_modules/.pnpm/@radix-ui+react-icons@1.3.2_react@18.3.1/node_modules/@radix-ui/react-icons/dist/react-icons.esm.js 481.4 KiB
./node_modules/.pnpm/@posthog+brand@0.8.0_react@18.3.1/node_modules/@posthog/brand/dist/generated/hoggies/svg/x-ray.mjs 480.8 KiB
./node_modules/.pnpm/rehype-raw@7.0.0/node_modules/rehype-raw/lib/index.js + 29 modules 395.1 KiB
./node_modules/.pnpm/@posthog+brand@0.8.0_react@18.3.1/node_modules/@posthog/brand/dist/generated/hoggies/svg/im-the-driver.mjs 385.7 KiB
./src/hooks/useCustomers.tsx + 55 modules 370.0 KiB
./node_modules/.pnpm/@posthog+icons@0.36.6_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js 354.8 KiB
./node_modules/.pnpm/react-markdown@8.0.7_@types+react@16.14.66_react@18.3.1/node_modules/react-markdown/lib/react-markdown.js + 88 modules 351.4 KiB
./src/components/ProductComparisonTable/index.tsx + 126 modules 302.5 KiB
./node_modules/.pnpm/cloudinary-core@2.14.0_lodash@4.17.21/node_modules/cloudinary-core/cloudinary-core.js 281.9 KiB
./node_modules/.pnpm/@posthog+brand@0.8.0_react@18.3.1/node_modules/@posthog/brand/dist/generated/hoggies/svg/doll-house.mjs 281.7 KiB
./node_modules/.pnpm/@posthog+brand@0.8.0_react@18.3.1/node_modules/@posthog/brand/dist/generated/hoggies/svg/director.mjs 275.6 KiB
./src/components/SearchUI/index.tsx + 87 modules 273.0 KiB

Eager-graph budgets are report-only until a baseline is established. Sizes are gzip of public/**/*.js; eager size is webpack module source bytes for the modules actually shipped in the entrypoint's initial chunks (post-tree-shake).

@ianmatson

Copy link
Copy Markdown
Contributor Author

After testing on mobile, I can confirm that this unfortunately doesn't fix the crashing issue on mobile. Closing.

@ianmatson ianmatson closed this Sep 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

website About the website (beyond just landing pages)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant