How to make a progress bar ranging from 0 to 100% with CSS only?


Viewed 10,654 times


I am developing a page where there is a progress bar and I need to make it start from 0 and go to 100%.

But I only need to do it with CSS and, in addition, I need that when it reaches 100%, start again from 0.

Although there is some information about this progress bar, I need it to last exactly 6 seconds and when it reaches 100%, don’t go back, skip to 0%.

Has anyone ever had to do anything like this?

  • Only with . css was this the closest I could find.

3 answers


Would that be, young man?

    height: 15px;
    background-color: #777;
    position: relative;

.progress .progress-bar{
   position: absolute;
   height: 100%;
   background-color: #add555;
   animation: progress-animation 6s infinite;

@keyframes progress-animation{
    0% { width: 0%; }
    100% { width: 100%}
<div class="progress">
    <div class="progress-bar"></div>


The @keyframes is intended to specify an animation for the element. You can define the steps for each frame of the animation through a block of CSS styles declared through from or to, or even define through the percentages - as I did above, indicating that in 0% (the beginning) the animation should contain the size 0% and with 100% will have 100% size.


@keyframes example{
    0% {
        /* frame inicial da animação, poderia ser usado "from" */

   50% {
      /* frame do "meio" da animação */

   100% {
      /* último frame da animação */


Example with from:

@keyframes example {
      from {
          /* início da animação */
      to {
         /* final da animação */

To use a declared animation with @keyframes, you must use the attribute animation in the desired selector setting, you can also set the animation interval or repetitions.


 .example {
    /* use a animação "example" no intervalo de 2 segundos infinitamente */
    animation: example 2s infinite;

Note: The estate animation is just a shortcut to the various properties that can be set to configure an animation. They are:

  • Animation-name
  • Animation-Duration
  • Animation-timing-Function
  • Animation-delay
  • Animation-iteration-Count
  • Animation-Direction
  • Animation-Fill-mode
  • Animation-play-state

Read more on MDN


My idea follows the same of Wallace’s answer, with the alternative of not having to create more than one html element to deal with the "internal" progress look, the bar that increases the property width. Taking advantage of himself <div> and using the pseudo element ::after (or ::before) it is possible to do so:

@keyframes loading {
  from { width: 0 }
  to { width: 100% }

div {
  background: #E7E7E7;
  position: relative;
  height: 8px;
  width: 100%

div::after {
  animation: loading 6s infinite ease-in-out;
  background: #897FBA;
  content: '';
  position: absolute;
  left: 0; top: 0; bottom: 0

  • I was faster :D

  • 2

    @Wallacemaxters danadin +1. D

  • 2

    @Wallacemaxters was faster rsrs +1

  • @Renan liked the idea of ::after, made the element independent.

  • and how to make the bar only click once and keep progress filled when you finish?

  • 1

    @Wpfan change the animation of infinite for forwards: animation: loading 6s forwards ease-in-out;.

  • 1

    @Renangomes thank you very much

Show 2 more comments


Has a different way of doing using linear-gradiente, so no more than a tag or use pseudo-element.

Imagine you have one linear gradient as background of a container, and that this gradient is divided equally with 50% of the width with one color and the other 50% with another color.

Now with the background-size you put that background with 200% of the width of container. Thus each half of the gradient now occupies 100% of the width of the container.

inserir a descrição da imagem aqui

Now with background-position and the @keyframes we do the background walk 100% to one side, showing 100% of the other color that was "hidden" outside the container.

The animation

On the property animation it is composed as follows in shorthand:

animation: name duration timing-function delay iteration-count direction fill-mode;

Or in the Longhand (values initial)

animation-name: none
animation-duration: 0s
animation-timing-function: ease
animation-delay: 0s
animation-iteration-count: 1
animation-direction: normal
animation-fill-mode: none
animation-play-state: running

In your case you need to focus on name (name of the animation declared in @keyframes), duration (have you want it to last the animation 6s) and iteration-count (how many times the animation will repeat itself infinite "eternal loop") In this documentation of Mozilla you can find out more:

After defined this we will work on the animation intervals built within the at-Rule @keyframes. As I want you to have a 1s interval first to see the 100% empty bar at the beginning and then a 1s interval for you to see the 100% full bar at the end I will repeat some values at the beginning and at the end of the @keyframes The comments are in the code below:

OBS: as the animation has 6 second I will divide 100 by 6, so we have 6 16.66% intervals representing each second of animation. In the first second and in the last second the bar is stopped for 1 second. If you do not want this effect just put this way. So the full bar straight, without a "delay" at the beginning and at the end for you to see the two full bar states:

/* estado inicial da barra */
  0% {
    background-position: 99% 0;
/* estado final da barra */
  100% {
      background-position: 0 0;

To better understand how the animation fractionation works and the creation of this "delay" see the comments I left in the code below:

.barra {
    height: 16px;
    width: 50%;
    border-radius: 8px;
    margin: 10px auto;
 /* gradiente com 2 cores */
    background-image: linear-gradient(to right, #bada55 0, #bada55 50%, gray 50%);
 /* agradiente com 200% da largura do container 101% pra cada cor */
    background-size: 202% 100%;
 /* move o backgrount para mostrar apenas a primeira cor */
    background-position: 99% 0;
 /* animação que move o background para mostrar a segunda cor */
    animation: anima 6s ease-in-out infinite;
 /* coloquei 500ms de delay para vc poder ver a barra 100% "vazia" antes de iniciar a animação */

@keyframes anima {
    0% {
      /* movemo o background para mostrar apenas 100% de uma cor */
      background-position: 99% 0;
    /* do 0% do tempo da animação até 16,66% do tempo a barra permance parada */
    16.66% {
        background-position: 99% 0;
    /* esse intervalo representa 4 segundos do tempo da animação até a barra fica 100% cheia */
    83.34% {
        background-position: 0 0;
    /* esse intervalo de 83,34% a 100% representa 1 segundo da animação e a barra fica cheia por 1s antes de reiniciar*/
    100% {
        /* de 75% a 100% eu repito o estado da propriedade para vc ter um tempo de ver a barra 100% completa antes de reiniciar a animação */
        background-position: 0 0;
<div class="barra"></div>

An image to better explain the technique.

The blue border is the container, and the bottom moving is the background. The background has 200% the width of the parent, so each color that has 50% of the gradient actually gets 100% of the parent’s width. Then adjusting the horizontal value of the parent background-position in the @keyframes it is possible to move the bar, giving the impression that is completing the container.

inserir a descrição da imagem aqui

Browser support: According to the website this model works from IE10 up, Chrome, Fire Fox, Safari, etc...

Browser other questions tagged

You are not signed in. Login or sign up in order to post.