Material design progress bar in pure CSS
I stumbled upon an interesting HTML5 feature (or bug) that makes it possible to create a material design indeterminate progress bar in pure CSS. When a div
uses flex
display, the :before
pseudo-element is painted over it, and support margins. This means that the :before
pseudoelement can look as if it’s moving on top of the element, without any JavaScript or adding additional DOM elements. Here it is in action:
I’ve tested this in the current versions of Chrome, Firefox and (OSX and mobile) Safari, and it seems to work well. According to Can I Use, CSS animations work in all the major browsers now apart from Opera Mini, and Flexbox display is now generally supported, so this trick should be OK for general use as long as you don’t need support for old browsers. MS browsers might need some CSS prefixes on the classes for this to work, but IE is not on my critical path so I’ve not tested that.
How it works
Inspect the source, and you’ll find only a normal div
with a single class associated with it.
<div class="progress-line"></div>
The CSS is essentially using the main div
background colour for the neutral background of the progress bar, and the :before
pseudoelement background colour
for the active moving part. By animating the margin-left
and margin-right
of the :before
pseudoelement, the active background races across the neutral one.
.progress-line, .progress-line:before {
height: 3px;
width: 100%;
margin: 0;
}
.progress-line {
background-color: #b3d4fc;
display: -webkit-flex;
display: flex;
}
.progress-line:before {
background-color: #3f51b5;
content: '';
-webkit-animation: running-progress 2s cubic-bezier(0.4, 0, 0.2, 1) infinite;
animation: running-progress 2s cubic-bezier(0.4, 0, 0.2, 1) infinite;
}
@-webkit-keyframes running-progress {
0% { margin-left: 0px; margin-right: 100%; }
50% { margin-left: 25%; margin-right: 0%; }
100% { margin-left: 100%; margin-right: 0; }
}
@keyframes running-progress {
0% { margin-left: 0px; margin-right: 100%; }
50% { margin-left: 25%; margin-right: 0%; }
100% { margin-left: 100%; margin-right: 0; }
}