How to implement a basic, gracefully degrading progress bar using HTML and CSS.
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.
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:
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:
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:
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%; }
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; }
In response to Eddie’s comment, I’ve produced a modified jQuery progress bar which uses the markup and styling from this article.
Nice, i wonder if using "left" isn’t a bit confusing though, wouldn’t "remaining" be clearer…?
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?
@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.
@Amit: Yes; Firefox is my primary development browser, but this should work in all modern browsers and even — gasp — IE7.
I've tried it, and it works great! Thanks!
<a href="http://www.premierpixels.com">Las Vegas Web Design</a>
I've tried it, and it works great! Thanks!
<a href=" http://www.premierpixels.com"> Las Vegas Web Design</a>
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
great stuff. i’m just learning css so this tutorial is very useful for me. thanks a bunch!
komia
Wed 21 Oct 2009 04:54
TheWebTuts said:
Tutorial added to thewebtuts.com.