WordPress / performance

Performance plugin from the WordPress Performance Group, which is a collection of standalone performance modules.
https://wordpress.org/plugins/performance-lab/
GNU General Public License v2.0
338 stars 92 forks source link

Implement Async CSS #120

Open dainemawer opened 2 years ago

dainemawer commented 2 years ago

Another pattern to possibly follow is Asynchronous CSS - this was suggested by the Filament Group at some point and has had some decent results: https://www.filamentgroup.com/lab/load-css-simpler/

It is slightly hacky, but it involves loading CSS files with media attribute set to print - this allows the browser to load the stylesheet in a non render block fashion. Using the onload attribute, we can set media back to all so that the stylesheet is applied as one would expect. As fallback, the original link is included in a a <noscript> tag incase JS borks out:

<link rel="stylesheet" href="/path/to/my.css" media="print" onload="this.media='all'" />
<noscript>
<!-- No JS -->
<link rel="stylesheet" href="/path/to/my.css" media="all" /> 
</noscript>

This could possibly be an extension of wp_enqueue_style by adding another parameter:

<?php wp_enqueue_style( $handle, $src, $deps, $ver, $media = 'all', $async = true ); ?>
mehigh commented 2 years ago

This is a good pattern to have access to. It can also enable more developers to make use of it for non-critical things (let's say the CSS for some overlay that's not needed for the initial paint).

mitogh commented 2 years ago

The major problem I see with this approach is a problem with CLS (Cumulative Layout Shift) due to the styles being applied after the DOM is loaded via JavaScript. I think it makes sense for styles that might not be used on the page like print or other types of CSS that do not affect the layout of the page like modal or hidden DOM elements.

Technically if styles are not needed for the page shouldn't be loaded in the first place:

Related with:

mehigh commented 2 years ago

The CLS is introduced when the elements styled by those CSS files appear above the fold. And it's not always the case. Yes, you can't know for sure whether something is needed or not to block rendering off the gate, but giving this option via the API allows the usage of this useful feature. eg. I need font X, that's loaded from an external service. I don't want the first paint of the page to be slowed down by an entire second and allow the visitors to start reading the content straight away. There is some CLS when the font is re-rendered, but have tested it in various scenario and it's quite minimal for a measurable and observable improvement in how quick the information appears on screen.

Example of use cases automate-able: stylesheets loaded from typekit / googlefonts

Think of it as giving power to the people to use this without requiring the knowledge of how a stylesheet can be loaded async, from a technical perspective. Rather have it available right there in the API. It's the same as with deferring scripts - the easier it is for this positive impacting behavior to be used (i.e. less friction), the more we'd see it used in the wild.

dainemawer commented 2 years ago

@mitogh I can relate to your concern, it is a bit of a wild card - to be honest, this feature should be coupled with Critical CSS or only applied to stylesheets that are not considered to be part of the critical rendering path. I don't see this as being used for any stylesheet that applies styles above the fold.

However, if we can load bulky plugin CSS, or say, Gravity Form CSS (if the form is below the fold) and take that approach it will significantly reduce the amount of unused and render-blocking CSS that a page requests.

mauu1t commented 2 years ago

Is there a TRAC ticket for this?

mehigh commented 2 years ago

Trac ticket created: https://core.trac.wordpress.org/ticket/53232

it has patch & unit tests (contribution to core), maybe we need to mark this as in progress in the project board.

mehigh commented 2 years ago

You can also assing this to @mauu1t

futtta commented 2 years ago

Trac ticket created: https://core.trac.wordpress.org/ticket/53232

it has patch & unit tests (contribution to core), maybe we need to mark this as in progress in the project board.

that's for async decoding of an img, not for async CSS @mehigh ?

mauu1t commented 2 years ago

https://core.trac.wordpress.org/ticket/55639 This is the correct trac ticket

mehigh commented 2 years ago

Yes, yes. Thanks for correcting me, Mihai :) Too many tabs open.