How about that for an over-the-top title? But it’s true, that’s what we’re going to be doing. It’s been a while since my last post, unfortunately, so I thought I’d make up for it with this sizeable offering in which we will learn a lot of really great techniques in order to make something as simple as a progress bar. By which I mean a breadcrumb-esque meter of steps, such as you might find on a checkout process; we are making this:
And in doing so we will cover:
- Design and build a semantic, accessible and sensible progress bar.
- Utilise the much underused method of styling page-specific elements based on their IDs.
- Style the numbers in an ordered list!
- Progressively enhance it with some CSS3.
- Optimise it for mobile.
N.B. This article isn’t so much about a progress bar, but more an illustration that best practices and more advanced techniques can be applied to even the most insignificant aspects of a build to create something awesome!
Design and build
Let us assume the brief is this:
We require a numbered progress bar to indicate user location (past, current and future) during a checkout path on the OurService™ website. It must:
- Be fully accessible.
- Provide a section title with supporting information.
- Highlight the user’s current location in the process.
- Be navigable.
- Work on mobile devices.
The design, let’s assume, is predefined. It looks as above, purely because it has to. The design is not the major focus of this article, the code and techniques are.
“Code what you consume, not what you see.”
Design
The progress bar shall be a linear, left to right series of linked labels. Current location shall be indicated by a change in colour, progression onto the next step shall be indicated by an arrow.
Build
For the purposes of this tutorial we shall assume the current page is the payment page.
One school of though I find invaluable when it comes to sensible and semantic builds is code what you consume, not what you see
. This is a very broad generalisation but works for the most part. Code content independently of (and before you consider) coding any styles. Web development basics, but fundamental to web standards and progressive enhancement.
So, what are we consuming? It’s an ordered list of steps which indicate location in a process.
Okay so first off we know we need an <ol> as this list has fixed and definite order. We also require titles and supporting copy for each item. As the titles and supporting copy require separation from one another we are going to wrap the titles in a <span>; a generic inline container. This leaves us with:
<body id="payment-page">
<ol id="progress">
<li class="details-step">
<a href="#">
<span>Your details</span>
Name, email, address.
</a>
</li>
<li class="account-step">
<a href="#">
<span>Create account</span>
Username and custom URL.
</a>
</li>
<li class="products-step">
<a href="#">
<span>Product options</span>
Choose your package.
</a>
</li>
<li class="payment-step">
<a href="#">
<strong>
<span>Payment</span>
PayPal, or credit card.
</strong>
</a>
</li>
<li class="go-step">
<a href="#">
<span>Go!</span>
Start using OurService™
</a>
</li>
</ol>
</body>
There are a few things in this code which I’ve not yet mentioned, one is the ID on the <body>, another being the class on each <li> and the last being the <strong> wrapped around the payment page’s text. I shall explain these next.
<body> ID and list item classes
A combination of an ID on the <body> and a class on a list item can allow you to know what the current page is. The CSS #payment-page .payment-step{} will target the payment section of the progress bar when it is on the payment page in the process. Similarly, #go-page .go-step{} will target the go item on the go page. I wrote a much more in-depth article on this over at Venturelab.
The <strong> around the payment step’s text
If you’re determining current page programatically, one could argue inserting a class="current" on the relevant item. This is doable, but avoiding such a class name is far nicer.
As stated earlier we are assuming the current page to be the payment page. Now, we can style the current step on any page using CSS, as outlined above, however how would a user with styles disabled be able to tell what the current page is? How can we highlight this for those users?
Well the solution would be to programatically wrap a <strong> around the text on that page, undo the bolding effects with CSS for browsers with styles enabled, and allow people viewing unstyled content to see that the bolded item is the current page. This gives us:

As you can see, users with styles disabled can clearly see the current link is the bolded one, this makes the progress bar that little bit more accessible to those who might need it.
So there we have it, the markup that powers the whole thing.
Styling
Now to style this thing up. First off we’ll look at the very basic CSS, and that alone:
/*------------------------------------*\
MAIN
\*------------------------------------*/
html{
height:101%;
}
body{
font-family:Calibri, Arial, Verdana, sans-serif;
background:#fff;
color:#88979e;
width:940px;
padding:10px;
margin:0 auto;
}
/*------------------------------------*\
PROGRESS
\*------------------------------------*/
#progress{
list-style:none; /* Remove the bullets */
float:left; /* Make its width equal to the combined width of the items inside it */
margin-bottom:20px;
}
#progress li{
float:left; /* Stack them all up left */
font-size:0.75em; /* Make the entire item smaller */
font-style:italic; /* Make the entire item italic */
}
#progress a{
display:block;
text-decoration:none;
padding:10px 25px 10px 10px; /* Padding to accomodate background image */
background:#7b8d77;
color:#fff;
}
#progress span{
font-size:1.333em; /* Bring the size of the title only back up */
font-weight:bold;
display:block;
font-style:normal; /* Undo the italics */
}
#progress strong{
font-weight:normal; /* Remove the bolding for CSS enabled browsers */
}
#progress a:hover{
text-decoration:none;
}
#progress a:hover span{
text-decoration:underline; /* Underline the title on hover */
}
#details-page .details-step a,
#account-page .account-step a,
#products-page .products-step a,
#payment-page .payment-step a{
background:url(../img/css/splitter.gif) right center no-repeat #a49d4d; /* Arrow image on the current step */
}
#go-page .go-step a{
background:#a49d4d; /* Arrow image not needed on final step, colour only */
}
All of the above is very obvious, it is essentially just like creating a normal navigational menu, and gives us this:

Styling numbers in ordered lists
Next up we style the numbers in the ordered list by using the very very useful and much unknown CSS counter module. Because you have such limited control over the appearance of your bullets in (ordered) lists they can be a pain to style. This pain is alleviated somewhat when using an unordered list as you can simply use a background image. It is an altogether different story when you’re using an ordered list as the bullet needs to change with each list item.
What we do here is use CSS to do a very prog-like job; we get it to loop through each item in a parent container and then increment a user-defined value each time it encounters a specified child. Sounds Greek? Read this.
Once we have this number available to us we use the CSS :before pseudo-element and the content:; property to insert the number before each item. How cool is that?!
/*------------------------------------*\
PROGRESS
\*------------------------------------*/
#progress{
list-style:none;
float:left;
margin-bottom:20px;
counter-reset:step; /* Set up name of increment on parent */
}
#progress li{
float:left;
font-size:0.75em;
font-style:italic;
}
#progress a{
display:block;
text-decoration:none;
padding:10px 25px 10px 30px; /* Padding changed to 30px to accomodate number */
background:#7b8d77;
color:#fff;
position:relative; /* Relative position to allow absolute positioning later on */
}
#progress span{
font-size:1.333em;
font-weight:bold;
display:block;
font-style:normal;
}
#progress strong{
font-weight:normal
}
#progress a:hover{
text-decoration:none;
}
#progress a:hover span{
text-decoration:underline;
}
#progress li a:before{
counter-increment:step; /* Increment the step on each occurance of this (pesudo) element */
content:counter(step); /* Write out value of the increment */
text-align:center;
font-weight:bold;
position:absolute; /* Position number */
top:50%;
left:5px;
margin-top:-8px;
}
#details-page .details-step a,
#account-page .account-step a,
#products-page .products-step a,
#payment-page .payment-step a{
background:url(../img/css/splitter.gif) right center no-repeat #a49d4d;
}
#go-page .go-step a{
background:#a49d4d;
}
This then gives us this:

Progressively enhancing
Now for the CSS3 progressive bits:
/*------------------------------------*\
PROGRESS
\*------------------------------------*/
#progress{
background:#7b8d77; /* Give the ol a background to prevent white showing through behind the items' round corners (change value to #f00 to see what I mean) */
}
#progress{
-moz-border-radius:5px;/* Round all corners of the progress bar */
-webkit-border-radius:5px;
border-radius:5px;
}
#progress a{
text-shadow:1px 1px 1px rgba(0,0,0,0.25); /* A small text-shadow */
-moz-border-radius:5px 0 0 5px; /* Round the top- and bottom-left corners */
-webkit-border-radius:5px 0 0 5px;
border-radius:5px 0 0 5px;
}
#details-page .details-step a,
#account-page .account-step a,
#products-page .products-step a,
#payment-page .payment-step a{
background:url(../img/css/splitter.gif) right center no-repeat #a49d4d;
}
#progress .go-step a{
-moz-border-radius:5px; /* Round all corners of the final step */
-webkit-border-radius:5px;
border-radius:5px;
}
The full, combined CSS for the progress bar so far is:
/*------------------------------------*\
MAIN
\*------------------------------------*/
html{
height:101%;
}
body{
font-family:Calibri, Arial, Verdana, sans-serif;
background:#fff;
color:#88979e;
width:940px;
padding:10px;
margin:0 auto;
}
/*------------------------------------*\
PROGRESS
\*------------------------------------*/
#progress{
list-style:none;
background:#7b8d77;
float:left;
margin-bottom:20px;
counter-reset:step;
-moz-border-radius:5px;
-webkit-border-radius:5px;
border-radius:5px;
}
#progress li{
float:left;
font-size:0.75em;
font-style:italic;
}
#progress a{
display:block;
text-decoration:none;
padding:10px 25px 10px 30px;
background:#7b8d77;
color:#fff;
text-shadow:1px 1px 1px rgba(0,0,0,0.25);
position:relative;
-moz-border-radius:5px 0 0 5px;
-webkit-border-radius:5px 0 0 5px;
border-radius:5px 0 0 5px;
}
#progress span{
font-size:1.333em;
font-weight:bold;
display:block;
font-style:normal;
}
#progress strong{
font-weight:normal
}
#progress a:hover{
text-decoration:none;
}
#progress a:hover span{
text-decoration:underline;
}
#progress li a:before{
counter-increment:step;
content:counter(step);
text-align:center;
font-weight:bold;
position:absolute;
top:50%;
left:5px;
margin-top:-8px;
padding:2px 6px;
background:rgba(0,0,0,0.25);
-moz-border-radius:20px;
-webkit-border-radius:20px;
border-radius:20px;
}
#details-page .details-step a,
#account-page .account-step a,
#products-page .products-step a,
#payment-page .payment-step a{
background:url(../img/css/splitter.gif) right center no-repeat #a49d4d;
}
#go-page .go-step a{
background:#a49d4d;
}
#progress .go-step a{
-moz-border-radius:5px;
-webkit-border-radius:5px;
border-radius:5px;
}
Which, when coupled with the markup, gives this:
Recap
So, let’s cover what we’ve done so far. We’ve:
- Coded up a semantic progress bar (using an ordered list and correct generic elements).
- Made it accessible (addition of the strong around the content for non-CSS browsers).
- Styled it all up.
- Made use of the body ID trick to mark the current page.
- Used CSS counters to style the numbers of an ordered list
- Progressively enhanced it all to make it a little easier on the eyes.
Mobile optimisation
For more information on mobile/iPhone optimised sites please see my associated article.
Next up we need to optimise this thing for mobile. This couldn’t be simpler. The key to optimising sites for mobile is linearise. Linearise everything.
In your markup, add this line to the <head> section, thus:
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Progress</title>
<meta name="viewport" content="width=device-width; initial-scale=1.0; maximum-scale=1.0; user-scalable=0;" />
<link rel="stylesheet" type="text/css" href="css/new-style.css" />
</head>
This tells the user agent that the viewport should be the same as the device’s own screen-size, that it should be initially set to a scale of 1 (i.e. no scale), its maximum scale is set to 1, and that users can’t scale themselves.
Now, add the following to the very end of your CSS file:
/*------------------------------------*\
MOBILE
\*------------------------------------*/
@media (max-width: 480px){ /* In any browser narrower that 480px... */
body{
width:auto; /* Give the body a fluid width... */
padding:5px; /* And a slight padding */
}
#progress{
width:auto; /* Give the list a fluid width */
background:none; /* Remove the list's background... */
float:none; /* ...and float */
}
#progress li{
float:none; /* Remove list item float, causing them to stack */
margin-bottom:1px; /* Space them slightly */
}
#progress a{
margin:0 10px; /* Indent the left and right of each item by 10px */
-moz-border-radius:5px; /* Round all corners */
-webkit-border-radius:5px;
border-radius:5px;
}
#details-page .details-step a,
#account-page .account-step a,
#products-page .products-step a,
#payment-page .payment-step a,
#go-page .go-step a{
background:#a49d4d; /* Set the background of the current item */
margin:0 auto; /* Remove the 10px indent to show that the step is current */
}
}
Now, if you want to test this and don’t have a smartphone, or haven’t got this hosted in a live environment, simply resize your browser window right down until you see the change. I tend to use the Firefox Web Developer Toolbar addon to resize the window to 480×800px.
On the iPhone this now looks like:

Demo
For the full working demo head to http://csswizardry.com/demos/progress-bar/. For the complete CSS (with reset) please see http://csswizardry.com/demos/progress-bar/css/style.css. Also, try using Firebug to change the <body>’s ID to go-page.
Final words
As I stated previously, this article isn’t so much about the progress bar itself. What I hope this article has shown is how something as small and trivial as a progress bar has a wealth of little nooks and crannies in which to immerse yourself. Semantics, accessibility, using <body> IDs to style current states without a class="current", how to use CSS counters to style the numbers in an ordered list, how to progressively enhance lean markup, and how to optimise things for mobile in a flash.
All of the above skills are easily and quickly transferable. It might be a progress bar today, but what could it be tomorrow? Skills like the ones covered here give you the potential to make something great, out of something very very simple.
By Harry Roberts on Wednesday, November 17th, 2010 in Web Development. Tags: Accessibility, CSS, CSS3, HTML, Markup, Mobile, Progressive Enhancement, Semantics | 11 Comments »
+

James said on 18 November, 2010 at 9:48 am
Great write up Harry, the counter-reset propery is a new one on me, it appears I’ve got more reading to do.
J.
Hen Asraf said on 18 November, 2010 at 10:17 am
Wow, good article. I have to dig up on CSS counters soon, looks relally useful. Great job!
~ Hen
Chris Marsh said on 18 November, 2010 at 11:34 am
Great article Harry.
Would an alternative to programmatically adding
<strong>tags or using body id attributes be to use the :target pseudo class?If you use sensible link hrefs you can style a:target as the class=”current” equivalent. I know it’s not supported by every browser, but using a library like http://selectivizr.com/ (which is excellent) allows you to use advanced CSS selectors and have them work cross-browser.
Chris
Harry Roberts said on 18 November, 2010 at 12:46 pm
Chris,
That probably wouldn’t work, as for the
:targetpseudo-class to work you’d need to have a fragment identifier on each page’s URL (e.g. ourservice.com/signup/details#step-01, ourservice.com/signup/account#step-02 etc.).This means that as soon as the fragment identifier is lost from the URL the progress bar is rendered completely inactive, and furthermore each page load would then jump awkwardly down to the progress bar because of the appearance of the fragment-identifier.
The
<body>IDs method is far more fail-safe for such an application. The:targetmethod is very very progressive, and the need to mark current states on the progress bar is pretty important.Also, the idea of adding a
<strong>is so that browsers without CSS get the benefits, so it is important that this is done with anything but CSS.Cheers for the comment!
H
Bruno said on 19 November, 2010 at 5:29 pm
Very nice post!
A quick question: I would have expected you to use the “:first/last-of-type” pseudo-classes in order to round the left/right corners of the first/last step. I think this would have avoided the need for the ol’s background.
I must admit I didn’t test, though. Any comment?
Thanks, B.
Harry Roberts said on 22 November, 2010 at 9:13 am
Bruno,
That would be an option, however all the
<li>s have rounded left corners which means that without a background on the<ol>itself, small sections of white would show through (try changing the<ol>tobackground:none;using Firebug in the demo).Harry
kangax said on 23 November, 2010 at 10:50 pm
Thinking to use this example for a checkout navigation for our site, but noticed that splitter is represented as an image. Would be nice to avoid that, and instead use CSS to create an arrow — faster page load and much easier maintenance. It would be a pain to change splitter every time colors/size of a navigation bar change.
What do you think?
Other than that, great attention to semantic, accessible markup.
Harry Roberts said on 24 November, 2010 at 9:28 am
kangax,
If by that you mean something like this then I’d strongly suggest no.
That method doesn’t work in IE at all, offering no clue as to sections on a very important piece of the UI.
Furthermore the CSS is very verbose and uses a lot of very long-winded and tricky selectors.
The advantages gained in not using an image are far smaller than the disadvantages of doing so.
kangax said on 27 November, 2010 at 8:23 am
I used :after to create a triangle after “selected” element. It resulted in just about 12 more lines of code [1]. I don’t think this is verbose or long-winded (considering that your example already uses :before). It degrades similar to how border-radius degrades in IE (and other non-supporting clients) — it simply doesn’t appear. Selected state is still denoted by background color of a tab; it’s just that there’s no arrow pointing to the right (which does result in loss of clarity, but I think 1, 2, 3… certainly helps there).
Are you sure the advantage of using images worth it?
[1]
a:after
content: “>”
color: transparent
font-size: 0
position: absolute
background: $bg_color
top: 0
right: 0
bottom: 0
border: 30px solid #888
border-left-color: #ef9000
margin-right: -20px
heavyG said on 31 May, 2011 at 4:28 pm
If one of the focus of this article is accessibility, CSS Counters should not be used at all.
CSS Counters are not good for accessibility – Generated content (via the content property) is not accessible by current screen readers.
Harry Roberts said on 31 May, 2011 at 6:41 pm
@heavyG: But as it’s marked up as an
<ol>a screenreader will still know that numbers are apparent; we’re just replacing the appearance of those numbers for non-screenreader users…