Revisiting FOUCE

It's been awhile since I wrote about FOUCE and I've since come up with an improved solution that I think is worth a post.

This approach is similar to hiding the page content and then fading it in, but I've noticed it's far less distracting without the fade. It also adds a two second timeout to prevent network issues or latency from rendering an "empty" page.

First, we'll add a class called reduce-fouce to the <html> element.

<html class="reduce-fouce">
  ...
</html>

Then we'll add this rule to the CSS.

<style>
  html.reduce-fouce {
    opacity: 0;
  }
</style>

Finally, we'll wait until all the custom elements have loaded or two seconds have elapsed, whichever comes first, and we'll remove the class causing the content to show immediately.

<script type="module">
  await Promise.race([
    // Load all custom elements
    Promise.allSettled([
      customElements.whenDefined('my-button'),
      customElements.whenDefined('my-card'),
      customElements.whenDefined('my-rating')
      // ...
    ]),
    // Resolve after two seconds
    new Promise(resolve => setTimeout(resolve, 2000))
  ]);

  // Remove the class, showing the page content
  document.documentElement.classList.remove('reduce-fouce');
</script>

This approach seems to work especially well and won't end up "stranding" the user if network issues occur.