Skip to main content

FOUC vs. Progressive Enhancement

In his recent post on web standards and progressive enhancement, Zeldman notes:

When a site like Facebook stops functioning when a script forgets to load, that is a failure of education and understanding on the part of those who created the site.

This has me rethinking the way I’ve approached progressive enhancement.

How I do progressive enhancement

I use modern JavaScript API’s in my scripts that don’t work in older and less capable browsers.

When I write scripts that hide or manipulate content on a page, I make all content visible by default, and use a feature test to add a .js class to the <body> element. I only hide and manipulate content when that class is present, esnuring that no one is ever prevented from seeing the content. (Learn more about how I use feature tests.)

Unfortunately, this can cause FOUC.

Fixing FOUC

FOUC, or flashes of unstyled content, happen when markup is loaded before the styles of scripts that style it.

I load my scripts in the footer for better performance. Because no content is hidden or manipulated before those scripts load (progressive enhancement), visitors will often see the page build before them, with all of the drop-down navigation elements visible and tab content stacked on top of each other. Once the script loads, everything looks normal.

A simple way to avoid this is by putting the feature test up in the <head> element, which is what I now do on all of the sites that build. Since it loads before any of the markup is painted, there’s no FOUC.

So we’re good, right? Well, not exactly…

Failure to Load

The Zeldman quote at the beginning of this piece identifies a giant hole in this approach. If a browser supports the appropriate API’s but fails to load a JS file, the content is hidden, but the script that would allow a user to manipulate and view it isn’t there.

I’ve encountered this countless times, especially on mobile devices where things just time out because they take too long. Not being able to view the content you want is a terribly frustrating user experience—one far worse than a momentary flash of unstyled content.

As Jake Archibald says:

Progressive enhancement has never been about users who’ve turned JavaScript off, or least it wasn’t for me… Basically, when an elevator fails, it’s useless. When an escalator fails, it becomes stairs. We should be building escalators, not elevators.

On Building Escalators

Unfotunately, it feels like progressive ehancement and FOUC are a bit at odds with each other. You could just load all of your scripts in the <head>, but then your left with flashes of no content, which is even worse.

For now, I plan on updating all of my scripts to run the feature test as part of the JS file itself. It will introduce some FOUC, but if the file doesn’t download, users can still access the content. If anyone can come up with a better solution—one that removes FOUC and ensures users always have access to the content—let me know!


Leave a comment or contact me on Twitter at @ChrisFerdinandi.

Chris Pearce

I hardcode a .no-js class to <head> element then replace it with a .js if JS is enabled (either use Modernizr or custom 1 liner in the <head>). I then style my page expecting JS to be enabled and use the .no-js class to override any styles that are needed when JS is disabled.

Chris Ferdinandi

@Chris Pearce – That’s pretty similar to what I do (though I assume no JS by default). I guess my biggest concern is that you’re testing with one file that has no visibility to whether or not the other one has loaded. So you can end up in a situation where the browser supports JS, but the needed file failed to download and so content is inaccessible to the user.

I’m finding that preventing FOUC is at odds with that challenge.

(PS: I added the missing text back in to your original comment for you.)

Ian McBurnie

Thank you! I’ve been looking everywhere for an article that mentions this issue! I’m surprised there aren’t more comments or follow ups (none that I’ve found yet anyway).

It seems that proponents of progressive enhancement (which I include myself as) are no longer just citing the ‘no-script’ case (e.g. JS is disabled or unavailable) as a good reason to build pages this way, but now also the ‘unknown-script’ state (e.g. JS is enabled and available, but a script is slow to load or execute, potentially failing entirely).

Unfortunately, as you mention, the tried and tested formula for preventing FOUC for the no-script scenario does appear at odds with the unknown-script scenario.

A simple example would be a tabs widget and preventing the content of all it’s tab panels briefly flashing up on screen. For the classic no-script case I would typically use JS in the head to add a .js class to the body which I then leverage in my CSS selector to hide all but one of the tab panels. The tabs widget JS will then come along and initialize the widget so that the tabs are fully operable. Great. But the big problem, like you say, is if the tabs widget JS fails to load or execute properly; then the tabs are inoperable and their content stays hidden. Not so great.

It could be argued that this failure to load or execute might just be a temporary network glitch and a quick page refresh or two fixes the issue. But what if it doesn’t?

Well, after all my musing, I’m sorry I didn’t actually come here with any kind of solution (anti-climax), but until somebody does I think I too will be reintroducing some FOUC into my progressive enhancement. If you do have any links, tweets, follow-ups, etc I would be most glad to see them.

Chris Ferdinandi

@Ian McBurnie – I, too, am surprised at the lack of interest around this. I’ve shared my thoughts around PE vs. FOUC a few times but haven’t gotten much in the way of discussion.

I’ve ultimately settled on FOUC in the name of complete, won’t-ever-fail access, and have updated all of my scripts accordingly. As you note, sometimes these glitches don’t quickly resolve themselves (for example, downloading a page just a train goes into a tunnel).

What I have found is that by using native JavaScript instead of libraries like jQuery, and implementing web performance best practices, it’s minimal at worst. On most of the sites I’ve built, you may experience a 300ms flash of FOUC on initial page load, and then all subsequent loads, browser cacheing kicks in and loads JS files instantly, avoiding any issues.

See for a working example.

Ian McBurnie

Yes, I think in cases where you have complete control of the code and a handle on the performance then the first-load FOUC can be negligible on a fast network. It must have felt liberating to update all of your scripts that way :-)

For large enterprise sites though, that are traditionally less performant, I am more wary and I think there might have to be some trade-offs and further thinking to alleviate at least some of the FOUC where possible.

For example, regarding the tabs widget I mentioned in my comment, instead of hiding the panels I could potentially just give the panel container a fixed height with overflow:scroll, then reset both styles to ‘auto’ in the widget JS. It wont be as seamless as before, as the panel container will now have to quickly resize itself, but it’s arguably better than seeing the unstyled content and is now completely usable if the JS bails out.

Chris Ferdinandi

@Ian McBurnie – That’s a good point. Having worked in corporate settings before, I know exactly what you mean. I really like your example with the tabs. Nice win/win.

Leave a Comment

Share links, code and more with Markdown.