CSS / Performance · Lighthouse
From 73 to 97: deferring offscreen layout with CSS
Author: Grundy · Checked: October 21, 2026
The homepage scored 99 in mobile PageSpeed Insights, while Quick sort scored 73 and its largest contentful paint took 5.2 seconds. The LCP element was the introductory paragraph, not the canvas. Instead of delaying the player or removing analytics on suspicion, we measured the initial layout and compared CSS variants. Deferring the lower sections brought a subsequent production run to 97, with LCP at about 2.6 seconds. Here is what we tested, what the numbers establish and what still needs checking.
1. First check whether the measurement is trustworthy
Local Lighthouse initially reported 80 for the homepage and 45–49 for Quick sort. It also warned about a slower-than-expected CPU; a repeat run reported cache-clearing timeouts. Those scores were not a reliable basis for blaming a script. PageSpeed Insights used Lighthouse 13.5.0 on Google's infrastructure, while the local tool was 12.8.2. We compared before and after runs within PageSpeed, rather than treating scores from different environments and versions as interchangeable. Neither report contained field data from real visitors.
2. Find what happens before the text is painted
The page already renders its introduction on the server. Video iframes load only after a click. In a diagnostic trace without artificial CPU throttling, the first long task took 304 ms, including 281 ms of Layout. The introductory paragraph appeared after that task. Generating the initial sorting steps took about 0.15–0.17 ms in a separate Node benchmark; a canvas-related layout in the trace took about 1.4 ms. These measurements narrowed the investigation to initial layout, but they do not mean Node timings reproduce a slow phone or that every canvas is cheap.
3. Compare CSS with the same content
We froze the rendered HTML, embedded the same CSS and fonts, removed JavaScript and tested local copies. Disabling heading balancing, button balancing, container queries or the outer flex layout did not produce a comparable consistent improvement. Baseline runs spent roughly 190–258 ms in Layout in total. Deferring both the video and article sections below the player reduced that to 94–108 ms in two runs. Deferring only code blocks or only videos did not reproduce the same gain. This was a diagnostic experiment, not the final website: it isolated CSS work but did not measure hydration or predict the exact production score. We found no single broken CSS declaration.
4. Defer offscreen layout, not the interactive player
content-visibility: auto lets the browser skip offscreen layout and painting when appropriate. It does not remove the server-rendered article or postpone downloading its JavaScript. We apply it to lower sections and leave the heading, introduction and player outside. contain-intrinsic-block-size reserves an estimated height while contents are skipped; auto allows a previously rendered size to be reused. The estimates differ for mobile, wide screens, long code and portrait video. The snippet shows the shared CSS; each section supplies its own height variables. Browsers that do not support the optimization retain ordinary layout.
.deferred {
content-visibility: auto;
contain-intrinsic-block-size: auto var(--estimated-height, 500px);
scroll-margin-block-start: 24px;
}
@media (min-width: 768px) {
.deferred {
contain-intrinsic-block-size: auto var(--estimated-height-wide, 250px);
}
}
@media print {
.deferred {
content-visibility: visible;
contain-intrinsic-block-size: none;
}
}6. Validate on production and keep the limits visible
After publishing the Quick sort change, a new mobile PageSpeed run improved performance from 73 to 97, FCP from about 2.4 to 0.9 seconds, LCP from 5.2 to 2.6 seconds and TBT from 150 to 70 ms. CLS stayed at zero; accessibility, best practices and SEO remained 100. These are two laboratory runs, not medians or a guarantee for every visitor. The direction agrees with the controlled experiment, but a field-performance improvement still needs real-user data.
7. Apply the optimization where measurements justify it
We then checked the remaining algorithm pages. Insertion sort and bubble sort scored 77, selection sort 73 and merge sort 77, with LCP around five seconds. After publishing the same lower-section optimization, new mobile runs showed 77, 80, 99 and 77 respectively. Selection sort's LCP fell from 5.2 to 2.0 seconds; bubble sort's fell from 5.2 to 4.8 seconds. Insertion sort and merge sort remained near five seconds. TBT fell from about 130 to 0 ms for insertion sort and from 110 to 10 ms for bubble sort, but merge sort's changed from 170 to 180 ms. Selection sort's CLS was 0.001; the other new runs showed 0. Heap sort scored 99 and binary search 98 in the earlier checks, so we left them unchanged. The table preserves both runs and their report links: Quick sort's large score gain did not generalize to every page. Each before/after pair is a single-run comparison, not proof of a stable effect. Keeping a shared server component avoids copying the behavior while preserving per-section height estimates.
| Page | Performance | LCP, s | TBT, ms | Measurement status | PageSpeed |
|---|---|---|---|---|---|
| Insertion sort | 77 | 5.1 | 130 | Before optimization | Report |
| Insertion sort | 77 | 5 | 0 | After optimization | Report |
| Bubble sort | 77 | 5.2 | 110 | Before optimization | Report |
| Bubble sort | 80 | 4.8 | 10 | After optimization | Report |
| Selection sort | 73 | 5.2 | 250 | Before optimization | Report |
| Selection sort | 99 | 2 | 10 | After optimization | Report |
| Merge sort | 77 | 5 | 170 | Before optimization | Report |
| Merge sort | 77 | 4.9 | 180 | After optimization | Report |
| Quick sort | 97 | 2.6 | 70 | After publication | Report |
| Heap sort | 99 | 1.8 | 70 | Unchanged | Report |
| Binary search | 98 | 2.3 | 10 | Unchanged | Report |