Hidden in plain sight

Flowty, the Webflow optimisation services I’m building, has starting taking on its first cohort of users. While it’s still in early alpha, the reception to it so far has been terrific. As a developer it feel pretty cool when a user calls something you’ve built “magic”. I’ve still got a few things to build out, but things are looking good for a more open beta sometime in June.

This week’s issue looks at a small code snippet that I was shown during a conversation with one of Flowty’s users. The code aims to eliminate flash of unstyled text (FOUT) from a page, but actually exposes their site to some pretty nasty performance issues.


On a recent call I was shown the below code snippet by a designer.

<script>
    var Webflow = Webflow || [];
    Webflow.push(function () {
        $('html').addClass('webflow-loaded')
    });
</script>
<style>
    .wf-loading * {
        opacity: 0;
    }
</style>

Upon seeing it, I broke out into a cold sweat. The designer went on to explain how they were looking for a way to prevent text visually changing as the page was loading (FOUT), and had found this code snippet recommended on a forum. It did the trick. Happy days.

But this code also sets their site up for the possibility of not showing any content at all. Let’s take a look at what it’s doing, and why it’s not so great from a performance perspective.

An invisibility cloak

To understand what’s going on we’ll start from the bottom.

<style>
    .wf-loading * {
        opacity: 0;
    }
</style>

You might be able to take a guess at what this small style block is doing. It tells the browser that anything (*) that lives inside a parent element with a class of .wf-loading should be hidden (opacity: 0).

On the page we were looking at, the .wf-loading class was applied to the <html> tag. On a web page, everything is a child of the <html> tag. The code above was effectively telling the browser “just hide everything on the page”.

The great reveal. Maybe.

The script block above the style tag is where the magic happens.

<script>
    var Webflow = Webflow || [];
    Webflow.push(function () {
        $('html').addClass('webflow-loaded')
    });
</script>

This block declares a Webflow variable, and then adds a function to it that adds the webflow-loaded class to the <html> tag on the page. That class will reveal the page by setting opacity: 1 for the all children of the <html> tag.

As an aside, this is a very similar technique to what most A-B testing services use. That’s a topic for another time, but it’s worth being aware of how they might be impacting your site’s performance as well.

What could possibly go wrong?

When the page is loaded on a fast, desktop internet connection, things go pretty smoothly. Largest Contentful Paint fires well within the threshold for a “Good” Core Web Vitals score.

Things start getting a little hairy when moving over to mobile though. On a 4G network, testing on a Motorola G4, the Largest Contentful Paint time jumps out to over 5 seconds. On a 3G connection that goes up to almost 9 seconds.

That’s 9 seconds during which the user is presented with no content, despite the fact that most of the page’s CSS, fonts, and images have already been downloaded. This is because the browser is waiting for the Webflow variable to be declared. This declaration relies on the Webflow JavaScript file being downloaded and parsed.

“All your users are non-JS while they’re downloading your JS.”

So said Jake Archibald. And how true that is. As I touched on just above, while the user is waiting for the JavaScript package to finish downloading and executing, they’re looking at a plain white screen for close to 10 seconds.

Research suggests 53% of visits are likely to be abandoned if pages take longer than 3 seconds to load (Google, 2016). That 10 second window could be seeing hundreds, or thousands, of visitors abandoning the site.

What could be done to fix this?

First up, I’d remove the script entirely. Browsers do an amazing job of loading websites, and we should embrace that (plus give them a bit of help occasionally). You’d be surprised how often performance issues are a result of us trying to be too clever for our own good.

But okay, there’s still the desire on the part of the client to reduce/remove FOUT on the page. That could be tackled in a few different ways. We touched on a few during the series on web font optimisation last year.

  1. Use system fonts for most (or all) of the text content.
  2. Limit the number of custom web fonts that are used.
  3. Subset the web fonts you’re using. This removes characters that are surplus to requirements, and brings down the file size.
  4. Use the font-display: optional declaration. This instructs the browser to hide text for 100ms and then load the web font only if it's available. If it's not ready, then the browser will use a fallback font instead for that page view. The custom font will be saved in cache, ready for the next time it’s needed.
  5. Set an appropriate fallback font that most closely matches the custom font you’re using.

Using one, or a combination of, the techniques above we can minimise FOUT on the page while also ensuring users are presented with content as soon as possible during page load.

Just one more thing

Before we wrap up, I’ll be speaking at Lazy Load, an online web performance conference, next month. Held over two days (June 10 & 17), it features a slew of great speakers and industry leaders. It’s 100% online, so you can join from anywhere in the world. I’ve also got a coupon code for 20% off the conference price. Use the coupon fershadsentme when you register to grab the sweet discount!


The next issue of Optimised will be in your inbox on June 10th. The website has an archive of all previous emails. It's a good place to recap on anything we've covered, and also handy to share with friends or colleagues.

Keep well, stay safe.
Fershad