A percentage counts from 0 to 100 while a bar fills beside it on the same easing, then the label flips to Complete. The number is a registered integer printed through a CSS counter.
Also known as: percentage counter css, progress percentage animation, count to 100 percent.
<div class="txt-percentage-counter" aria-label="Upload complete, 100%"> <span class="txt-percentage-counter-label"></span> <span class="txt-percentage-counter-num"></span> <span class="txt-percentage-counter-bar"></span> </div>
@property --txt-percentage-counter-n {
syntax: "<integer>";
initial-value: 0;
inherits: false;
}
.txt-percentage-counter {
--c: var(--txt-color, #3b82f6);
font-size: var(--txt-size, 32px);
font-family: system-ui, sans-serif;
font-weight: 700;
line-height: 1.2;
display: grid;
grid-template-columns: 1fr auto;
align-items: baseline;
gap: 0.2em 1em;
width: 9em;
--ease: cubic-bezier(0.25, 1, 0.5, 1);}
.txt-percentage-counter-label::before {
content: "Uploading";
font-size: 0.6em;
font-weight: 600;
opacity: 0.7;
animation: txt-percentage-counter-done 2.2s forwards;
}
.txt-percentage-counter-num {
color: var(--c);
font-variant-numeric: tabular-nums;
counter-reset: txt-percentage-counter-v var(--txt-percentage-counter-n);
animation: txt-percentage-counter-count 2.2s var(--ease) forwards;
}
.txt-percentage-counter-num::after {
content: counter(txt-percentage-counter-v) "%";
}
.txt-percentage-counter-bar {
grid-column: 1 / -1;
height: 0.22em;
border-radius: 1em;
background: linear-gradient(var(--c), var(--c)) 0 0 / 100% 100% no-repeat, color-mix(in srgb, var(--c) 18%, transparent);
background-size: 0% 100%, auto;
animation: txt-percentage-counter-fill 2.2s var(--ease) forwards;
}
@keyframes txt-percentage-counter-count {
to { --txt-percentage-counter-n: 100; }
}
@keyframes txt-percentage-counter-fill {
to { background-size: 100% 100%, auto; }
}
@keyframes txt-percentage-counter-done {
0%, 99.9% { content: "Uploading"; }
100% { content: "Complete"; }
}
@media (prefers-reduced-motion: reduce) {
.txt-percentage-counter, .txt-percentage-counter *, .txt-percentage-counter::before, .txt-percentage-counter::after, .txt-percentage-counter *::before, .txt-percentage-counter *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}--txt-color on the element or a parent.
Currently previewing #3b82f6.--txt-size. Every other dimension is in
em, so the whole thing scales together. Currently previewing 40px.transition or animation
shorthand. Staggered delays are relative, so they keep their rhythm.