Utilities✨ Premium

Countdown Timer

A countdown on flipping cards, to a real end date: it stops at zero, shows "Finished" and tells your code.

JSAnimationTimer

Updated

This effect is part of Effect.Labs — 840 vanilla effects, some free, some premium. Utilities has 43 effects, including 3 free. Explore the category →

3 usage examples

Festival ticket office opening — days, hours, minutes, seconds, large — Countdown Timer example 1

① Festival ticket office opening — days, hours, minutes, seconds, large

WhenAt the top of the Ondrivel festival page, the countdown takes the centre: 12 days are left before sales open. At zero it would read "The ticket office is open".
WhyA wait of several days needs a days card: add it to the HTML and the script picks it up. Large, the flip of each second gives rhythm to an otherwise still page.
Settingsdata-cdt-unit="d" card added first, 74×86 px cards and 2.6 rem figures from the scene CSS, festival colours. The end is set with setEnd() 12 d 7 h 42 min after the scene shows, so the capture always shows the same numbers.
Quiz round timer — the end of the round reveals the answer — Countdown Timer example 2

② Quiz round timer — the end of the round reveals the answer

WhenOn the question 7 screen, the minutes:seconds timer runs down; at 00:00 it stops, "Time is up" shows underneath, the right answer turns green and the chosen one red.
WhyThe cdt-done event is enough to move the game on: no need to track time separately. Only two cards are present, so the count is in minutes and seconds.
Settingsm and s cards only, 00:30 in the HTML, data-cdt-done set to the end message. For the capture, the scene calls setEnd() 1.6 s before the end; it listens to cdt-done to colour the answers.
Auction lot list — one small countdown per lot — Countdown Timer example 3

③ Auction lot list — one small countdown per lot

WhenUnder each lot of the Vendrevaux sale, a shrunk countdown gives the time left. The rattan armchair reaches zero: its countdown freezes, "Sale closed" appears and the row is highlighted.
WhyThree independent instances on one page, each with its own end. Shrunk (28×30 px cards, units inline), they stay readable within a list row.
SettingsOne root per lot, h, m, s cards, colons hidden and units written next to the figures by the scene CSS, end message in data-cdt-done. Ends set with setEnd() (1.3 s, 1 h 12, 5 h 22); the row reacts to cdt-done.

How it works

Each figure is a .cdt-card split into two halves; when the value changes, the top half swings 90° in 0.3 s (cdt-flip-top) before the new number settles in. The units shown are the cards present in the HTML, marked with data-cdt-unit: d (days), h, m, s. The largest unit holds the remainder: with no days card, 26 hours read 26:00:00.

The end is set with data-cdt-end, an ISO date (2026-12-31T20:00:00+01:00; with no time zone, it is the visitor's local time). Without that attribute, the countdown starts from the duration written in the cards when the page loads (12:34:56 by default). The time left is recomputed from the clock every second: a tab left in the background never falls behind.

At zero, the countdown stops on 00:00:00. The root gets the cdt-done class, the colons stop blinking, the data-cdt-done text shows under the cards and the cdt-done event fires on the root. root.fx0738.setEnd(date) restarts the countdown towards another end (a Date object, a number of milliseconds or ISO text). Each root has its own timer.

Accessibility

  • Readable time, never chatty: the cards are hidden from screen readers (aria-hidden); a hidden role="timer" text reads "12 hours 34 minutes 56 seconds". The timer role is not announced by itself: people read it when they want, with no announcement every second.
  • End announced once: the data-cdt-done message is written into a role="status" region, read politely a single time.
  • Reduced motion: under prefers-reduced-motion: reduce, figures change without the flip and the colons stay still; the countdown carries on as usual.
  • Not a legal clock: the time comes from the visitor's computer. For a deadline that matters (end of an auction, close of registrations), your server is the reference; the countdown is only a display.

Browser compatibility

Dependency-free JavaScript (Date.parse, setTimeout, CustomEvent, padStart) and a light 3D CSS animation (perspective, rotateX). Measured in Chromium 151, Firefox 153 and WebKit 26.5 (Playwright): two copies (HTML duration and real end date), code pasted twice, stop at zero with the event, setEnd to 26 h, 390 px, reduced motion, English page, no name added to window, 0 errors. Playwright's WebKit flattens 3D effects: the flip was not checked by eye in Safari.

Chrome 57+✓ Full (measured)
Firefox 48+✓ Full (measured)
Safari 10+✓ Countdown measured in WebKit; 3D flip not checked
Edge 79+✓ Full (Chromium engine)
Mobile iOS 10+✓ WebKit engine, not checked on a device
Android Chrome 57+✓ Full (Chromium engine)

Without JavaScript, the cards show the value written in the HTML, standing still. A badly written data-cdt-end is ignored: the countdown then starts from the cards' duration.

The code

HTML structure to paste into your page (the CSS + JS come with your purchase):

index.html — structure
<div class="fx-0738-root" data-cdt-end="2026-12-31T20:00:00+01:00" data-cdt-done="Finished">
  <div class="cdt-board" aria-hidden="true">
    <div class="cdt-unit">
      <div class="cdt-card" data-cdt-unit="h">
        <div class="cdt-card-inner"><div class="cdt-front">12</div><div class="cdt-back">12</div></div>
      </div>
      <span class="cdt-label">Hours</span>
    </div>
    <span class="cdt-sep">:</span>
    <!-- same block for data-cdt-unit="m" and "s" (and "d" first for days) -->
  </div>
  <p class="cdt-sr" role="timer"></p>
  <p class="cdt-status" role="status"></p>
</div>

One-time payment, no subscription — the code is yours forever

Customize

Options passed to the API or data-* attributes:

Option / propertyDefaultEffect
End date (HTML — data-cdt-end) absent: duration written in the cards ISO date with a time zone (2026-12-31T20:00:00+01:00) for a deadline that is the same for every visitor.
Units (HTML — data-cdt-unit cards) h, m, s Add a d card for days, remove h for a minutes:seconds timer. The largest unit holds the remainder.
End message (HTML — data-cdt-done) "Finished" Text shown under the cards and announced once at zero: "Sale closed", "Registrations are open"…
New end (JS — root.fx0738.setEnd(date)) — Restarts the countdown towards another date (Date, milliseconds or ISO text), for instance an end received from your server after loading.
React to the end (JS — cdt-done event) — root.addEventListener('cdt-done', …): show a button, reload a price, move to the next question.
Card size (CSS — .cdt-card, .cdt-front) 60×70px, 2rem figures Enlarge the card and the figure size together; both halves follow.
Colours (CSS — .cdt-board, .cdt-sep, .cdt-status) #1a1a2e background, #6366f1 colons, #a5b4fc message Your brand hues; the finished state is styled through .cdt-done.

FAQ

Yes if the end date carries a time zone (+01:00, Z). Without one, it is read in each visitor's local time: 8 pm in Paris and 8 pm in Montreal are not the same moment. The count uses the visitor's computer clock; a clock that is off shifts the display, not your real deadline.
The countdown shows 00:00:00 as soon as it loads, switches to the finished state, shows the data-cdt-done message and fires cdt-done. It never restarts by itself; to restart it, call setEnd().
Yes: the hours card keeps the whole remainder, for example 126:05:12. Three digits just fit the 60 px card; give it a 76 px min-width for some breathing room. For a wait of several days, a d card reads better.