14 / 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 Scheduled Maintenance Countdown Status Page
A scheduled-maintenance card that counts down to the window opening, then hands over to a second clock counting down to it closing — with per-service impact beside it, because during a window the only question is what still works. The port prints each clock with one counter in decimal-leading-zero, and a phase rail plus status pill now advance on schedule 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-14 w-full min-h-svh bg-tcd14-page font-tcd14-sans text-tcd14-ink antialiased" aria-labelledby="tcd-14-title">
<div class="mx-auto grid min-h-svh max-w-[820px] content-center gap-5 px-[clamp(16px,4vw,44px)] py-14">
<header class="flex flex-wrap items-center justify-between gap-3">
<p class="flex items-center gap-2 text-sm font-semibold"><span aria-hidden="true" class="grid size-6 place-items-center rounded-md bg-tcd14-acc font-tcd14-mono text-[11px] font-bold text-tcd14-page">T</span>Talon status</p>
<span class="relative grid rounded-full border border-tcd14-acc/40 bg-tcd14-acc/10 px-3 py-1.5 text-xs font-semibold text-tcd14-acc">
<span class="col-start-1 row-start-1 animate-tcd14-p1">Scheduled maintenance</span>
<span class="col-start-1 row-start-1 opacity-0 animate-tcd14-p2">Maintenance in progress</span>
<span class="col-start-1 row-start-1 text-tcd14-ok opacity-0 animate-tcd14-p3">Completed · all systems normal</span>
</span>
</header>
<article class="rounded-2xl border border-tcd14-rule bg-tcd14-card p-[clamp(18px,4vw,30px)] shadow-[0_40px_80px_-50px_rgb(0_0_0/0.9)]">
<h2 id="tcd-14-title" class="mb-1.5 text-[clamp(20px,3.6vw,26px)] font-semibold leading-tight tracking-[-0.015em] text-balance">Primary database failover · region eu-west</h2>
<p class="mb-6 font-tcd14-mono text-[12.5px] text-tcd14-mut"><time datetime="2026-08-22T01:00:00Z">Sat 22 Aug, 01:00 UTC</time> → <time datetime="2026-08-22T02:30:00Z">02:30 UTC</time> · <time datetime="PT90M">90 min</time></p>
<div aria-hidden="true" class="mb-4 grid grid-cols-[8fr_9fr] gap-1.5">
<span class="h-1.5 overflow-hidden rounded-full bg-tcd14-rule"><span class="block h-full origin-left bg-tcd14-mut animate-tcd14-ra"></span></span>
<span class="h-1.5 overflow-hidden rounded-full bg-tcd14-rule"><span class="block h-full origin-left scale-x-0 bg-tcd14-acc animate-tcd14-rb"></span></span>
</div>
<div role="timer" aria-label="Maintenance window countdown" class="grid gap-3 sm:grid-cols-2">
<div class="rounded-xl border border-tcd14-rule bg-tcd14-sunk p-4 animate-tcd14-fa">
<p class="text-[11px] font-semibold uppercase tracking-[0.14em] text-tcd14-mut">Window opens in</p>
<p aria-hidden="true" class="my-2 font-tcd14-mono text-[clamp(34px,7vw,46px)] font-semibold leading-none tracking-[-0.03em] tabular-nums"><em class="not-italic animate-tcd14-am [counter-reset:tcd14am_var(--tcd14-am)] before:content-[counter(tcd14am,decimal-leading-zero)]"></em><span class="text-tcd14-mut">:</span><em class="not-italic animate-tcd14-as [counter-reset:tcd14as_var(--tcd14-as)] before:content-[counter(tcd14as,decimal-leading-zero)]"></em></p>
<p class="text-[12.5px] leading-relaxed text-tcd14-mut">Finish long-running exports before this reaches zero.</p>
</div>
<div class="rounded-xl border border-tcd14-rule bg-tcd14-sunk p-4 opacity-50 animate-tcd14-fb">
<p class="text-[11px] font-semibold uppercase tracking-[0.14em] text-tcd14-acc">Window closes in</p>
<p aria-hidden="true" class="my-2 font-tcd14-mono text-[clamp(34px,7vw,46px)] font-semibold leading-none tracking-[-0.03em] tabular-nums"><em class="not-italic animate-tcd14-bm [counter-reset:tcd14bm_var(--tcd14-bm)] before:content-[counter(tcd14bm,decimal-leading-zero)]"></em><span class="text-tcd14-mut">:</span><em class="not-italic animate-tcd14-bs [counter-reset:tcd14bs_var(--tcd14-bs)] before:content-[counter(tcd14bs,decimal-leading-zero)]"></em></p>
<p class="text-[12.5px] leading-relaxed text-tcd14-mut">Failover in progress. Writes resume automatically when this reaches zero.</p>
</div>
</div>
<h3 class="mt-7 mb-2 text-[11px] font-semibold uppercase tracking-[0.14em] text-tcd14-mut">Service impact during the window</h3>
<dl class="divide-y divide-tcd14-rule overflow-hidden rounded-xl border border-tcd14-rule">
<div class="grid gap-1 px-4 py-3 sm:grid-cols-[180px_minmax(0,1fr)] sm:gap-4"><dt class="text-sm font-semibold">API · writes</dt><dd data-impact="down" class="flex items-start gap-2 text-[13px] text-tcd14-mut before:mt-1.5 before:size-2 before:flex-none before:rounded-full before:bg-current before:content-[''] data-[impact=down]:text-tcd14-down data-[impact=ok]:text-tcd14-ok data-[impact=partial]:text-tcd14-acc"><span class="text-tcd14-ink/85">Rejected with 503 for the full window</span></dd></div>
<div class="grid gap-1 px-4 py-3 sm:grid-cols-[180px_minmax(0,1fr)] sm:gap-4"><dt class="text-sm font-semibold">API · reads</dt><dd data-impact="ok" class="flex items-start gap-2 text-[13px] text-tcd14-mut before:mt-1.5 before:size-2 before:flex-none before:rounded-full before:bg-current before:content-[''] data-[impact=down]:text-tcd14-down data-[impact=ok]:text-tcd14-ok data-[impact=partial]:text-tcd14-acc"><span class="text-tcd14-ink/85">Unaffected, served from replicas</span></dd></div>
<div class="grid gap-1 px-4 py-3 sm:grid-cols-[180px_minmax(0,1fr)] sm:gap-4"><dt class="text-sm font-semibold">Dashboards</dt><dd data-impact="partial" class="flex items-start gap-2 text-[13px] text-tcd14-mut before:mt-1.5 before:size-2 before:flex-none before:rounded-full before:bg-current before:content-[''] data-[impact=down]:text-tcd14-down data-[impact=ok]:text-tcd14-ok data-[impact=partial]:text-tcd14-acc"><span class="text-tcd14-ink/85">Read-only, cached up to 5 minutes stale</span></dd></div>
<div class="grid gap-1 px-4 py-3 sm:grid-cols-[180px_minmax(0,1fr)] sm:gap-4"><dt class="text-sm font-semibold">Scheduled exports</dt><dd data-impact="partial" class="flex items-start gap-2 text-[13px] text-tcd14-mut before:mt-1.5 before:size-2 before:flex-none before:rounded-full before:bg-current before:content-[''] data-[impact=down]:text-tcd14-down data-[impact=ok]:text-tcd14-ok data-[impact=partial]:text-tcd14-acc"><span class="text-tcd14-ink/85">Queued and replayed after the window</span></dd></div>
<div class="grid gap-1 px-4 py-3 sm:grid-cols-[180px_minmax(0,1fr)] sm:gap-4"><dt class="text-sm font-semibold">Webhooks</dt><dd data-impact="partial" class="flex items-start gap-2 text-[13px] text-tcd14-mut before:mt-1.5 before:size-2 before:flex-none before:rounded-full before:bg-current before:content-[''] data-[impact=down]:text-tcd14-down data-[impact=ok]:text-tcd14-ok data-[impact=partial]:text-tcd14-acc"><span class="text-tcd14-ink/85">Retried with backoff for 24 hours</span></dd></div>
</dl>
<p class="mt-3 flex flex-wrap gap-x-4 gap-y-1 text-[11.5px] text-tcd14-mut" aria-hidden="true"><span class="flex items-center gap-1.5"><span class="size-2 rounded-full bg-tcd14-down"></span>Unavailable</span><span class="flex items-center gap-1.5"><span class="size-2 rounded-full bg-tcd14-acc"></span>Degraded</span><span class="flex items-center gap-1.5"><span class="size-2 rounded-full bg-tcd14-ok"></span>Unaffected</span></p>
<p class="mt-5 border-t border-tcd14-rule pt-4 text-[12px] leading-relaxed text-tcd14-mut text-pretty">Posted by Jordan Lee · updates every 15 minutes during the window. Counters are CSS-only, so they measure from render; a live page seeds both remaining durations from the server.</p>
</article>
</div>
</section><section class="tcd-14 w-full min-h-svh bg-tcd14-page font-tcd14-sans text-tcd14-ink antialiased" aria-labelledby="tcd-14-title">
<div class="mx-auto grid min-h-svh max-w-[820px] content-center gap-5 px-[clamp(16px,4vw,44px)] py-14">
<header class="flex flex-wrap items-center justify-between gap-3">
<p class="flex items-center gap-2 text-sm font-semibold"><span aria-hidden="true" class="grid size-6 place-items-center rounded-md bg-tcd14-acc font-tcd14-mono text-[11px] font-bold text-tcd14-page">T</span>Talon status</p>
<span class="relative grid rounded-full border border-tcd14-acc/40 bg-tcd14-acc/10 px-3 py-1.5 text-xs font-semibold text-tcd14-acc">
<span class="col-start-1 row-start-1 animate-tcd14-p1">Scheduled maintenance</span>
<span class="col-start-1 row-start-1 opacity-0 animate-tcd14-p2">Maintenance in progress</span>
<span class="col-start-1 row-start-1 text-tcd14-ok opacity-0 animate-tcd14-p3">Completed · all systems normal</span>
</span>
</header>
<article class="rounded-2xl border border-tcd14-rule bg-tcd14-card p-[clamp(18px,4vw,30px)] shadow-[0_40px_80px_-50px_rgb(0_0_0/0.9)]">
<h2 id="tcd-14-title" class="mb-1.5 text-[clamp(20px,3.6vw,26px)] font-semibold leading-tight tracking-[-0.015em] text-balance">Primary database failover · region eu-west</h2>
<p class="mb-6 font-tcd14-mono text-[12.5px] text-tcd14-mut"><time datetime="2026-08-22T01:00:00Z">Sat 22 Aug, 01:00 UTC</time> → <time datetime="2026-08-22T02:30:00Z">02:30 UTC</time> · <time datetime="PT90M">90 min</time></p>
<div aria-hidden="true" class="mb-4 grid grid-cols-[8fr_9fr] gap-1.5">
<span class="h-1.5 overflow-hidden rounded-full bg-tcd14-rule"><span class="block h-full origin-left bg-tcd14-mut animate-tcd14-ra"></span></span>
<span class="h-1.5 overflow-hidden rounded-full bg-tcd14-rule"><span class="block h-full origin-left scale-x-0 bg-tcd14-acc animate-tcd14-rb"></span></span>
</div>
<div role="timer" aria-label="Maintenance window countdown" class="grid gap-3 sm:grid-cols-2">
<div class="rounded-xl border border-tcd14-rule bg-tcd14-sunk p-4 animate-tcd14-fa">
<p class="text-[11px] font-semibold uppercase tracking-[0.14em] text-tcd14-mut">Window opens in</p>
<p aria-hidden="true" class="my-2 font-tcd14-mono text-[clamp(34px,7vw,46px)] font-semibold leading-none tracking-[-0.03em] tabular-nums"><em class="not-italic animate-tcd14-am [counter-reset:tcd14am_var(--tcd14-am)] before:content-[counter(tcd14am,decimal-leading-zero)]"></em><span class="text-tcd14-mut">:</span><em class="not-italic animate-tcd14-as [counter-reset:tcd14as_var(--tcd14-as)] before:content-[counter(tcd14as,decimal-leading-zero)]"></em></p>
<p class="text-[12.5px] leading-relaxed text-tcd14-mut">Finish long-running exports before this reaches zero.</p>
</div>
<div class="rounded-xl border border-tcd14-rule bg-tcd14-sunk p-4 opacity-50 animate-tcd14-fb">
<p class="text-[11px] font-semibold uppercase tracking-[0.14em] text-tcd14-acc">Window closes in</p>
<p aria-hidden="true" class="my-2 font-tcd14-mono text-[clamp(34px,7vw,46px)] font-semibold leading-none tracking-[-0.03em] tabular-nums"><em class="not-italic animate-tcd14-bm [counter-reset:tcd14bm_var(--tcd14-bm)] before:content-[counter(tcd14bm,decimal-leading-zero)]"></em><span class="text-tcd14-mut">:</span><em class="not-italic animate-tcd14-bs [counter-reset:tcd14bs_var(--tcd14-bs)] before:content-[counter(tcd14bs,decimal-leading-zero)]"></em></p>
<p class="text-[12.5px] leading-relaxed text-tcd14-mut">Failover in progress. Writes resume automatically when this reaches zero.</p>
</div>
</div>
<h3 class="mt-7 mb-2 text-[11px] font-semibold uppercase tracking-[0.14em] text-tcd14-mut">Service impact during the window</h3>
<dl class="divide-y divide-tcd14-rule overflow-hidden rounded-xl border border-tcd14-rule">
<div class="grid gap-1 px-4 py-3 sm:grid-cols-[180px_minmax(0,1fr)] sm:gap-4"><dt class="text-sm font-semibold">API · writes</dt><dd data-impact="down" class="flex items-start gap-2 text-[13px] text-tcd14-mut before:mt-1.5 before:size-2 before:flex-none before:rounded-full before:bg-current before:content-[''] data-[impact=down]:text-tcd14-down data-[impact=ok]:text-tcd14-ok data-[impact=partial]:text-tcd14-acc"><span class="text-tcd14-ink/85">Rejected with 503 for the full window</span></dd></div>
<div class="grid gap-1 px-4 py-3 sm:grid-cols-[180px_minmax(0,1fr)] sm:gap-4"><dt class="text-sm font-semibold">API · reads</dt><dd data-impact="ok" class="flex items-start gap-2 text-[13px] text-tcd14-mut before:mt-1.5 before:size-2 before:flex-none before:rounded-full before:bg-current before:content-[''] data-[impact=down]:text-tcd14-down data-[impact=ok]:text-tcd14-ok data-[impact=partial]:text-tcd14-acc"><span class="text-tcd14-ink/85">Unaffected, served from replicas</span></dd></div>
<div class="grid gap-1 px-4 py-3 sm:grid-cols-[180px_minmax(0,1fr)] sm:gap-4"><dt class="text-sm font-semibold">Dashboards</dt><dd data-impact="partial" class="flex items-start gap-2 text-[13px] text-tcd14-mut before:mt-1.5 before:size-2 before:flex-none before:rounded-full before:bg-current before:content-[''] data-[impact=down]:text-tcd14-down data-[impact=ok]:text-tcd14-ok data-[impact=partial]:text-tcd14-acc"><span class="text-tcd14-ink/85">Read-only, cached up to 5 minutes stale</span></dd></div>
<div class="grid gap-1 px-4 py-3 sm:grid-cols-[180px_minmax(0,1fr)] sm:gap-4"><dt class="text-sm font-semibold">Scheduled exports</dt><dd data-impact="partial" class="flex items-start gap-2 text-[13px] text-tcd14-mut before:mt-1.5 before:size-2 before:flex-none before:rounded-full before:bg-current before:content-[''] data-[impact=down]:text-tcd14-down data-[impact=ok]:text-tcd14-ok data-[impact=partial]:text-tcd14-acc"><span class="text-tcd14-ink/85">Queued and replayed after the window</span></dd></div>
<div class="grid gap-1 px-4 py-3 sm:grid-cols-[180px_minmax(0,1fr)] sm:gap-4"><dt class="text-sm font-semibold">Webhooks</dt><dd data-impact="partial" class="flex items-start gap-2 text-[13px] text-tcd14-mut before:mt-1.5 before:size-2 before:flex-none before:rounded-full before:bg-current before:content-[''] data-[impact=down]:text-tcd14-down data-[impact=ok]:text-tcd14-ok data-[impact=partial]:text-tcd14-acc"><span class="text-tcd14-ink/85">Retried with backoff for 24 hours</span></dd></div>
</dl>
<p class="mt-3 flex flex-wrap gap-x-4 gap-y-1 text-[11.5px] text-tcd14-mut" aria-hidden="true"><span class="flex items-center gap-1.5"><span class="size-2 rounded-full bg-tcd14-down"></span>Unavailable</span><span class="flex items-center gap-1.5"><span class="size-2 rounded-full bg-tcd14-acc"></span>Degraded</span><span class="flex items-center gap-1.5"><span class="size-2 rounded-full bg-tcd14-ok"></span>Unaffected</span></p>
<p class="mt-5 border-t border-tcd14-rule pt-4 text-[12px] leading-relaxed text-tcd14-mut text-pretty">Posted by Jordan Lee · updates every 15 minutes during the window. Counters are CSS-only, so they measure from render; a live page seeds both remaining durations from the server.</p>
</article>
</div>
</section>@import "tailwindcss";
@property --tcd14-am {
syntax: '<integer>';
inherits: false;
initial-value: 79;
}
@property --tcd14-as {
syntax: '<integer>';
inherits: false;
initial-value: 59;
}
@property --tcd14-bm {
syntax: '<integer>';
inherits: false;
initial-value: 90;
}
@property --tcd14-bs {
syntax: '<integer>';
inherits: false;
initial-value: 0;
}
@theme {
--color-tcd14-page: #14120f;
--color-tcd14-page: oklch(0.18 0.008 70);
--color-tcd14-card: #1c1a16;
--color-tcd14-card: oklch(0.22 0.01 70);
--color-tcd14-sunk: #23201b;
--color-tcd14-sunk: oklch(0.25 0.012 70);
--color-tcd14-ink: #f4f0e8;
--color-tcd14-ink: oklch(0.95 0.012 80);
--color-tcd14-mut: #aaa294;
--color-tcd14-mut: oklch(0.72 0.02 78);
--color-tcd14-rule: #2e2a23;
--color-tcd14-rule: oklch(0.3 0.012 70);
--color-tcd14-acc: #f0a92c;
--color-tcd14-acc: oklch(0.79 0.15 75);
--color-tcd14-ok: #63c98a;
--color-tcd14-ok: oklch(0.77 0.14 152);
--color-tcd14-down: #f2705f;
--color-tcd14-down: oklch(0.7 0.16 30);
--font-tcd14-sans: 'Geist', system-ui, sans-serif;
--font-tcd14-mono: 'JetBrains Mono', ui-monospace, monospace;
--animate-tcd14-am: tcd14-am 4800s steps(80, end) forwards;
--animate-tcd14-as: tcd14-as 60s steps(60, end) 80;
--animate-tcd14-bm: tcd14-bm 5400s steps(90, end) 4800s forwards;
--animate-tcd14-bs: tcd14-bs 60s steps(60, end) 4800s 90;
--animate-tcd14-fa: tcd14-dim 0.6s ease 4800s forwards;
--animate-tcd14-fb: tcd14-lift 0.6s ease 4800s forwards;
--animate-tcd14-ra: tcd14-fill 4800s linear forwards;
--animate-tcd14-rb: tcd14-fill 5400s linear 4800s forwards;
--animate-tcd14-p1: tcd14-out 0.01s linear 4800s forwards;
--animate-tcd14-p2: tcd14-in 0.01s linear 4800s forwards, tcd14-out 0.01s linear 10200s forwards;
--animate-tcd14-p3: tcd14-in 0.01s linear 10200s forwards;
@keyframes tcd14-am {
from {
--tcd14-am: 79;
}
to {
--tcd14-am: -1;
}
}
@keyframes tcd14-as {
from {
--tcd14-as: 59;
}
to {
--tcd14-as: -1;
}
}
@keyframes tcd14-bm {
from {
--tcd14-bm: 89;
}
to {
--tcd14-bm: -1;
}
}
@keyframes tcd14-bs {
from {
--tcd14-bs: 59;
}
to {
--tcd14-bs: -1;
}
}
@keyframes tcd14-dim {
to {
opacity: 0.5;
}
}
@keyframes tcd14-lift {
to {
opacity: 1;
border-color: var(--color-tcd14-acc);
}
}
@keyframes tcd14-fill {
from {
transform: scaleX(0);
}
to {
transform: scaleX(1);
}
}
@keyframes tcd14-in {
to {
opacity: 1;
}
}
@keyframes tcd14-out {
to {
opacity: 0;
}
}
}
.tcd-14 {
width: 100%;
min-height: 100svh;
}@import "tailwindcss";
@property --tcd14-am {
syntax: '<integer>';
inherits: false;
initial-value: 79;
}
@property --tcd14-as {
syntax: '<integer>';
inherits: false;
initial-value: 59;
}
@property --tcd14-bm {
syntax: '<integer>';
inherits: false;
initial-value: 90;
}
@property --tcd14-bs {
syntax: '<integer>';
inherits: false;
initial-value: 0;
}
@theme {
--color-tcd14-page: #14120f;
--color-tcd14-page: oklch(0.18 0.008 70);
--color-tcd14-card: #1c1a16;
--color-tcd14-card: oklch(0.22 0.01 70);
--color-tcd14-sunk: #23201b;
--color-tcd14-sunk: oklch(0.25 0.012 70);
--color-tcd14-ink: #f4f0e8;
--color-tcd14-ink: oklch(0.95 0.012 80);
--color-tcd14-mut: #aaa294;
--color-tcd14-mut: oklch(0.72 0.02 78);
--color-tcd14-rule: #2e2a23;
--color-tcd14-rule: oklch(0.3 0.012 70);
--color-tcd14-acc: #f0a92c;
--color-tcd14-acc: oklch(0.79 0.15 75);
--color-tcd14-ok: #63c98a;
--color-tcd14-ok: oklch(0.77 0.14 152);
--color-tcd14-down: #f2705f;
--color-tcd14-down: oklch(0.7 0.16 30);
--font-tcd14-sans: 'Geist', system-ui, sans-serif;
--font-tcd14-mono: 'JetBrains Mono', ui-monospace, monospace;
--animate-tcd14-am: tcd14-am 4800s steps(80, end) forwards;
--animate-tcd14-as: tcd14-as 60s steps(60, end) 80;
--animate-tcd14-bm: tcd14-bm 5400s steps(90, end) 4800s forwards;
--animate-tcd14-bs: tcd14-bs 60s steps(60, end) 4800s 90;
--animate-tcd14-fa: tcd14-dim 0.6s ease 4800s forwards;
--animate-tcd14-fb: tcd14-lift 0.6s ease 4800s forwards;
--animate-tcd14-ra: tcd14-fill 4800s linear forwards;
--animate-tcd14-rb: tcd14-fill 5400s linear 4800s forwards;
--animate-tcd14-p1: tcd14-out 0.01s linear 4800s forwards;
--animate-tcd14-p2: tcd14-in 0.01s linear 4800s forwards, tcd14-out 0.01s linear 10200s forwards;
--animate-tcd14-p3: tcd14-in 0.01s linear 10200s forwards;
@keyframes tcd14-am {
from {
--tcd14-am: 79;
}
to {
--tcd14-am: -1;
}
}
@keyframes tcd14-as {
from {
--tcd14-as: 59;
}
to {
--tcd14-as: -1;
}
}
@keyframes tcd14-bm {
from {
--tcd14-bm: 89;
}
to {
--tcd14-bm: -1;
}
}
@keyframes tcd14-bs {
from {
--tcd14-bs: 59;
}
to {
--tcd14-bs: -1;
}
}
@keyframes tcd14-dim {
to {
opacity: 0.5;
}
}
@keyframes tcd14-lift {
to {
opacity: 1;
border-color: var(--color-tcd14-acc);
}
}
@keyframes tcd14-fill {
from {
transform: scaleX(0);
}
to {
transform: scaleX(1);
}
}
@keyframes tcd14-in {
to {
opacity: 1;
}
}
@keyframes tcd14-out {
to {
opacity: 0;
}
}
}
.tcd-14 {
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 --tcd14-am {
syntax: '<integer>';
inherits: false;
initial-value: 79;
}
@property --tcd14-as {
syntax: '<integer>';
inherits: false;
initial-value: 59;
}
@property --tcd14-bm {
syntax: '<integer>';
inherits: false;
initial-value: 90;
}
@property --tcd14-bs {
syntax: '<integer>';
inherits: false;
initial-value: 0;
}
@theme {
--color-tcd14-page: #14120f;
--color-tcd14-page: oklch(0.18 0.008 70);
--color-tcd14-card: #1c1a16;
--color-tcd14-card: oklch(0.22 0.01 70);
--color-tcd14-sunk: #23201b;
--color-tcd14-sunk: oklch(0.25 0.012 70);
--color-tcd14-ink: #f4f0e8;
--color-tcd14-ink: oklch(0.95 0.012 80);
--color-tcd14-mut: #aaa294;
--color-tcd14-mut: oklch(0.72 0.02 78);
--color-tcd14-rule: #2e2a23;
--color-tcd14-rule: oklch(0.3 0.012 70);
--color-tcd14-acc: #f0a92c;
--color-tcd14-acc: oklch(0.79 0.15 75);
--color-tcd14-ok: #63c98a;
--color-tcd14-ok: oklch(0.77 0.14 152);
--color-tcd14-down: #f2705f;
--color-tcd14-down: oklch(0.7 0.16 30);
--font-tcd14-sans: 'Geist', system-ui, sans-serif;
--font-tcd14-mono: 'JetBrains Mono', ui-monospace, monospace;
--animate-tcd14-am: tcd14-am 4800s steps(80, end) forwards;
--animate-tcd14-as: tcd14-as 60s steps(60, end) 80;
--animate-tcd14-bm: tcd14-bm 5400s steps(90, end) 4800s forwards;
--animate-tcd14-bs: tcd14-bs 60s steps(60, end) 4800s 90;
--animate-tcd14-fa: tcd14-dim 0.6s ease 4800s forwards;
--animate-tcd14-fb: tcd14-lift 0.6s ease 4800s forwards;
--animate-tcd14-ra: tcd14-fill 4800s linear forwards;
--animate-tcd14-rb: tcd14-fill 5400s linear 4800s forwards;
--animate-tcd14-p1: tcd14-out 0.01s linear 4800s forwards;
--animate-tcd14-p2: tcd14-in 0.01s linear 4800s forwards, tcd14-out 0.01s linear 10200s forwards;
--animate-tcd14-p3: tcd14-in 0.01s linear 10200s forwards;
@keyframes tcd14-am {
from {
--tcd14-am: 79;
}
to {
--tcd14-am: -1;
}
}
@keyframes tcd14-as {
from {
--tcd14-as: 59;
}
to {
--tcd14-as: -1;
}
}
@keyframes tcd14-bm {
from {
--tcd14-bm: 89;
}
to {
--tcd14-bm: -1;
}
}
@keyframes tcd14-bs {
from {
--tcd14-bs: 59;
}
to {
--tcd14-bs: -1;
}
}
@keyframes tcd14-dim {
to {
opacity: 0.5;
}
}
@keyframes tcd14-lift {
to {
opacity: 1;
border-color: var(--color-tcd14-acc);
}
}
@keyframes tcd14-fill {
from {
transform: scaleX(0);
}
to {
transform: scaleX(1);
}
}
@keyframes tcd14-in {
to {
opacity: 1;
}
}
@keyframes tcd14-out {
to {
opacity: 0;
}
}
}
.tcd-14 {
width: 100%;
min-height: 100svh;
}@import "tailwindcss";
@property --tcd14-am {
syntax: '<integer>';
inherits: false;
initial-value: 79;
}
@property --tcd14-as {
syntax: '<integer>';
inherits: false;
initial-value: 59;
}
@property --tcd14-bm {
syntax: '<integer>';
inherits: false;
initial-value: 90;
}
@property --tcd14-bs {
syntax: '<integer>';
inherits: false;
initial-value: 0;
}
@theme {
--color-tcd14-page: #14120f;
--color-tcd14-page: oklch(0.18 0.008 70);
--color-tcd14-card: #1c1a16;
--color-tcd14-card: oklch(0.22 0.01 70);
--color-tcd14-sunk: #23201b;
--color-tcd14-sunk: oklch(0.25 0.012 70);
--color-tcd14-ink: #f4f0e8;
--color-tcd14-ink: oklch(0.95 0.012 80);
--color-tcd14-mut: #aaa294;
--color-tcd14-mut: oklch(0.72 0.02 78);
--color-tcd14-rule: #2e2a23;
--color-tcd14-rule: oklch(0.3 0.012 70);
--color-tcd14-acc: #f0a92c;
--color-tcd14-acc: oklch(0.79 0.15 75);
--color-tcd14-ok: #63c98a;
--color-tcd14-ok: oklch(0.77 0.14 152);
--color-tcd14-down: #f2705f;
--color-tcd14-down: oklch(0.7 0.16 30);
--font-tcd14-sans: 'Geist', system-ui, sans-serif;
--font-tcd14-mono: 'JetBrains Mono', ui-monospace, monospace;
--animate-tcd14-am: tcd14-am 4800s steps(80, end) forwards;
--animate-tcd14-as: tcd14-as 60s steps(60, end) 80;
--animate-tcd14-bm: tcd14-bm 5400s steps(90, end) 4800s forwards;
--animate-tcd14-bs: tcd14-bs 60s steps(60, end) 4800s 90;
--animate-tcd14-fa: tcd14-dim 0.6s ease 4800s forwards;
--animate-tcd14-fb: tcd14-lift 0.6s ease 4800s forwards;
--animate-tcd14-ra: tcd14-fill 4800s linear forwards;
--animate-tcd14-rb: tcd14-fill 5400s linear 4800s forwards;
--animate-tcd14-p1: tcd14-out 0.01s linear 4800s forwards;
--animate-tcd14-p2: tcd14-in 0.01s linear 4800s forwards, tcd14-out 0.01s linear 10200s forwards;
--animate-tcd14-p3: tcd14-in 0.01s linear 10200s forwards;
@keyframes tcd14-am {
from {
--tcd14-am: 79;
}
to {
--tcd14-am: -1;
}
}
@keyframes tcd14-as {
from {
--tcd14-as: 59;
}
to {
--tcd14-as: -1;
}
}
@keyframes tcd14-bm {
from {
--tcd14-bm: 89;
}
to {
--tcd14-bm: -1;
}
}
@keyframes tcd14-bs {
from {
--tcd14-bs: 59;
}
to {
--tcd14-bs: -1;
}
}
@keyframes tcd14-dim {
to {
opacity: 0.5;
}
}
@keyframes tcd14-lift {
to {
opacity: 1;
border-color: var(--color-tcd14-acc);
}
}
@keyframes tcd14-fill {
from {
transform: scaleX(0);
}
to {
transform: scaleX(1);
}
}
@keyframes tcd14-in {
to {
opacity: 1;
}
}
@keyframes tcd14-out {
to {
opacity: 0;
}
}
}
.tcd-14 {
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=Geist:wght@400;500;600;700&family=JetBrains+Mono:wght@500;600&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=Geist:wght@400;500;600;700&family=JetBrains+Mono:wght@500;600&display=swap">What changed in the port
Which parts became utilities, which stayed CSS, and why.
- Ten registered properties (tens and units for every field) collapsed to four: total minutes and seconds per phase, zero-padded with
counter(x, decimal-leading-zero). .cdt-14__svc dd[data-impact="down"]::beforeand its siblings becamedata-[impact=…]:+before:bg-currenton each<dd>.- UI upgrade: the header pill now advances Scheduled → In progress → Completed on schedule, a two-segment phase rail fills in real time, and the active phase card lifts while the other dims.
- UI upgrade: the service list is a divided table with an impact legend, the impact text stays high-contrast (only the dot carries colour), and muted copy rose to
0.72lightness. - Bug fix: the original's tens/units split started phase A at
1:19:59but rolled its tens digit on an hourly cycle, so it skipped from1:10to0:59incorrectly; one integer per field cannot desynchronise.
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 Scheduled Maintenance Countdown Status Page
Source: https://codefronts.com/snippets/tailwind-css-countdown-timers/tailwind-scheduled-maintenance-countdown-status-page/
A scheduled-maintenance card that counts down to the window opening, then hands over to a second clock counting down to it closing — with per-service impact beside it, because during a window the only question is what still works. The port prints each clock with one counter in decimal-leading-zero, and a phase rail plus status pill now advance on schedule with zero JavaScript.
## HTML
```html
<section class="tcd-14 w-full min-h-svh bg-tcd14-page font-tcd14-sans text-tcd14-ink antialiased" aria-labelledby="tcd-14-title">
<div class="mx-auto grid min-h-svh max-w-[820px] content-center gap-5 px-[clamp(16px,4vw,44px)] py-14">
<header class="flex flex-wrap items-center justify-between gap-3">
<p class="flex items-center gap-2 text-sm font-semibold"><span aria-hidden="true" class="grid size-6 place-items-center rounded-md bg-tcd14-acc font-tcd14-mono text-[11px] font-bold text-tcd14-page">T</span>Talon status</p>
<span class="relative grid rounded-full border border-tcd14-acc/40 bg-tcd14-acc/10 px-3 py-1.5 text-xs font-semibold text-tcd14-acc">
<span class="col-start-1 row-start-1 animate-tcd14-p1">Scheduled maintenance</span>
<span class="col-start-1 row-start-1 opacity-0 animate-tcd14-p2">Maintenance in progress</span>
<span class="col-start-1 row-start-1 text-tcd14-ok opacity-0 animate-tcd14-p3">Completed · all systems normal</span>
</span>
</header>
<article class="rounded-2xl border border-tcd14-rule bg-tcd14-card p-[clamp(18px,4vw,30px)] shadow-[0_40px_80px_-50px_rgb(0_0_0/0.9)]">
<h2 id="tcd-14-title" class="mb-1.5 text-[clamp(20px,3.6vw,26px)] font-semibold leading-tight tracking-[-0.015em] text-balance">Primary database failover · region eu-west</h2>
<p class="mb-6 font-tcd14-mono text-[12.5px] text-tcd14-mut"><time datetime="2026-08-22T01:00:00Z">Sat 22 Aug, 01:00 UTC</time> → <time datetime="2026-08-22T02:30:00Z">02:30 UTC</time> · <time datetime="PT90M">90 min</time></p>
<div aria-hidden="true" class="mb-4 grid grid-cols-[8fr_9fr] gap-1.5">
<span class="h-1.5 overflow-hidden rounded-full bg-tcd14-rule"><span class="block h-full origin-left bg-tcd14-mut animate-tcd14-ra"></span></span>
<span class="h-1.5 overflow-hidden rounded-full bg-tcd14-rule"><span class="block h-full origin-left scale-x-0 bg-tcd14-acc animate-tcd14-rb"></span></span>
</div>
<div role="timer" aria-label="Maintenance window countdown" class="grid gap-3 sm:grid-cols-2">
<div class="rounded-xl border border-tcd14-rule bg-tcd14-sunk p-4 animate-tcd14-fa">
<p class="text-[11px] font-semibold uppercase tracking-[0.14em] text-tcd14-mut">Window opens in</p>
<p aria-hidden="true" class="my-2 font-tcd14-mono text-[clamp(34px,7vw,46px)] font-semibold leading-none tracking-[-0.03em] tabular-nums"><em class="not-italic animate-tcd14-am [counter-reset:tcd14am_var(--tcd14-am)] before:content-[counter(tcd14am,decimal-leading-zero)]"></em><span class="text-tcd14-mut">:</span><em class="not-italic animate-tcd14-as [counter-reset:tcd14as_var(--tcd14-as)] before:content-[counter(tcd14as,decimal-leading-zero)]"></em></p>
<p class="text-[12.5px] leading-relaxed text-tcd14-mut">Finish long-running exports before this reaches zero.</p>
</div>
<div class="rounded-xl border border-tcd14-rule bg-tcd14-sunk p-4 opacity-50 animate-tcd14-fb">
<p class="text-[11px] font-semibold uppercase tracking-[0.14em] text-tcd14-acc">Window closes in</p>
<p aria-hidden="true" class="my-2 font-tcd14-mono text-[clamp(34px,7vw,46px)] font-semibold leading-none tracking-[-0.03em] tabular-nums"><em class="not-italic animate-tcd14-bm [counter-reset:tcd14bm_var(--tcd14-bm)] before:content-[counter(tcd14bm,decimal-leading-zero)]"></em><span class="text-tcd14-mut">:</span><em class="not-italic animate-tcd14-bs [counter-reset:tcd14bs_var(--tcd14-bs)] before:content-[counter(tcd14bs,decimal-leading-zero)]"></em></p>
<p class="text-[12.5px] leading-relaxed text-tcd14-mut">Failover in progress. Writes resume automatically when this reaches zero.</p>
</div>
</div>
<h3 class="mt-7 mb-2 text-[11px] font-semibold uppercase tracking-[0.14em] text-tcd14-mut">Service impact during the window</h3>
<dl class="divide-y divide-tcd14-rule overflow-hidden rounded-xl border border-tcd14-rule">
<div class="grid gap-1 px-4 py-3 sm:grid-cols-[180px_minmax(0,1fr)] sm:gap-4"><dt class="text-sm font-semibold">API · writes</dt><dd data-impact="down" class="flex items-start gap-2 text-[13px] text-tcd14-mut before:mt-1.5 before:size-2 before:flex-none before:rounded-full before:bg-current before:content-[''] data-[impact=down]:text-tcd14-down data-[impact=ok]:text-tcd14-ok data-[impact=partial]:text-tcd14-acc"><span class="text-tcd14-ink/85">Rejected with 503 for the full window</span></dd></div>
<div class="grid gap-1 px-4 py-3 sm:grid-cols-[180px_minmax(0,1fr)] sm:gap-4"><dt class="text-sm font-semibold">API · reads</dt><dd data-impact="ok" class="flex items-start gap-2 text-[13px] text-tcd14-mut before:mt-1.5 before:size-2 before:flex-none before:rounded-full before:bg-current before:content-[''] data-[impact=down]:text-tcd14-down data-[impact=ok]:text-tcd14-ok data-[impact=partial]:text-tcd14-acc"><span class="text-tcd14-ink/85">Unaffected, served from replicas</span></dd></div>
<div class="grid gap-1 px-4 py-3 sm:grid-cols-[180px_minmax(0,1fr)] sm:gap-4"><dt class="text-sm font-semibold">Dashboards</dt><dd data-impact="partial" class="flex items-start gap-2 text-[13px] text-tcd14-mut before:mt-1.5 before:size-2 before:flex-none before:rounded-full before:bg-current before:content-[''] data-[impact=down]:text-tcd14-down data-[impact=ok]:text-tcd14-ok data-[impact=partial]:text-tcd14-acc"><span class="text-tcd14-ink/85">Read-only, cached up to 5 minutes stale</span></dd></div>
<div class="grid gap-1 px-4 py-3 sm:grid-cols-[180px_minmax(0,1fr)] sm:gap-4"><dt class="text-sm font-semibold">Scheduled exports</dt><dd data-impact="partial" class="flex items-start gap-2 text-[13px] text-tcd14-mut before:mt-1.5 before:size-2 before:flex-none before:rounded-full before:bg-current before:content-[''] data-[impact=down]:text-tcd14-down data-[impact=ok]:text-tcd14-ok data-[impact=partial]:text-tcd14-acc"><span class="text-tcd14-ink/85">Queued and replayed after the window</span></dd></div>
<div class="grid gap-1 px-4 py-3 sm:grid-cols-[180px_minmax(0,1fr)] sm:gap-4"><dt class="text-sm font-semibold">Webhooks</dt><dd data-impact="partial" class="flex items-start gap-2 text-[13px] text-tcd14-mut before:mt-1.5 before:size-2 before:flex-none before:rounded-full before:bg-current before:content-[''] data-[impact=down]:text-tcd14-down data-[impact=ok]:text-tcd14-ok data-[impact=partial]:text-tcd14-acc"><span class="text-tcd14-ink/85">Retried with backoff for 24 hours</span></dd></div>
</dl>
<p class="mt-3 flex flex-wrap gap-x-4 gap-y-1 text-[11.5px] text-tcd14-mut" aria-hidden="true"><span class="flex items-center gap-1.5"><span class="size-2 rounded-full bg-tcd14-down"></span>Unavailable</span><span class="flex items-center gap-1.5"><span class="size-2 rounded-full bg-tcd14-acc"></span>Degraded</span><span class="flex items-center gap-1.5"><span class="size-2 rounded-full bg-tcd14-ok"></span>Unaffected</span></p>
<p class="mt-5 border-t border-tcd14-rule pt-4 text-[12px] leading-relaxed text-tcd14-mut text-pretty">Posted by Jordan Lee · updates every 15 minutes during the window. Counters are CSS-only, so they measure from render; a live page seeds both remaining durations from the server.</p>
</article>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@property --tcd14-am {
syntax: '<integer>';
inherits: false;
initial-value: 79;
}
@property --tcd14-as {
syntax: '<integer>';
inherits: false;
initial-value: 59;
}
@property --tcd14-bm {
syntax: '<integer>';
inherits: false;
initial-value: 90;
}
@property --tcd14-bs {
syntax: '<integer>';
inherits: false;
initial-value: 0;
}
@theme {
--color-tcd14-page: #14120f;
--color-tcd14-page: oklch(0.18 0.008 70);
--color-tcd14-card: #1c1a16;
--color-tcd14-card: oklch(0.22 0.01 70);
--color-tcd14-sunk: #23201b;
--color-tcd14-sunk: oklch(0.25 0.012 70);
--color-tcd14-ink: #f4f0e8;
--color-tcd14-ink: oklch(0.95 0.012 80);
--color-tcd14-mut: #aaa294;
--color-tcd14-mut: oklch(0.72 0.02 78);
--color-tcd14-rule: #2e2a23;
--color-tcd14-rule: oklch(0.3 0.012 70);
--color-tcd14-acc: #f0a92c;
--color-tcd14-acc: oklch(0.79 0.15 75);
--color-tcd14-ok: #63c98a;
--color-tcd14-ok: oklch(0.77 0.14 152);
--color-tcd14-down: #f2705f;
--color-tcd14-down: oklch(0.7 0.16 30);
--font-tcd14-sans: 'Geist', system-ui, sans-serif;
--font-tcd14-mono: 'JetBrains Mono', ui-monospace, monospace;
--animate-tcd14-am: tcd14-am 4800s steps(80, end) forwards;
--animate-tcd14-as: tcd14-as 60s steps(60, end) 80;
--animate-tcd14-bm: tcd14-bm 5400s steps(90, end) 4800s forwards;
--animate-tcd14-bs: tcd14-bs 60s steps(60, end) 4800s 90;
--animate-tcd14-fa: tcd14-dim 0.6s ease 4800s forwards;
--animate-tcd14-fb: tcd14-lift 0.6s ease 4800s forwards;
--animate-tcd14-ra: tcd14-fill 4800s linear forwards;
--animate-tcd14-rb: tcd14-fill 5400s linear 4800s forwards;
--animate-tcd14-p1: tcd14-out 0.01s linear 4800s forwards;
--animate-tcd14-p2: tcd14-in 0.01s linear 4800s forwards, tcd14-out 0.01s linear 10200s forwards;
--animate-tcd14-p3: tcd14-in 0.01s linear 10200s forwards;
@keyframes tcd14-am {
from {
--tcd14-am: 79;
}
to {
--tcd14-am: -1;
}
}
@keyframes tcd14-as {
from {
--tcd14-as: 59;
}
to {
--tcd14-as: -1;
}
}
@keyframes tcd14-bm {
from {
--tcd14-bm: 89;
}
to {
--tcd14-bm: -1;
}
}
@keyframes tcd14-bs {
from {
--tcd14-bs: 59;
}
to {
--tcd14-bs: -1;
}
}
@keyframes tcd14-dim {
to {
opacity: 0.5;
}
}
@keyframes tcd14-lift {
to {
opacity: 1;
border-color: var(--color-tcd14-acc);
}
}
@keyframes tcd14-fill {
from {
transform: scaleX(0);
}
to {
transform: scaleX(1);
}
}
@keyframes tcd14-in {
to {
opacity: 1;
}
}
@keyframes tcd14-out {
to {
opacity: 0;
}
}
}
.tcd-14 {
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 Scheduled Maintenance Countdown Status Page
Source: https://codefronts.com/snippets/tailwind-css-countdown-timers/tailwind-scheduled-maintenance-countdown-status-page/
A scheduled-maintenance card that counts down to the window opening, then hands over to a second clock counting down to it closing — with per-service impact beside it, because during a window the only question is what still works. The port prints each clock with one counter in decimal-leading-zero, and a phase rail plus status pill now advance on schedule with zero JavaScript.
## HTML
```html
<section class="tcd-14 w-full min-h-svh bg-tcd14-page font-tcd14-sans text-tcd14-ink antialiased" aria-labelledby="tcd-14-title">
<div class="mx-auto grid min-h-svh max-w-[820px] content-center gap-5 px-[clamp(16px,4vw,44px)] py-14">
<header class="flex flex-wrap items-center justify-between gap-3">
<p class="flex items-center gap-2 text-sm font-semibold"><span aria-hidden="true" class="grid size-6 place-items-center rounded-md bg-tcd14-acc font-tcd14-mono text-[11px] font-bold text-tcd14-page">T</span>Talon status</p>
<span class="relative grid rounded-full border border-tcd14-acc/40 bg-tcd14-acc/10 px-3 py-1.5 text-xs font-semibold text-tcd14-acc">
<span class="col-start-1 row-start-1 animate-tcd14-p1">Scheduled maintenance</span>
<span class="col-start-1 row-start-1 opacity-0 animate-tcd14-p2">Maintenance in progress</span>
<span class="col-start-1 row-start-1 text-tcd14-ok opacity-0 animate-tcd14-p3">Completed · all systems normal</span>
</span>
</header>
<article class="rounded-2xl border border-tcd14-rule bg-tcd14-card p-[clamp(18px,4vw,30px)] shadow-[0_40px_80px_-50px_rgb(0_0_0/0.9)]">
<h2 id="tcd-14-title" class="mb-1.5 text-[clamp(20px,3.6vw,26px)] font-semibold leading-tight tracking-[-0.015em] text-balance">Primary database failover · region eu-west</h2>
<p class="mb-6 font-tcd14-mono text-[12.5px] text-tcd14-mut"><time datetime="2026-08-22T01:00:00Z">Sat 22 Aug, 01:00 UTC</time> → <time datetime="2026-08-22T02:30:00Z">02:30 UTC</time> · <time datetime="PT90M">90 min</time></p>
<div aria-hidden="true" class="mb-4 grid grid-cols-[8fr_9fr] gap-1.5">
<span class="h-1.5 overflow-hidden rounded-full bg-tcd14-rule"><span class="block h-full origin-left bg-tcd14-mut animate-tcd14-ra"></span></span>
<span class="h-1.5 overflow-hidden rounded-full bg-tcd14-rule"><span class="block h-full origin-left scale-x-0 bg-tcd14-acc animate-tcd14-rb"></span></span>
</div>
<div role="timer" aria-label="Maintenance window countdown" class="grid gap-3 sm:grid-cols-2">
<div class="rounded-xl border border-tcd14-rule bg-tcd14-sunk p-4 animate-tcd14-fa">
<p class="text-[11px] font-semibold uppercase tracking-[0.14em] text-tcd14-mut">Window opens in</p>
<p aria-hidden="true" class="my-2 font-tcd14-mono text-[clamp(34px,7vw,46px)] font-semibold leading-none tracking-[-0.03em] tabular-nums"><em class="not-italic animate-tcd14-am [counter-reset:tcd14am_var(--tcd14-am)] before:content-[counter(tcd14am,decimal-leading-zero)]"></em><span class="text-tcd14-mut">:</span><em class="not-italic animate-tcd14-as [counter-reset:tcd14as_var(--tcd14-as)] before:content-[counter(tcd14as,decimal-leading-zero)]"></em></p>
<p class="text-[12.5px] leading-relaxed text-tcd14-mut">Finish long-running exports before this reaches zero.</p>
</div>
<div class="rounded-xl border border-tcd14-rule bg-tcd14-sunk p-4 opacity-50 animate-tcd14-fb">
<p class="text-[11px] font-semibold uppercase tracking-[0.14em] text-tcd14-acc">Window closes in</p>
<p aria-hidden="true" class="my-2 font-tcd14-mono text-[clamp(34px,7vw,46px)] font-semibold leading-none tracking-[-0.03em] tabular-nums"><em class="not-italic animate-tcd14-bm [counter-reset:tcd14bm_var(--tcd14-bm)] before:content-[counter(tcd14bm,decimal-leading-zero)]"></em><span class="text-tcd14-mut">:</span><em class="not-italic animate-tcd14-bs [counter-reset:tcd14bs_var(--tcd14-bs)] before:content-[counter(tcd14bs,decimal-leading-zero)]"></em></p>
<p class="text-[12.5px] leading-relaxed text-tcd14-mut">Failover in progress. Writes resume automatically when this reaches zero.</p>
</div>
</div>
<h3 class="mt-7 mb-2 text-[11px] font-semibold uppercase tracking-[0.14em] text-tcd14-mut">Service impact during the window</h3>
<dl class="divide-y divide-tcd14-rule overflow-hidden rounded-xl border border-tcd14-rule">
<div class="grid gap-1 px-4 py-3 sm:grid-cols-[180px_minmax(0,1fr)] sm:gap-4"><dt class="text-sm font-semibold">API · writes</dt><dd data-impact="down" class="flex items-start gap-2 text-[13px] text-tcd14-mut before:mt-1.5 before:size-2 before:flex-none before:rounded-full before:bg-current before:content-[''] data-[impact=down]:text-tcd14-down data-[impact=ok]:text-tcd14-ok data-[impact=partial]:text-tcd14-acc"><span class="text-tcd14-ink/85">Rejected with 503 for the full window</span></dd></div>
<div class="grid gap-1 px-4 py-3 sm:grid-cols-[180px_minmax(0,1fr)] sm:gap-4"><dt class="text-sm font-semibold">API · reads</dt><dd data-impact="ok" class="flex items-start gap-2 text-[13px] text-tcd14-mut before:mt-1.5 before:size-2 before:flex-none before:rounded-full before:bg-current before:content-[''] data-[impact=down]:text-tcd14-down data-[impact=ok]:text-tcd14-ok data-[impact=partial]:text-tcd14-acc"><span class="text-tcd14-ink/85">Unaffected, served from replicas</span></dd></div>
<div class="grid gap-1 px-4 py-3 sm:grid-cols-[180px_minmax(0,1fr)] sm:gap-4"><dt class="text-sm font-semibold">Dashboards</dt><dd data-impact="partial" class="flex items-start gap-2 text-[13px] text-tcd14-mut before:mt-1.5 before:size-2 before:flex-none before:rounded-full before:bg-current before:content-[''] data-[impact=down]:text-tcd14-down data-[impact=ok]:text-tcd14-ok data-[impact=partial]:text-tcd14-acc"><span class="text-tcd14-ink/85">Read-only, cached up to 5 minutes stale</span></dd></div>
<div class="grid gap-1 px-4 py-3 sm:grid-cols-[180px_minmax(0,1fr)] sm:gap-4"><dt class="text-sm font-semibold">Scheduled exports</dt><dd data-impact="partial" class="flex items-start gap-2 text-[13px] text-tcd14-mut before:mt-1.5 before:size-2 before:flex-none before:rounded-full before:bg-current before:content-[''] data-[impact=down]:text-tcd14-down data-[impact=ok]:text-tcd14-ok data-[impact=partial]:text-tcd14-acc"><span class="text-tcd14-ink/85">Queued and replayed after the window</span></dd></div>
<div class="grid gap-1 px-4 py-3 sm:grid-cols-[180px_minmax(0,1fr)] sm:gap-4"><dt class="text-sm font-semibold">Webhooks</dt><dd data-impact="partial" class="flex items-start gap-2 text-[13px] text-tcd14-mut before:mt-1.5 before:size-2 before:flex-none before:rounded-full before:bg-current before:content-[''] data-[impact=down]:text-tcd14-down data-[impact=ok]:text-tcd14-ok data-[impact=partial]:text-tcd14-acc"><span class="text-tcd14-ink/85">Retried with backoff for 24 hours</span></dd></div>
</dl>
<p class="mt-3 flex flex-wrap gap-x-4 gap-y-1 text-[11.5px] text-tcd14-mut" aria-hidden="true"><span class="flex items-center gap-1.5"><span class="size-2 rounded-full bg-tcd14-down"></span>Unavailable</span><span class="flex items-center gap-1.5"><span class="size-2 rounded-full bg-tcd14-acc"></span>Degraded</span><span class="flex items-center gap-1.5"><span class="size-2 rounded-full bg-tcd14-ok"></span>Unaffected</span></p>
<p class="mt-5 border-t border-tcd14-rule pt-4 text-[12px] leading-relaxed text-tcd14-mut text-pretty">Posted by Jordan Lee · updates every 15 minutes during the window. Counters are CSS-only, so they measure from render; a live page seeds both remaining durations from the server.</p>
</article>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@property --tcd14-am {
syntax: '<integer>';
inherits: false;
initial-value: 79;
}
@property --tcd14-as {
syntax: '<integer>';
inherits: false;
initial-value: 59;
}
@property --tcd14-bm {
syntax: '<integer>';
inherits: false;
initial-value: 90;
}
@property --tcd14-bs {
syntax: '<integer>';
inherits: false;
initial-value: 0;
}
@theme {
--color-tcd14-page: #14120f;
--color-tcd14-page: oklch(0.18 0.008 70);
--color-tcd14-card: #1c1a16;
--color-tcd14-card: oklch(0.22 0.01 70);
--color-tcd14-sunk: #23201b;
--color-tcd14-sunk: oklch(0.25 0.012 70);
--color-tcd14-ink: #f4f0e8;
--color-tcd14-ink: oklch(0.95 0.012 80);
--color-tcd14-mut: #aaa294;
--color-tcd14-mut: oklch(0.72 0.02 78);
--color-tcd14-rule: #2e2a23;
--color-tcd14-rule: oklch(0.3 0.012 70);
--color-tcd14-acc: #f0a92c;
--color-tcd14-acc: oklch(0.79 0.15 75);
--color-tcd14-ok: #63c98a;
--color-tcd14-ok: oklch(0.77 0.14 152);
--color-tcd14-down: #f2705f;
--color-tcd14-down: oklch(0.7 0.16 30);
--font-tcd14-sans: 'Geist', system-ui, sans-serif;
--font-tcd14-mono: 'JetBrains Mono', ui-monospace, monospace;
--animate-tcd14-am: tcd14-am 4800s steps(80, end) forwards;
--animate-tcd14-as: tcd14-as 60s steps(60, end) 80;
--animate-tcd14-bm: tcd14-bm 5400s steps(90, end) 4800s forwards;
--animate-tcd14-bs: tcd14-bs 60s steps(60, end) 4800s 90;
--animate-tcd14-fa: tcd14-dim 0.6s ease 4800s forwards;
--animate-tcd14-fb: tcd14-lift 0.6s ease 4800s forwards;
--animate-tcd14-ra: tcd14-fill 4800s linear forwards;
--animate-tcd14-rb: tcd14-fill 5400s linear 4800s forwards;
--animate-tcd14-p1: tcd14-out 0.01s linear 4800s forwards;
--animate-tcd14-p2: tcd14-in 0.01s linear 4800s forwards, tcd14-out 0.01s linear 10200s forwards;
--animate-tcd14-p3: tcd14-in 0.01s linear 10200s forwards;
@keyframes tcd14-am {
from {
--tcd14-am: 79;
}
to {
--tcd14-am: -1;
}
}
@keyframes tcd14-as {
from {
--tcd14-as: 59;
}
to {
--tcd14-as: -1;
}
}
@keyframes tcd14-bm {
from {
--tcd14-bm: 89;
}
to {
--tcd14-bm: -1;
}
}
@keyframes tcd14-bs {
from {
--tcd14-bs: 59;
}
to {
--tcd14-bs: -1;
}
}
@keyframes tcd14-dim {
to {
opacity: 0.5;
}
}
@keyframes tcd14-lift {
to {
opacity: 1;
border-color: var(--color-tcd14-acc);
}
}
@keyframes tcd14-fill {
from {
transform: scaleX(0);
}
to {
transform: scaleX(1);
}
}
@keyframes tcd14-in {
to {
opacity: 1;
}
}
@keyframes tcd14-out {
to {
opacity: 0;
}
}
}
.tcd-14 {
width: 100%;
min-height: 100svh;
}
```How this works
One counter per field. Instead of splitting minutes into tens and units, each clock uses a single registered integer for total minutes and one for seconds, printed with the decimal-leading-zero counter style:
<em class="animate-tcd14-am
[counter-reset:tcd14am_var(--tcd14-am)]
before:content-[counter(tcd14am,decimal-leading-zero)]">A hand-off on delay. Phase A runs for 80 minutes. Every phase-B animation carries an 80-minute delay, so at the moment A hits 00:00, B starts from 90:00. The pill, phase rail and emphasis swap on the same timestamps.
Impact is the content. Each service row carries data-impact="down|partial|ok"; the dot and label colour come from data-[impact=…]: variants, and the text says exactly what happens (503s, cached reads, replayed exports).
Make it yours
- Change the lead time with
4800s(and--tcd14-am's initial value) and the window with5400s. - On a live status page, seed both remaining durations server-side as negative
animation-delays so every visitor sees the same clock. - Add services by copying a row and setting
data-impact. - Retint the amber accent via
--color-tcd14-acc.
Gotchas — read before shipping
- A CSS-only clock starts at render — two visitors see different times unless you seed it from the server.
- Over 99 minutes,
decimal-leading-zerostill prints three digits correctly but the layout widens; reservemin-won the clock. - Times on a status page must say UTC (or show both zones); "01:00" alone is ambiguous.
- Do not colour-code impact without words — every row states what happens.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
Counter styles and animated @property integers sit on Tailwind v4's floor. Older engines show the initial values and still swap phases on time.