Implementing a progress bar

How to implement a basic, gracefully degrading progress bar using HTML and CSS.

Introduction

A recent question to the css-d mailing list raised the issue of progress bars and how they should be marked-up and styled. Specifically, the question dealt with how the progress bar could be linked whilst remaining valid.

The core issue is one that persistently arises — how to link block level elements — but the general question raised several issues, and led me to wonder how ‘best’ a progress bar can be marked-up and styled. Here, I present my favoured approach.

The method

I’ve chosen to represent the progress as a definition list, with definition terms (dt) for the ‘completed’ part (.done) and the part that ‘remains’ (.left):

<dl class="progress">
    <dt>Completed:</dt>
    <dd class="done"><a href="/">23%</a></dd>

    <dt>Left:</dt>
    <dd class="left"><a href="/">77%</a></dd>
</dl>

Each progress value is then the definition (dd) of that term. Notice that these values are each linked individually, although these links would likely lead to the same location. Ideally, the markup would be:

<a href="/">
    <dl class="progress">
        <dt>Completed:</dt>
        <dd class="done">23%</dd>

        <dt>Left:</dt>
        <dd class="left">77%</dd>
    </dl>
</a>

but valid HTML prevents us from doing this. Eric Meyer’s written about this shortcoming; his suggestion is to allow any element to support the href attribute. This makes an awful lot of sense, would allow us to simply link the entire list in this example and, hopefully, might be adopted in HTML 5. My one concern, demonstrated well in this case, is that, without any styling, this would normally look pretty ugly; all text would be blue, underlined, and — probably — become a bit of a distraction.

This structure is, I believe, about as accurate a portrayal of the data as is possible using HTML. It also ensures a reasonable display if CSS is unavailable:

Completed:
23%
Left:
77%

All of the relevant information is present, allowing screenreaders and other non-visual agents to read and digest it. Now, onto the styling.

First off, let’s size each individual progress bar and colour their backgrounds.

.progress dl,
.progress dl *
	{ margin: 0; padding: 0; }

.progress .done { width: 23%; background-color: #9DB029; }
.progress .left { width: 77%; background-color: #eee; }

Note that I’m also resetting all margins/padding within this entire element. This gives us the following presentation:

Completed:
23%
Left:
77%

To ensure the links fill each bar, make them block elements with 100% width and height:

.progress a { display: block; width: 100%; height: 100%; }

The progress values are hidden using the ‘text-indent trick’. We’ll also hide the definition terms ("completed", "left"):

.progress a { text-indent: -9000px; }
.progress dt {display: none; }

And our progress bar now looks as follows:

Completed:
23%
Left:
77%

The only big step that remains is to float each bar to the left, aligning them horizontally. Whilst we’re at it, we’ll also set the overall list’s height and width, and remove the ‘left’ background colour which was purely for the purposes of this demonstration. An overall border on the list will be required to enclose our entire progress bar, and a pixel of padding inside will make this nice and pretty:

.progress dl { height: 20px; width: 300px; }
.progress .done, .progress .left { height: 100%; }
Completed:
23%
Left:
77%

Summary

dl.progress,
dl.progress *
    { margin: 0; padding: 0; }

dl.progress {
    padding: 1px; border: 1px solid #ddd;
    height: 20px; width: 300px; }

.progress dt
    { width: 0; height: 0; overflow: hidden; }

.progress .done,
.progress .left
    { height: 100%; float: left; }

.progress .done
    { background-color: #9DB029; }

.progress .done
    { width: 23%; }
.progress .left
    { width: 77%; }

.progress a {
    display: block; width: 100%;
    height: 100%; text-indent: -9000px; }

Update

In response to Eddie’s comment, I’ve produced a modified jQuery progress bar which uses the markup and styling from this article.


Tweet

Comments

Wed 21 Oct 2009 04:54

TheWebTuts

TheWebTuts said:

Tutorial added to thewebtuts.com.

Wed 16 Dec 2009 02:34

Aaron Bassett

Aaron Bassett said:

Would you not be better using the meter tag?

http://dev.w3.org/html5/markup/meter.html#meter

Wed 16 Dec 2009 02:44

Jon D

Jon D said:

Nice, i wonder if using "left" isn’t a bit confusing though, wouldn’t "remaining" be clearer…?

Wed 16 Dec 2009 02:49

Gerben van Dijk

Gerben van Dijk said:

would have to agree with aaron here — use the html5 meter tag :)

Wed 16 Dec 2009 03:14

Eddie van Dorland

Eddie van Dorland said:

Looks good and works great, just a bit concerned about the fact it doesn’t move due to no variables?!? 23% & 77%?!?

How do I get it to move?

Wed 16 Dec 2009 03:48

Five Minute Argument

Five Minute Argument said:

@Aaron, Gerben: You’re right: this is strictly an HTML4 solution. There’s actually a progress element which is, of course, the most suitable. However, in both those cases, the values are stored as attributes, and I’m unaware of a current solution to derive the width from those, other than by using javascript. With only a single element to style, I’m not sure this approach will fly, although there’s some potential for a follow-up post on this topic — thanks for your feedback.

@Jon: Probably only because of the profusion of non-semantic class names out there, but I guess you’re right :-)

@Eddie: This is just for the static styling of a progress bar at a given state. It would be fairly simple to update the values using javascript, and the progress bar from the jquery-ui library would be ideal for this; its default styling, however, doesn’t degrade gracefully at all, so a combination of the two would be perfect if you’re looking for something that needs to update dynamically.

Wed 16 Dec 2009 08:30

Future Webs

Future Webs said:

Thanks for the post, Looks good.

Wed 16 Dec 2009 11:50

Amit

Amit said:

Nice post..thanks for sharing..Will this work in Firefox?

Wed 16 Dec 2009 16:10

Five Minute Argument

Five Minute Argument said:

@Amit: Yes; Firefox is my primary development browser, but this should work in all modern browsers and even — gasp — IE7.

Mon 21 Dec 2009 09:54

Hazır Emlak Sitesi Paketi

Hazır Emlak Sitesi Paketi said:

good share! thank you for this page!

Tue 5 Jan 2010 11:32

Web Design Cambridgeshire

Web Design Cambridgeshire said:

Thanks for this — good ideas.

Sun 8 Aug 2010 20:21

Las Vegas Web Design

Las Vegas Web Design said:

I've tried it, and it works great! Thanks!

<a href="http://www.premierpixels.com">Las Vegas Web Design</a>

Sun 8 Aug 2010 20:29

Las Vegas Web Design

Las Vegas Web Design said:

I've tried it, and it works great! Thanks!

<a href=" http://www.premierpixels.com"> Las Vegas Web Design</a>

Sun 12 Sep 2010 09:19

best interest rates on savings

best interest rates on savings said:

I got it to work with HTML but not with CSS

Tue 5 Oct 2010 08:08

hotels in Killarney

hotels in Killarney said:

Same problem here, i cannot get it to work with CSS. I will be back in my hotel room later today and I will try it again

Sat 5 Mar 2011 19:17

golf swing speed

golf swing speed said:

well i have tried it and no problem at all.

http://www.golf-swing-speed.com

Sat 7 May 2011 00:10

said:

Thu 23 Jun 2011 06:50

chw 12

chw 12 said:

great stuff. i’m just learning css so this tutorial is very useful for me. thanks a bunch!

komia

<href="http://www.squidoo.com/cuisinart-chw-12">chw-12</a>

Wed 28 Dec 2011 03:52

SEO tips

SEO tips said:

Great article! Thanks for sharing with us!

Fri 9 Mar 2012 00:28

Vasan

Vasan said:

Great article. i have tried it, it is working but how to align this one as center?

Leave a comment