I’ve previously talked about how you can use simple HTML and CSS to create basic shapes and visual objects (such as buttons and tool tips) for the web.
I got into this stuff because I really wanted to create something called progress bars for my portfolio.
You can see it in this screenshot – click or touch to enlarge.

A progress bar is usually meant to signify accomplishment toward completing a certain task, such as downloading a file. It can be static, or animated to track real-time progress of an activity. You likely will have seen an animated progress bar while waiting to load up a game on the web, for example.
I wanted to have progress bars in my portfolio to signify the extent to which I personally contributed to a project.
Creating this in HTML and CSS is basically straightforward, though there are a few small tricks involved. Let’s start with the basics.
First, let me present the final product.
The foundation of the progress bar is a rectangle – a basic HTML div
element with a border.
<div class="meter"></div>
div.meter {
width: 250px;
height: 22px;
background-color: #fff;
border: 2px #333 solid;
}
Create the basic shape for the progress bar by rounding the ends. Set the radius of the corners to the height of the bar.
div.meter {
width: 250px;
height: 22px;
background-color: #fff;
border: 2px #333 solid;
border-radius: 22px;
}
Next, we’ll fill in the bar with color. This is done by nesting a span
element within the div
. (You could also nest another div
element within.) I added a small amount of padding in the div
element to create some distinction. Then, I calculated the border radius for the span
and added a green color.
<div class="meter">
<span class="width"></span>
</div>
div.meter {
width: 250px;
height: 22px;
background-color: #fff;
border: 2px #333 solid;
border-radius: 22px;
padding: 3px;
}
div.meter > span.width {
display: block;
height: 100%;
border-radius: 19px;
background-color: rgb(43, 194, 83);
}
So now, we have a progress bar at 100%.
Of course, I want to show the bar at less than 100%. I could just shorten the width of the inner element:
That is okay, but it’s probably better to flatten the leading edge somewhat. To do this, just shorten the radius for the corners of the leading edge.
div.meter > span.width {
display: block;
height: 100%;
border-top-right-radius: 8px;
border-bottom-right-radius: 8px;
border-top-left-radius: 19px;
border-bottom-left-radius: 19px;
background-color: rgb(43, 194, 83);
}
Finally, I wanted to specify the actual percentage amount that’s reflected in the width of the green bar:
Now, here comes a challenge. I want to be able to specify any percentage value in the HTML, and then the width of the inner bar rendered accordingly. The code would need to apply a flat leading edge at less 100%, and a rounded edge at 100%.
I had to do some research to come up with the answer. It’s getting deep in the weeds of CSS, but basically this involves setting a variable for the percentage value. This would prove useful later, as I would bring this variable into some JavaScript to load in content for my portfolio from JSON files.
Looking at the below, the variable is --meter-value
, set to a value of 75 (percent).
<div class="meter">
<span style="--meter-value: 75;" class="width"></span>
</div>
Additional CSS was necessary to automatically determine the shape of the leading edge for the progress bar.
/* Determine width of the progress bar indicator */
div.meter > span.width {
width: calc(var(--meter-value) * 1%);
}
/* Display the progress bar value (in %) */
div.meter > span.width::after {
counter-reset: meter var(--meter-value);
content: counter(meter) '%';
}
/* Make the leading edge rounded when 100% */
div.meter > span.width[style*="100"] {
border-top-right-radius: 19px;
border-bottom-right-radius: 19px;
}
Now before closing out this post, I wanted to mention that there actually is a native HTML progress
element, as well a similar meter
element that’s used to present a measurement.
HTML Progress Element
HTML Meter Element
CSS Progress Bar
However, there are two problems with using these elements. First, their appearance varies between the different browsers (Chrome, Safari, etc.). Second, it is very difficult to apply custom styling to them. Therefore, you’re far better off creating bespoke progress bars.
Here is the complete HTML and CSS used to create the progress bars in this post. You’ll see some additional CSS properties in there to tweak things out and make the styling work.
<div class="meter">
<span style="--meter-value: 75;" class="width"></span>
</div>
div.meter {
box-sizing: content-box;
width: 40%;
height: 22px;
line-height: 22px; /* Added here to enable vertical text align, and is equal to height value */
position: relative;
background: #fff;
border: 2px #333 solid;
border-radius: 22px;
padding: 3px;
text-align: center;
}
div.meter > span {
display: block;
height: 100%;
border-top-right-radius: 8px;
border-bottom-right-radius: 8px;
border-top-left-radius: 19px;
border-bottom-left-radius: 19px;
background-color: rgb(43, 194, 83);
position: relative;
overflow: hidden;
font-size: 90%;
}
div.meter > span.width {
width: calc(var(--meter-value) * 1%);
}
div.meter > span.width::after {
counter-reset: meter var(--meter-value);
content: counter(meter) '%';
}
div.meter > span.width[style*="100"] {
border-top-right-radius: 19px;
border-bottom-right-radius: 19px;
}