Grow your CSS skills. Land your dream job.

CSS3 Progress Bars

Published by Chris Coyier

I made some progress bars. They look like this:

View Demo   Download Files

They use no images, just CSS3 fancies. Like a good little designer always does, they fall back to totally acceptable experience. Here's what they look like in Opera 11 which supports some of the CSS3 used here but not all.

As you might imagine, in browsers that support no CSS3 at all will look similar to the above, only even more simplified.

HTML Base

The bar itself will be a <div> with a class of meter. Within that is a <span> which acts as the "filled" area of the progress bar. This is set with an inline style. It's the markup which will know how far to fill a progress bar, so this is a case where inline styles make perfect sense. The CSS alternative would be to create classes like "fill-10-percent", "fill-one-third" or stuff like that, which is heavier and less flexible.

The basic:

<div class="meter">
	<span style="width: 25%"></span>
</div>

Start of CSS

The div wrapper is the track of the progress bar. We won't set a width, so it will stretch as wide as it's parent as a block level element does. You could though. Height is also arbitrary. It's set at 20px here but could be anything. We'll round the corners in as many browsers as we can and set an inset shadow to give it a hair of depth.

.meter { 
	height: 20px;  /* Can be anything */
	position: relative;
	background: #555;
	-moz-border-radius: 25px;
	-webkit-border-radius: 25px;
	border-radius: 25px;
	padding: 10px;
	-webkit-box-shadow: inset 0 -1px 1px rgba(255,255,255,0.3);
	-moz-box-shadow   : inset 0 -1px 1px rgba(255,255,255,0.3);
	box-shadow        : inset 0 -1px 1px rgba(255,255,255,0.3);
}

Then span inside will be the fill in part of the progress bar. We'll make it display as a block with 100% height, so it stretches to fit whatever room it has. We'll then use a bunch of CSS3 to give it gradient look and round it's corners.

.meter > span {
	display: block;
	height: 100%;
	   -webkit-border-top-right-radius: 8px;
	-webkit-border-bottom-right-radius: 8px;
	       -moz-border-radius-topright: 8px;
	    -moz-border-radius-bottomright: 8px;
	           border-top-right-radius: 8px;
	        border-bottom-right-radius: 8px;
	    -webkit-border-top-left-radius: 20px;
	 -webkit-border-bottom-left-radius: 20px;
	        -moz-border-radius-topleft: 20px;
	     -moz-border-radius-bottomleft: 20px;
	            border-top-left-radius: 20px;
	         border-bottom-left-radius: 20px;
	background-color: rgb(43,194,83);
	background-image: -webkit-gradient(
	  linear,
	  left bottom,
	  left top,
	  color-stop(0, rgb(43,194,83)),
	  color-stop(1, rgb(84,240,84))
	 );
	background-image: -webkit-linear-gradient(
	  center bottom,
	  rgb(43,194,83) 37%,
	  rgb(84,240,84) 69%
	 );
	background-image: -moz-linear-gradient(
	  center bottom,
	  rgb(43,194,83) 37%,
	  rgb(84,240,84) 69%
	 );
	background-image: -ms-linear-gradient(
	  center bottom,
	  rgb(43,194,83) 37%,
	  rgb(84,240,84) 69%
	 );
	background-image: -o-linear-gradient(
	  center bottom,
	  rgb(43,194,83) 37%,
	  rgb(84,240,84) 69%
	 );
	-webkit-box-shadow: 
	  inset 0 2px 9px  rgba(255,255,255,0.3),
	  inset 0 -2px 6px rgba(0,0,0,0.4);
	-moz-box-shadow: 
	  inset 0 2px 9px  rgba(255,255,255,0.3),
	  inset 0 -2px 6px rgba(0,0,0,0.4);
	position: relative;
	overflow: hidden;
}

Thems the basics.

Other Colors

Let's make it as easy as possible to change the color. Just add a class name of "orange" or "red" to the div wrapper and the color will override.

.orange > span {
	background-color: #f1a165;
	background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0, #f1a165),color-stop(1, #f36d0a));
	background-image: -webkit-linear-gradient(top, #f1a165, #f36d0a); 
        background-image: -moz-linear-gradient(top, #f1a165, #f36d0a);
        background-image: -ms-linear-gradient(top, #f1a165, #f36d0a);
        background-image: -o-linear-gradient(top, #f1a165, #f36d0a);
}

.red > span {
	background-color: #f0a3a3;
	background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0, #f0a3a3),color-stop(1, #f42323));
	background-image: -webkit-linear-gradient(top, #f0a3a3, #f42323);
        background-image: -moz-linear-gradient(top, #f0a3a3, #f42323);
        background-image: -ms-linear-gradient(top, #f0a3a3, #f42323);
        background-image: -o-linear-gradient(top, #f0a3a3, #f42323);
}

Candystriping

We can get a cool striped effect by adding another element on top of that span and laying a repeated CSS3 gradient over it. Semantically this is best achieved with a pseudo element, so let's do it that way. We're going to absolutely position it over the exact area of the span (which already has relative positioning) and then round the corners the same so the stripes don't stick out weird.

.meter > span:after {
	content: "";
	position: absolute;
	top: 0; left: 0; bottom: 0; right: 0;
	background-image: 
	   -webkit-gradient(linear, 0 0, 100% 100%, 
	      color-stop(.25, rgba(255, 255, 255, .2)), 
	      color-stop(.25, transparent), color-stop(.5, transparent), 
	      color-stop(.5, rgba(255, 255, 255, .2)), 
	      color-stop(.75, rgba(255, 255, 255, .2)), 
	      color-stop(.75, transparent), to(transparent)
	   );
	background-image: 
		-webkit-linear-gradient(
		  -45deg, 
	      rgba(255, 255, 255, .2) 25%, 
	      transparent 25%, 
	      transparent 50%, 
	      rgba(255, 255, 255, .2) 50%, 
	      rgba(255, 255, 255, .2) 75%, 
	      transparent 75%, 
	      transparent
	   );
	background-image: 
		-moz-linear-gradient(
		  -45deg, 
	      rgba(255, 255, 255, .2) 25%, 
	      transparent 25%, 
	      transparent 50%, 
	      rgba(255, 255, 255, .2) 50%, 
	      rgba(255, 255, 255, .2) 75%, 
	      transparent 75%, 
	      transparent
	   );
	background-image: 
		-ms-linear-gradient(
		  -45deg, 
	      rgba(255, 255, 255, .2) 25%, 
	      transparent 25%, 
	      transparent 50%, 
	      rgba(255, 255, 255, .2) 50%, 
	      rgba(255, 255, 255, .2) 75%, 
	      transparent 75%, 
	      transparent
	   );
	background-image: 
		-o-linear-gradient(
		  -45deg, 
	      rgba(255, 255, 255, .2) 25%, 
	      transparent 25%, 
	      transparent 50%, 
	      rgba(255, 255, 255, .2) 50%, 
	      rgba(255, 255, 255, .2) 75%, 
	      transparent 75%, 
	      transparent
	   );
	z-index: 1;
	-webkit-background-size: 50px 50px;
	-moz-background-size:    50px 50px;
	background-size:         50px 50px;
	-webkit-animation: move 2s linear infinite;
	   -webkit-border-top-right-radius: 8px;
	-webkit-border-bottom-right-radius: 8px;
	       -moz-border-radius-topright: 8px;
	    -moz-border-radius-bottomright: 8px;
	           border-top-right-radius: 8px;
	        border-bottom-right-radius: 8px;
	    -webkit-border-top-left-radius: 20px;
	 -webkit-border-bottom-left-radius: 20px;
	        -moz-border-radius-topleft: 20px;
	     -moz-border-radius-bottomleft: 20px;
	            border-top-left-radius: 20px;
	         border-bottom-left-radius: 20px;
	overflow: hidden;
}

I first saw and snagged this idea from Lea Verou.

Animating Candystripes

Only Firefox 4 can animate pseudo elements, and only WebKit can do keyframe animations. So unfortunately we're between a rock and a hardplace in terms of animating those stripes. If we're set on it, let's add an additional span and then WebKit animate that.

<div class="meter animate">
	<span style="width: 50%"><span></span></span>
</div>

The span will be exactly the same as the pseudo element, so we'll just use the same values...

.meter > span:after, .animate > span > span {

... and avoid doubling up:

.animate > span:after {
	display: none;
}

We'll move the background position as far as the size of it:

@-webkit-keyframes move {
    0% {
       background-position: 0 0;
    }
    100% {
       background-position: 50px 50px;
    }
}

And call that animation:

.meter > span:after, .animate > span > span {
  -webkit-animation: move 2s linear infinite;
}

Might as well leave the animation tied to the pseudo element too, so as soon as WebKit starts supporting that, it will work.

Animating the Filled Width

Unfortunately you can't animate to an auto or natural width, which might let us animate from a forced zero to the inline style.

@-webkit-animation expandWidth {
   0% { width: 0; }
   100% { width: auto; }
}
Update 1/25/2012: Turns out you CAN animate to an inline style. Just omit the "to" or "100%" ending value in the @keyframe

I've submitted it to major browsers bug trackers just to push it a long a little, but for now, unsupported. Instead, let's do it with jQuery. Measure the original width, force it down to zero, then animate back up:

$(".meter > span").each(function() {
	$(this)
		.data("origWidth", $(this).width())
		.width(0)
		.animate({
			width: $(this).data("origWidth")
		}, 1200);
});

And done:

View Demo   Download Files

Others Takes

HEY?! What about HTML5?

Dude my dude. HTML5 has features specifically for this. <progress> and <meter>! Yep, it does, but here's the rub. These elements have very specific appearance already applied to them. By default, they look like progress bars used elsewhere on the platform you are on. Like this on Mac:

You can turn off that default styling like this:

progress {
   -webkit-appearance: none;
}

That'll allow you to remove the glossly thing going on with that default styling, but it's still pretty limited as to what you can do. You can change the progress bar inside like this:

progress::-webkit-progress-bar-value {
  -webkit-appearance: none;
  background: orangered;
}

...and that's fairly limited in what you can do with it afterward as well. To make things worse, things are very different across browsers, even between different WebKit browsers. Pseudo elements also work inconsistently. I hate to leave things hanging like this, but this is really a topic for another time. Suffice it to say, for these particular progress bars, the div/span thing is the ticket for now.

Comments

  1. Whoa, these are awesome!

    The rounded corners for the containers seem to be buggy in chrome/xp.

  2. Arturs
    Permalink to comment#

    Thank you, I will be useful, cool

  3. Just wanted to leave a comment to say thanks for the mention of my own attempts at creating progress bars.

    I found that jQuery is the only way (currently) to animate things in terms of progression, but CSS3 would really be a much nicer way to do it once browser support actually allows for it.

  4. Permalink to comment#

    hello i hava a website http://www.jkrt.org
    want to do friendlink with you website

  5. You featured my progress bars! How cool!

  6. Permalink to comment#

    Funny css3 tutorial,but I don’t know where can show it.

  7. Permalink to comment#

    Neat, neat, neat!! I don’t know much about this, so this is cool!! A well constructed process bar turns me on.

  8. very neat results

  9. aw aw aw…so smooth…good color

  10. Permalink to comment#

    Cool colors. Thanks for sharing.

  11. Hey Chris,

    Thanks for posting my implementation and the retweet the other night! I appreciate all your hard work, I’ve learned a bunch from you over the past couple years. Cheers!

  12. Permalink to comment#

    Very cool. Thanks!

  13. This is killer! Nice work Chris!

  14. Just wanted to say that just under the paragraph just after the “HTML Base” heading there should be a highlighted span which doesn’t appear (at least on Chrome, win XP).

  15. Permalink to comment#

    Those border-radius props can be condensed into shortform. Only Safari <5 and iOS <3 (prior to WebKit 532.5) have a problem with unique corner radii.

    So that can become:

    -moz-border-radius: 8px 20px 20px 8px;
    -webkit-border-radius: 8px 20px 20px 8px;
    border-radius: 8px 20px 20px 8px;

    and if you want iOS 3.1 and Saf4 support:

    -webkit-border-top-right-radius: 8px;
    -webkit-border-bottom-right-radius: 8px;
    -webkit-border-top-left-radius: 20px;
    -webkit-border-bottom-left-radius: 20px;

    You can add that top the top of it.

  16. Gordon
    Permalink to comment#

    I think it would be better when the progress bar reaches 100%, its right end can become round (just like left end) instead of a square with round corners.

  17. Permalink to comment#

    Wonderful!!

    Thanks!

  18. Wow chris sheer genius i really like it!

  19. Jasper
    Permalink to comment#

    Is there any way to slow down the animation?

    • Jasper
      Permalink to comment#

      nevermind! was a muppet and reduced the value in JS rather than increase it.

    • Lucifer
      Permalink to comment#

      hei bro how you did that? please let me know :) i cant do that :p

  20. Scott
    Permalink to comment#

    (Don’t seem to be able to reply to others on mobile site)

    @Paul, did the border radius spec change? It always used to be that multiple values created elliptical corners (though I personally never understood why they did it that way).

    Anyway, what is the need for different corner radii in this example? Shouldn’t all 4 corners be the same?

  21. Permalink to comment#

    awesome

    can the border-radius increases as the bar moving to the end?

  22. The HTML part of this source code isn’t W3C compliant (XHTML -HTML5). You can’t specify style attribute on element must be in the stylesheet.

    Nice use of CSS, in all case, the final result look awesome and fully skinnable.

    Nico

  23. Permalink to comment#

    I’ve been playing around with progress bars and came up with one funny thing to mark the progress bar that reached 100%.
    If you use percentage to specify the width


    .meter > span[style*="100"] { background: gray}

    or if you specify width with pixels


    .meter > span[style*="470"] { background: gray}

    of course this selector is vulnerable to numbers showing in other inline styling so use with caution.

  24. Permalink to comment#

    this is really cool! i always thought that loading images or sequences were some kind of programming. it was like a science i didn’t understand or something.

    it’s nice when it’s explained for the dummy! thanks

  25. Ali Bahrami
    Permalink to comment#

    OMG,
    That’s awesome , Chris.

    I thought I know a little about CSS but I guess I know nothing about it :P

    Wish you better jobs ^_^

  26. It is good news for my wordpress (style)

  27. While all of this stuff is super cool, they are not going to be used until CSS3 and HTML5 have become a standard. I wish we cool jump ahead a few years in terms of HTML standards….too many dang people are still running IE7/IE8. AGGGHH

  28. Andrew
    Permalink to comment#

    Very cool but you gotta have the animation running the other way man! That way the progress bar looks like it’s making more progress than it is! This way it looks like it’s slower.

  29. Excellent job done. Can easily be used

  30. Permalink to comment#

    I’ve adjusted the code somewhat. I’ve added a span to the markup that holds the percentage.


    <div class="meter-wrapper">
    <div class="meter"><span style="width:35%"></span></div>
    <span class="progress">35%</span>
    </div>

    The progress span gets hidden on pageload and is then faded in when the bar animation is finished.

    I also needed the progress bar to resize with the container when the browser gets resized. jQuery’s width() only returns px, so I needed a little workaround.


    jQuery('.meter-wrapper .progress').hide();
    jQuery(".meter > span").each(function() {
    var w = ( 100 * parseFloat(jQuery(this).css('width')) / parseFloat(jQuery(this).parent().css('width')) ) + '%';
    jQuery(this).width('0%').animate({width: w}, 1200, function() { jQuery('.meter-wrapper .progress').fadeIn(); } );
    });

  31. Permalink to comment#

    Really great job! And thanks for sharing…

  32. Some guy
    Permalink to comment#

    Looks bad in Firefox 3 however.

  33. I really like how this looks and works! Thank you.

  34. Just wanted to give a quick thanks – this is excellent working code and solved a big problem of mine simply! I’m so pleased and happy for the open-source information.

    jenn.e.

  35. Kasun Harshana
    Permalink to comment#

    Hi Chris…. I’m trying to use this bars on my admin panel cms. anyway i wanna hide it (display:none;) & wanna show it from jquery fadeIn. when i’m trying I noticed that Width of Bar’s doesn’t take exactly. please help me. here is my code.

    CSS =>

    .prev-email-campaign-report{
        display: hidden;
    }
    

    Jquery =>

    $('.view-prev-stastic').click(function(){
        $('.prev-email-campaign-report').fadeOut("slow", function(){
            $('.prev-email-campaign-report').fadeIn("slow");
        });
    });
    

    Html =>

    `<div class="prev-email-campaign-report">
        <div class="meter">
            <span class="bg-blue" style="width: 60%"></span>
        </div>
    </div>`
    
  36. Jesse
    Permalink to comment#

    I have a valid cross-browser solution to the meter element (would work for the progress as well).
    Screen-capture a 100% meter (or progress bar) in FireFox. Crop it in photoshop to create meter.png

    Then:

    $meterValue = 90;

    $meterOffset = $meterValue – 100;

    <meter style="border:solid 1px #000;display:inline-block;width:84px;height:17px;background:url("img/meter.png") no-repeat scroll transparent -'.$meterOffset.'px 0px;" title="liked '.$meterValue.'% by users" value="'.$meterValue.'" min="0" max="100">'.$meterValue.'%</meter>

    Tada!

    • Jesse
      Permalink to comment#

      Sorry…. it’s important to make your meter 100px so that it offsets correctly. Too bad you can’t use negative percents, otherwise it wouldn’t matter.

  37. Jesse
    Permalink to comment#

    eh, because the css is inline either omit the double-quotes around the background image or use single quotes

  38. Nicolas
    Permalink to comment#

    Hi, amazing trick ! Thanks to share. That’s nearly what I was looking for.

    I try do do two things more, but don’t know if it’s possible with this approach :

    1) I would like to put an image with transparency above the background. But with no result. Imagine a glass of beer empty in png format, registered with transparency from illustrator, so that the content of the glass is empty. I dream to fill the glass to 30% for example with the animated background. In this purpose, the image should be above and the background color behind, to fill the glass. Is it possible ?

    2) Is it possible, also, to make the backgound growing from bottom to top of the div, instead of left to right as it is in your example ?

    Many thanks (and sorry for my poor english language…) ;-)

  39. Cool colors. Thanks for sharing.

This comment thread is closed. If you have important information to share, you can always contact me.

*May or may not contain any actual "CSS" or "Tricks".