How to Simulate a Realistic Progress Bar Using Only CSS

This article shows step‑by‑step how to create a more authentic‑looking loading bar with pure CSS by using a container element, a ::before pseudo‑element, keyframe animations, easing functions like cubic‑bezier and the new linear() function, and finally how to finish the bar instantly with CSS variables and a tiny JavaScript trigger.

Full-Stack Cultivation Path
Full-Stack Cultivation Path
Full-Stack Cultivation Path
How to Simulate a Realistic Progress Bar Using Only CSS

1. Basic progress bar and CSS buffering animation

The simplest progress bar needs only a container element: <div class="progress"></div> Two layers are created: the container and a ::before pseudo‑element that represents the current fill.

.progress {
  position: relative;
  width: 300px;
  height: 10px;
  margin: 25px 0;
  border-radius: 10px;
  overflow: hidden;
  background-color: #E4CCFF;
}
.progress::before {
  position: absolute;
  content: '';
  width: 0%;
  height: 100%;
  background: #9747FF;
  animation: progress 10s forwards;
}
@keyframes progress {
  to { width: 100%; }
}

The default timing function is ease, which starts fast and slows down, giving a perception of quicker loading than a linear motion.

When the animation reaches 99 % the bar colour is switched to green to indicate completion.

@keyframes progress {
  99% { background-color: #9747FF; }
  100% { background-color: #14AE5C; width: 100%; }
}

2. Using linear() for finer‑grained control

The cubic‑bezier approach works but feels monotonous. Real network loading often speeds up and slows down irregularly, which can be mimicked with the newer linear() function that accepts multiple stop points.

.progress {
  --ease: linear(
    0 0%,
    0.25 4.14%,
    0.53 13.29%,
    0.61 25.03%,
    0.75 34.8%,
    0.88 43.99%,
    0.93 58.77%,
    0.98 68.88%,
    0.99 79.22%,
    1 88.79%,
    1 100%
  );
}
.progress::before {
  animation: progress 10s var(--ease) forwards;
}

Because linear() has limited browser support, a fallback to the cubic‑bezier method can be provided with @supports:

@supports (animation-timing-function: linear(0,1)) {
  .progress { --ease: linear(...); }
} @else {
  .progress { --ease: cubic-bezier(.08,.81,.29,.99); }
}

3. Actively completing the progress bar

Often the visual bar is only a transition; the actual resource may finish earlier. To finish the bar smoothly, two identical animations are added with different durations (10 s and 1 s). The short animation is paused by default and started when the resource is ready.

.progress::before {
  animation: progress 10s var(--ease) forwards,
             progress 1s var(--ease) forwards;
  animation-play-state: var(--running, paused, running);
}

A click handler (or any resource‑ready callback) switches the custom property --running to paused, running, causing the short animation to play and finish the bar within one second.

btn.onclick = function() {
  document.body.style.setProperty('--running', 'paused, running');
};

After the short animation ends, the default ease timing can be restored for any subsequent visual effect.

4. Summary

Progress bars reduce users' waiting anxiety.

Because true loading progress is often unavailable, we simulate it as realistically as possible.

The default ease timing gives a fast‑then‑slow feel.

Using cubic‑bezier we can make the initial speed even faster.

The linear() function lets us add many intermediate stops to mimic unstable network conditions.

In real pages the bar is usually a transition; we need to trigger completion manually.

Multiple animations with different durations, combined with CSS variables and a play‑state toggle, achieve a smooth, instant finish without breaking the overall animation flow.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

animationCSSprogress barcubic-bezierpseudo-elementlinear()
Full-Stack Cultivation Path
Written by

Full-Stack Cultivation Path

Focused on sharing practical tech content about TypeScript, Vue 3, front-end architecture, and source code analysis.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.