Number Count

v1.0.03,7 kB gzipfree

Numbers count up as soon as they come into view — in exactly the notation already written in the Designer.

Tutorial video
You can build this with AI, the prompt is ready
View live demoGet the cloneable
The principle · Why Number Count

The target value is not configured, it is read. Whatever stands in the Designer as text — 1.234, 1,234, 1'234 or 1 234, with “€ ” in front or “ Mio.” behind — is taken apart: the number for the animation, everything else as the shape it returns to. That is why the final state matches the Designer text exactly, and when the number changes in the CMS, the counter changes with it.

  1. ReadThe text content is parsed: thousands separator, decimal separator, prefix and suffix are recognised and preserved.
  2. WaitAn IntersectionObserver at a threshold of 0.4 waits until the number is in view — once, unless it is meant to repeat.
  3. CountThe animation runs via requestAnimationFrame with easeOutCubic; formatting goes through Intl.NumberFormat using the language from <html lang>.

Add this <script> to your project's custom code

Once per project is enough, even if you use the solution on several pages: Project Settings → Custom Code → Head Code. The script loads alongside the page without holding up rendering; it runs once the page is in place and does nothing while no fpu attribute is there.

<!-- FlowPowerUser · Number Count -->
<script defer src="https://cdn.jsdelivr.net/npm/@flowpoweruser/attributes@1/number-count.js"></script>

Mark the number

Write the number into the Designer the way it should read in the end — with separators, with the percent sign, with everything that belongs to it. Then set the attribute on it. The target value is read from exactly that text; a separate value is only needed if it should differ from the text.

On the numbertext only inside
AttributeValueDescription
Turns the solution on for this element. The target value is parsed from the text content — thousands separator, decimal separator, prefix (e.g. “+”, “€ ”) and suffix (e.g. “%”, “ Mio.”) are recognised and preserved. Required.
In the Navigator
Text Block   fpu-count-element="number"
             Content: 1,250 +

Publish and check

Publish the page and scroll far enough for the number to come into view. Two things have to hold: it starts counting once visible, and in the end exactly the Designer text is back — the same separators, the same decimals, the same prefix and suffix. If it stays put, fpu-count-logging="true" helps: it writes one line to the browser console on start.

OptionalOnly needed when you need it. Expand
OptionalStart several numbers staggeredDefault: 150 ms offset

When several metrics sit side by side, they should rarely all jump at once. Set the attribute on the surrounding container: it starts all counters inside it staggered as soon as it becomes visible, and passes its options down to its members — whatever you set on the container applies to every number within.

On the container

AttributeValueDefaultDescription
Starts all number counters inside it staggered as soon as the container becomes visible, and passes its options down to its members.
150Offset in milliseconds between two counters of the group. Belongs on the group element.
OptionalValues, duration and repeatingDefault: 0 → text, 2000 ms

Without any setting the number counts from 0 to the value in the text, taking two seconds. These four attributes set each part of that individually. If the start value is greater than the target value, the number counts down instead of up.

On the number or the group

on the group it applies to all
AttributeValueDefaultDescription
value from the textTarget value override, in case it should not come from the text content. Period as the decimal separator.
0Start value. If it is above the target value, it counts down.
2000Animation duration in milliseconds.
falseCounts again on every viewport entry. Without this attribute the animation runs exactly once.
OptionalThousands separator and consoleOtherwise as in the text

The thousands separator normally comes from the original text. If it should be a different one, this attribute sets it — either as a keyword or literally as the character to appear.

On the number or the group

AttributeValueDefaultDescription
as in the original textThousands separator override. “none” does not group at all; “space”, “nbsp”, “thin” and “apostrophe” are keywords. Any other value is taken literally as the separator.
falseWrites one line to the browser console on start.
OptionalDefaults for the whole projectthe attribute always wins

If every number in the project should share the same duration or separator, that can be set centrally once instead of writing it onto every element. An attribute on the element always beats this value.

Before the script in the head code
<script>
  window.fpuNumberCountConfig = {
    duration: 2000,
    stagger: 150,
    repeat: false,
    separator: ",",
    logging: false,
  };
</script>
More capabilities

Additional features

Anyone asking for less motion gets none
With “reduce motion” set in the operating system there is no counting: the number sits at its final value right away. The content is therefore fully readable from the first second.
Tall elements are measured differently
The threshold sits at 40 percent of the element. If an element is taller than 40 percent of the viewport that could never be reached — there the visible portion decides instead, so large number blocks start reliably too.
CMS counters loaded later come along
The solution listens for the “fpu:items-changed” event and initialises newly rendered counters afterwards. Numbers that only appear through filtering, sorting or loading more therefore count just the same.
Your own code can hook in
The running counters, a fresh start and the cleanup are reachable from outside: instances, init and destroyAll.
The element should contain text only
During the animation the content is replaced via textContent. Child elements inside the number are lost in the process — prefix and suffix therefore belong in the same text content, not in separate spans.