Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CLS happening before the first paint #593

Open
UweOhse opened this issue Mar 4, 2025 · 1 comment
Open

CLS happening before the first paint #593

UweOhse opened this issue Mar 4, 2025 · 1 comment

Comments

@UweOhse
Copy link

UweOhse commented Mar 4, 2025

I'm hunting down layout shifts / visual instability on some sites i'm responsible for, and while i did find and fix some of them, i just never found the main one on the image pages. I rewrote the almost 15 years old image container code (clean in 2009, not so clean in 2024) to no avail, and then i started thinking about the webvitals CLS calculation, did my own calculation, and came to the conclusion that the webvitals CLS value includes "layout shifts" happening before the first paint.
Meaning that there is no visual instability at all. Which matches my feeling.

I'm not sure who's at fault here, whether webvital should disregard layout-shift observer entries before the first paint, or whether the browser shouldn't even generate them.

What happens is this (Chromium 133 on the desktop, 2358x1394, but i see that with 130 on 1326x900, too).

0.0	100.7	navigation	non-blocking	200	45459	186380	/o2051785-Eleganz%20im%20Landeanflug#nfmain
100.7		responseEnd
126.0		unloadEventEnd
126.0		unloadEventStart

// CSS, JS and img loading phase. shortened. note that the main image is loaded between 131.3 and 290.2 ms
131.0 -	213.2	link	non-blocking	200	40962	223780	nf2009-1739124132.css
131.2	213.5	link	blocking	200	2241	6610	1736288819-9945e908aed2e3282b226adaa3e72721.css
131.3	232.5	link	non-blocking	200	153271	152936	Vollkorn-Regular.woff2
131.3	290.2	img	non-blocking	200	605160	604860	image::Sebastian_Dargel_hoeckerschwan.jpg, the main image
132.0	328.6	script	non-blocking	200	171718	574475	1738071933-2067661292e2bf7533767f32656cae67.js
132.1	248.3	script	non-blocking	200	31258	93164	color-20220122.esm.js?1643036263
132.1	327.1	script	non-blocking	200	71064	304868	1737064082-39ecef2807b07e7aa7fd5033c7241223.js
132.3	239.0	script	non-blocking	200	13641	57633	fx2.js?1736280962

manually calculated CLS, done by my own performance observer of type: 'layout-shift'. 1st entry:
258.0		cumulative-layout-shift: 0.12277339974450473
258.0		layout-shift: SECTION.nf_imageblock.uf_imageblock.img_splugin_bgcolor_element.autoscaled.bgcolored 
                        MAIN.nf_image.performance_layoutshifted 2343*24@0x583 -> 2343*667@0x206: 
                            v=0.12277339974450473, session=0.12277339974450473

2nd entry:
267.8		cumulative-layout-shift: 0.12312417628234174
267.8		layout-shift: DIV#mainmenu_datenschutz.menuentry.performance_layoutshifted 71*32@640x0 -> 77*32@648x0
SPAN.objectpath-entry A.with_menu_box.uf_micropreview.uf_oid6.uf_draggable_object.performance_layoutshifted 155*18@0x134 -> 159*18@0x132
DIV#nf_header_bottom_right.performance_layoutshifted 247*24@2096x132 -> 260*24@2083x132
DIV#nf_header_bottom_right.performance_layoutshifted 247*22@2096x135 -> 260*22@2083x133
HEADER H1.img-name.uf_draggable_object.p-name.uf_filedrop_allowed.draggable.performance_layoutshifted 2103*35@120x172 -> 2103*35@120x168: v=0.00035077653783700314, session=0.12312417628234174

So far, so good - my own calculation matches the value of webvitals (see below).

318.3		first-contentful-paint (FCP)  //// <<<<<<<<<<<<<<<<<<<<<<<<
318.3		first-paint (FP)

351.6		largest-contentful-paint (LCP)

365.2		img autoscaled

397.5		domContentLoadedEventStart, heavy JS work begins here.
410.3		domContentLoadedEventEnd

431.3		webvital onCLS callback, with a value of 0.12312417628234174 and a rating of needs-improvement.               

webvitals also based its calculation on the same sources i used, MAIN.nf_image and the elements in the second entry.
That means it measured a layout shift / visual instability before the first paint.

URL: https://naturfotografen-forum.de/o2051785-Eleganz%20im%20Landeanflug

Replication is somewhat difficult because of randomness. I've a helper for that, though:

  • Open the URL on the desktop, a large screen might help.
  • click on the T with the white background in the top line,
  • click on "CLS debugging" (it's active if green),
  • close the overlay,
  • use the big arrows to navigate to other image pages.
  • stop when you see a red dotted outline around or over the big image
  • click on the T again to see the logging.

Network and computer speed affect the timing, unfortunately.

Full log:

0.0	100.7	navigation	non-blocking	200	45459	186380	/o2051785-Eleganz%20im%20Landeanflug#nfmain
4.3		connectEnd
4.3		connectStart
4.3		domainLookupEnd
4.3		domainLookupStart
4.3		fetchStart
4.3		secureConnectionStart
14.0		requestStart
95.7		responseStart (TTFB)
100.7		got	non-blocking	200	45459	186380	/o2051785-Eleganz%20im%20Landeanflug#nfmain
100.7		responseEnd
126.0		unloadEventEnd
126.0		unloadEventStart
131.0	213.2	link	non-blocking	200	40962	223780	/cache/bundles/nf2009-1739124132.css
131.2	213.5	link	blocking	200	2241	6610	/cache/bundles/1736288819-9945e908aed2e3282b226adaa3e72721.css
131.3	232.5	link	non-blocking	200	153271	152936	/data/fonts/Vollkorn-Regular.woff2
131.3	290.2	img	non-blocking	200	605160	604860	/data/o/410/2051785/image::Sebastian_Dargel_hoeckerschwan.jpg
131.4	317.4	img	non-blocking	200	1523	1223	/images/scroll_da_left.png
131.4	317.9	img	non-blocking	200	1547	1247	/images/scroll_da_right.png
131.6	231.6	img	non-blocking	200	5639	5339	/data/p/410/2051804/135x89.avif
131.7	231.1	img	non-blocking	200	3965	3665	/data/p/410/2051790/135x75.avif
131.7	316.6	img	non-blocking	200	3596	3296	/data/p/410/2051785/135x90.avif
131.7	317.0	img	non-blocking	200	854	554	/data/p/410/2051772/123x135.webp
131.8	320.9	img	non-blocking	200	3151	2851	/data/p/410/2051715/135x89.avif
131.9	318.3	img	non-blocking	200	1627	1327	/smileys/thumbs-up.png
131.9	321.5	img	non-blocking	200	7664	7364	/styles/ajax-loader.gif
132.0	328.6	script	non-blocking	200	171718	574475	/cache/bundles/1738071933-2067661292e2bf7533767f32656cae67.js
132.1	248.3	script	non-blocking	200	31258	93164	/js/versioned/color-20220122.esm.js?1643036263
132.1	327.1	script	non-blocking	200	71064	304868	/cache/bundles/1737064082-39ecef2807b07e7aa7fd5033c7241223.js
132.3	239.0	script	non-blocking	200	13641	57633	/site/types/img/js/fx2.js?1736280962
213.2		got	non-blocking	200	40962	223780	/cache/bundles/nf2009-1739124132.css
213.5		got	blocking	200	2241	6610	/cache/bundles/1736288819-9945e908aed2e3282b226adaa3e72721.css
231.1		got	non-blocking	200	3965	3665	/data/p/410/2051790/135x75.avif
231.6		got	non-blocking	200	5639	5339	/data/p/410/2051804/135x89.avif
232.5		got	non-blocking	200	153271	152936	/data/fonts/Vollkorn-Regular.woff2
239.0		got	non-blocking	200	13641	57633	/site/types/img/js/fx2.js?1736280962
248.3		got	non-blocking	200	31258	93164	/js/versioned/color-20220122.esm.js?1643036263
258.0		cumulative-layout-shift: 0.12277339974450473
258.0		layout-shift: SECTION.nf_imageblock.uf_imageblock.img_splugin_bgcolor_element.autoscaled.bgcolored MAIN.nf_image.performance_layoutshifted 2343*24@0x583 -> 2343*667@0x206: v=0.12277339974450473, session=0.12277339974450473
267.8		cumulative-layout-shift: 0.12312417628234174
267.8		layout-shift: DIV#mainmenu_datenschutz.menuentry.performance_layoutshifted 71*32@640x0 -> 77*32@648x0
SPAN.objectpath-entry A.with_menu_box.uf_micropreview.uf_oid6.uf_draggable_object.performance_layoutshifted 155*18@0x134 -> 159*18@0x132
DIV#nf_header_bottom_right.performance_layoutshifted 247*24@2096x132 -> 260*24@2083x132
DIV#nf_header_bottom_right.performance_layoutshifted 247*22@2096x135 -> 260*22@2083x133
HEADER H1.img-name.uf_draggable_object.p-name.uf_filedrop_allowed.draggable.performance_layoutshifted 2103*35@120x172 -> 2103*35@120x168: v=0.00035077653783700314, session=0.12312417628234174
290.2		got	non-blocking	200	605160	604860	/data/o/410/2051785/image::Sebastian_Dargel_hoeckerschwan.jpg
316.6		got	non-blocking	200	3596	3296	/data/p/410/2051785/135x90.avif
317.0		got	non-blocking	200	854	554	/data/p/410/2051772/123x135.webp
317.4		got	non-blocking	200	1523	1223	/images/scroll_da_left.png
317.9		got	non-blocking	200	1547	1247	/images/scroll_da_right.png
318.3		first-contentful-paint (FCP)
318.3		first-paint (FP)
318.3		got	non-blocking	200	1627	1327	/smileys/thumbs-up.png
320.9		got	non-blocking	200	3151	2851	/data/p/410/2051715/135x89.avif
321.5		got	non-blocking	200	7664	7364	/styles/ajax-loader.gif
327.1		got	non-blocking	200	71064	304868	/cache/bundles/1737064082-39ecef2807b07e7aa7fd5033c7241223.js
328.6		got	non-blocking	200	171718	574475	/cache/bundles/1738071933-2067661292e2bf7533767f32656cae67.js
340.6		uflib start
340.7		uf init
340.9		uflib loaded
347.4		uf_baselib_start
348.4		uf_baselib_end
351.6		largest-contentful-paint (LCP)
365.2		img autoscaled
367.5	394.9	script	non-blocking	200	5250	16669	/site/types/img/js/colorconversion.js
368.5		domInteractive
394.9		got	non-blocking	200	5250	16669	/site/types/img/js/colorconversion.js
397.5		domContentLoadedEventStart
398.0		uf_baselib_ready_start
399.2		uf_baselib_ready_end
406.1	432.5	img	non-blocking	200	4726	4426	/data/p/376/1883961/fast-thumb.jpg
406.2	433.2	img	non-blocking	200	17937	17637	/data/o/213/1069693/thumb.jpg
406.2	438.7	img	non-blocking	200	13992	13692	/data/o/308/1541719/thumb.jpg
406.3	439.1	img	non-blocking	200	19501	19201	/data/o/1/9909/thumb.jpg
410.3		domContentLoadedEventEnd
422.5	455.8	beacon	non-blocking	200	520	220	/ajax.php?action=perf_cls
431.3		webvital onCLS callback
@tunetheweb
Copy link
Member

IMHO it is possible for CLS to occur before first paint. For example if you have empty <div>s of 0 height, then provide then with a height, then fill them with content. Or in your case empty <img> elements

This was discussed in the spec in WICG/layout-instability#45 and WICG/layout-instability#61 and some allowances were made (specifically if the element has opacity: 0 of visibility: hidden) but no overall solution was proposed to cap this before FP or FCP.

I understand this does not track the user experience of there being no noticeable shift, so CLS is not doing what we want it to do here.

We could potentially only report CLS after FCP (we already only report CLS if FCP is reported), but not entirely sure if that is correct either. FP is probably a better option, but even then "invisible shifts" could still happen later in the page.

So I think this should be raised as an issue on the Layout Instability Spec rather than worked around in this library.

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

No branches or pull requests

2 participants