CodeinWP CodeinWP

Fading in a Page on Load with CSS & JavaScript

Fading in a Page on Load with CSS & JavaScript When I visit a page, I get annoyed when I try to interact with elements while the website is still loading. Often stuff is moving around, fonts aren’t quite loaded, and it feels broken.

I know nowadays we’re obsessed in this industry with gaining every millisecond in page performance. But in a couple of projects that I recently overhauled, I added a subtle and clean loading mechanism that I think makes the experience nicer, even if it does ultimately slightly delay the time that the user is able to start interacting with my page.

The two websites I’m talking about are Web Tools Weekly and CSS Values. Visit each of those home pages and you’ll notice in each case the content fades in rather than loading up in a clunky fashion. It’s more noticeable on CSS Values because there’s a lot more content on there and some JavaScript that has to load. Also, it happens on every “page” for CSS Values, but only on the home page for Web Tools Weekly (though technically the CSS Values pages are more like “states”).

Steps I Want to Accomplish

I’m sure there are a few ways to accomplish this but I think my method is clean and easy to understand. I’m basically doing two things:

  1. As soon as the <body> element is ready, set it to opacity: 0 in CSS.
  2. When the DOM is ready and any scripts finish running, set the <body> element back to opacity: 1, animating it so it comes in gracefully.

I’m also ensuring that the page is fully visible and accessible to anyone viewing with JavaScript turned off. Those users shouldn’t notice anything wrong. This means I have to set the opacity using JavaScript.

The concept here is basically the same as if I was adding a loading spinner to the page, but because the page shouldn’t be loading for long, I don’t care about indicating what’s happening to the user. In other cases, a loading spinner might be more appropriate, but here I’m happy with a simple fade-in.

The Code

I could have used classes in my HTML that are pulled in from my stylesheet. But the stylesheet is external and I’m not inlining it. So if I’m trying to interact with the page before the stylesheet finishes loading, this might not work as well. Because of this, I opted for adding some CSS in the <head> of the document.

Here’s what my HTML looks like:

  <style>
    .hidden {
      opacity: 0;
    }

    .visible {
      opacity: 1;
      transition: opacity 1s ease-out;
    }
  </style>
</head>
<body>
  <script>
    document.body.className = 'hidden';
  </script>

Notice what’s happening above:

  • I’ve added two declaration blocks in a <style> element, just above the opening <body> tag
  • I’m using a CSS transition for the subtle animation that causes the fade-in effect
  • I’ve put a <script> element right after the opening <body> tag
  • The JavaScript adds the hidden class, which removes the body’s content

I’m doing this right at the top of the body element because I want the content to disappear as soon as possible. If I wait for this to happen in my external JavaScript, then it’s not going to happen quickly enough.

In my external JavaScript, I have something like the following:

function doStuff (callback) {
  // do all app scripts here...
  callback();
}

doStuff(function () {
  document.body.className = 'visible';
});

In this instance I’m using a simple callback structure. I suppose you could also use Promises for this, but I didn’t really look into that much since I’m not yet that familiar with Promises. I believe the concept would be the same.

I’m basically ensuring all the app or website code that initializes any functionality is completely loaded before I set the <body> element back to visible.

To enhance it a little more, I could also wrap the whole thing in a jQuery-like document.ready handler:

window.addEventListener('DOMContentLoaded', function () {
  // do everything here...
});

This means I’d be waiting for the DOM to be ready before running my scripts. Then the scripts run and after they finish I make the page visible. I can alternatively use the load event, which means I’d be waiting for the full page resources to load, rather than just the DOM to be ready.

Conclusion

If you want a stripped down demo version of this, you can check out this page. Note that it’s intentionally a very large page, so maybe avoid it on mobile. I’m also running some useless JavaScript (adding a bunch of classes to the HTML) just to pretend something’s happening to give the appearance of a pause before the fade-in happens.

As mentioned, you can also see how this works on Web Tools Weekly and CSS Values.

You could easily switch this up and put a loading spinner in place instead while the page is “getting ready”. I chose to do the fade-in because I think it looks good and I’m not concerned that either website is going to take a long time to load.

If you know of a way to improve on this, I’m glad to hear your feedback.

13 Responses

  1. Hi Louis! Thanks for this helpful information! Regards

  2. sb says:

    I guess you could set the fade in and fade out the timing of the information on the page?

    • I’m not sure I understand your question…?

      Maybe the following will help: This doesn’t work on any sort of ‘timer’, it’s literally making the page page invisible as soon as the body element is available, then fading the page in after all the resources are fully loaded. So the timing could be different on a slower connection, or if there’s more content to load.

  3. M. Inam says:

    Why

    document.body.className = ‘hidden’;

    ….

    Not

  4. Scott Rod says:

    Why just fading in the home page, and not all landing pages, on Web Tools Weekly?

    • I could do that. But I feel like I want the experience to be much faster on the other pages. The home page is like the “entry” point for most users. Once they “enter”, there’s no need to delay their visit if they’re clicking to other pages. I think the experience is better this way.

  5. Hi Louis,
    Yes, I like smooth transitions! And that’s coincidence: I was building last weeks something on the same prncipes; together with some speed improving other css and js for compensation. – Have a plan to make an “How it’s build” page (in EN), but first I’ve to make the (Dutch) site itself. :-)
    Test page for the homepage:
    * clba.nl/experiments/fadingtest/
    Not an awfull CLS (Cumulative Layout Shift: 0.007) and Google’s Page Speed Insights is satisfied for Desktop, with 100/100.

    (I posted the same reaction at chris’ review on css-tricks)

    • Very nice!

      Looks like only the images get faded in, correct?

      • Yes and No!
        Yes: at the opening of the page.
        And no: if you click on an image, the whole page is fading into darkness. If you return to the page, the page has a fading comeback (and has the same scroll position as when you “left the page”).
        And yes again: if you click on an enlargement with the time lapse button at the bottom (in the first 3 images), then the corresponding image (forwards and backwards) is fading in/out without changing the dark page background of the overlay.

  6. starc says:

    Love how simple this is. I wonder how (if at all) it would alter the accessibility score during a lighthouse test, like overall contrast for example.

  7. peet says:

    hi
    love this and its simplicity
    do youhave a similar simple script that will fade out the page when an anchor _ eg a url link is clicked anywhere on the page.

    • No, but I honestly would not recommend that. When people leave a page, they want to leave quickly and I don’t think the fade out would make sense in that case.

  8. Ameen says:

    Hello
    I Like this and its simplicity

Leave a Reply

Comment Rules: Please use a real name or alias. Keywords are not allowed in the "name" field and deep URLs are not allowed in the "Website" field. If you use keywords or deep URLs, your comment or URL will be removed. No foul language, please. Thank you for cooperating.

Markdown in use! Use `backticks` for inline code snippets and triple backticks at start and end for code blocks. You can also indent a code block four spaces. And no need to escape HTML, just type it correctly but make sure it's inside code delimeters (backticks or triple backticks).