Search code examples
htmlcsscss-animations

CSS animition interger counters


I am trying to get a banner placed on an html page that uses the CSS animation to run the counter up to a specific number, but having a bit of a problem when trying to get more than one working in the same banner, the numbers will be counting up to different amounts dependant on the div, first time trying to implement this and would appreciate any help.

html,
body {
  width: 100%;
  height: 100%;
}

* {
  box-sizing: border-box;
}

body {
  padding: 0;
  margin: 0;
  font-family: roboto, segoe ui, Verdena, Helvetica, Sans-Serif;
  font-size: 16px;
  /*text-transform: uppercase;
    color: #000; */
}

@property --num {
  syntax: "<integer>";
  initial-value: 0;
  inherits: false;
}

div span.years-number {
  animation: counter 5s forwards ease-in-out;
  counter-reset: num var(--num);
  font: 800 40px system-ui;
  padding: 2rem;
}

div span.years-number::after {
  content: counter(num);
}

@keyframes counter {
  from {
    --num: 0;
  }
  to {
    --num: 20;
  }
}

div span.project-number::after {
  content: counter(num);
}

@keyframes counter {
  from {
    --num: 0;
  }
  to {
    --num: 55;
  }
}

div span.employee-number::after {
  content: counter(num);
}

@keyframes counter {
  from {
    --num: 0;
  }
  to {
    --num: 10;
  }
}

div span.customers-number::after {
  content: counter(num);
}

@keyframes counter {
  from {
    --num: 0;
  }
  to {
    --num: 115;
  }
}
<section style="position: relative; z-index: 1; text-align: center;">
  <div style="flex: 0 0 auto; width: 25%; scroll-behavior: inherit !important;">
    <div class="single-counter">
      <div class="counter-contents">
        <h2 style="color: #000000; font-size: 45px; font-weight: 700; margin-bottom: 5px; word-spacing: -10px;">
          <span class="years-number"></span>
          <span>+</span>
        </h2>
        <h3 style="color: #000; font-size: 20px; font-weight: 600;">Years of Experience</h3>
      </div>
    </div>
  </div>
  <div style="flex: 0 0 auto; width: 25%; scroll-behavior: inherit !important;">
    <div class="single-counter">
      <div class="counter-contents">
        <h2 style="color: #000; font-size: 45px; font-weight: 700; margin-bottom: 5px; word-spacing: -10px;">
          <span class="project-number"></span>
          <span>+</span>
        </h2>
        <h3 style="color: #000; font-size: 20px; font-weight: 600;">Complete Project</h3>
      </div>
    </div>
  </div>
  <div style="flex: 0 0 auto; width: 25%; scroll-behavior: inherit !important;">
    <div class="single-counter">
      <div class="counter-contents">
        <h2 style="color: #000; font-size: 45px; font-weight: 700; margin-bottom: 5px; word-spacing: -10px;">
          <span class="employee-number"></span>
          <span>+</span>
        </h2>
        <h3 style="color: #000; font-size: 20px; font-weight: 600;">Employees</h3>
      </div>
    </div>
  </div>
  <div style="flex: 0 0 auto; width: 25%; scroll-behavior: inherit !important;">
    <div class="single-counter">
      <div class="counter-contents">
        <h2 style="color: #000; font-size: 45px; font-weight: 700; margin-bottom: 5px; word-spacing: -10px;">
          <span class="customers-number"></span>
          <span>+</span>
        </h2>
        <h3 style="color: #000; font-size: 20px; font-weight: 600;">Customers</h3>
      </div>
    </div>
  </div>
</section>


Solution

  • I see the issue you're encountering. The problem arises because CSS animations, as you've tried to implement them here, do not support dynamic final values directly within the @keyframes rule for different elements. Additionally, you've defined multiple @keyframes rules with the same name (counter), which isn't valid because each @keyframes animation name must be unique.

    To solve your issue, we'll need to make use of CSS custom properties (variables) more effectively and ensure that each counter has its own unique animation. Here's an improved approach:

    Define unique @keyframes animations for each counter type (years, projects, employees, and customers). This is necessary because each counter is supposed to end at a different value. Utilize CSS custom properties to set the end value of each counter. This will allow us to control the end value of the counter directly on the element via a style attribute or a CSS rule.

    Here's the modified code:

    html,
    body {
      width: 100%;
      height: 100%;
    }
    
    * {
      box-sizing: border-box;
    }
    
    body {
      padding: 0;
      margin: 0;
      font-family: roboto, segoe ui, Verdena, Helvetica, Sans-Serif;
      font-size: 16px;
    }
    
    @property --num {
      syntax: '<integer>';
      initial-value: 0;
      inherits: false;
    }
    
    div span {
      counter-reset: num var(--num);
      font: 800 40px system-ui;
      padding: 2rem;
      animation: var(--animation) 5s forwards ease-in-out;
    }
    
    div span::after {
      content: counter(num) '+';
    }
    
    @keyframes counter-years {
      from {
        --num: 0;
      }
      to {
        --num: 20;
      }
      /* Adjust these values as needed */
    }
    
    @keyframes counter-projects {
      from {
        --num: 0;
      }
      to {
        --num: 55;
      }
      /* Adjust these values as needed */
    }
    
    @keyframes counter-employees {
      from {
        --num: 0;
      }
      to {
        --num: 10;
      }
      /* Adjust these values as needed */
    }
    
    @keyframes counter-customers {
      from {
        --num: 0;
      }
      to {
        --num: 115;
      }
      /* Adjust these values as needed */
    }
    <section style="
            display: flex;
            justify-content: space-around;
            align-items: center;
            height: 100%;
          ">
      <div class="single-counter">
        <div class="counter-contents">
          <h2>
            <span class="years-number" style="--animation: counter-years"></span>
          </h2>
          <h3>Years of Experience</h3>
        </div>
      </div>
      <div class="single-counter">
        <div class="counter-contents">
          <h2>
            <span class="project-number" style="--animation: counter-projects"></span>
          </h2>
          <h3>Complete Projects</h3>
        </div>
      </div>
      <div class="single-counter">
        <div class="counter-contents">
          <h2>
            <span class="employee-number" style="--animation: counter-employees"></span>
          </h2>
          <h3>Employees</h3>
        </div>
      </div>
      <div class="single-counter">
        <div class="counter-contents">
          <h2>
            <span class="customers-number" style="--animation: counter-customers"></span>
          </h2>
          <h3>Customers</h3>
        </div>
      </div>
    </section>

    By doing this, you ensure that each counter has its own animation timeline and end value, which is controlled through CSS custom properties.

    If it was helpful and solved the issue, please accept the answer :)