Pages Easy
Maintenance Page
A scheduled maintenance page with a countdown timer, progress bar, and email notification signup. No libraries.
Open in Lab
MCP
vanilla-js css
Targets: JS HTML
Code
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
background: linear-gradient(135deg, #eff6ff 0%, #f0fdf4 100%);
min-height: 100vh;
color: #111;
display: flex;
align-items: center;
justify-content: center;
}
.page {
width: 100%;
max-width: 720px;
padding: 40px 24px 60px;
display: flex;
flex-direction: column;
align-items: center;
gap: 24px;
text-align: center;
}
.logo {
font-size: 22px;
font-weight: 800;
color: #111;
margin-bottom: 8px;
}
.content {
display: flex;
flex-direction: column;
align-items: center;
gap: 20px;
width: 100%;
}
.icon-wrap svg {
width: 80px;
height: 80px;
}
h1 {
font-size: clamp(24px, 5vw, 32px);
font-weight: 800;
color: #111;
}
p {
font-size: 16px;
color: #555;
line-height: 1.6;
max-width: 100%;
}
/* Countdown */
.countdown {
display: flex;
align-items: center;
gap: 12px;
justify-content: center;
}
.count-unit {
display: flex;
flex-direction: column;
align-items: center;
gap: 4px;
}
.count-val {
font-size: clamp(32px, 8vw, 52px);
font-weight: 800;
color: #111;
font-variant-numeric: tabular-nums;
min-width: 64px;
text-align: center;
background: #fff;
border-radius: 12px;
padding: 8px 12px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.08);
}
.count-label {
font-size: 11px;
color: #888;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.08em;
}
.count-sep {
font-size: 36px;
font-weight: 700;
color: #d1d5db;
margin-bottom: 20px;
}
/* Progress */
.progress-wrap {
width: 100%;
display: flex;
flex-direction: column;
gap: 8px;
align-items: center;
}
.progress-bar {
width: 100%;
max-width: 100%;
height: 8px;
background: #e5e7eb;
border-radius: 4px;
overflow: hidden;
}
.progress-fill {
height: 100%;
background: linear-gradient(90deg, #3b82f6, #6366f1);
border-radius: 4px;
transition: width 0.5s ease;
animation: progress-shimmer 2s ease-in-out infinite;
}
@keyframes progress-shimmer {
0%,
100% {
opacity: 1;
}
50% {
opacity: 0.8;
}
}
.progress-label {
font-size: 13px;
color: #6b7280;
font-weight: 500;
}
/* Notify form */
.notify-form {
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
gap: 12px;
}
.notify-form p {
font-size: 14px;
color: #666;
}
.email-form {
display: flex;
gap: 0;
width: 100%;
max-width: 480px;
border-radius: 12px;
overflow: hidden;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.08);
}
.email-form input {
flex: 1;
padding: 12px 16px;
border: 1.5px solid #e5e7eb;
border-right: none;
border-radius: 12px 0 0 12px;
font-size: 14px;
outline: none;
background: #fff;
}
.email-form input:focus {
border-color: #6366f1;
}
.email-form button {
padding: 12px 20px;
background: #6366f1;
color: #fff;
border: none;
font-size: 14px;
font-weight: 600;
cursor: pointer;
border-radius: 0 12px 12px 0;
transition: opacity 0.15s;
white-space: nowrap;
}
.email-form button:active {
opacity: 0.85;
}
.notify-success {
display: none;
align-items: center;
gap: 8px;
padding: 10px 20px;
background: #f0fdf4;
border: 1px solid #86efac;
border-radius: 10px;
font-size: 14px;
color: #16a34a;
font-weight: 500;
}
.notify-success svg {
width: 16px;
height: 16px;
flex-shrink: 0;
}
.notify-success.visible {
display: flex;
}
.email-form.hidden {
display: none;
}
/* Social links */
.social-links {
display: flex;
gap: 20px;
}
.social-links a {
font-size: 14px;
color: #6b7280;
text-decoration: none;
font-weight: 500;
transition: color 0.15s;
}
.social-links a:hover {
color: #6366f1;
}
@media (max-width: 480px) {
.count-sep {
font-size: 24px;
}
.count-val {
min-width: 48px;
padding: 6px 8px;
}
.countdown {
gap: 8px;
flex-wrap: wrap;
}
.email-form {
flex-direction: column;
border-radius: 12px;
}
.email-form input {
border-right: 1.5px solid #e5e7eb;
border-radius: 12px 12px 0 0;
}
.email-form button {
border-radius: 0 0 12px 12px;
}
}// Set target date: 2 hours from now for demo purposes
const targetDate = new Date(Date.now() + 2 * 60 * 60 * 1000 + 15 * 60 * 1000);
function pad(n) {
return String(n).padStart(2, "0");
}
function updateCountdown() {
const now = Date.now();
const diff = Math.max(0, targetDate - now);
const days = Math.floor(diff / 86400000);
const hours = Math.floor((diff % 86400000) / 3600000);
const mins = Math.floor((diff % 3600000) / 60000);
const secs = Math.floor((diff % 60000) / 1000);
document.getElementById("countDays").textContent = pad(days);
document.getElementById("countHours").textContent = pad(hours);
document.getElementById("countMins").textContent = pad(mins);
document.getElementById("countSecs").textContent = pad(secs);
}
updateCountdown();
setInterval(updateCountdown, 1000);
// Animated progress bar (simulated at 74%)
const fill = document.getElementById("progressFill");
const label = document.getElementById("progressLabel");
const progress = 74;
setTimeout(() => {
fill.style.width = `${progress}%`;
label.textContent = `${progress}% complete`;
}, 300);
// Notify form
const form = document.getElementById("notifyForm");
const success = document.getElementById("notifySuccess");
form.addEventListener("submit", (e) => {
e.preventDefault();
form.classList.add("hidden");
success.classList.add("visible");
});<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="style.css" />
<title>Under Maintenance</title>
</head>
<body>
<div class="page">
<div class="logo">Brand</div>
<main class="content">
<div class="icon-wrap">
<svg viewBox="0 0 80 80" fill="none">
<circle cx="40" cy="40" r="36" fill="#eff6ff" stroke="#bfdbfe" stroke-width="2"/>
<path d="M52 30a14 14 0 0 0-24 9.9l-4 4 2.1 2.1 4-4A14 14 0 1 0 52 30z" stroke="#3b82f6" stroke-width="2.5" stroke-linecap="round"/>
<path d="M36 38l-4 4 2 2 4-4" stroke="#3b82f6" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
<circle cx="46" cy="34" r="3" fill="#3b82f6"/>
</svg>
</div>
<h1>We're upgrading things</h1>
<p>Scheduled maintenance is in progress. We'll be back shortly with improvements and new features.</p>
<div class="countdown" id="countdown">
<div class="count-unit">
<span class="count-val" id="countDays">00</span>
<span class="count-label">Days</span>
</div>
<span class="count-sep">:</span>
<div class="count-unit">
<span class="count-val" id="countHours">00</span>
<span class="count-label">Hours</span>
</div>
<span class="count-sep">:</span>
<div class="count-unit">
<span class="count-val" id="countMins">00</span>
<span class="count-label">Mins</span>
</div>
<span class="count-sep">:</span>
<div class="count-unit">
<span class="count-val" id="countSecs">00</span>
<span class="count-label">Secs</span>
</div>
</div>
<div class="progress-wrap">
<div class="progress-bar">
<div class="progress-fill" id="progressFill"></div>
</div>
<span class="progress-label" id="progressLabel">74% complete</span>
</div>
<div class="notify-form">
<p>Get notified when we're back:</p>
<form class="email-form" id="notifyForm">
<input type="email" placeholder="your@email.com" required />
<button type="submit">Notify Me</button>
</form>
<div class="notify-success" id="notifySuccess">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><polyline points="20 6 9 17 4 12"/></svg>
Got it! We'll email you when we're back.
</div>
</div>
<div class="social-links">
<a href="#">Twitter</a>
<a href="#">Status</a>
<a href="#">Support</a>
</div>
</main>
</div>
<script src="script.js"></script>
</body>
</html>Maintenance Page
A scheduled downtime page that shows users when the site will be back, with a live countdown, animated progress bar, and an email capture for “notify me” signups.
Features
- Live countdown timer (days, hours, minutes, seconds)
- Animated progress bar showing completion percentage
- Email input with validation for “notify me when back”
- Social links and brand logo
When to use it
- Planned maintenance windows
- Deploy-in-progress holding pages
- Scheduled feature launches