This was originally posted on my personal blog at https://omarsinan.com/blog/increasing-my-pagespeed-score-from-92-to-100-using-css-only
In this blog post I will show you how I was able to improve my personal website’s PageSpeed score from 92% to 100% and decrease the loading time by 0.3 seconds on average using CSS only!
Let’s talk about CSS sprites
The trick here is using CSS sprites, I’ve spent the past couple of days looking through different portfolios of people I follow on Twitter and there was a pattern. A lot of people didn’t make use of CSS sprites.
So what are CSS sprites? Like any other sprite sheet, CSS sprites are a collection of icons/images in a single image file. Let’s say you have a social media icons bar on your website. Instead of having every icon as a single image, you could create an image with all the icons separated by 1 pixel as shown below:
How are CSS sprites beneficial?
Well, instead of making 6 requests for your 6 social media icons, you’ll make 1 request for the sprite sheet and then by using CSS, you could chop the image up appropriately so that you can create 6 separate images out of one.
I was also able to decrease my page loading speed by 0.3 seconds on average and improve my Google PageSpeed score by 8% (from 92% to 100%)
Compatible with all major browsers
The CSS property used here is the background-position property which is supported by all major browsers as shown below:
How can you use them?
They’re very simple to use, once you create an image with all icons separated by 1 pixel (you could use any photo editing software or applications like Figma and sketch), you create a div whose width is the width of 1 icon, let’s say it’s 26px and whose height is the height of 1 icon let’s say it’s 30px, and then set the background of the div to be the URL of the image at x y. Where x is the background-position-x property and y is the background-position-y property. Below is an example of how I styled my divs on my personal website (https://omarsinan.com/)
<div class="githubSocial"></div>
<div class="linkedinSocial"></div>
<div class="devSocial"></div>
<div class="twitterSocial"></div>
<div class="rssSocial"></div>
<div class="resumeSocial"></div>
div {
display: inline-block;
height: 30px;
}
.github-social {
width: 26.25px;
background: url($url) 0 0;
}
.linkedin-social {
width: 26.25px;
background: url($url) -27.25px 0;
}
.dev-social {
width: 26.25px;
background: url($url) -54.5px 0;
}
.twitter-social {
width: 26.25px;
background: url($url) -81.75px 0;
}
.rss-social {
width: 26.25px;
background: url($url) -109px 0;
}
.resume-social {
width: 22.5px;
background: url($url) -136.25px 0;
}
As you can see it is very simple. I am using the background property in CSS while using the shorthand form of combining it with the background-position property in a single line. It could be split into 2 lines and would have the same effect.
It also still looks the same so there are no disadvantages to adopting this method:
What’s next?
Check out https://web.dev/fast/ where there are more tips on how you could make your website load faster. Also, try to serve your images in .webp format and if the quality of the image isn’t an issue, reduce the quality of the image to maybe 80%.
Using tools like Cloudinary makes this soooo much easier by using the f_auto
parameter which selects the optimal format and the q_auto
parameter which selects the optimal quality.
By following many of the tips highlighted in https://web.dev, i was able to reduce my website’s loading time from the initial 2.1 seconds to 1.3 seconds. This is a significant improvement and it could be even more significant on your website!
If you decide to use CSS sprites, please share your results and thoughts with me I’d love to see the changes it has made to your website.
You can also connect with me on Twitter @oohsinan
Top comments (19)
Your icons could be svg, probably A LOT smaller than whatever sprite you got there. ;)
And it would be much crisper, than this blurry png/webp ;)
PS. For some reason on my firefox its not working at all on your site, but i have aggressive adblock so that might be my fault.
This. SVGs are pretty much the standard these days for icons. Simple Icons has tons of social media icons.
Hmmm very interesting thanks for the feedback I’ll look into it! 🙌
If you have your icons as plain divs with background images like this, you lose the semantic html that allows screen readers to pick them up, and for users to tab and click them via keyboard 🤔
Oh, i didn’t know this! Would using the img tag be better?
Yep, the problem is
div
doesn't have any semantic value, and isn't focusable by keyboard by default either.Usually for this use case you would have a link with an
img
inside with meaningful alt text.There's a nice article on this by W3: w3.org/WAI/tutorials/images/functi...
Ah that’s so interesting, I’ll check it out. Thank you for sharing! 🙌
Notably, sprites are becoming less useful with H2 and with Google increasingly checking for unused image space. If you're not actually using every sprite element on first load, this method may instead cause a penalty rather than a gain!
So for other people, I'll also echo using SVG here, as well as very closely checking what is penalizing your pagespeed score!
I appreciate this article, as these are definitely performance best practices in the past, but with HTTP/2 capability on the server and in the browser this is now an anti-pattern.
The best solution (easier to create & maintain, faster to send to browsers, and better at caching) is to utilize HTTP/2. Whatever server you're using should support it. If it doesn't, or if you can't use it, consider serving your domain with Cloudflare, which has supported HTTP/2 for a few years.
Totally! With HTTP/2 it can be done the way it was intended without the sprite hack and it means altering one image won't ruin the cache of others let alone techniques like lazy loading of images. Never forget that tools like PageSpeed Insights are there to give you insights not for you to serve them.
Is it still worth it, though? 🤔
That's the question I've been asking myself now, these days. For it's time, this was a pretty revolutionary idea. The technique has been around for many years now, really catching on by the late 2000's. These days, however, with HTTP/2 and HTTP/3, the overall benefit of bundling images together into one (to reduce HTTP overhead due to TCP/IP latency + protocol overhead) it's becoming theoretically less important. This is assuming your server is using those new protocols and the connecting clients support it as well (most modern browsers support HTTP/2 right out of the box). I say "theoretically" because we need benchmarking to be more certain.
I ended up stepping back and asking myself this question thanks to the fact that a 5yr-6yr old
grunt
workflow we already had setup was becoming cumbersome to manage, beginning to fail due to legacy module/node reasons (especially sincephantomjs
is is now a... ghost). I personally still see a huge value in "spriting" single color SVG's into a font (due to the flexibility of being able to easily colorize) but I've begun to wonder if it's still worthwhile for PNG/GIF (bitmaps) and colorized SVG's (vectors), considering the benefits of the alternative, i.e.: Lower maintenance (reduced legacy support), the ability to inline SVG into an alreadygzip
'd orbrotli
'd page and better caching (by keeping them separate), as noted elsewhere.Aside from the obvious comments about using SVG instead of background image hacks, why do you need 1 pixel gaps and then to use sub-pixel sizing for the sizing? Sub-pixel sizing will create blurred pixelmaps which really isn't so useful
Nice article.
I would preffer to use font icons instead of image sprite.
You can use service like ICOMOON to convert your svg's and create your own font icons to get better performance.
Yeah 2 popular opinions I’m seeing are font icons and SVGs. I found it very interesting, will certainly look into both to see how the performance improves. Thanks for sharing 🙌
Good article.
As a Hunt: If these are mono-color, you can try to make a font from them. Or if multicolor you can use svg, it decrease size a lot in compare to png.
Awesome I will check also I loved you landing page
Thank you! Means a lot 🙌
The comments mentioning svg make me think of base64. Has anyone tested if background-image was a sprite in base64? It would mean one request for CSS AND for icons which is pretty neat.
Since base64 image urls are just that - urls - it should work. That said, you wouldn't want a sprite in there (unless used as a custom property). You'd just want individual images. That said, reusability, readability, and effort to change the icons later can make this more tedious than it's worth for pagespeed.