Newsletter

Hear about new files and fixes

I send an email when I publish something new or release a fix for a file you might already use. There’s no fixed schedule.

Every email has a one-click unsubscribe link, and none of them contain tracking pixels.

Etch component

Animated Counter

Count a statistic up to its value when it scrolls into view. The final number is in the HTML from the start, so screen readers, search engines and visitors without JavaScript always get it. Prefix, suffix, decimals, five separator styles, four label positions and an icon slot, all set in the Etch props panel.

Download .jsonSee the demo

Paste it on the Etch canvas with Ctrl+V or ⌘V, then click Save.

Stay up to date. Etch won’t update a pasted component for you. Subscribe and get one email when a new version is out.

Free on unlimited personal, client and commercial sites. Modify it however you like.License: FSL-1.1-MIT
Version
1.0.0-beta.3Changelog
Options
19 settings+ 40 CSS variablesSee all
Tested on Etch
1.7.1
Tested on WordPress
7.1.3
Live demo

21 counters in real layouts

Scroll inside the frame: each counter starts when it comes into view. Switch to Mobile to see the layouts at phone width.

Live
  • Instances: 21
  • Label positions: 4
  • Separators: 5
  • Slots: icon, customLabel
Open in a new tab
Features

What it does

One pasted component and no setup. CountUp.js is bundled, so there is no plugin, no CDN and no extra request, and every counter on a page shares one observer.

  • Real number in the HTML

    Etch renders the final value on the server, so search engines, screen readers, printouts and visitors without JavaScript get 1,250, never 0.

  • Starts when it can be seen

    Counting begins once at least 1% of the counter is in view and visible. Counters in a hidden tab panel, a faded-out slide or a background browser tab wait until they are shown.

  • Accessible by default

    Screen readers hear the final value once, never the moving digits. Reduced motion shows the final value straight away, and no animation runs longer than 5 seconds.

  • Any number format

    Up to 10 decimal places, a prefix and a suffix, five thousands separators and a period or comma for decimals. Negative values and counting down work too.

  • Label in four positions

    Below, above, left or right of the number. Side labels wrap below on narrow screens, and the reading order stays number first.

  • Icon and rich label slots

    Put an SVG or an image in the icon slot, or a heading and a link in the customLabel slot.

  • 40 styling variables

    Colors, sizes, weights, spacing and layout, set on any parent class. ACSS 4 tokens with plain fallbacks, so it works without Automatic.css too.

  • Preview in the builder

    Change End value, Start value, Duration, Delay or Use easing and the counter plays once, so you can judge the timing without saving. A setting that stops it from animating gets a dashed outline and a note.

Install

Add it in four steps

No plugin and no build step. The component brings its own CSS and script with CountUp.js bundled, so nothing loads from a CDN. It works with or without Automatic.css 4.

Shows without its styles? It was pasted but not saved, and pasting again won’t repair it. Delete ED Animated Counter from your components, paste it again and click Save.

  1. Copy the componentUse Copy component at the top of this page, or download the .json file.
  2. Paste it on the Etch canvasSelect where it should go, press Ctrl+V or ⌘V, then click Save. Etch adds the component to your list right away but stores its CSS only when you save.
  3. Enter your numberSet End value with a dot for decimals and no grouping, such as 1250 or 98.7. Add Decimal places, Prefix, Suffix and a Label as needed.
  4. Save and open the pageThe builder shows the final value and plays a short preview when you change the number or the timing. On the page, each counter starts when it scrolls into view.
Options

19 settings in the Etch props panel

Grouped by topic here; the names match the Etch props panel, where each setting has help text too. Only End value is required: a new counter shows 1,250 and nothing else until you fill in the rest.

Type numbers as plain digits

Etch has no number field yet, so End value, Start value, Decimal places, Duration and Delay are text fields. Type 1250 or 98.7: a dot for decimals and no grouping. The separators below only change how the number is shown.

A number the counter can’t read stays as typed and doesn’t animate. In the builder that counter gets a dashed outline and a note such as “Will not animate. Check End value.”

Number

End valueRequiredendValue
The number to count to. Type digits with a dot for decimals and no grouping, such as 1250 or 98.7.Default: 1250
Start valuestartValue
Where counting begins. A start above End value counts down, and negative numbers work.Default: 0
Decimal placesdecimalPlaces
A whole number from 0 to 10. Set 1 for 98.7 or 2 for 1.25; the page and the animation round the same way.Default: 0
Prefixprefix
Text before the number, such as $.Empty by default.
Suffixsuffix
Text after the number, such as +, % or M.Empty by default.

Format

Thousands separatorthousandsSeparator
Comma (1,250), Period (1.250), Space (1 250), Apostrophe (1’250) or None (1250). Space keeps the groups on one line while the number fits.Default: Comma
Decimal separatordecimalSeparator
A period or a comma, as in 98.7 or 98,7.Default: .

Label

Labellabel
Plain text below or beside the number. For a heading or a link, leave it empty and use the customLabel slot.Empty by default.
Label positionlabelPosition
Below, Above, Left or Right of the number. Left and right follow the text direction and wrap below when there is no room. The reading order stays number first.Default: Below the number
Screen reader textaccessibleText
Read instead of the visible number, such as “1.25 million dollars” for $1.25M. Leave it empty to have the number read as shown.Empty by default.

Typography

Number sizenumberSize
Any CSS font size, such as 3rem or var(--h1, 3rem). Give a variable a fallback. Empty uses the styling variables, H2 size by default.Empty by default.
Number weightnumberWeight
Any CSS font weight, such as 400 or 800. Empty uses the heading weight.Empty by default.
Number fontnumberFont
Inherited keeps the surrounding font. Heading font uses the ACSS heading font family when one is set in the ACSS dashboard.Default: Inherited
Label sizelabelSize
Any CSS font size, such as 1.25rem or var(--text-l, 1.25rem). Empty uses the styling variables.Empty by default.
Label weightlabelWeight
Any CSS font weight, such as 600. Empty uses the styling variables.Empty by default.

Animation

Duration (seconds)duration
Up to 5 seconds; longer values play in 5. 0 shows the final value without animation.Default: 2
Delay (milliseconds)delay
Wait after the counter comes into view, from 0 to 60000. Handy for staggering a row of counters.Default: 0
Use easinguseEasing
Yes slows down toward the end; No counts at a steady pace.Default: Yes
Animate onceanimateOnce
Yes plays once per page view; No plays again each time the counter comes back into view.Default: Yes

Slots

Iconicon
A decorative SVG or image, shown first in every label position and scaled by the icon size variable. Screen readers skip it, so no links or meaningful text.Empty: the icon area is hidden.
Custom labelcustomLabel
Rich label content, such as a heading or a link, shown after the Label text. Give it meaningful text and accessible links.Empty by default.
Styling: 40 CSS variables

Set them on any parent element or class, such as your stats section. They all inherit, so no extra selectors or !important are needed. The same list sits at the top of the component’s CSS in Etch, where uncommenting a line changes every counter, but a parent class is safer: it survives updates.

Container

--ed-animated-counter-text-color
Base color, inherited by the number and the label unless they have their own.Default: inherit
--ed-animated-counter-font-family
Base typeface.Default: inherit
--ed-animated-counter-text-size
Base text size.Default: inherit
--ed-animated-counter-text-weight
Base weight.Default: inherit
--ed-animated-counter-line-height
Base line height; prefer a unitless value.Default: inherit
--ed-animated-counter-text-align
Text alignment, such as center for a stats row.Default: inherit
--ed-animated-counter-background
Optional container background.Default: transparent
--ed-animated-counter-padding
Container spacing, one length.Default: 0
--ed-animated-counter-padding-block
Top and bottom spacing; wins over padding.Default: var(--ed-animated-counter-padding, 0)
--ed-animated-counter-padding-inline
Side spacing; wins over padding.Default: var(--ed-animated-counter-padding, 0)
--ed-animated-counter-border
Optional border shorthand.Default: 0
--ed-animated-counter-radius
Corner radius.Default: 0
--ed-animated-counter-shadow
Optional box shadow.Default: none
--ed-animated-counter-gap
Space between the number and the label.Default: var(--space-xs, 0.75rem)

Layout

--ed-animated-counter-align-items
Cross-axis alignment. Unset, a label beside the number sits on its baseline; a value you set applies to all four positions.Default: normal
--ed-animated-counter-justify-content
Main-axis placement, such as center for a label beside the number.Default: normal

Number

--ed-animated-counter-number-color
Number color; the prefix and suffix follow it.Default: inherit
--ed-animated-counter-number-font-family
Number typeface.Default: inherit
--ed-animated-counter-number-size
Number size; the prefix and suffix follow it.Default: var(--h2, 2rem)
--ed-animated-counter-number-weight
Number weight.Default: var(--heading-font-weight, 700)
--ed-animated-counter-number-line-height
Number line height, resolved for the number’s own font.Default: var(--heading-line-height, 1.2)
--ed-animated-counter-number-letter-spacing
Number tracking.Default: inherit
--ed-animated-counter-number-min-block-size
Optional minimum height that reserves room for a number that wraps.Default: 0

Icon

--ed-animated-counter-icon-size
Icon size; an SVG or image in the icon slot is 1em tall.Default: 2em
--ed-animated-counter-icon-color
Icon color for SVGs that use currentColor.Default: inherit

Label

--ed-animated-counter-label-color
Label color.Default: inherit
--ed-animated-counter-label-font-family
Label typeface.Default: inherit
--ed-animated-counter-label-size
Label size.Default: 1em
--ed-animated-counter-label-weight
Label weight.Default: inherit
--ed-animated-counter-label-line-height
Label line height; follows the container line height, otherwise resolves for the label’s own font.Default: var(--ed-animated-counter-line-height, var(--text-line-height, 1.5))
--ed-animated-counter-label-letter-spacing
Label tracking.Default: inherit
--ed-animated-counter-label-max-inline-size
Maximum label width; a limit such as 12ch keeps a long side label beside the number.Default: none

Prefix and suffix

--ed-animated-counter-prefix-color
Prefix color.Default: inherit
--ed-animated-counter-prefix-font-family
Prefix typeface.Default: inherit
--ed-animated-counter-prefix-size
Prefix size; em is relative to the number.Default: 1em
--ed-animated-counter-prefix-weight
Prefix weight.Default: inherit
--ed-animated-counter-suffix-color
Suffix color.Default: inherit
--ed-animated-counter-suffix-font-family
Suffix typeface.Default: inherit
--ed-animated-counter-suffix-size
Suffix size; em is relative to the number, such as 0.6em for a small %.Default: 1em
--ed-animated-counter-suffix-weight
Suffix weight.Default: inherit
License

Free to use. Not for resale.

Licensed under FSL-1.1-MIT, the Functional Source License with an MIT future license. Each version becomes MIT-licensed two years after its release.

You can
  • Use it on unlimited personal, client and commercial websites
  • Modify and adapt it however you like
  • Use it in paid design and development work for your clients
You can’t
  • Sell it, or a modified version, as a component: in a library, bundle, template kit, plugin or marketplace listing
  • Offer it in another commercial product or service that competes with it

Keep the copyright and license notices in the code, and link to this page instead of re-hosting the download, so everyone gets fixes and updates. Read the full license

Third-party code

This component bundles the open-source library below. It keeps its own license, and FSL-1.1-MIT does not apply to it. Its notice is included in the component’s script; keep it when you use or change the component.

  • CountUp.js2.10.1© Jamie Perkins and contributors
Updates

How to update

Etch has no update button for pasted components. Pasting a newer version over an installed one changes nothing: Etch keeps the component and the CSS it already has. To update, reinstall in five steps.

  1. Note what you setWrite down the settings and slot content of each counter, and copy any CSS you changed in the component’s own styles.
  2. Delete the componentIn Etch, delete ED Animated Counter from your components. Every counter disappears from every page that used it.
  3. Delete its stylesIn the Style Manager, search for ed-animated-counter and delete the eight styles listed below.
  4. Paste the new versionCopy or download the new JSON from this page, paste it on the canvas and click Save. Etch creates the component and its CSS fresh.
  5. Put your counters backInsert a counter wherever one was, re-enter its settings and save. Reload any other open builder tab.

Styles to delete in step 3

  • .ed-animated-counter
  • .ed-animated-counter__icon
  • .ed-animated-counter__accessible-text
  • .ed-animated-counter__number
  • .ed-animated-counter__prefix
  • .ed-animated-counter__value
  • .ed-animated-counter__suffix
  • .ed-animated-counter__label

Trade-offs

  • Placements are not kept. Etch doesn’t link old counters to the new component, so you insert each one again and re-enter its settings.
  • Edits inside the component’s CSS are replaced. Keep overrides on a parent class with the --ed-animated-counter-* variables; those survive an update.
  • Skipping a step leaves a mix. Without step 2, Etch keeps the old component. Without step 3, the new version runs on the old CSS.

Coming from 1.0.0-beta.2? Follow the five steps above. Settings and slots are unchanged, so you can re-enter them as they were. Subscribe and you’ll get one email when the next version is out.

Changelog

What changed

Versions follow semantic versioning. When an update changes how existing options behave, its entry says so.

1.0.0-beta.3Latest
  • FixedWith TalkBack, a counter that started under the screen reader focus read its value twice and once moved the focus to the top of the page. A counter that finished under the focus was read again on the next swipe.
  • FixedDuring an animation, touching the number found nothing when an icon or a label sat above it (TalkBack, VoiceOver on iOS). With Screen reader text, touching the number found nothing in TalkBack. The number now stays in the accessibility tree and reads its value the whole time.
  • ChangedThe HTML and CSS of the number changed, so update the component and its eight styles together (see How to update). Props, slots, classes and styling variables are the same. Screen reader text now sits inside .ed-animated-counter__number, so your own CSS that targeted .ed-animated-counter__accessible-text as a direct child of the counter needs the same change.
  • TestedTalkBack, VoiceOver on macOS and in the iOS Simulator, NVDA, Safari and Microsoft Edge. It stays a beta until VoiceOver is tested on a real iPhone.
1.0.0-beta.2
  • ReleasedFirst public version. It stays a beta until VoiceOver on iOS, TalkBack and Microsoft Edge are tested.
Feedback

Tell me what this component needs

A bug, a missing option, or anything that made the component awkward to use in a real build — it all belongs here. The versions above are the ones it was tested on; if yours differ, say so.