SolutionsCase StudiesAI ToolsResourcesAboutContact
Get Your Free AI Automation Audit Book a Free 30-Minute Strategy Call
Playground~/frontend Open editor

~/css/countdown-timers

7 CSS Countdown Timers

0 Pure CSS7 with JSPublished 2026-10-05Free · no sign-up

Seven countdowns: boxes for days, hours, minutes and seconds, digits that slide as they change, rings that empty, a countdown inside a sentence, a sale banner that retires itself, a quiz timer with a pause button and a minimal launch countdown.

01. Days, Hours, Minutes, Seconds Boxes

CountdownTimer

Four boxes counting down to an end time three days from when the page loads. The end time is also written as a time element, which is what screen readers and search engines use; the ticking boxes are a role="timer" region, which is not announced every second.

View code
<section class="cd1" aria-labelledby="cd1h"><h3 id="cd1h">Spring sale ends <time></time></h3><div role="timer" aria-label="Time left"><p><b class="d">00</b><span>days</span></p><p><b class="h">00</b><span>hours</span></p><p><b class="m">00</b><span>min</span></p><p><b class="s">00</b><span>sec</span></p></div></section>
.cd1 { width: 320px; max-width: 100%; font-family: Inter, system-ui, sans-serif; text-align: center; }
.cd1 h3 { margin: 0 0 12px; color: #cbd5e1; font-size: 14px; font-weight: 600; } .cd1 time { color: #fff; }
.cd1 [role=timer] { display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px; }
.cd1 p { display: grid; margin: 0; padding: 12px 4px; border-radius: 12px; background: #1e293b; }
.cd1 b { color: #fff; font-size: 28px; font-variant-numeric: tabular-nums; } .cd1 span { color: #94a3b8; font-size: 11px; text-transform: uppercase; letter-spacing: .06em; }
var end = new Date(Date.now() + 3 * 864e5 + 5 * 36e5), t = document.querySelector('.cd1 time');
t.dateTime = end.toISOString(); t.textContent = end.toLocaleString('en-GB', { weekday: 'short', day: 'numeric', month: 'short', hour: '2-digit', minute: '2-digit' });
function pad(n) { return String(n).padStart(2, '0'); }
(function tick() { var s = Math.max(0, Math.floor((end - Date.now()) / 1000));
  document.querySelector('.cd1 .d').textContent = pad(Math.floor(s / 86400)); document.querySelector('.cd1 .h').textContent = pad(Math.floor(s % 86400 / 3600));
  document.querySelector('.cd1 .m').textContent = pad(Math.floor(s % 3600 / 60)); document.querySelector('.cd1 .s').textContent = pad(s % 60);
  if (s > 0) setTimeout(tick, 1000); })();

02. Digits That Slide on Change

CountdownAnimation

Each time a digit changes it slides up out of view and the new one slides in, a softer cousin of the flip clock. Only the digits that changed animate, by restarting a keyframe on that one element, and reduced motion swaps them instantly.

View code
<div class="cd2" role="timer" aria-label="Minutes and seconds left"><span class="u"><i>0</i><i>5</i></span><b>:</b><span class="u"><i>0</i><i>0</i></span></div>
.cd2 { display: flex; align-items: center; gap: 6px; font: 800 44px "JetBrains Mono", ui-monospace, monospace; color: #101828; }
.cd2 .u { display: flex; gap: 4px; }
.cd2 i { display: grid; place-items: center; width: 44px; height: 64px; border-radius: 10px; background: #f2f4f7; font-style: normal; overflow: hidden; }
.cd2 i.go { animation: cd2 .35s cubic-bezier(.2,.8,.2,1); }
.cd2 b { color: #98a2b3; }
@keyframes cd2 { from { transform: translateY(-60%); opacity: 0; } }
@media (prefers-reduced-motion: reduce) { .cd2 i.go { animation: none; } }
var digits = document.querySelectorAll('.cd2 i'), left = 300;
setInterval(function () { if (left <= 0) return; left--; var str = String(Math.floor(left / 60)).padStart(2, '0') + String(left % 60).padStart(2, '0');
  digits.forEach(function (d, k) { if (d.textContent !== str[k]) { d.textContent = str[k]; d.classList.remove('go'); void d.offsetWidth; d.classList.add('go'); } }); }, 1000);

03. Countdown Rings

CountdownRings

Hours, minutes and seconds each shown as a ring that empties as time passes, drawn with conic gradients whose fill comes from a custom property. The numbers sit in the middle of each ring and the group is a timer region.

View code
<div class="cd3" role="timer" aria-label="Time left"><p style="--p:0" class="h"><b>00</b><span>hrs</span></p><p style="--p:0" class="m"><b>00</b><span>min</span></p><p style="--p:0" class="s"><b>00</b><span>sec</span></p></div>
.cd3 { display: flex; gap: 12px; font-family: Inter, system-ui, sans-serif; }
.cd3 p { display: grid; place-content: center; justify-items: center; width: 80px; height: 80px; margin: 0; border-radius: 50%; background: radial-gradient(closest-side, #0e1118 82%, transparent 83%), conic-gradient(#38bdf8 calc(var(--p) * 1%), #1e293b 0); }
.cd3 .m { background: radial-gradient(closest-side, #0e1118 82%, transparent 83%), conic-gradient(#a78bfa calc(var(--p) * 1%), #1e293b 0); }
.cd3 .s { background: radial-gradient(closest-side, #0e1118 82%, transparent 83%), conic-gradient(#f472b6 calc(var(--p) * 1%), #1e293b 0); }
.cd3 b { color: #fff; font-size: 20px; font-variant-numeric: tabular-nums; line-height: 1; } .cd3 span { color: #94a3b8; font-size: 10px; }
var end = Date.now() + (5 * 3600 + 42 * 60 + 18) * 1000, ps = document.querySelectorAll('.cd3 p');
(function tick() { var s = Math.max(0, Math.floor((end - Date.now()) / 1000)), v = [Math.floor(s / 3600), Math.floor(s % 3600 / 60), s % 60], max = [24, 60, 60];
  ps.forEach(function (p, k) { p.style.setProperty('--p', v[k] / max[k] * 100); p.querySelector('b').textContent = String(v[k]).padStart(2, '0'); });
  if (s > 0) setTimeout(tick, 1000); })();

04. Inline Countdown in a Sentence

CountdownInline

A countdown that sits inside a line of text, like Order within 2h 14m for delivery tomorrow, which is often more persuasive than a giant clock. It updates once a minute rather than every second, which is all this kind of message needs.

View code
<p class="cd4">Order within <strong class="t" role="timer">2h 14m</strong> for delivery <b>tomorrow</b>.</p>
.cd4 { margin: 0; padding: 12px 14px; border-radius: 10px; background: #ecfdf3; color: #054f31; font: 15px Inter, system-ui, sans-serif; }
.cd4 .t { font-variant-numeric: tabular-nums; }
var end = Date.now() + (2 * 60 + 14) * 60000, t = document.querySelector('.cd4 .t');
(function tick() { var m = Math.max(0, Math.ceil((end - Date.now()) / 60000)); t.textContent = Math.floor(m / 60) + 'h ' + (m % 60) + 'm'; if (m > 0) setTimeout(tick, 60000); })();

05. Sale Banner with Countdown

CountdownBanner

A slim promotional banner with the offer, a compact HH:MM:SS countdown and a link. When the time runs out, the banner changes its own text to say the sale has ended instead of showing zeros, so it never shows an expired offer.

View code
<div class="cd5" role="region" aria-label="Promotion"><p><b>24-hour sale:</b> 30% off everything</p><span role="timer" class="t">--:--:--</span><a href="#sale">Shop now</a></div>
.cd5 { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 8px 14px; width: 400px; max-width: 100%; padding: 10px 14px; box-sizing: border-box; border-radius: 10px; background: #7c2d12; color: #ffedd5; font: 14px Inter, system-ui, sans-serif; }
.cd5 p { margin: 0; } .cd5 b { color: #fff; }
.cd5 .t { padding: 3px 8px; border-radius: 6px; background: #431407; color: #fff; font: 700 14px "JetBrains Mono", ui-monospace, monospace; }
.cd5 a { color: #fff; font-weight: 700; }
.cd5 a:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }
var w = document.querySelector('.cd5'), t = w.querySelector('.t'), end = Date.now() + (6 * 3600 + 3 * 60 + 9) * 1000;
(function tick() { var s = Math.floor((end - Date.now()) / 1000); if (s <= 0) { w.innerHTML = '<p>The 24-hour sale has ended. Thanks for shopping.</p>'; return; }
  t.textContent = [Math.floor(s / 3600), Math.floor(s % 3600 / 60), s % 60].map(function (n) { return String(n).padStart(2, '0'); }).join(':'); setTimeout(tick, 1000); })();

06. Quiz Timer with Pause

CountdownProgress

A 30-second timer for a quiz question with a draining bar and a Pause button, because timed tasks must let people pause or extend them under WCAG. The remaining time is announced only at 10 seconds and at the end, not every tick.

View code
<div class="cd6"><div class="bar"><span></span></div><p><span class="t">30</span> seconds left</p><button aria-pressed="false">Pause</button><p class="sr" aria-live="assertive"></p></div>
.cd6 { width: 280px; max-width: 100%; font: 14px Inter, system-ui, sans-serif; color: #344054; }
.cd6 .bar { height: 8px; border-radius: 4px; background: #eaecf0; overflow: hidden; }
.cd6 .bar span { display: block; height: 100%; width: 100%; background: #7f56d9; transform-origin: left; transition: transform 1s linear; }
.cd6 p { margin: 8px 0; } .cd6 .t { color: #101828; font-weight: 800; font-variant-numeric: tabular-nums; }
.cd6 button { padding: 8px 14px; border: 1px solid #d0d5dd; border-radius: 8px; background: #fff; font: 600 13px Inter, system-ui, sans-serif; cursor: pointer; }
.cd6 button:focus-visible { outline: 3px solid #d6bbfb; outline-offset: 2px; }
.cd6 .sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
var w = document.querySelector('.cd6'), bar = w.querySelector('.bar span'), t = w.querySelector('.t'), b = w.querySelector('button'), live = w.querySelector('.sr'), left = 30, paused = false;
var id = setInterval(function () { if (paused || left <= 0) return; left--; t.textContent = left; bar.style.transform = 'scaleX(' + left / 30 + ')';
  if (left === 10) live.textContent = '10 seconds left'; if (left === 0) { live.textContent = 'Time is up'; b.disabled = true; clearInterval(id); } }, 1000);
b.onclick = function () { paused = !paused; b.setAttribute('aria-pressed', paused); b.textContent = paused ? 'Resume' : 'Pause'; };

07. Minimal Launch Countdown

CountdownLaunch

A big, quiet countdown for a launch page: large thin numbers, small labels and a sign-up link underneath. The launch date is a real time element in the heading, so the page still makes sense if the script never runs.

View code
<section class="cd7"><h3>Launching <time datetime="2026-12-01">1 December 2026</time></h3><div role="timer" aria-label="Time until launch"><span><b class="d">--</b>days</span><span><b class="h">--</b>hours</span><span><b class="m">--</b>minutes</span></div><a href="#notify">Get notified</a></section>
.cd7 { display: grid; justify-items: center; gap: 14px; width: 340px; max-width: 100%; font-family: Inter, system-ui, sans-serif; text-align: center; }
.cd7 h3 { margin: 0; color: #94a3b8; font-size: 14px; font-weight: 600; } .cd7 time { color: #fff; }
.cd7 [role=timer] { display: flex; gap: 20px; }
.cd7 span { display: grid; color: #94a3b8; font-size: 11px; text-transform: uppercase; letter-spacing: .1em; }
.cd7 b { color: #fff; font-size: 48px; font-weight: 200; font-variant-numeric: tabular-nums; letter-spacing: 0; text-transform: none; }
.cd7 a { padding: 9px 16px; border: 1px solid #475569; border-radius: 999px; color: #f8fafc; font-size: 14px; text-decoration: none; }
.cd7 a:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }
var end = new Date('2026-12-01T09:00:00Z').getTime();
(function tick() { var s = Math.max(0, Math.floor((end - Date.now()) / 1000));
  document.querySelector('.cd7 .d').textContent = Math.floor(s / 86400); document.querySelector('.cd7 .h').textContent = String(Math.floor(s % 86400 / 3600)).padStart(2, '0'); document.querySelector('.cd7 .m').textContent = String(Math.floor(s % 3600 / 60)).padStart(2, '0');
  if (s > 0) setTimeout(tick, 30000); })();

About these CSS Countdown Timers

Do not announce every second

A ticking number read aloud every second is unbearable. Put the countdown in a role="timer" element, which is not announced by default, and announce only meaningful moments such as 10 seconds left.

Write the end time down

Show the deadline as a real <time datetime> too, such as Ends Friday 18:00. It is useful to everyone, readable by assistive technology and still correct if the script fails.

Timed tasks need a pause

WCAG asks that people can pause, stop or extend time limits. A quiz or checkout timer needs a Pause or More time button.

Handle the end

When the countdown reaches zero, change the message: The sale has ended. Never leave a banner showing 00:00:00 or an offer that no longer exists.

Frequently asked questions

Are fake urgency timers OK?

No. A countdown that resets on every visit misleads buyers and breaks consumer law in many places. Count to a real deadline.

Which time zone do the timers use?

They count to an absolute moment, so every visitor sees the right time left wherever they are.

Will the timer drift?

Each tick recalculates from the end time rather than subtracting a second, so it stays accurate.