08 / 22
All 22 in 22 Tailwind CSS Countdown Timers
- 01Accessible Tailwind CSS Countdown Timer with ARIA Live Region
- 02Pure Tailwind CSS Countdown Timer Without JavaScript
- 03Tailwind Session Timeout Warning Modal with Countdown
- 04Tailwind Circular Countdown Timer with SVG Progress Ring
- 05Tailwind OTP Input with Resend Code Countdown Timer
- 06Tailwind Rate Limit Button with Retry-After Countdown
- 07Tailwind Auction Countdown Timer with Anti-Snipe Extension
- 08Tailwind Delivery ETA Countdown with Order Tracking Steps
- 09Tailwind Waitlist Landing Page with Launch Countdown
- 10Tailwind Webinar Countdown Timer with Time Zone Display
- 11Tailwind Appointment Reminder Card with Countdown
- 12Tailwind Exam Timer with Time Remaining Warnings
- 13Tailwind Free Trial Countdown Banner for SaaS Apps
- 14Tailwind Scheduled Maintenance Countdown Status Page
- 15Tailwind Virtual Waiting Room Queue Countdown
- 16Tailwind Daily Streak Countdown Widget for Habit Apps
- 17Tailwind Flash Sale Countdown Timer for Product Pages
- 18Tailwind Sticky Announcement Bar with Countdown Timer
- 19Tailwind Shopping Cart Reservation Countdown Timer
- 20Tailwind Coming Soon Page with Launch Countdown Timer
- 21Tailwind Interval Workout Timer with Circular Progress
- 22Tailwind Retro Flip Clock Countdown Timer
Tailwind Delivery ETA Countdown with Order Tracking Steps
An order-tracking ETA that counts down beside a four-step status rail — and at zero flips to Arriving any moment instead of drifting into negative minutes. In the port, step states are data-state attributes read by group-data-[state=done]/step:, and a delayed keyframe now advances the rail to its final step at the same moment, still with zero JavaScript.
Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time
Untitled UI React · Affiliate link
The code
<section class="tcd-08 w-full min-h-svh grid place-items-center bg-tcd08-page px-4 py-12 font-tcd08-sans text-tcd08-ink" aria-labelledby="tcd-08-title">
<article class="w-full max-w-[560px] rounded-[26px] bg-tcd08-card p-[clamp(20px,4.4vw,32px)] shadow-tcd08-card">
<header class="flex flex-wrap items-start justify-between gap-x-4 gap-y-3">
<div class="min-w-0">
<p class="mb-1 text-[11px] font-extrabold uppercase tracking-[0.16em] text-tcd08-acc">Fern Kitchen</p>
<h2 id="tcd-08-title" class="text-[clamp(17px,3vw,20px)] font-extrabold leading-snug text-balance">Order 8842 · two bowls, one flatbread</h2>
</div>
<div class="flex items-center gap-2.5 rounded-full bg-tcd08-soft py-1.5 pr-4 pl-1.5">
<span aria-hidden="true" class="grid size-8 place-items-center rounded-full bg-tcd08-acc text-xs font-black text-white">SR</span>
<span class="text-[13px] leading-tight font-bold">Sam Rivera<br><em class="text-[11px] font-semibold not-italic text-tcd08-mut">Courier</em></span>
</div>
</header>
<div role="timer" aria-label="Estimated arrival countdown" class="relative mt-6 overflow-hidden rounded-2xl bg-tcd08-soft px-5 py-6 text-center">
<p class="text-xs font-extrabold uppercase tracking-[0.14em] text-tcd08-mut">Arriving in</p>
<p aria-hidden="true" class="my-1 flex items-baseline justify-center text-[clamp(56px,16vw,84px)] font-black leading-none tracking-[-0.03em] tabular-nums">
<em class="not-italic animate-tcd08-min [counter-reset:tcd08min_var(--tcd08-min)] before:content-[counter(tcd08min)]"></em><span class="px-0.5 text-tcd08-acc">:</span><em class="not-italic animate-tcd08-st [counter-reset:tcd08st_var(--tcd08-st)] before:content-[counter(tcd08st)]"></em><em class="not-italic animate-tcd08-su [counter-reset:tcd08su_var(--tcd08-su)] before:content-[counter(tcd08su)]"></em>
</p>
<p class="text-[13px] font-semibold text-tcd08-mut">Estimated <time datetime="PT14M">14 minutes</time> from Vale Street</p>
<div aria-hidden="true" class="mx-auto mt-4 h-1.5 max-w-[280px] overflow-hidden rounded-full bg-tcd08-rule"><span class="block h-full origin-right rounded-full bg-tcd08-acc animate-tcd08-bar"></span></div>
<div role="status" class="invisible absolute inset-0 grid place-content-center gap-1.5 bg-tcd08-acc px-5 text-white opacity-0 animate-tcd08-done">
<strong class="text-[clamp(24px,6vw,32px)] font-black tracking-[-0.01em]">Arriving any moment</strong>
<span class="text-[13px] font-semibold opacity-90">Sam is on your street — meet at the door</span>
</div>
</div>
<ol class="relative mt-7 grid gap-5 before:absolute before:top-2 before:bottom-2 before:left-[9px] before:w-0.5 before:bg-tcd08-rule before:content-['']">
<li data-state="done" class="group/step relative grid grid-cols-[20px_1fr_auto] items-center gap-3.5 text-[14px] font-bold"><span aria-hidden="true" class="relative grid size-5 place-items-center rounded-full bg-tcd08-rule group-data-[state=done]/step:bg-tcd08-acc"><svg viewBox="0 0 12 12" width="10" height="10" class="hidden text-white group-data-[state=done]/step:block"><path d="M2.5 6.2 5 8.5l4.5-5" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/></svg></span>Order placed<em class="text-xs font-semibold not-italic text-tcd08-mut">7:02 pm</em></li>
<li data-state="done" class="group/step relative grid grid-cols-[20px_1fr_auto] items-center gap-3.5 text-[14px] font-bold"><span aria-hidden="true" class="relative grid size-5 place-items-center rounded-full bg-tcd08-rule group-data-[state=done]/step:bg-tcd08-acc"><svg viewBox="0 0 12 12" width="10" height="10" class="hidden text-white group-data-[state=done]/step:block"><path d="M2.5 6.2 5 8.5l4.5-5" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/></svg></span>Preparing<em class="text-xs font-semibold not-italic text-tcd08-mut">7:09 pm</em></li>
<li data-state="active" aria-current="step" class="group/step relative grid grid-cols-[20px_1fr_auto] items-center gap-3.5 text-[14px] font-extrabold text-tcd08-acc"><span aria-hidden="true" class="relative grid size-5 place-items-center rounded-full bg-tcd08-card ring-[5px] ring-tcd08-acc ring-inset"><span class="absolute inset-0 rounded-full bg-tcd08-acc/40 animate-ping motion-reduce:hidden"></span></span>On the way<em class="text-xs font-bold not-italic">now</em></li>
<li class="group/step relative grid grid-cols-[20px_1fr_auto] items-center gap-3.5 text-[14px] font-bold text-tcd08-mut animate-tcd08-arrive"><span aria-hidden="true" class="relative size-5 rounded-full border-2 border-tcd08-rule bg-tcd08-card"></span>Arriving<em class="text-xs font-semibold not-italic">7:36 pm</em></li>
</ol>
<p class="mt-6 border-t border-tcd08-rule pt-4 text-[12.5px] leading-relaxed text-tcd08-mut text-pretty">At zero this ETA flips to a terminal message instead of counting into negative minutes. Written in Tailwind only, so it measures fourteen minutes from render rather than tracking a server timestamp.</p>
</article>
</section><section class="tcd-08 w-full min-h-svh grid place-items-center bg-tcd08-page px-4 py-12 font-tcd08-sans text-tcd08-ink" aria-labelledby="tcd-08-title">
<article class="w-full max-w-[560px] rounded-[26px] bg-tcd08-card p-[clamp(20px,4.4vw,32px)] shadow-tcd08-card">
<header class="flex flex-wrap items-start justify-between gap-x-4 gap-y-3">
<div class="min-w-0">
<p class="mb-1 text-[11px] font-extrabold uppercase tracking-[0.16em] text-tcd08-acc">Fern Kitchen</p>
<h2 id="tcd-08-title" class="text-[clamp(17px,3vw,20px)] font-extrabold leading-snug text-balance">Order 8842 · two bowls, one flatbread</h2>
</div>
<div class="flex items-center gap-2.5 rounded-full bg-tcd08-soft py-1.5 pr-4 pl-1.5">
<span aria-hidden="true" class="grid size-8 place-items-center rounded-full bg-tcd08-acc text-xs font-black text-white">SR</span>
<span class="text-[13px] leading-tight font-bold">Sam Rivera<br><em class="text-[11px] font-semibold not-italic text-tcd08-mut">Courier</em></span>
</div>
</header>
<div role="timer" aria-label="Estimated arrival countdown" class="relative mt-6 overflow-hidden rounded-2xl bg-tcd08-soft px-5 py-6 text-center">
<p class="text-xs font-extrabold uppercase tracking-[0.14em] text-tcd08-mut">Arriving in</p>
<p aria-hidden="true" class="my-1 flex items-baseline justify-center text-[clamp(56px,16vw,84px)] font-black leading-none tracking-[-0.03em] tabular-nums">
<em class="not-italic animate-tcd08-min [counter-reset:tcd08min_var(--tcd08-min)] before:content-[counter(tcd08min)]"></em><span class="px-0.5 text-tcd08-acc">:</span><em class="not-italic animate-tcd08-st [counter-reset:tcd08st_var(--tcd08-st)] before:content-[counter(tcd08st)]"></em><em class="not-italic animate-tcd08-su [counter-reset:tcd08su_var(--tcd08-su)] before:content-[counter(tcd08su)]"></em>
</p>
<p class="text-[13px] font-semibold text-tcd08-mut">Estimated <time datetime="PT14M">14 minutes</time> from Vale Street</p>
<div aria-hidden="true" class="mx-auto mt-4 h-1.5 max-w-[280px] overflow-hidden rounded-full bg-tcd08-rule"><span class="block h-full origin-right rounded-full bg-tcd08-acc animate-tcd08-bar"></span></div>
<div role="status" class="invisible absolute inset-0 grid place-content-center gap-1.5 bg-tcd08-acc px-5 text-white opacity-0 animate-tcd08-done">
<strong class="text-[clamp(24px,6vw,32px)] font-black tracking-[-0.01em]">Arriving any moment</strong>
<span class="text-[13px] font-semibold opacity-90">Sam is on your street — meet at the door</span>
</div>
</div>
<ol class="relative mt-7 grid gap-5 before:absolute before:top-2 before:bottom-2 before:left-[9px] before:w-0.5 before:bg-tcd08-rule before:content-['']">
<li data-state="done" class="group/step relative grid grid-cols-[20px_1fr_auto] items-center gap-3.5 text-[14px] font-bold"><span aria-hidden="true" class="relative grid size-5 place-items-center rounded-full bg-tcd08-rule group-data-[state=done]/step:bg-tcd08-acc"><svg viewBox="0 0 12 12" width="10" height="10" class="hidden text-white group-data-[state=done]/step:block"><path d="M2.5 6.2 5 8.5l4.5-5" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/></svg></span>Order placed<em class="text-xs font-semibold not-italic text-tcd08-mut">7:02 pm</em></li>
<li data-state="done" class="group/step relative grid grid-cols-[20px_1fr_auto] items-center gap-3.5 text-[14px] font-bold"><span aria-hidden="true" class="relative grid size-5 place-items-center rounded-full bg-tcd08-rule group-data-[state=done]/step:bg-tcd08-acc"><svg viewBox="0 0 12 12" width="10" height="10" class="hidden text-white group-data-[state=done]/step:block"><path d="M2.5 6.2 5 8.5l4.5-5" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/></svg></span>Preparing<em class="text-xs font-semibold not-italic text-tcd08-mut">7:09 pm</em></li>
<li data-state="active" aria-current="step" class="group/step relative grid grid-cols-[20px_1fr_auto] items-center gap-3.5 text-[14px] font-extrabold text-tcd08-acc"><span aria-hidden="true" class="relative grid size-5 place-items-center rounded-full bg-tcd08-card ring-[5px] ring-tcd08-acc ring-inset"><span class="absolute inset-0 rounded-full bg-tcd08-acc/40 animate-ping motion-reduce:hidden"></span></span>On the way<em class="text-xs font-bold not-italic">now</em></li>
<li class="group/step relative grid grid-cols-[20px_1fr_auto] items-center gap-3.5 text-[14px] font-bold text-tcd08-mut animate-tcd08-arrive"><span aria-hidden="true" class="relative size-5 rounded-full border-2 border-tcd08-rule bg-tcd08-card"></span>Arriving<em class="text-xs font-semibold not-italic">7:36 pm</em></li>
</ol>
<p class="mt-6 border-t border-tcd08-rule pt-4 text-[12.5px] leading-relaxed text-tcd08-mut text-pretty">At zero this ETA flips to a terminal message instead of counting into negative minutes. Written in Tailwind only, so it measures fourteen minutes from render rather than tracking a server timestamp.</p>
</article>
</section>@import "tailwindcss";
@property --tcd08-min {
syntax: '<integer>';
inherits: false;
initial-value: 13;
}
@property --tcd08-st {
syntax: '<integer>';
inherits: false;
initial-value: 5;
}
@property --tcd08-su {
syntax: '<integer>';
inherits: false;
initial-value: 9;
}
@theme {
--color-tcd08-page: #e9efe4;
--color-tcd08-page: oklch(0.94 0.018 140);
--color-tcd08-card: #ffffff;
--color-tcd08-card: oklch(1 0 0);
--color-tcd08-soft: #eef3ea;
--color-tcd08-soft: oklch(0.96 0.015 140);
--color-tcd08-ink: #17231b;
--color-tcd08-ink: oklch(0.23 0.03 150);
--color-tcd08-mut: #546659;
--color-tcd08-mut: oklch(0.48 0.028 155);
--color-tcd08-rule: #d5e0d0;
--color-tcd08-rule: oklch(0.88 0.02 145);
--color-tcd08-acc: #2f7d4f;
--color-tcd08-acc: oklch(0.52 0.12 150);
--font-tcd08-sans: 'Nunito', ui-rounded, system-ui, sans-serif;
--shadow-tcd08-card: 0 1px 2px rgb(23 35 27 / 0.05), 0 30px 60px -36px rgb(23 35 27 / 0.45);
--animate-tcd08-min: tcd08-min 840s steps(14, end) forwards;
--animate-tcd08-st: tcd08-st 60s steps(6, end) infinite;
--animate-tcd08-su: tcd08-su 10s steps(10, end) infinite;
--animate-tcd08-bar: tcd08-bar 840s linear forwards;
--animate-tcd08-done: tcd08-done 0.01s linear 840s forwards;
--animate-tcd08-arrive: tcd08-arrive 0.4s ease-out 840s forwards;
@keyframes tcd08-min {
from {
--tcd08-min: 13;
}
to {
--tcd08-min: -1;
}
}
@keyframes tcd08-st {
from {
--tcd08-st: 5;
}
to {
--tcd08-st: -1;
}
}
@keyframes tcd08-su {
from {
--tcd08-su: 9;
}
to {
--tcd08-su: -1;
}
}
@keyframes tcd08-bar {
from {
transform: scaleX(0.04);
}
to {
transform: scaleX(1);
}
}
@keyframes tcd08-done {
to {
opacity: 1;
visibility: visible;
}
}
@keyframes tcd08-arrive {
to {
color: var(--color-tcd08-acc);
}
}
}
.tcd-08 {
width: 100%;
min-height: 100svh;
}@import "tailwindcss";
@property --tcd08-min {
syntax: '<integer>';
inherits: false;
initial-value: 13;
}
@property --tcd08-st {
syntax: '<integer>';
inherits: false;
initial-value: 5;
}
@property --tcd08-su {
syntax: '<integer>';
inherits: false;
initial-value: 9;
}
@theme {
--color-tcd08-page: #e9efe4;
--color-tcd08-page: oklch(0.94 0.018 140);
--color-tcd08-card: #ffffff;
--color-tcd08-card: oklch(1 0 0);
--color-tcd08-soft: #eef3ea;
--color-tcd08-soft: oklch(0.96 0.015 140);
--color-tcd08-ink: #17231b;
--color-tcd08-ink: oklch(0.23 0.03 150);
--color-tcd08-mut: #546659;
--color-tcd08-mut: oklch(0.48 0.028 155);
--color-tcd08-rule: #d5e0d0;
--color-tcd08-rule: oklch(0.88 0.02 145);
--color-tcd08-acc: #2f7d4f;
--color-tcd08-acc: oklch(0.52 0.12 150);
--font-tcd08-sans: 'Nunito', ui-rounded, system-ui, sans-serif;
--shadow-tcd08-card: 0 1px 2px rgb(23 35 27 / 0.05), 0 30px 60px -36px rgb(23 35 27 / 0.45);
--animate-tcd08-min: tcd08-min 840s steps(14, end) forwards;
--animate-tcd08-st: tcd08-st 60s steps(6, end) infinite;
--animate-tcd08-su: tcd08-su 10s steps(10, end) infinite;
--animate-tcd08-bar: tcd08-bar 840s linear forwards;
--animate-tcd08-done: tcd08-done 0.01s linear 840s forwards;
--animate-tcd08-arrive: tcd08-arrive 0.4s ease-out 840s forwards;
@keyframes tcd08-min {
from {
--tcd08-min: 13;
}
to {
--tcd08-min: -1;
}
}
@keyframes tcd08-st {
from {
--tcd08-st: 5;
}
to {
--tcd08-st: -1;
}
}
@keyframes tcd08-su {
from {
--tcd08-su: 9;
}
to {
--tcd08-su: -1;
}
}
@keyframes tcd08-bar {
from {
transform: scaleX(0.04);
}
to {
transform: scaleX(1);
}
}
@keyframes tcd08-done {
to {
opacity: 1;
visibility: visible;
}
}
@keyframes tcd08-arrive {
to {
color: var(--color-tcd08-acc);
}
}
}
.tcd-08 {
width: 100%;
min-height: 100svh;
}Setup — do this once per project
Skip this if your project already runs Tailwind v4.
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>Compiles in the browser — perfect for a CodePen or a quick test, not for production.
npm install tailwindcss @tailwindcss/vite
npm install tailwindcss @tailwindcss/vite// vite.config.js ("type": "module" in package.json)
import { defineConfig } from 'vite'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [tailwindcss()],
})// vite.config.js ("type": "module" in package.json)
import { defineConfig } from 'vite'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [tailwindcss()],
})Any v4 setup works — Vite, PostCSS or the CLI. Nothing in this demo is bundler-specific.
@import "tailwindcss";
@property --tcd08-min {
syntax: '<integer>';
inherits: false;
initial-value: 13;
}
@property --tcd08-st {
syntax: '<integer>';
inherits: false;
initial-value: 5;
}
@property --tcd08-su {
syntax: '<integer>';
inherits: false;
initial-value: 9;
}
@theme {
--color-tcd08-page: #e9efe4;
--color-tcd08-page: oklch(0.94 0.018 140);
--color-tcd08-card: #ffffff;
--color-tcd08-card: oklch(1 0 0);
--color-tcd08-soft: #eef3ea;
--color-tcd08-soft: oklch(0.96 0.015 140);
--color-tcd08-ink: #17231b;
--color-tcd08-ink: oklch(0.23 0.03 150);
--color-tcd08-mut: #546659;
--color-tcd08-mut: oklch(0.48 0.028 155);
--color-tcd08-rule: #d5e0d0;
--color-tcd08-rule: oklch(0.88 0.02 145);
--color-tcd08-acc: #2f7d4f;
--color-tcd08-acc: oklch(0.52 0.12 150);
--font-tcd08-sans: 'Nunito', ui-rounded, system-ui, sans-serif;
--shadow-tcd08-card: 0 1px 2px rgb(23 35 27 / 0.05), 0 30px 60px -36px rgb(23 35 27 / 0.45);
--animate-tcd08-min: tcd08-min 840s steps(14, end) forwards;
--animate-tcd08-st: tcd08-st 60s steps(6, end) infinite;
--animate-tcd08-su: tcd08-su 10s steps(10, end) infinite;
--animate-tcd08-bar: tcd08-bar 840s linear forwards;
--animate-tcd08-done: tcd08-done 0.01s linear 840s forwards;
--animate-tcd08-arrive: tcd08-arrive 0.4s ease-out 840s forwards;
@keyframes tcd08-min {
from {
--tcd08-min: 13;
}
to {
--tcd08-min: -1;
}
}
@keyframes tcd08-st {
from {
--tcd08-st: 5;
}
to {
--tcd08-st: -1;
}
}
@keyframes tcd08-su {
from {
--tcd08-su: 9;
}
to {
--tcd08-su: -1;
}
}
@keyframes tcd08-bar {
from {
transform: scaleX(0.04);
}
to {
transform: scaleX(1);
}
}
@keyframes tcd08-done {
to {
opacity: 1;
visibility: visible;
}
}
@keyframes tcd08-arrive {
to {
color: var(--color-tcd08-acc);
}
}
}
.tcd-08 {
width: 100%;
min-height: 100svh;
}@import "tailwindcss";
@property --tcd08-min {
syntax: '<integer>';
inherits: false;
initial-value: 13;
}
@property --tcd08-st {
syntax: '<integer>';
inherits: false;
initial-value: 5;
}
@property --tcd08-su {
syntax: '<integer>';
inherits: false;
initial-value: 9;
}
@theme {
--color-tcd08-page: #e9efe4;
--color-tcd08-page: oklch(0.94 0.018 140);
--color-tcd08-card: #ffffff;
--color-tcd08-card: oklch(1 0 0);
--color-tcd08-soft: #eef3ea;
--color-tcd08-soft: oklch(0.96 0.015 140);
--color-tcd08-ink: #17231b;
--color-tcd08-ink: oklch(0.23 0.03 150);
--color-tcd08-mut: #546659;
--color-tcd08-mut: oklch(0.48 0.028 155);
--color-tcd08-rule: #d5e0d0;
--color-tcd08-rule: oklch(0.88 0.02 145);
--color-tcd08-acc: #2f7d4f;
--color-tcd08-acc: oklch(0.52 0.12 150);
--font-tcd08-sans: 'Nunito', ui-rounded, system-ui, sans-serif;
--shadow-tcd08-card: 0 1px 2px rgb(23 35 27 / 0.05), 0 30px 60px -36px rgb(23 35 27 / 0.45);
--animate-tcd08-min: tcd08-min 840s steps(14, end) forwards;
--animate-tcd08-st: tcd08-st 60s steps(6, end) infinite;
--animate-tcd08-su: tcd08-su 10s steps(10, end) infinite;
--animate-tcd08-bar: tcd08-bar 840s linear forwards;
--animate-tcd08-done: tcd08-done 0.01s linear 840s forwards;
--animate-tcd08-arrive: tcd08-arrive 0.4s ease-out 840s forwards;
@keyframes tcd08-min {
from {
--tcd08-min: 13;
}
to {
--tcd08-min: -1;
}
}
@keyframes tcd08-st {
from {
--tcd08-st: 5;
}
to {
--tcd08-st: -1;
}
}
@keyframes tcd08-su {
from {
--tcd08-su: 9;
}
to {
--tcd08-su: -1;
}
}
@keyframes tcd08-bar {
from {
transform: scaleX(0.04);
}
to {
transform: scaleX(1);
}
}
@keyframes tcd08-done {
to {
opacity: 1;
visibility: visible;
}
}
@keyframes tcd08-arrive {
to {
color: var(--color-tcd08-acc);
}
}
}
.tcd-08 {
width: 100%;
min-height: 100svh;
}<link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Nunito:wght@500;600;700;800;900&display=swap">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Nunito:wght@500;600;700;800;900&display=swap">What changed in the port
Which parts became utilities, which stayed CSS, and why.
.cdt-08__step[data-state="done"] spanand its siblings becamegroup-data-[state=…]/step:variants on the dot and its checkmark; the rail line is abefore:on the<ol>.- The hand-written pulse keyframe was replaced by Tailwind's built-in
animate-pingon a dedicated halo span. - UI upgrade: the final Arriving step now turns green at zero via a delayed keyframe, so the rail and the ETA never contradict each other; a progress bar fills toward arrival.
- UI upgrade: done steps show a checkmark, the active step has
aria-current="step", the courier gained an initials avatar pill, and the terminal panel fills with the brand green. - Font moved from the system
ui-roundedstack (which falls back to plain sans outside Apple platforms) to Nunito, so the rounded voice is consistent everywhere.
Here's a working CSS demo from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Tailwind Delivery ETA Countdown with Order Tracking Steps
Source: https://codefronts.com/snippets/tailwind-css-countdown-timers/tailwind-delivery-eta-countdown-order-tracking/
An order-tracking ETA that counts down beside a four-step status rail — and at zero flips to Arriving any moment instead of drifting into negative minutes. In the port, step states are data-state attributes read by group-data-[state=done]/step:, and a delayed keyframe now advances the rail to its final step at the same moment, still with zero JavaScript.
## HTML
```html
<section class="tcd-08 w-full min-h-svh grid place-items-center bg-tcd08-page px-4 py-12 font-tcd08-sans text-tcd08-ink" aria-labelledby="tcd-08-title">
<article class="w-full max-w-[560px] rounded-[26px] bg-tcd08-card p-[clamp(20px,4.4vw,32px)] shadow-tcd08-card">
<header class="flex flex-wrap items-start justify-between gap-x-4 gap-y-3">
<div class="min-w-0">
<p class="mb-1 text-[11px] font-extrabold uppercase tracking-[0.16em] text-tcd08-acc">Fern Kitchen</p>
<h2 id="tcd-08-title" class="text-[clamp(17px,3vw,20px)] font-extrabold leading-snug text-balance">Order 8842 · two bowls, one flatbread</h2>
</div>
<div class="flex items-center gap-2.5 rounded-full bg-tcd08-soft py-1.5 pr-4 pl-1.5">
<span aria-hidden="true" class="grid size-8 place-items-center rounded-full bg-tcd08-acc text-xs font-black text-white">SR</span>
<span class="text-[13px] leading-tight font-bold">Sam Rivera<br><em class="text-[11px] font-semibold not-italic text-tcd08-mut">Courier</em></span>
</div>
</header>
<div role="timer" aria-label="Estimated arrival countdown" class="relative mt-6 overflow-hidden rounded-2xl bg-tcd08-soft px-5 py-6 text-center">
<p class="text-xs font-extrabold uppercase tracking-[0.14em] text-tcd08-mut">Arriving in</p>
<p aria-hidden="true" class="my-1 flex items-baseline justify-center text-[clamp(56px,16vw,84px)] font-black leading-none tracking-[-0.03em] tabular-nums">
<em class="not-italic animate-tcd08-min [counter-reset:tcd08min_var(--tcd08-min)] before:content-[counter(tcd08min)]"></em><span class="px-0.5 text-tcd08-acc">:</span><em class="not-italic animate-tcd08-st [counter-reset:tcd08st_var(--tcd08-st)] before:content-[counter(tcd08st)]"></em><em class="not-italic animate-tcd08-su [counter-reset:tcd08su_var(--tcd08-su)] before:content-[counter(tcd08su)]"></em>
</p>
<p class="text-[13px] font-semibold text-tcd08-mut">Estimated <time datetime="PT14M">14 minutes</time> from Vale Street</p>
<div aria-hidden="true" class="mx-auto mt-4 h-1.5 max-w-[280px] overflow-hidden rounded-full bg-tcd08-rule"><span class="block h-full origin-right rounded-full bg-tcd08-acc animate-tcd08-bar"></span></div>
<div role="status" class="invisible absolute inset-0 grid place-content-center gap-1.5 bg-tcd08-acc px-5 text-white opacity-0 animate-tcd08-done">
<strong class="text-[clamp(24px,6vw,32px)] font-black tracking-[-0.01em]">Arriving any moment</strong>
<span class="text-[13px] font-semibold opacity-90">Sam is on your street — meet at the door</span>
</div>
</div>
<ol class="relative mt-7 grid gap-5 before:absolute before:top-2 before:bottom-2 before:left-[9px] before:w-0.5 before:bg-tcd08-rule before:content-['']">
<li data-state="done" class="group/step relative grid grid-cols-[20px_1fr_auto] items-center gap-3.5 text-[14px] font-bold"><span aria-hidden="true" class="relative grid size-5 place-items-center rounded-full bg-tcd08-rule group-data-[state=done]/step:bg-tcd08-acc"><svg viewBox="0 0 12 12" width="10" height="10" class="hidden text-white group-data-[state=done]/step:block"><path d="M2.5 6.2 5 8.5l4.5-5" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/></svg></span>Order placed<em class="text-xs font-semibold not-italic text-tcd08-mut">7:02 pm</em></li>
<li data-state="done" class="group/step relative grid grid-cols-[20px_1fr_auto] items-center gap-3.5 text-[14px] font-bold"><span aria-hidden="true" class="relative grid size-5 place-items-center rounded-full bg-tcd08-rule group-data-[state=done]/step:bg-tcd08-acc"><svg viewBox="0 0 12 12" width="10" height="10" class="hidden text-white group-data-[state=done]/step:block"><path d="M2.5 6.2 5 8.5l4.5-5" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/></svg></span>Preparing<em class="text-xs font-semibold not-italic text-tcd08-mut">7:09 pm</em></li>
<li data-state="active" aria-current="step" class="group/step relative grid grid-cols-[20px_1fr_auto] items-center gap-3.5 text-[14px] font-extrabold text-tcd08-acc"><span aria-hidden="true" class="relative grid size-5 place-items-center rounded-full bg-tcd08-card ring-[5px] ring-tcd08-acc ring-inset"><span class="absolute inset-0 rounded-full bg-tcd08-acc/40 animate-ping motion-reduce:hidden"></span></span>On the way<em class="text-xs font-bold not-italic">now</em></li>
<li class="group/step relative grid grid-cols-[20px_1fr_auto] items-center gap-3.5 text-[14px] font-bold text-tcd08-mut animate-tcd08-arrive"><span aria-hidden="true" class="relative size-5 rounded-full border-2 border-tcd08-rule bg-tcd08-card"></span>Arriving<em class="text-xs font-semibold not-italic">7:36 pm</em></li>
</ol>
<p class="mt-6 border-t border-tcd08-rule pt-4 text-[12.5px] leading-relaxed text-tcd08-mut text-pretty">At zero this ETA flips to a terminal message instead of counting into negative minutes. Written in Tailwind only, so it measures fourteen minutes from render rather than tracking a server timestamp.</p>
</article>
</section>
```
## CSS
```css
@import "tailwindcss";
@property --tcd08-min {
syntax: '<integer>';
inherits: false;
initial-value: 13;
}
@property --tcd08-st {
syntax: '<integer>';
inherits: false;
initial-value: 5;
}
@property --tcd08-su {
syntax: '<integer>';
inherits: false;
initial-value: 9;
}
@theme {
--color-tcd08-page: #e9efe4;
--color-tcd08-page: oklch(0.94 0.018 140);
--color-tcd08-card: #ffffff;
--color-tcd08-card: oklch(1 0 0);
--color-tcd08-soft: #eef3ea;
--color-tcd08-soft: oklch(0.96 0.015 140);
--color-tcd08-ink: #17231b;
--color-tcd08-ink: oklch(0.23 0.03 150);
--color-tcd08-mut: #546659;
--color-tcd08-mut: oklch(0.48 0.028 155);
--color-tcd08-rule: #d5e0d0;
--color-tcd08-rule: oklch(0.88 0.02 145);
--color-tcd08-acc: #2f7d4f;
--color-tcd08-acc: oklch(0.52 0.12 150);
--font-tcd08-sans: 'Nunito', ui-rounded, system-ui, sans-serif;
--shadow-tcd08-card: 0 1px 2px rgb(23 35 27 / 0.05), 0 30px 60px -36px rgb(23 35 27 / 0.45);
--animate-tcd08-min: tcd08-min 840s steps(14, end) forwards;
--animate-tcd08-st: tcd08-st 60s steps(6, end) infinite;
--animate-tcd08-su: tcd08-su 10s steps(10, end) infinite;
--animate-tcd08-bar: tcd08-bar 840s linear forwards;
--animate-tcd08-done: tcd08-done 0.01s linear 840s forwards;
--animate-tcd08-arrive: tcd08-arrive 0.4s ease-out 840s forwards;
@keyframes tcd08-min {
from {
--tcd08-min: 13;
}
to {
--tcd08-min: -1;
}
}
@keyframes tcd08-st {
from {
--tcd08-st: 5;
}
to {
--tcd08-st: -1;
}
}
@keyframes tcd08-su {
from {
--tcd08-su: 9;
}
to {
--tcd08-su: -1;
}
}
@keyframes tcd08-bar {
from {
transform: scaleX(0.04);
}
to {
transform: scaleX(1);
}
}
@keyframes tcd08-done {
to {
opacity: 1;
visibility: visible;
}
}
@keyframes tcd08-arrive {
to {
color: var(--color-tcd08-acc);
}
}
}
.tcd-08 {
width: 100%;
min-height: 100svh;
}
```Here's a working CSS demo from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Tailwind Delivery ETA Countdown with Order Tracking Steps
Source: https://codefronts.com/snippets/tailwind-css-countdown-timers/tailwind-delivery-eta-countdown-order-tracking/
An order-tracking ETA that counts down beside a four-step status rail — and at zero flips to Arriving any moment instead of drifting into negative minutes. In the port, step states are data-state attributes read by group-data-[state=done]/step:, and a delayed keyframe now advances the rail to its final step at the same moment, still with zero JavaScript.
## HTML
```html
<section class="tcd-08 w-full min-h-svh grid place-items-center bg-tcd08-page px-4 py-12 font-tcd08-sans text-tcd08-ink" aria-labelledby="tcd-08-title">
<article class="w-full max-w-[560px] rounded-[26px] bg-tcd08-card p-[clamp(20px,4.4vw,32px)] shadow-tcd08-card">
<header class="flex flex-wrap items-start justify-between gap-x-4 gap-y-3">
<div class="min-w-0">
<p class="mb-1 text-[11px] font-extrabold uppercase tracking-[0.16em] text-tcd08-acc">Fern Kitchen</p>
<h2 id="tcd-08-title" class="text-[clamp(17px,3vw,20px)] font-extrabold leading-snug text-balance">Order 8842 · two bowls, one flatbread</h2>
</div>
<div class="flex items-center gap-2.5 rounded-full bg-tcd08-soft py-1.5 pr-4 pl-1.5">
<span aria-hidden="true" class="grid size-8 place-items-center rounded-full bg-tcd08-acc text-xs font-black text-white">SR</span>
<span class="text-[13px] leading-tight font-bold">Sam Rivera<br><em class="text-[11px] font-semibold not-italic text-tcd08-mut">Courier</em></span>
</div>
</header>
<div role="timer" aria-label="Estimated arrival countdown" class="relative mt-6 overflow-hidden rounded-2xl bg-tcd08-soft px-5 py-6 text-center">
<p class="text-xs font-extrabold uppercase tracking-[0.14em] text-tcd08-mut">Arriving in</p>
<p aria-hidden="true" class="my-1 flex items-baseline justify-center text-[clamp(56px,16vw,84px)] font-black leading-none tracking-[-0.03em] tabular-nums">
<em class="not-italic animate-tcd08-min [counter-reset:tcd08min_var(--tcd08-min)] before:content-[counter(tcd08min)]"></em><span class="px-0.5 text-tcd08-acc">:</span><em class="not-italic animate-tcd08-st [counter-reset:tcd08st_var(--tcd08-st)] before:content-[counter(tcd08st)]"></em><em class="not-italic animate-tcd08-su [counter-reset:tcd08su_var(--tcd08-su)] before:content-[counter(tcd08su)]"></em>
</p>
<p class="text-[13px] font-semibold text-tcd08-mut">Estimated <time datetime="PT14M">14 minutes</time> from Vale Street</p>
<div aria-hidden="true" class="mx-auto mt-4 h-1.5 max-w-[280px] overflow-hidden rounded-full bg-tcd08-rule"><span class="block h-full origin-right rounded-full bg-tcd08-acc animate-tcd08-bar"></span></div>
<div role="status" class="invisible absolute inset-0 grid place-content-center gap-1.5 bg-tcd08-acc px-5 text-white opacity-0 animate-tcd08-done">
<strong class="text-[clamp(24px,6vw,32px)] font-black tracking-[-0.01em]">Arriving any moment</strong>
<span class="text-[13px] font-semibold opacity-90">Sam is on your street — meet at the door</span>
</div>
</div>
<ol class="relative mt-7 grid gap-5 before:absolute before:top-2 before:bottom-2 before:left-[9px] before:w-0.5 before:bg-tcd08-rule before:content-['']">
<li data-state="done" class="group/step relative grid grid-cols-[20px_1fr_auto] items-center gap-3.5 text-[14px] font-bold"><span aria-hidden="true" class="relative grid size-5 place-items-center rounded-full bg-tcd08-rule group-data-[state=done]/step:bg-tcd08-acc"><svg viewBox="0 0 12 12" width="10" height="10" class="hidden text-white group-data-[state=done]/step:block"><path d="M2.5 6.2 5 8.5l4.5-5" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/></svg></span>Order placed<em class="text-xs font-semibold not-italic text-tcd08-mut">7:02 pm</em></li>
<li data-state="done" class="group/step relative grid grid-cols-[20px_1fr_auto] items-center gap-3.5 text-[14px] font-bold"><span aria-hidden="true" class="relative grid size-5 place-items-center rounded-full bg-tcd08-rule group-data-[state=done]/step:bg-tcd08-acc"><svg viewBox="0 0 12 12" width="10" height="10" class="hidden text-white group-data-[state=done]/step:block"><path d="M2.5 6.2 5 8.5l4.5-5" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/></svg></span>Preparing<em class="text-xs font-semibold not-italic text-tcd08-mut">7:09 pm</em></li>
<li data-state="active" aria-current="step" class="group/step relative grid grid-cols-[20px_1fr_auto] items-center gap-3.5 text-[14px] font-extrabold text-tcd08-acc"><span aria-hidden="true" class="relative grid size-5 place-items-center rounded-full bg-tcd08-card ring-[5px] ring-tcd08-acc ring-inset"><span class="absolute inset-0 rounded-full bg-tcd08-acc/40 animate-ping motion-reduce:hidden"></span></span>On the way<em class="text-xs font-bold not-italic">now</em></li>
<li class="group/step relative grid grid-cols-[20px_1fr_auto] items-center gap-3.5 text-[14px] font-bold text-tcd08-mut animate-tcd08-arrive"><span aria-hidden="true" class="relative size-5 rounded-full border-2 border-tcd08-rule bg-tcd08-card"></span>Arriving<em class="text-xs font-semibold not-italic">7:36 pm</em></li>
</ol>
<p class="mt-6 border-t border-tcd08-rule pt-4 text-[12.5px] leading-relaxed text-tcd08-mut text-pretty">At zero this ETA flips to a terminal message instead of counting into negative minutes. Written in Tailwind only, so it measures fourteen minutes from render rather than tracking a server timestamp.</p>
</article>
</section>
```
## CSS
```css
@import "tailwindcss";
@property --tcd08-min {
syntax: '<integer>';
inherits: false;
initial-value: 13;
}
@property --tcd08-st {
syntax: '<integer>';
inherits: false;
initial-value: 5;
}
@property --tcd08-su {
syntax: '<integer>';
inherits: false;
initial-value: 9;
}
@theme {
--color-tcd08-page: #e9efe4;
--color-tcd08-page: oklch(0.94 0.018 140);
--color-tcd08-card: #ffffff;
--color-tcd08-card: oklch(1 0 0);
--color-tcd08-soft: #eef3ea;
--color-tcd08-soft: oklch(0.96 0.015 140);
--color-tcd08-ink: #17231b;
--color-tcd08-ink: oklch(0.23 0.03 150);
--color-tcd08-mut: #546659;
--color-tcd08-mut: oklch(0.48 0.028 155);
--color-tcd08-rule: #d5e0d0;
--color-tcd08-rule: oklch(0.88 0.02 145);
--color-tcd08-acc: #2f7d4f;
--color-tcd08-acc: oklch(0.52 0.12 150);
--font-tcd08-sans: 'Nunito', ui-rounded, system-ui, sans-serif;
--shadow-tcd08-card: 0 1px 2px rgb(23 35 27 / 0.05), 0 30px 60px -36px rgb(23 35 27 / 0.45);
--animate-tcd08-min: tcd08-min 840s steps(14, end) forwards;
--animate-tcd08-st: tcd08-st 60s steps(6, end) infinite;
--animate-tcd08-su: tcd08-su 10s steps(10, end) infinite;
--animate-tcd08-bar: tcd08-bar 840s linear forwards;
--animate-tcd08-done: tcd08-done 0.01s linear 840s forwards;
--animate-tcd08-arrive: tcd08-arrive 0.4s ease-out 840s forwards;
@keyframes tcd08-min {
from {
--tcd08-min: 13;
}
to {
--tcd08-min: -1;
}
}
@keyframes tcd08-st {
from {
--tcd08-st: 5;
}
to {
--tcd08-st: -1;
}
}
@keyframes tcd08-su {
from {
--tcd08-su: 9;
}
to {
--tcd08-su: -1;
}
}
@keyframes tcd08-bar {
from {
transform: scaleX(0.04);
}
to {
transform: scaleX(1);
}
}
@keyframes tcd08-done {
to {
opacity: 1;
visibility: visible;
}
}
@keyframes tcd08-arrive {
to {
color: var(--color-tcd08-acc);
}
}
}
.tcd-08 {
width: 100%;
min-height: 100svh;
}
```How this works
Three states, one attribute. Each <li> carries data-state="done|active" (or nothing for upcoming) and names itself group/step. The dot, label and time all read it:
<span class="bg-tcd08-rule
group-data-[state=done]/step:bg-tcd08-acc
group-data-[state=active]/step:ring-4">Zero is a state, not a number. At 840 seconds a 10ms forwards animation reveals the terminal panel. A second animation with the same delay restyles the final step, so the rail and the ETA agree without script.
Digits. Minutes run 13 → 0 on steps(14, end); tens and units of seconds loop. Counters are reset on the element and printed in ::before, which keeps the non-inheriting registered property in scope.
Make it yours
- Change the ETA by editing
840sin every token and the minuteinitial-value. - Add a step: copy an
<li>and set itsdata-state. - For a server-driven ETA, drop the keyframes and write
--tcd08-minetc. from your tracking socket. - Retint everything via
--color-tcd08-acc.
Gotchas — read before shipping
- Pure CSS measures from render; a reload restarts the ETA. Real tracking needs the server's estimate.
- Never print negative minutes — a late courier should not make the UI look broken.
animate-pingscales a copy; put it on a separate absolutely-positioned span or the dot itself balloons.- Keep the terminal copy human: "meet at the door" beats "ETA 0:00".
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
Animated @property integers sit exactly on Tailwind v4's floor. Below it the digits freeze at their initial values, but the delayed terminal state still appears on time.