{"id":5158,"date":"2021-06-03T02:00:00","date_gmt":"2021-06-03T00:00:00","guid":{"rendered":"https:\/\/alvarotrigo.com\/epa\/how-to-make-a-progress-bar-in-css\/"},"modified":"2025-06-10T10:52:43","modified_gmt":"2025-06-10T08:52:43","slug":"how-to-make-a-progress-bar-in-css","status":"publish","type":"post","link":"https:\/\/alvarotrigo.com\/blog\/how-to-make-a-progress-bar-in-css\/","title":{"rendered":"How to make a progress bar in CSS"},"content":{"rendered":"\n<p>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?<\/p>\n\n\n\n<p>In this post I&#8217;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&#8230;<\/p>\n\n\n<h2 class=\"wp-block-heading\" id=\"progress-bars-hit-the-big-screen\">Progress bars hit the big screen<\/h2>\n\n\n<p>Every once in a while, elements from our line of work show up in pop culture &#8211; and in 2008, the humble progress bar got a chance to star in a blockbuster Hollywood movie.<\/p>\n\n\n\n<p>In the excellent <em>Iron Man<\/em>, 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.<\/p>\n\n\n\n<p>There&#8217;s a really cool scene when he&#8217;s just about to break out of his cavey prison. He asks Yinsen &#8211; his cellmate and assistant &#8211; to load up the software for the suit.<\/p>\n\n\n\n<p><strong>Stark:<\/strong> Initialise the sequence. Now!<br>\n<strong>Yinsen:<\/strong> Tell me, tell me!<br>\n<strong>Stark:<\/strong> Function 11. You should see a progress bar. Tell me when you see it.<br>\n<strong>Yinsen:<\/strong> I have it!<\/p>\n\n\n\n<p>Here&#8217;s the scene:<\/p>\n\n\n\n<figure class=\"wp-block-embed is-type-video is-provider-youtube wp-block-embed-youtube wp-embed-aspect-16-9 wp-has-aspect-ratio\"><div class=\"wp-block-embed__wrapper\">\nhttps:\/\/www.youtube.com\/watch?v=FHU49CD0TOs\n<\/div><\/figure>\n\n\n\n<p>I love this. Stark had been captured by ruthless terrorists, and secretly plotting his escape. There was no time to lose &#8211; a single wasted second could mean the difference between life and death. But he <em>still<\/em> took the time to code up a visual progress bar for the suit&#8217;s initialisation sequence.<\/p>\n\n\n\n<p>This guy takes UX <em>really<\/em> seriously!<\/p>\n\n\n\n<p>Now, if Stark can do that in a cave (with a box of scraps) &#8211; then you my friend, with the full power of CSS at your disposal, have no excuse!<\/p>\n\n\n\n<p>So let&#8217;s talk about a few ways you can create a progress bar in CSS.<\/p>\n\n\n<h2 class=\"wp-block-heading\" id=\"the-quickest-and-easiest-css-progress-bar\">The quickest and easiest CSS progress bar<\/h2>\n\n\n<p>In its simplest form, a progress bar just needs two elements:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>One element to create a gap or space that will be filled (you might call it the &#8216;track&#8217; or &#8216;container&#8217;)<\/li>\n\n\n\n<li>Another element to actually fill that space (the bar itself)<\/li>\n<\/ul>\n\n\n\n<p>To be fair to Tony Stark, this simple option is the one he went for when stuck in the cave. It&#8217;s pretty easy to set up &#8211; in fact, lets&#8217;s replicate the one Stark made:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"progress\"<\/span>&gt;<\/span> \n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"progress__bar\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>So <code>progress<\/code> is the container, and <code>progress__bar<\/code> is the element that will fill it up and indicate progress. Now for the CSS:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-class\">.progress<\/span> {\n  <span class=\"hljs-attribute\">margin<\/span>: <span class=\"hljs-number\">50px<\/span> auto;\n  <span class=\"hljs-attribute\">padding<\/span>: <span class=\"hljs-number\">2px<\/span>;\n  <span class=\"hljs-attribute\">width<\/span>: <span class=\"hljs-number\">100%<\/span>;\n  <span class=\"hljs-attribute\">max-width<\/span>: <span class=\"hljs-number\">500px<\/span>;\n  <span class=\"hljs-attribute\">border<\/span>: <span class=\"hljs-number\">3px<\/span> solid <span class=\"hljs-number\">#05e35e<\/span>;\n  <span class=\"hljs-attribute\">height<\/span>: <span class=\"hljs-number\">30px<\/span>;\n}\n\n<span class=\"hljs-selector-class\">.progress<\/span> <span class=\"hljs-selector-class\">.progress__bar<\/span> {\n  <span class=\"hljs-attribute\">height<\/span>: <span class=\"hljs-number\">100%<\/span>;\n  <span class=\"hljs-attribute\">width<\/span>: <span class=\"hljs-number\">0%<\/span>;\n  <span class=\"hljs-attribute\">background-color<\/span>: <span class=\"hljs-number\">#05e35e<\/span>;\n  <span class=\"hljs-attribute\">animation<\/span>: fill-bar <span class=\"hljs-number\">3s<\/span> infinite;\n}\n\n<span class=\"hljs-keyword\">@keyframes<\/span> fill-bar {\n  <span class=\"hljs-selector-tag\">from<\/span> {<span class=\"hljs-attribute\">width<\/span>: <span class=\"hljs-number\">0%<\/span>;}\n  <span class=\"hljs-selector-tag\">to<\/span> {<span class=\"hljs-attribute\">width<\/span>: <span class=\"hljs-number\">100%<\/span>;}\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Set whatever height and width you need for <code>progress<\/code>, and give it a border. You&#8217;ll basically just end up with a rectangle. To replicate the bar in the movie, I&#8217;ve added 2px of padding, which creates a little gap between the progress bar itself and the area it&#8217;s filling up.<\/p>\n\n\n\n<p>For <code>progress__bar<\/code>, I&#8217;ve set the height to 100% and width to 0%. BUT&#8230; I&#8217;ve added an animation, which increases the width from 0 to 100%. I&#8217;ve set that animation to repeat every 3 seconds.<\/p>\n\n\n\n<p>Here&#8217;s how it looks:<\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_YzZvexE\" data-src=\"\/\/codepen.io\/anon\/embed\/YzZvexE?height=450&amp;theme-id=1&amp;slug-hash=YzZvexE&amp;default-tab=css,result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed YzZvexE\" title=\"CodePen Embed YzZvexE\" class=\"cp_embed_iframe lazyload\" style=\"width:100%;overflow:hidden\" src=\"data:image\/svg+xml;base64,PHN2ZyB3aWR0aD0iMSIgaGVpZ2h0PSIxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPg==\" data-load-mode=\"1\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<p>Pretty nice!<\/p>\n\n\n\n<p>Of course, to show the <em>actual<\/em> progress of something, we can&#8217;t just have an automatic animation. We&#8217;d have to match the percentage width of <code>progress__bar<\/code> to the percentage <em>completion<\/em> of the thing your user is waiting for &#8211; whether that&#8217;s files loading, data processing, or whatever. You&#8217;ll need JavaScript for this &#8211; we&#8217;ll come back to it in a bit.<\/p>\n\n\n\n<p>But first, let&#8217;s investigate some ways to style these progress bars.<\/p>\n\n\n<h2 class=\"wp-block-heading\" id=\"styling-of-css-progress-bars\">Styling of CSS progress bars<\/h2>\n\n\n<p>Once you&#8217;ve got that basic structure &#8211; the container, and the element that fills it &#8211; you can got wild and style the bar however you want. It&#8217;s completely up to you!<\/p>\n\n\n\n<p>Here&#8217;s a simple version:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-class\">.progress<\/span> {\n  <span class=\"hljs-attribute\">margin<\/span>: <span class=\"hljs-number\">50px<\/span> auto;\n  <span class=\"hljs-attribute\">padding<\/span>: <span class=\"hljs-number\">2px<\/span>;\n  <span class=\"hljs-attribute\">width<\/span>: <span class=\"hljs-number\">100%<\/span>;\n  <span class=\"hljs-attribute\">max-width<\/span>: <span class=\"hljs-number\">500px<\/span>;\n  <span class=\"hljs-attribute\">background<\/span>: white;\n  <span class=\"hljs-attribute\">border<\/span>: <span class=\"hljs-number\">3px<\/span> solid <span class=\"hljs-number\">#000<\/span>;\n  <span class=\"hljs-attribute\">border-radius<\/span>: <span class=\"hljs-number\">20px<\/span>;\n  <span class=\"hljs-attribute\">height<\/span>: <span class=\"hljs-number\">30px<\/span>;\n}\n\n<span class=\"hljs-selector-class\">.progress<\/span> <span class=\"hljs-selector-class\">.progress__bar<\/span> {\n  <span class=\"hljs-attribute\">height<\/span>: <span class=\"hljs-number\">100%<\/span>;\n  <span class=\"hljs-attribute\">width<\/span>: <span class=\"hljs-number\">5%<\/span>;\n  <span class=\"hljs-attribute\">border-radius<\/span>: <span class=\"hljs-number\">15px<\/span>;\n  <span class=\"hljs-attribute\">background<\/span>: <span class=\"hljs-built_in\">repeating-linear-gradient<\/span>(\n    <span class=\"hljs-number\">135deg<\/span>,\n    #<span class=\"hljs-number\">036<\/span>ffc,\n    #<span class=\"hljs-number\">036<\/span>ffc <span class=\"hljs-number\">20px<\/span>,\n    #<span class=\"hljs-number\">1163<\/span>cf <span class=\"hljs-number\">20px<\/span>,\n    #<span class=\"hljs-number\">1163<\/span>cf <span class=\"hljs-number\">40px<\/span>\n  );\n  <span class=\"hljs-attribute\">animation<\/span>: fill-bar <span class=\"hljs-number\">3s<\/span> infinite;\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>All I&#8217;ve done here is change the colours, round off the corners, and add a gradient background to the bar. Here&#8217;s how it looks now:<\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_poeKarM\" data-src=\"\/\/codepen.io\/anon\/embed\/poeKarM?height=450&amp;theme-id=1&amp;slug-hash=poeKarM&amp;default-tab=css,result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed poeKarM\" title=\"CodePen Embed poeKarM\" class=\"cp_embed_iframe lazyload\" style=\"width:100%;overflow:hidden\" src=\"data:image\/svg+xml;base64,PHN2ZyB3aWR0aD0iMSIgaGVpZ2h0PSIxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPg==\" data-load-mode=\"1\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<p>That&#8217;s quite cool &#8211; I guess the rounded corners make it a a bit friendlier than the hard-edged version we had before (as Steve Jobs said, &#8220;Rectangles with rounded corners are everywhere! Just look around this room!&#8221;. Try it &#8211; look around your room).<\/p>\n\n\n\n<p>Oh, by the way, there are a couple of things to look out for if you want to use rounded corners:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>The bar itself will usually be smaller that the containing element, so you&#8217;ll usually want a little less <code>border-radius<\/code> on the bar than you have on the container<\/li>\n\n\n\n<li>Let&#8217;s say you use a 15px <code>border-radius<\/code> like I have here. That border won&#8217;t reach its full radius until the bar is 30px wide. If it&#8217;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 &#8211; go from 5% to 100% rather than 0% to 100%. This will ensure the progress bar doesn&#8217;t overlap with its container, like this (I&#8217;ve frozen it at 3% width so you can see what I mean):<\/li>\n<\/ul>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_xxqzYXg\" data-src=\"\/\/codepen.io\/anon\/embed\/xxqzYXg?height=450&amp;theme-id=1&amp;slug-hash=xxqzYXg&amp;default-tab=css,result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed xxqzYXg\" title=\"CodePen Embed xxqzYXg\" class=\"cp_embed_iframe lazyload\" style=\"width:100%;overflow:hidden\" src=\"data:image\/svg+xml;base64,PHN2ZyB3aWR0aD0iMSIgaGVpZ2h0PSIxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPg==\" data-load-mode=\"1\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<p>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!<\/p>\n\n\n<h2 class=\"wp-block-heading\" id=\"semantics---the-progress-html-element\">Semantics &#8211; the progress HTML element<\/h2>\n\n\n<p>HTML5 includes a <code>&lt;progress&gt;<\/code> element, which is the semantially-correct way to make a progress bar. There are two kinds:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Determinate:<\/strong> When you have specified a current value of the bar (e.g., 50%).<\/li>\n\n\n\n<li><strong>Indeterminate:<\/strong> When you <em>haven&#8217;t<\/em> specified a current value of the bar.<\/li>\n<\/ul>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"hljs-comment\">&lt;!-- Determinate --&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">progress<\/span> <span class=\"hljs-attr\">id<\/span>=<span class=\"hljs-string\">\"determinate\"<\/span>  <span class=\"hljs-attr\">value<\/span>=<span class=\"hljs-string\">\"75\"<\/span> <span class=\"hljs-attr\">min<\/span>=<span class=\"hljs-string\">\"0\"<\/span> <span class=\"hljs-attr\">max<\/span>=<span class=\"hljs-string\">\"100\"<\/span>&gt;<\/span> 50% <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">progress<\/span>&gt;<\/span>\n\n<span class=\"hljs-comment\">&lt;!-- Indeterminate --&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">progress<\/span> <span class=\"hljs-attr\">id<\/span>=<span class=\"hljs-string\">\"indeterminate\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">progress<\/span>&gt;<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>And here&#8217;s how they look (you can also assign <code>label<\/code> elements to them):<\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_GRWGQMG\" data-src=\"\/\/codepen.io\/anon\/embed\/GRWGQMG?height=450&amp;theme-id=1&amp;slug-hash=GRWGQMG&amp;default-tab=css,result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed GRWGQMG\" title=\"CodePen Embed GRWGQMG\" class=\"cp_embed_iframe lazyload\" style=\"width:100%;overflow:hidden\" src=\"data:image\/svg+xml;base64,PHN2ZyB3aWR0aD0iMSIgaGVpZ2h0PSIxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPg==\" data-load-mode=\"1\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<p>The indeterminate one could be handy in some situations &#8211; maybe when you&#8217;re not sure how long the user has to wait, or before a download actually starts.<\/p>\n\n\n<h2 class=\"wp-block-heading\" id=\"styling-the-html-progress-element\">Styling the HTML progress element<\/h2>\n\n\n<p>What&#8217;s tricky about <code>progress<\/code> is that each browser displays them differently by default &#8211; usually, but not always, based on the operating system and browser you&#8217;re using. And they are a bit fiddly to style.<\/p>\n\n\n\n<p>To customise them, you first you need to remove the browser&#8217;s default styles by setting <code>appearance<\/code> to <code>none<\/code>:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">progress<\/span> {\n    <span class=\"hljs-attribute\">-webkit-appearance<\/span>: none;\n    <span class=\"hljs-attribute\">-moz-appearance<\/span>: none;\n    <span class=\"hljs-attribute\">appearance<\/span>: none;\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Note that you can target determinate <code>progress<\/code> elements and indeterminate ones separately:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-comment\">\/* Styles determinate progress bars (with a value property) *\/<\/span>\n<span class=\"hljs-selector-tag\">progress<\/span><span class=\"hljs-selector-attr\">&#91;value]<\/span> {\n    <span class=\"hljs-attribute\">-webkit-appearance<\/span>: none;\n    <span class=\"hljs-attribute\">-moz-appearance<\/span>: none;\n    <span class=\"hljs-attribute\">appearance<\/span>: none;\n}\n\n<span class=\"hljs-comment\">\/* Styles indeterminate progress bars (without a value property) *\/<\/span>\n<span class=\"hljs-selector-tag\">progress<\/span><span class=\"hljs-selector-pseudo\">:not(<\/span><span class=\"hljs-selector-attr\">&#91;value]<\/span>) {\n    <span class=\"hljs-attribute\">-webkit-appearance<\/span>: none;\n    <span class=\"hljs-attribute\">-moz-appearance<\/span>: none;\n    <span class=\"hljs-attribute\">appearance<\/span>: none;\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Now to add your own styles! Again, it&#8217;s a little tricky. In Chrome and Safari, you use the <code>-webkit-progress-bar<\/code> pseudo class to style the container part of the bar, and <code>-webkit-progress-value<\/code> to style the actual bar that fills it up:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">progress<\/span><span class=\"hljs-selector-pseudo\">::-webkit-progress-bar<\/span> {\n    <span class=\"hljs-attribute\">background<\/span>: #;\n    <span class=\"hljs-attribute\">box-shadow<\/span>: <span class=\"hljs-number\">0<\/span> <span class=\"hljs-number\">2px<\/span> <span class=\"hljs-number\">3px<\/span>;\n    <span class=\"hljs-attribute\">border-radius<\/span>: <span class=\"hljs-number\">3px<\/span>;\n}\n\n<span class=\"hljs-selector-tag\">progress<\/span><span class=\"hljs-selector-pseudo\">::-webkit-progress-value<\/span> {\n    <span class=\"hljs-attribute\">background-color<\/span>: <span class=\"hljs-number\">#CC0000<\/span>;\n    <span class=\"hljs-attribute\">border-radius<\/span>: <span class=\"hljs-number\">3px<\/span>;\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>For Firefox, you style the container with the <code>progress<\/code> element itself, and the bar with <code>-moz-progress-value<\/code>, like this:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-8\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">progress<\/span> {\n  <span class=\"hljs-attribute\">background<\/span>: <span class=\"hljs-built_in\">linear-gradient<\/span>(#ccc <span class=\"hljs-number\">0%<\/span>, #eee <span class=\"hljs-number\">50%<\/span>, #eee <span class=\"hljs-number\">50%<\/span>, #ccc <span class=\"hljs-number\">100%<\/span>);\n  <span class=\"hljs-attribute\">border-radius<\/span>: <span class=\"hljs-number\">2px<\/span>;\n  <span class=\"hljs-attribute\">width<\/span>: <span class=\"hljs-number\">500px<\/span>;\n  <span class=\"hljs-attribute\">margin<\/span>: <span class=\"hljs-number\">0px<\/span> auto;\n  <span class=\"hljs-attribute\">max-width<\/span>: <span class=\"hljs-number\">500px<\/span>;\n  <span class=\"hljs-attribute\">height<\/span>: <span class=\"hljs-number\">30px<\/span>;\n}\n\n<span class=\"hljs-selector-tag\">progress<\/span><span class=\"hljs-selector-pseudo\">::-moz-progress-value<\/span> {\n  <span class=\"hljs-attribute\">background<\/span>: <span class=\"hljs-built_in\">linear-gradient<\/span>(#b98cf5 <span class=\"hljs-number\">0%<\/span>, #f0e6ff <span class=\"hljs-number\">50%<\/span>, #f0e6ff <span class=\"hljs-number\">50%<\/span>, #b98cf5 <span class=\"hljs-number\">100%<\/span>);\n    <span class=\"hljs-attribute\">border-radius<\/span>: <span class=\"hljs-number\">3px<\/span>;\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-8\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Like I said, a bit tricky! Both of those will give you this:<\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_WNpyMZW\" data-src=\"\/\/codepen.io\/anon\/embed\/WNpyMZW?height=450&amp;theme-id=1&amp;slug-hash=WNpyMZW&amp;default-tab=css,result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed WNpyMZW\" title=\"CodePen Embed WNpyMZW\" class=\"cp_embed_iframe lazyload\" style=\"width:100%;overflow:hidden\" src=\"data:image\/svg+xml;base64,PHN2ZyB3aWR0aD0iMSIgaGVpZ2h0PSIxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPg==\" data-load-mode=\"1\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<p>However, note that these pseudoclasses are non-standard &#8211; Mozilla <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/CSS\/::-webkit-progress-bar\" target=\"_blank\" rel=\"noopener nofollow\">doesn&#8217;t recommend<\/a> we use them. But if you&#8217;re worried about semantics and accessibility (as you should be!), there&#8217;s a compromise to consider&#8230;<\/p>\n\n\n<h2 class=\"wp-block-heading\" id=\"the-progressbar-role\">The progressbar role<\/h2>\n\n\n<p>There is an <a href=\"https:\/\/www.w3.org\/TR\/wai-aria-1.1\/#intro_ria_accessibility\" target=\"_blank\" rel=\"noopener nofollow\">aria<\/a> role you can use on progress bars &#8211; which is called, believe it or not, <code>progressbar<\/code>! This will help make the progress bar usable to people using screen readers and other assistive technologies.<\/p>\n\n\n\n<p>For this, you&#8217;d just use a normal <code>div<\/code>, but add the <code>progressbar<\/code> role to it. There are aria equivalents to the properties mentioned above:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>aria-valuenow<\/code> = <code>value<\/code><\/li>\n\n\n\n<li><code>aria-valuemin<\/code> = <code>min<\/code><\/li>\n\n\n\n<li><code>aria-valuemax<\/code> = <code>max<\/code><\/li>\n\n\n\n<li><code>aria-valuetext<\/code> is a new one, it&#8217;s basically an alt text<\/li>\n<\/ul>\n\n\n\n<p>These values don&#8217;t affect the actual appearance of the bar. So make sure that as you move the bar, you update <code>aria-valuenow<\/code> and <code>aria-valuetext<\/code> by equivalent amounts.<\/p>\n\n\n\n<p>Your html will look something like this:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-9\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">role<\/span>=<span class=\"hljs-string\">\"progressbar\"<\/span> <span class=\"hljs-attr\">aria-valuenow<\/span>=<span class=\"hljs-string\">\"20\"<\/span> <span class=\"hljs-attr\">aria-valuemin<\/span>=<span class=\"hljs-string\">\"0\"<\/span> <span class=\"hljs-attr\">aria-valuetext<\/span>=<span class=\"hljs-string\">\"Download progress: 20%\"<\/span> <span class=\"hljs-attr\">aria-valuemax<\/span>=<span class=\"hljs-string\">\"100\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-9\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n<h2 class=\"wp-block-heading\" id=\"adding-javascript-to-show-actual-progress\">Adding JavaScript to show actual progress<\/h2>\n\n\n<p>Finally, we just need to update our progress bar in-line with the actual progress of the operation.<\/p>\n\n\n\n<p>This is a lot simpler that it sounds &#8211; remember all we have to do is update the <code>width<\/code> property of our <code>progress__bar<\/code> element, which we can do easily in JavaScript:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-10\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">let<\/span> progressbar = <span class=\"hljs-built_in\">document<\/span>.getElementById(<span class=\"hljs-string\">\"download\"<\/span>);\n\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">updateProgressBar<\/span>(<span class=\"hljs-params\">value<\/span>) <\/span>{\n  <span class=\"hljs-keyword\">if<\/span> (value &gt;= <span class=\"hljs-number\">0<\/span> &amp;&amp; value &lt;= <span class=\"hljs-number\">100<\/span>) {\n    progressbar.style.width = value + <span class=\"hljs-string\">\"%\"<\/span>; \n  }\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-10\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>This function:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Accepts a value, which we&#8217;ve called <code>value<\/code>.<\/li>\n\n\n\n<li>Checks if <code>value<\/code> is between 0 and 100 (as we&#8217;re setting the <code>width<\/code> as a percentage, it needs to be in this range).<\/li>\n\n\n\n<li>If so, it goes to the element with the <code>id<\/code> of <code>download<\/code> (which we stored in the <code>progressbar<\/code> variable), and sets its width <code>value<\/code> (note: that you&#8217;ll also need to add the <code>download<\/code> ID to the <code>progress__bar<\/code> element, so that JS can select it).<\/li>\n<\/ul>\n\n\n\n<p>To make the progress bar move smoothly, you can give it a <a href=\"https:\/\/alvarotrigo.com\/blog\/css-transition-duration\/\">CSS <code>transition-duration<\/code> property<\/a>:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-11\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-class\">.progress<\/span> <span class=\"hljs-selector-class\">.progress__bar<\/span> {\n  <span class=\"hljs-attribute\">height<\/span>: <span class=\"hljs-number\">100%<\/span>;\n  <span class=\"hljs-attribute\">width<\/span>: <span class=\"hljs-number\">0<\/span>;\n  <span class=\"hljs-attribute\">border-radius<\/span>: <span class=\"hljs-number\">4px<\/span>;\n  <span class=\"hljs-attribute\">background<\/span>: <span class=\"hljs-built_in\">linear-gradient<\/span>(\n    to right, red, orange , yellow, green, cyan, blue, violet);\n  );\n  <span class=\"hljs-attribute\">transition<\/span>: <span class=\"hljs-number\">0.3s<\/span>;\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-11\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Now you&#8217;ve got the code that will move the progress bar, all you need to do is call <code>updateProgressBar<\/code> in your JavaScript, and pass it a new <code>value<\/code>.<\/p>\n\n\n\n<p>Exactly <em>how<\/em> to do this will of course depend on what exactly you&#8217;re measuring the progress of. For example, if it&#8217;s a file download, you could pass an updated <code>value<\/code> after each individual file has been downloaded &#8211; and the value you pass could be based on the size of the file.<\/p>\n\n\n\n<p>It&#8217;ll work like this:<\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_MWpXQOY\" data-src=\"\/\/codepen.io\/anon\/embed\/MWpXQOY?height=450&amp;theme-id=1&amp;slug-hash=MWpXQOY&amp;default-tab=css,result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed MWpXQOY\" title=\"CodePen Embed MWpXQOY\" class=\"cp_embed_iframe lazyload\" style=\"width:100%;overflow:hidden\" src=\"data:image\/svg+xml;base64,PHN2ZyB3aWR0aD0iMSIgaGVpZ2h0PSIxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPg==\" data-load-mode=\"1\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<p>If you type a value into the input box and click Submit, a function called <code>handleInput<\/code> function will be called. That function will take your input, and pass it to <code>updateProgressBar<\/code>. The bar will then move to the new point that you specified &#8211; and the time it takes to get there will be whatever value you set in the <code>transition<\/code> property of <code>progress__bar<\/code> &#8211; in this case, 0.3 seconds.<\/p>\n\n\n<h2 class=\"wp-block-heading\" id=\"progress-bars-that-don't-need-javascript\">Progress bars that don&#8217;t need JavaScript<\/h2>\n\n\n<p>Note that not all progress bars need to be dynamically updated in this way.<\/p>\n\n\n\n<p>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 &#8220;<em>Now checking 6 billion businesses to get you the best price!<\/em>&#8221; screens, and a little progress bar pops up with it?<\/p>\n\n\n\n<p>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.<\/p>\n\n\n\n<p>This is a little deceptive, but the argument is that if people <em>expect<\/em> 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 &#8211; maybe they&#8217;ll think the site just recommended the ones that give them the highest referral fee.<\/p>\n\n\n\n<p>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.<\/p>\n\n\n\n<p>There are also cases when you do need the progress bar to accurately represent progress, but where JavaScript isn&#8217;t needed &#8211; 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.<\/p>\n\n\n\n<p>In these cases, you can just put a static progress bar up on each separate page, with the <code>width<\/code> of the bar set to an appropriate amount based on the user&#8217;s progress so far.<\/p>\n\n\n<h2 class=\"wp-block-heading\" id=\"now-it's-your-turn!\">Now it&#8217;s your turn!<\/h2>\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" data-src=\"https:\/\/alvarotrigo.com\/blog\/assets\/imgs\/your-turn-to-shine-meme.jpeg\" alt=\"your turn to shine meme\" src=\"data:image\/svg+xml;base64,PHN2ZyB3aWR0aD0iMSIgaGVpZ2h0PSIxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPg==\" class=\"lazyload\" \/><\/figure>\n\n\n\n<p>Congratulations! You&#8217;ve just learned the fundamentals of how to create CSS progress bars for your site. The fact that you&#8217;re looking to set up a progress bar suggests that you&#8217;re working on a somewhat complex site at the moment. That&#8217;s cool!<\/p>\n\n\n\n<p>If you wanna take your site to the next level, take a peek at <a href=\"https:\/\/alvarotrigo.com\/fullPage\/\">fullPage.js<\/a>. You know those cool full-page sites? The ones where you scroll down, and get a nice animation, and then you&#8217;re taken to a whole new page? Well fullPage.js is how you can make them!<\/p>\n\n\n\n<p>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!<\/p>\n\n\n<h2 class=\"wp-block-heading\" id=\"related-articles\">Related articles<\/h2>\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/alvarotrigo.com\/blog\/progress-bar-css\/\">20+ Animated Progress Bars<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>How do you make really cool and functional progress bars for your site? Learn everything you need to know here!<\/p>\n","protected":false},"author":6,"featured_media":5157,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[106],"tags":[9,17,67],"class_list":["post-5158","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-css","tag-css","tag-web","tag-web-design"],"acf":[],"_links":{"self":[{"href":"https:\/\/alvarotrigo.com\/blog\/wp-json\/wp\/v2\/posts\/5158","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/alvarotrigo.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/alvarotrigo.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/alvarotrigo.com\/blog\/wp-json\/wp\/v2\/users\/6"}],"replies":[{"embeddable":true,"href":"https:\/\/alvarotrigo.com\/blog\/wp-json\/wp\/v2\/comments?post=5158"}],"version-history":[{"count":8,"href":"https:\/\/alvarotrigo.com\/blog\/wp-json\/wp\/v2\/posts\/5158\/revisions"}],"predecessor-version":[{"id":18888,"href":"https:\/\/alvarotrigo.com\/blog\/wp-json\/wp\/v2\/posts\/5158\/revisions\/18888"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/alvarotrigo.com\/blog\/wp-json\/wp\/v2\/media\/5157"}],"wp:attachment":[{"href":"https:\/\/alvarotrigo.com\/blog\/wp-json\/wp\/v2\/media?parent=5158"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/alvarotrigo.com\/blog\/wp-json\/wp\/v2\/categories?post=5158"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/alvarotrigo.com\/blog\/wp-json\/wp\/v2\/tags?post=5158"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}