perf: reserve search-result cover space to reduce layout shift#13134
Closed
lokesh wants to merge 1 commit into
Closed
perf: reserve search-result cover space to reduce layout shift#13134lokesh wants to merge 1 commit into
lokesh wants to merge 1 commit into
Conversation
Search result covers had a CSS-pinned width (175px) but no height signal until the image loaded, so every cover — including the first four, which load eagerly — grew from 0 to up to 300px tall and pushed the results below it down. Add aspect-ratio: auto 2 / 3 so the browser reserves a typical book-cover box before load; the auto keyword defers to the image's intrinsic ratio once loaded, so settled layout is unchanged. Grid view already pins height: 180px and carousel cards reserve a fixed 200px container, so list view was the remaining shift source. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015ky5Sc19MGPkDgbgTWWWET
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #
Performance fix (Core Web Vitals): reduces Cumulative Layout Shift on the search results page — the most-used page on the site — with a one-line CSS change.
Estimated performance gain
~85–90% reduction in cover-driven layout shift on search results. Before: each cover box grows from 0 to its loaded height (up to 300 px; ~262 px for a typical 2:3 cover at 175 px wide), and the four above-the-fold covers do this eagerly on every search — enough on its own to push a page past the 0.1 "good" CLS threshold on mobile. After: the box is pre-reserved at 2:3, and the only remaining shift is the difference between the reserved ratio and each cover's true ratio (typically ±20–40 px). Illustrative per-cover numbers (shift is proportional to these heights):
xychart-beta title "Layout shift per eager cover as it loads (px, typical cover)" x-axis ["Before (no reserved height)", "After (aspect-ratio 2/3)"] y-axis "vertical shift in px" 0 --> 280 bar [262, 30]These are geometry-derived estimates, not field data; confirm with DevTools Performance → Web Vitals or CrUX once deployed. Settled layout is pixel-identical, so there is no LCP/visual trade-off.
Technical
Problem. In list view, search-result cover images (
macros/SearchResultsWork.html) render with nowidth/heightattributes, and the CSS (static/css/components/search-result-item.css) pins only the width:Until each cover downloads, its box is 175×0; on load it grows to up to 300px tall and pushes every result below it down. The first four covers load eagerly (only
seq_index > 3getsloading="lazy"inSearchResultsWork.html) — deliberately, for LCP — so this shift reliably happens above the fold on every search.Why not intrinsic
width/heightattributes per image: search docs come from Solr, which doesn't store cover pixel dimensions; fetching them would add a blocking coverstore API call per result (Image.infois a live HTTP request) — strictly worse than the CLS.Fix:
The
autokeyword is load-bearing: before the image loads, the browser reserves a typical book-cover box (175×262.5, within the 300px cap); once loaded, the image's intrinsic ratio takes over, so the settled layout is pixel-identical to today. Precedent for the ratio already exists instatic/css/page-design.css(aspect-ratio: 2 / 3).Scope — checked and deliberately not touched:
.list-books--grid) already pinsheight: 180px+object-fit: contain— no shift there.custom_carousel_card.html):.book .book-coveralready reserves a fixedheight: 200pxcontainer, and lazy carousels reserve 278.6px — no shift there.macros/CoverImage.htmllooked like a candidate (no dimensions/lazy/srcset) but is dead code — it has no callers and references Python 2'sbasestring, so it would crash if ever rendered. Left for a separate cleanup.Testing
pre-commit run --files static/css/components/search-result-item.css— all hooks pass, including stylelint.Manual verification for reviewers:
/search?q=...with devtools network throttling (Slow 3G) and observe results no longer jump downward as covers stream in..searchResultItemcover images drop.autokeyword defers to intrinsic ratios after load).Screenshot
No settled-layout change; the improvement is visible only during load (see manual steps above).
Stakeholders
Part of a performance-review series; see the review report branch
claude/performance-review-top-changes-5wcs7g.🤖 Generated with Claude Code
https://claude.ai/code/session_015ky5Sc19MGPkDgbgTWWWET