I present to you a concept of implementation of Skeleton Screens in web Single Page Applications. Before thinking about using this concept in your project, make sure to read the Known issues section.
The main benefit is to provide a great user experience on first page load and to decrease the First Contentful Paint time that has a role to play in Google's website evaluation, you can learn more in Benefits section
An example is available at the following address: dimitrinicolas.github.io/skeleton-screens-concept.
I made sure that the style sheet weighs at least 300 kb for the example by adding random strings inside. You can use your browser developer tools to simulate a slow internet connection.
The source code is available in this repository: index.html.
First, we load the application style sheet as a non-blocking resource using the
following pattern in the HTML head
element:
<link rel="preload" href="style.css" as="style">
And add the stylesheet at the end of your body
element:
<link rel="stylesheet" href="style.css">
Then, we add some CSS inside a style
tag just before the non-blocking style
sheet loading:
<head>
<!-- Any head things -->
<style>
/** Standard style of the application */
html {
background-color: white;
}
body {
margin: 0;
}
/** Skeleton style element */
.header {
height: 80px;
background-color: blue;
}
.header__content {
display: none;
}
.footer {
display: none;
}
</style>
<!-- Non-blocking style sheet loading -->
<link rel="preload" href="style.css" as="style">
</head>
Then, we need to crush some pre-loading style by adding display: block
to
.header__content
and .footer
in our style.css
file.
We could add skeleton blocks and highlighting animations in our pre-loading
style using before
and after
pseudo-elements and CSS animations.
To create a fake logo block inside the header, we can add the following style:
.header__wrap::before {
content: "";
position: absolute;
top: 16px;
left: 0;
bottom: 16px;
width: 100px;
background-color: #efefef;
}
Then we can add an highlight sweeping animation using like that:
.header__wrap::before {
/* Block style */
background-image:
linear-gradient(to right, transparent, rgba(255, 255, 255, 0.6) 50%, transparent 100%);
background-repeat: no-repeat;
background-size: 80px 100%;
background-position: -80px 0;
animation: sweep 1000ms ease-in-out 0s infinite;
}
@keyframes sweep {
to {
background-position: calc(100% + 80px) 0;
}
}
When loading the application style as a non-blocking resource, any element with
a transition
CSS property will get triggered on style sheet load if its
animated properties values are different from the default or from the
pre-loader style.
To prevent this problem to trigger transition on every property of the element,
we can target the properties to animate using the transition-duration
and
transition-property
CSS properties. You can learn more about theses on Mozilla
Web documentation:
developer.mozilla.org/fr/docs/Web/CSS/transition-property.
To totally prevent this issue we'll need to add a specific class name to the
html
tag like .js-can-use-transitions
on style loading after a small time
and use the transition
property in our style only when this class name has
been added:
At the end of the body
element:
<link
rel="stylesheet"
href="style.css"
onload="setTimeout(function () { document.documentElement.className += ' js-can-use-transitions';}, 100);"
>
In style.css
:
.js-can-use-transitions .header {
transition: background-color 300ms;
}
This solution is not ideal, the user will need to have JavaScript enabled to
have CSS transitions and the time to wait before adding the class name to the
html
element depends on the user device computation performance. The 100
milliseconds value is the maximum average time the browser will need to compute
the style sheet.
I did five Google PageSpeed Insights tests on each version of the same example page: the standard one without skeleton screens and with render-blocking CSS and the version with this concept.
The Mobile score and First Contentful Paint time were always the same after each test. The version with skeleton screens got a 99% mobile score with 0,8 seconds for the First Contentful Paint. The standard version got a 97% mobile score with 2,2 seconds for the First Contentful Paint.
These results are not the best example because the example page has a super simple DOM and the 300 kb style sheet only contains a small amount of CSS to compute, the whole file size come from a random string in a CSS comment.
I'll need to implement this concept into a real website to better understand the performance benefit.
This approach of skeleton screens is useful for Single Page Applications with a heavy style sheet.
I need to find solutions to use this concept with static website frameworks like Gatsby or Next.js.
- Everything you need to know about skeleton screens (uxdesign.cc) - An article by Bill Chung about differents skeleton screens design solutions and studies of their performances on the user perception.
This project is licensed under the MIT license.