Counting down to a launch, a stream, a game release, or a birthday? This guide builds a page that ticks down to any moment you pick — days, hours, minutes, seconds — and flips to a celebration message when it arrives. It's pure front-end: HTML, CSS, and a small piece of JavaScript that runs in the visitor's browser. No backend, so it hosts on the static-site setup.
| At a glance | |
|---|---|
| You need | A server running the PHP Web Server application (only its nginx part is used — no PHP) |
| Build with | HTML + CSS + a little browser JavaScript |
| Plan | Any — free runs while your session timer has time, premium runs 24/7 |
| Time | About fifteen minutes |
This build rides on the static website setup — deploy that first, then drop these three files into public/.
Step 1 — Get the static setup running
Follow Host a static website to deploy the Static Website template. It gives you a public/ folder that nginx serves on your public port, with SERVER_PORT wired in for you. You'll add three files to it.
Step 2 — The page
public/index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Countdown</title>
<link rel="stylesheet" href="/style.css">
</head>
<body>
<main>
<h1 id="event">Launch Day</h1>
<div id="clock" class="clock">
<div><span id="d">--</span><small>days</small></div>
<div><span id="h">--</span><small>hours</small></div>
<div><span id="m">--</span><small>min</small></div>
<div><span id="s">--</span><small>sec</small></div>
</div>
<p id="done" class="done" hidden>It's here! 🎉</p>
</main>
<script src="/countdown.js"></script>
</body>
</html>
Step 3 — The countdown logic
public/countdown.js — the one file you'll edit to set your date:
// Set your target moment here (ISO 8601, with a timezone offset).
// "Z" means UTC. For a specific zone, use e.g. 2026-12-31T20:00:00-05:00.
const TARGET = new Date('2026-12-31T23:59:59Z').getTime();
const el = (id) => document.getElementById(id);
const pad = (n) => String(n).padStart(2, '0');
function tick() {
const diff = TARGET - Date.now();
if (diff <= 0) {
el('clock').hidden = true;
el('done').hidden = false;
clearInterval(timer);
return;
}
const s = Math.floor(diff / 1000);
el('d').textContent = Math.floor(s / 86400);
el('h').textContent = pad(Math.floor((s % 86400) / 3600));
el('m').textContent = pad(Math.floor((s % 3600) / 60));
el('s').textContent = pad(s % 60);
}
tick();
const timer = setInterval(tick, 1000);
Step 4 — The style
public/style.css:
body { margin:0; min-height:100vh; display:grid; place-items:center;
font-family: system-ui, sans-serif; background:#0b0b16; color:#eef; text-align:center; }
h1 { font-size: clamp(1.5rem, 5vw, 3rem); }
.clock { display:flex; gap:1rem; justify-content:center; flex-wrap:wrap; }
.clock div { background:#151527; border-radius:14px; padding:1rem 1.25rem; min-width:80px; }
.clock span { font-size: clamp(1.5rem, 6vw, 2.5rem); font-weight:700; display:block; }
.clock small { opacity:.6; text-transform:uppercase; letter-spacing:.05em; font-size:.7rem; }
.done { font-size: 2rem; }
Step 5 — See it live
Static content serves live from disk, so no restart is needed. Open your server's address (from the Network page) and the clock is already ticking. When the target passes, the numbers vanish and the "It's here!" line appears.
🎯 Good to know: The countdown runs in each visitor's browser, using their clock. That means it needs no server logic at all — but it also means the page reads whatever time the visitor's device is set to. Anchoring
TARGETto a timezone (theZfor UTC, or a-05:00-style offset) keeps everyone counting to the same real moment regardless of where they are.
Make it yours
| Want to change | Where |
|---|---|
| The date/time | TARGET at the top of countdown.js |
| The event name | The <h1 id="event"> text in index.html |
| The "it's here" message | The <p id="done"> text |
| Colours and size | style.css |
The countdown math was checked ahead of time: a target 166 days out shows 166 days, 23:59:59, and a target already in the past goes straight to the "done" state. Everything here is standard browser JavaScript — Mozilla's official reference at developer.mozilla.org covers setInterval, Date, and the rest when you want to extend it.
Step 6 — A real address
Ready to share it properly? Put it on a domain with automatic HTTPS via Domains and HTTPS.
Troubleshooting
- The clock shows
--and never updates — the browser couldn't loadcountdown.js. Confirm it's atpublic/countdown.jsand the<script src="/countdown.js">path matches; open the browser console (F12) for the error. - It counts to the wrong moment — check
TARGET. Without a timezone, the browser assumes the visitor's local zone; addZ(UTC) or an offset like-05:00to pin it. - 404 on the page — the home file must be exactly
public/index.html(lowercase). - A change doesn't show — browser cache. Hard-refresh (Ctrl/Cmd+Shift+R); static files serve live, no restart needed.
- Won't load at all — reachability, not files: I can't reach my app.