I'm Dudley Storey, the author of Pro CSS3 Animation. This is my blog, where I talk about web design and development with HTML, CSS and SVG. To receive more information, including news, updates, and tips, you should follow me on Twitter or add me on Google+.

my books

Pro CSS3 Animation book coverPro CSS3 Animation, Apress, 2013

my other blogs

Massive Head CanonMassive Head Canon: Intelligent discussion of movies, books, games, and technology.

my projects

The New DefaultsThe New Defaults — A Sass color keyword system for designers.

CSSslidyCSSslidy — an auto-generated #RWD image slider. 3.8K of JS, no JQuery.

Using The HTML5 progress Element

Marking time, progress, and achievement on web pages with HTML5

Aimless browsing on the web is changing to a goal-directed activity: uploading a file, completing a form, working through a lesson plan, watching a video. In response, we need a more complex web UI: not just signifiers for visited links, but ways of measuring progress.

HTML5 addresses that need with the <progress> element. Like all additions to HTML5, the <progress> element comes with supplementary CSS and JavaScript, which I’ll also cover in this article.

progress vs. meter

<progress> is often confused with the <meter> element, so it’s important to distinguish the two:

  • <progress> is used to display development over time of a specific task. The upper bound or limit of the task may be known (“determinate”) – proceeding through a set series of steps in an online exercise, or playing a music track of known length – or unknown (“indeterminate”) such as uploading a file to a server. The maximum value of <progress> may not be known before the element is displayed. For example, the number of steps needed to complete a form may change depending on the user’s answers, while the percentage of a file upload may change due to bandwidth, server activity,and other factors.
  • The conditions for <meter> are different in that its minimum and maximum values must be known beforehand. It would be fair to characterize <meter> as displaying a value that might fluctuate wildly (a volume meter for a music track, for example) between known minimum and maximum values.

Another example: if you were running a fundraising drive on a website, <progress> could be used to show collective donations towards a goal, whereas <meter> might display the number of online visitors to the donations page at any moment, to the maximum capacity of the server.

Another distinction: <progress> can have a minimum value of 0. The minimum value of a <meter> may be any floating-point number, including negative numbers: think of a temperature gauge, for example.

Using The Progress Tag

The <progress> element itself is pretty simple. It’s a standard closed tag:

<progress></progress>

This creates an indeterminate element. Right now, since its value is unknown, <progress> will be animated in a “waiting” or “working” state in browsers that support it:

Adding a max value to the element sets an upper limit, but doesn’t change the animation:

<progress max="100"></progress>

Adding a value sets the current state of progress:

<progress value="10" max="100"></progress>

And changes the appearance of the element:

The progress bar is still animated in most browsers, although much more subtly; naturally, it can also be completely customized with CSS.

It should be noted that progress is inline (“phrasing”) content, and can be mixed in with other content:

<p> We’ve made it to 50% of our funding goal! <span>$0</span><progress value="5012" max="10000" id="funding"></progress><span>$10,000</span>

It might be somewhat surprising to lean that you can’t use <label> with <progress> in valid HTML5, even though we might consider <progress> to be a form element. The fact is that the user will never need to interact with <progress>: it is a “reporting” element, not something that the user would ever need to enter information into, and therefore does not need a <label>.

However, the <output> element can be used with <progress> to show its value. A simple example:

Progress towards our $10,000 pledge drive:
<progress min=0 max=10000 value=5012 id=funding onchange=pledgeUpdate></progress>
<output for=funding id=totalDonations>0</output>

A script to update the total pledge amount and convert the value into a dollar amount with commas:

<script>
var funding = document.getElementById('funding').value;
pledgeUpdate();
function numberWithCommas(x) {
 return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}
function pledgeUpdate() {
document.getElementById('totalDonations').value = "$"+numberWithCommas(funding);
}
</script>

The result:

Progress towards our $10,000 pledge drive: 0

Of course, it’s easy to set the value of the <progress> element by writing to it directly:

document.getElementById('funding').value = 555;

The appearance of the <progress> element is up to individual browser implementation, and leans heavily on the underlying operating system’s established UI theming; customizing the appearance of the progress bar to make it appear consistent is up to CSS.

Progressing In Style

The <progress> element appears at a default size on a web page, no matter what its maximum value. We can change this by setting an explicit width and height:

progress { width: 500px; height: 12px; }

Firefox and IE obey both declarations, but you’ll find that Webkit/Blink, while it follows the width, ignores responds oddly to height. To get the element to work predictably in Chrome, Safari and Opera we need to reset its appearance:

progress { -webkit-appearance: none; width: 500px; height: 12px; }

You’ll find that this removes all native OS styling on the element in Webkit: the progress bar turns green, and the background a dark grey. To get the equivalent in Firefox, we must take a similar approach:

progress { -webkit-appearance: none; -moz-appearance: none; width: 500px; height: 12px; }

However, the result differs: the progress bar will appear blue in Firefox, for example. To make things perfectly consistent, we have to dig a little deeper into the CSS:

progress {
-webkit-appearance: none; -moz-appearance: none; appearance: none;
width: 500px; height: 12px;
background-color: #888; border: none;
color: green;
}
progress::-webkit-progress-bar, progress::-moz-progress-bar, progress::progress-bar {
background-color: green;
}
progress::-moz-progress-bar {
background-color: green;
}

The result:

You’ll note some deliberate repetition in the CSS above, due to the fact that browsers exhibit eccentricities in HTML5 CSS pseudo-element selectors. For example, Firefox will completely ignore ::-moz-progress-bar if it is grouped with any other selector. For this reason, I strongly suggest you use the selector order shown above when attempting to customize HTML5 progress bars.

Going Vertical

Like range elements, progress elements may be oriented vertically: think of the “thermometers” displayed beside heritage buildings to track donations towards a restoration project. The most consistent way I know to achieve this is to use a CSS transform. Note that it is particularly important to use a box-sizing: border-box CSS reset in this case, otherwise Mozilla and Webkit will size and shape the <progress> element differently:

* { -moz-box-sizing: border-box; box-sizing: border-box; }
progress {
-webkit-appearance: none; -moz-appearance: none; appearance: none;
background: #fff;
width: 300px; height: 20px;
border-radius: 10px; border: 5px double #aaa;
display: block;
-webkit-transform-origin: center left;
-webkit-transform: rotate(-90deg) translateX(-100%);
transform-origin: center left;
transform: rotate(-90deg) translateX(-100%);
margin-left: 1%;
}

This could be customized further:

progress::-webkit-progress-bar { background: #fff; }
progress::-webkit-progress-value {
border-radius: 6px;
background: linear-gradient(90deg, #000,#f00);
}
progress::-ms-fill {
border-radius: 6px;
background: linear-gradient(90deg, #000,#f00);}
progress::-moz-progress-bar {
border-radius: 6px;
background: linear-gradient(90deg, #000,#f00);
}

The result can be seen below:

We gain several insights from this experiment:

  • Firefox uses background to style the background color of <progress> element; Webkit uses ::webkit-progress-bar, and IE uses ::-ms-fill
  • The active measuring portion of the progress bar is referred to as ::-moz-progress-bar in Firefox, and webkit-progress-value in Webkit. In IE10, the bar’s color can only be affected by the color property.

Conclusion

<progress> is a very useful element, although it lacks utility without a means to constantly update its value. I’ll look at that, and various uses of the <progress> element, in future articles.

Photograph by Ming Gullo, licensed under Creative Commons.

This site helps millions of visitors while remaining ad-free. For less than the price of a cup of coffee, you can help pay for bandwidth and server costs while encouraging further articles.