2

I have a critcal CSS process in place that prevents a flash-of-unstyled-content (FOUC) on above-the-fold content of a page.

I'm stuck on 'defer unused CSS' point that's being highlighted by Google PageSpeed insights (lighthouse) and/or Chrome's Performance Audit.

I've gone through other articles but they do not work.

To summarize I've tried so far.

  • loadCSS()
  • A script that uses requestAnimationFrame

Ref: CSS delivery optimization: How to defer css loading?

If I delay loading the script via setTimeout by a fixed time of 3 seconds the 'defer unused CSS' issue goes away. 3 seconds is what's needed for Google PageSpeed Insights test (mobile) as they are slower devices but 3 seconds is a lot for desktops which generally has more processing power (note, not always true, hence excluding user-agent based logic).

So the question boils down to how do I delay loading the CSS by the least amount of time irrespective of the device type or specs.

Feel free to throw any rough ideas, I'll try them out and report back, if your idea works, we'll update the code and mark your answer has the chosen one.

Next on my list to try is requestAnimationFrame + small fixed delay.

  • Have you tried solution from Google PSI itself? https://developers.google.com/speed/docs/insights/OptimizeCSSDelivery – viator Feb 12 '19 at 09:13
  • 1
    I just glanced through that page, it's outdated now. But here are a few observations. requestAnimationFrame and setTimeout of 0 will still lead to PageSpeed insights recommendation to 'defer unused CSS'. Interpretation of 'defer unused css' : delay loading CSS that's not required to render the page by a sufficient delay. I observed a delay of 2500-3000ms was 'sufficient' – Prathamesh Gharat Feb 13 '19 at 10:15
  • You might want to read [How to get a 100% Google Lighthouse score](https://www.usecue.com/blog/how-to-get-a100-google-lighthouse-score/). It is about this exact point/problem. You might not like the solution in this article, as it requires a completely different approach. – Mr. Hugo Nov 16 '19 at 14:31
  • @JoostS Thanks for sharing the link. The article does share a valid approach. It might not be worth the effort for some to re-implement all the suggestions in existing projects. – Prathamesh Gharat Nov 25 '19 at 06:24

1 Answers1

1

Manually loading a CSS based on two triggers, whichever occurs first.

  • [setTimeout of 2500ms]
  • [Scroll event]
<script>
    var raf = requestAnimationFrame || mozRequestAnimationFrame ||
    webkitRequestAnimationFrame || msRequestAnimationFrame;
    var app_css_loaded = false;
    /* console.log(performance.now() + ' - ' + '1. async css script init'); */
    var loadAppCss = function(){
        if(!app_css_loaded) {
            app_css_loaded = true;
            var l = document.createElement('link'); l.rel = 'stylesheet';
            l.href = 'YOUR_COMBINED_AND_MINIFIED_CSS_HERE.css';
            var h = document.getElementsByTagName('head')[0]; h.parentNode.insertBefore(l, h);
            /* console.log(performance.now() + ' - ' + '5. script injected'); */
        }
    };
    var cb = function() {
        /* console.log(performance.now() + ' - ' + '3. cb called'); */
        setTimeout(function(){
            /* console.log(performance.now() + ' - ' + '4. timeout start'); */
            loadAppCss();
            /* console.log(performance.now() + ' - ' + '6. timeout end'); */
        }, 3000);
    };

    window.addEventListener('load', function(){
        /* console.log(performance.now() + ' - ' + '2. triggering cb directly'); */
        if(raf) { raf(cb); } else { cb(); };
    });
    var loadAppCssOnScroll = function(){
        /* console.log(performance.now() + ' - ' + '### triggering cb on scroll'); */
        window.removeEventListener('scroll', loadAppCssOnScroll);
        if(raf) { raf(loadAppCss); } else { loadAppCss() };
    };
    window.addEventListener('scroll', loadAppCssOnScroll);
</script>

This makes the PageSpeed insights recommendation regarding defer unused CSS go away.

requestAnimationFrame, if available, will stall the CSS file from loading if the tab has opened in the background in most browsers. You could remove it from the above code if it does not meet your requirements. Ref

console.log() is not available in all browsers. Do not use it in production. Ref