How can you make really cool CSS progress bars? What HTML do you need? And how do you use CSS and JavaScript to make it actually work?
In this post I’ll tell you everything you need to know about progress bars. But in order to do that, first I need to take you on a little journey to Hollywood…
Progress bars hit the big screen
Every once in a while, elements from our line of work show up in pop culture – and in 2008, the humble progress bar got a chance to star in a blockbuster Hollywood movie.
In the excellent Iron Man, genius inventor Tony Stark is kidnapped, trapped in a cave, and forced to build a missile for some nasty terrorists. But instead of doing this, our innovative hero builds a suit of armour loaded up with all kinds of weapons, and uses it to escape.
There’s a really cool scene when he’s just about to break out of his cavey prison. He asks Yinsen – his cellmate and assistant – to load up the software for the suit.
Stark: Initialise the sequence. Now!
Yinsen: Tell me, tell me!
Stark: Function 11. You should see a progress bar. Tell me when you see it.
Yinsen: I have it!
Here’s the scene:
I love this. Stark had been captured by ruthless terrorists, and secretly plotting his escape. There was no time to lose – a single wasted second could mean the difference between life and death. But he still took the time to code up a visual progress bar for the suit’s initialisation sequence.
This guy takes UX really seriously!
Now, if Stark can do that in a cave (with a box of scraps) – then you my friend, with the full power of CSS at your disposal, have no excuse!
So let’s talk about a few ways you can create a progress bar in CSS.
The quickest and easiest CSS progress bar
In its simplest form, a progress bar just needs two elements:
- One element to create a gap or space that will be filled (you might call it the ‘track’ or ‘container’)
- Another element to actually fill that space (the bar itself)
To be fair to Tony Stark, this simple option is the one he went for when stuck in the cave. It’s pretty easy to set up – in fact, lets’s replicate the one Stark made:
<div class="progress">
<div class="progress__bar"></div>
</div>
Code language: HTML, XML (xml)
So progress
is the container, and progress__bar
is the element that will fill it up and indicate progress. Now for the CSS:
.progress {
margin: 50px auto;
padding: 2px;
width: 100%;
max-width: 500px;
border: 3px solid #05e35e;
height: 30px;
}
.progress .progress__bar {
height: 100%;
width: 0%;
background-color: #05e35e;
animation: fill-bar 3s infinite;
}
@keyframes fill-bar {
from {width: 0%;}
to {width: 100%;}
}
Code language: CSS (css)
Set whatever height and width you need for progress
, and give it a border. You’ll basically just end up with a rectangle. To replicate the bar in the movie, I’ve added 2px of padding, which creates a little gap between the progress bar itself and the area it’s filling up.
For progress__bar
, I’ve set the height to 100% and width to 0%. BUT… I’ve added an animation, which increases the width from 0 to 100%. I’ve set that animation to repeat every 3 seconds.
Here’s how it looks:
Pretty nice!
Of course, to show the actual progress of something, we can’t just have an automatic animation. We’d have to match the percentage width of progress__bar
to the percentage completion of the thing your user is waiting for – whether that’s files loading, data processing, or whatever. You’ll need JavaScript for this – we’ll come back to it in a bit.
But first, let’s investigate some ways to style these progress bars.
Styling of CSS progress bars
Once you’ve got that basic structure – the container, and the element that fills it – you can got wild and style the bar however you want. It’s completely up to you!
Here’s a simple version:
.progress {
margin: 50px auto;
padding: 2px;
width: 100%;
max-width: 500px;
background: white;
border: 3px solid #000;
border-radius: 20px;
height: 30px;
}
.progress .progress__bar {
height: 100%;
width: 5%;
border-radius: 15px;
background: repeating-linear-gradient(
135deg,
#036ffc,
#036ffc 20px,
#1163cf 20px,
#1163cf 40px
);
animation: fill-bar 3s infinite;
}
Code language: CSS (css)
All I’ve done here is change the colours, round off the corners, and add a gradient background to the bar. Here’s how it looks now:
That’s quite cool – I guess the rounded corners make it a a bit friendlier than the hard-edged version we had before (as Steve Jobs said, “Rectangles with rounded corners are everywhere! Just look around this room!”. Try it – look around your room).
Oh, by the way, there are a couple of things to look out for if you want to use rounded corners:
- The bar itself will usually be smaller that the containing element, so you’ll usually want a little less
border-radius
on the bar than you have on the container - Let’s say you use a 15px
border-radius
like I have here. That border won’t reach its full radius until the bar is 30px wide. If it’s smaller than that, the border radius must be smaller too, so the bar will look taller that you want it to. To get around this, consider giving the bar a little headstart – go from 5% to 100% rather than 0% to 100%. This will ensure the progress bar doesn’t overlap with its container, like this (I’ve frozen it at 3% width so you can see what I mean):
Feel free to copy or fork these Codepens and play around with different colours, styles, and ideas. Let us know if you create something you like!
Semantics – the progress HTML element
HTML5 includes a <progress>
element, which is the semantially-correct way to make a progress bar. There are two kinds:
- Determinate: When you have specified a current value of the bar (e.g., 50%).
- Indeterminate: When you haven’t specified a current value of the bar.
<!-- Determinate -->
<progress id="determinate" value="75" min="0" max="100"> 50% </progress>
<!-- Indeterminate -->
<progress id="indeterminate"></progress>
Code language: HTML, XML (xml)
And here’s how they look (you can also assign label
elements to them):
The indeterminate one could be handy in some situations – maybe when you’re not sure how long the user has to wait, or before a download actually starts.
Styling the HTML progress element
What’s tricky about progress
is that each browser displays them differently by default – usually, but not always, based on the operating system and browser you’re using. And they are a bit fiddly to style.
To customise them, you first you need to remove the browser’s default styles by setting appearance
to none
:
progress {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
}
Code language: CSS (css)
Note that you can target determinate progress
elements and indeterminate ones separately:
/* Styles determinate progress bars (with a value property) */
progress[value] {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
}
/* Styles indeterminate progress bars (without a value property) */
progress:not([value]) {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
}
Code language: CSS (css)
Now to add your own styles! Again, it’s a little tricky. In Chrome and Safari, you use the -webkit-progress-bar
pseudo class to style the container part of the bar, and -webkit-progress-value
to style the actual bar that fills it up:
progress::-webkit-progress-bar {
background: #;
box-shadow: 0 2px 3px;
border-radius: 3px;
}
progress::-webkit-progress-value {
background-color: #CC0000;
border-radius: 3px;
}
Code language: CSS (css)
For Firefox, you style the container with the progress
element itself, and the bar with -moz-progress-value
, like this:
progress {
background: linear-gradient(#ccc 0%, #eee 50%, #eee 50%, #ccc 100%);
border-radius: 2px;
width: 500px;
margin: 0px auto;
max-width: 500px;
height: 30px;
}
progress::-moz-progress-value {
background: linear-gradient(#b98cf5 0%, #f0e6ff 50%, #f0e6ff 50%, #b98cf5 100%);
border-radius: 3px;
}
Code language: CSS (css)
Like I said, a bit tricky! Both of those will give you this:
However, note that these pseudoclasses are non-standard – Mozilla doesn’t recommend we use them. But if you’re worried about semantics and accessibility (as you should be!), there’s a compromise to consider…
The progressbar role
There is an aria role you can use on progress bars – which is called, believe it or not, progressbar
! This will help make the progress bar usable to people using screen readers and other assistive technologies.
For this, you’d just use a normal div
, but add the progressbar
role to it. There are aria equivalents to the properties mentioned above:
aria-valuenow
=value
aria-valuemin
=min
aria-valuemax
=max
aria-valuetext
is a new one, it’s basically an alt text
These values don’t affect the actual appearance of the bar. So make sure that as you move the bar, you update aria-valuenow
and aria-valuetext
by equivalent amounts.
Your html will look something like this:
<div role="progressbar" aria-valuenow="20" aria-valuemin="0" aria-valuetext="Download progress: 20%" aria-valuemax="100"></div>
Code language: HTML, XML (xml)
Adding JavaScript to show actual progress
Finally, we just need to update our progress bar in-line with the actual progress of the operation.
This is a lot simpler that it sounds – remember all we have to do is update the width
property of our progress__bar
element, which we can do easily in JavaScript:
let progressbar = document.getElementById("download");
function updateProgressBar(value) {
if (value >= 0 && value <= 100) {
progressbar.style.width = value + "%";
}
}
Code language: JavaScript (javascript)
This function:
- Accepts a value, which we’ve called
value
. - Checks if
value
is between 0 and 100 (as we’re setting thewidth
as a percentage, it needs to be in this range). - If so, it goes to the element with the
id
ofdownload
(which we stored in theprogressbar
variable), and sets its widthvalue
(note: that you’ll also need to add thedownload
ID to theprogress__bar
element, so that JS can select it).
To make the progress bar move smoothly, you can give it a CSS transition-duration
property:
.progress .progress__bar {
height: 100%;
width: 0;
border-radius: 4px;
background: linear-gradient(
to right, red, orange , yellow, green, cyan, blue, violet);
);
transition: 0.3s;
}
Code language: CSS (css)
Now you’ve got the code that will move the progress bar, all you need to do is call updateProgressBar
in your JavaScript, and pass it a new value
.
Exactly how to do this will of course depend on what exactly you’re measuring the progress of. For example, if it’s a file download, you could pass an updated value
after each individual file has been downloaded – and the value you pass could be based on the size of the file.
It’ll work like this:
If you type a value into the input box and click Submit, a function called handleInput
function will be called. That function will take your input, and pass it to updateProgressBar
. The bar will then move to the new point that you specified – and the time it takes to get there will be whatever value you set in the transition
property of progress__bar
– in this case, 0.3 seconds.
Progress bars that don’t need JavaScript
Note that not all progress bars need to be dynamically updated in this way.
One example is a little sneaky. Have you ever gone to something like a price comparison site, where you put in a query and get one of those “Now checking 6 billion businesses to get you the best price!” screens, and a little progress bar pops up with it?
Well, sometimes, those progress bars are completely fake! Developers often put them in to create a fake delay of 1 or 2 seconds, so that it looks like the server is working really really hard for you.
This is a little deceptive, but the argument is that if people expect something to take a little processing time, but then it appears instantly, they might get suspicious. They might doubt that any processing happened at all – maybe they’ll think the site just recommended the ones that give them the highest referral fee.
So if you have a use-case like this, where you just need a progress bar just for the visual effect, you can use the animated versions we talked about at the beginning of this post.
There are also cases when you do need the progress bar to accurately represent progress, but where JavaScript isn’t needed – for example if the progress is happening across multiple pages of the site. For example, maybe you have a multi-page checkout process, or a job application where first you put in your personal details, then your education, then your work history etc.
In these cases, you can just put a static progress bar up on each separate page, with the width
of the bar set to an appropriate amount based on the user’s progress so far.
Now it’s your turn!
Congratulations! You’ve just learned the fundamentals of how to create CSS progress bars for your site. The fact that you’re looking to set up a progress bar suggests that you’re working on a somewhat complex site at the moment. That’s cool!
If you wanna take your site to the next level, take a peek at fullPage.js. You know those cool full-page sites? The ones where you scroll down, and get a nice animation, and then you’re taken to a whole new page? Well fullPage.js is how you can make them!
You get a boatload of different effects and navigation options, and it fits right in with WordPress or your favourite JavaScript framework. Check it out, and see if you like it!