Introduce your upcoming website or product with this minimal coming soon page using HTML5, CSS3, and JavaScript. A subtle square grid background, bold headline, and clean typography create a simple layout that keeps attention on your launch message.
The template includes an email signup form and a clear “Notify Me” CTA button, giving visitors a place to express interest in launch updates. Its compact, responsive layout adapts to mobile, tablet, and desktop screens. Separate HTML, CSS, and JavaScript files make the branding, content, and grid styling easy to customize.
What you will build
You will build a responsive launching soon landing page with a minimal design and a subtle grid background. The template includes:
A customizable brand name or text logo.
A small “Currently Building” announcement label.
A bold coming soon headline and supporting paragraph.
A square grid background created entirely with CSS.
A labeled newsletter email field.
A prominent “Notify Me” CTA button.
Email validation with clear demo feedback.
A short newsletter note.
A link for following launch progress.
A minimal footer with the current copyright year.
A compact layout designed for one-screen viewing.
Accessible focus styles and reduced-motion support.
HTML source
<!doctype html>
<html lang="en"><head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="Something great is on the way. Follow Forma's progress and get ready for our launch.">
<title>Forma — Coming soon</title>
</head>
<body>
<header class="site-header">
<!-- Replace Forma with your brand name here and in the footer. -->
<a class="brand" href="./" aria-label="Forma home"><span class="brand-mark" aria-hidden="true"></span>forma<span class="brand-period">.</span></a>
</header>
<main id="main-content">
<section class="announcement" aria-labelledby="headline">
<p class="eyebrow"><span class="status-dot" aria-hidden="true"></span>CURRENTLY BUILDING</p>
<h1 id="headline">Something great<br>is on the way<span class="accent">.</span></h1>
<p class="intro">Good things take a little time. We’re putting the finishing touches on something new, and we can’t wait to share it with you.</p>
<!-- This frontend demo never sends or stores an email address. -->
<form id="notify-form" class="notify-form" novalidate="">
<label for="email">Email address</label>
<div class="form-row">
<input id="email" name="email" type="email" autocomplete="email" placeholder="Enter your email" required="" aria-describedby="email-note form-feedback">
<button type="submit" disabled="">Notify Me <span aria-hidden="true">↗</span></button>
</div>
<p id="email-note" class="form-note">Get a launch update. Unsubscribe anytime.</p>
<p id="form-feedback" class="form-feedback" role="status" aria-live="polite" aria-atomic="true"></p>
</form>
<noscript><p class="no-script">This demo requires JavaScript for inline feedback. No subscription is created; a newsletter service connection is required.</p></noscript>
<!-- REPLACE with your real social profile URL before publishing. -->
<a class="progress-link" href="https://example.com/replace-with-your-social-profile" target="_blank" rel="noopener noreferrer">Follow our progress <span aria-hidden="true">↗</span><span class="sr-only"> (placeholder social profile, opens in a new tab)</span></a>
</section>
</main>
<footer class="site-footer"><span>© <span id="year">2026</span> Forma</span><span>A little patience. A lot of possibility.</span></footer>
</body></html>
CSS source
:root {
color-scheme: light;
--background: #FAFAFA;
--text: #18181B;
--muted: #63636D;
--accent: #4F46E5;
--accent-hover: #4338CA;
--border: #DCDCE2;
--grid-line: rgba(24, 24, 27, 0.055);
--grid-size: 48px;
--space-1: 0.5rem;
--space-2: 1rem;
--space-3: 1.5rem;
--space-4: 2rem;
--space-5: 3rem;
--font: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
--body-size: 1rem;
--headline-size: clamp(2.6rem, 6.5vw, 4.5rem);
}
* { box-sizing: border-box; }
body {
margin: 0;
min-height: 100svh;
display: flex;
flex-direction: column;
position: relative;
isolation: isolate;
background: var(--background);
color: var(--text);
font-family: var(--font);
font-size: var(--body-size);
-webkit-font-smoothing: antialiased;
}
/* The mask fades only the decorative grid, keeping content fully readable. */
body::before {
content: "";
position: absolute;
inset: 0;
z-index: -1;
pointer-events: none;
background-image: linear-gradient(var(--grid-line) 1px, transparent 1px), linear-gradient(90deg, var(--grid-line) 1px, transparent 1px);
background-size: var(--grid-size) var(--grid-size);
mask-image: radial-gradient(ellipse at 50% 44%, #000 12%, transparent 73%);
-webkit-mask-image: radial-gradient(ellipse at 50% 44%, #000 12%, transparent 73%);
}
a { color: inherit; text-decoration: none; }
.site-header, .site-footer { padding-inline: clamp(1.25rem, 5vw, 4rem); }
.site-header { padding-block: clamp(1.5rem, 4vh, 2.5rem); }
.brand { display: inline-flex; align-items: center; min-height: 44px; font-size: 1.5rem; font-weight: 750; letter-spacing: -0.07em; }
.brand-mark { width: 19px; height: 19px; margin-right: 10px; border: 5px solid var(--text); border-radius: 3px; transform: rotate(-12deg); }
.brand-period, .accent { color: var(--accent); }
main { flex: 1; display: grid; place-items: center; padding: var(--space-4) var(--space-3) var(--space-5); }
.announcement { width: 100%; max-width: 640px; text-align: center; animation: arrive 650ms ease-out both; }
.eyebrow { display: inline-flex; align-items: center; gap: var(--space-1); margin: 0 0 var(--space-4); padding: 8px 12px; border: 1px solid var(--border); border-radius: 100px; background: var(--background); font-size: 0.625rem; font-weight: 650; letter-spacing: 0.13em; }
.status-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--accent); }
h1 { margin: 0; font-size: var(--headline-size); line-height: 1.08; font-weight: 650; letter-spacing: -0.065em; overflow-wrap: break-word; }
.intro { max-width: 410px; margin: var(--space-3) auto var(--space-4); color: var(--muted); font-size: clamp(0.95rem, 2vw, 1.0625rem); line-height: 1.75; }
.notify-form { max-width: 440px; margin-inline: auto; text-align: left; }
label { display: block; margin-bottom: 10px; font-size: 0.8125rem; font-weight: 600; }
.form-row { display: flex; gap: var(--space-1); }
input, button { min-height: 50px; border-radius: 5px; font: inherit; }
input { min-width: 0; width: 100%; flex: 1; border: 1px solid var(--border); padding: 0 15px; background: var(--background); color: var(--text); font-size: 0.875rem; }
input::placeholder { color: var(--muted); opacity: 1; }
input[aria-invalid="true"] { border-color: var(--accent); }
button { display: inline-flex; align-items: center; justify-content: center; gap: 20px; border: 1px solid transparent; padding: 0 20px; background: var(--accent); color: #fff; cursor: pointer; font-size: 0.8125rem; font-weight: 600; white-space: nowrap; transition: background 180ms ease, transform 180ms ease; }
button:hover { background: var(--accent-hover); transform: translateY(-1px); }
button:active { transform: translateY(0); }
button:disabled { opacity: 0.65; cursor: not-allowed; transform: none; }
:focus-visible { outline: 3px solid var(--accent); outline-offset: 4px; }
.form-note { margin: 12px 0 0; color: var(--muted); font-size: 0.75rem; line-height: 1.6; text-align: center; }
.form-feedback { margin: 0; color: var(--text); font-size: 0.8125rem; line-height: 1.6; }
.form-feedback:not(:empty) { margin-top: var(--space-2); padding: 12px; border: 1px solid var(--border); border-left: 3px solid var(--accent); border-radius: 4px; background: var(--background); }
.progress-link { display: inline-flex; align-items: center; gap: 10px; min-height: 44px; margin-top: var(--space-4); color: var(--muted); font-size: 0.8125rem; text-underline-offset: 4px; }
.progress-link:hover { color: var(--accent); text-decoration: underline; }
.site-footer { display: flex; justify-content: space-between; flex-wrap: wrap; gap: var(--space-2); padding-block: var(--space-3); color: var(--muted); font-size: 0.6875rem; line-height: 1.6; }
.no-script { color: var(--muted); font-size: 0.8125rem; line-height: 1.6; }
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0; }
@keyframes arrive { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }
@media (max-width: 480px) {
:root { --grid-size: 36px; }
.form-row { flex-direction: column; gap: 10px; }
input { flex: auto; }
.site-footer { justify-content: center; text-align: center; }
.site-footer span:last-child { display: none; }
}
@media (prefers-reduced-motion: reduce) {
.announcement { animation: none; }
button { transition: none; }
button:hover { transform: none; }
}
JavaScript source
"use strict";
const form = document.querySelector("#notify-form");
const emailInput = document.querySelector("#email");
const feedback = document.querySelector("#form-feedback");
document.querySelector("#year").textContent = new Date().getFullYear();
// The button stays disabled without JavaScript so emails cannot enter the URL.
// Validation is handled inline instead of by browser popovers.
form.querySelector("button").disabled = false;
form.addEventListener("submit", (event) => {
event.preventDefault();
emailInput.value = emailInput.value.trim();
if (!emailInput.validity.valid) {
emailInput.setAttribute("aria-invalid", "true");
feedback.textContent = emailInput.validity.valueMissing
? "Please enter your email address."
: "Please enter a valid email address, such as hello@example.com.";
emailInput.focus();
return;
}
emailInput.removeAttribute("aria-invalid");
// INTEGRATION POINT: Connect a backend endpoint or newsletter provider here.
// Use HTTPS POST, validate on the server, and show success only after the
// provider confirms it. Never put provider secrets in this client-side file.
// This demo intentionally makes no requests and does not store email addresses.
feedback.textContent = "This is a demo. A newsletter service connection is required; no subscription has been created.";
form.reset();
});
emailInput.addEventListener("input", () => {
if (emailInput.hasAttribute("aria-invalid")) {
emailInput.removeAttribute("aria-invalid");
feedback.textContent = "";
}
});
How it works
The project uses index.html for the page structure, style.css for the grid background and responsive design, and script.js for email validation and the copyright year. Collecting subscriptions requires connecting the form to a newsletter service or backend.
Try a change
Choose Edit code, make a small change, then press Run to preview it.
Use the source Copy buttons, choose Edit code to open the editor, or Download ZIP to save the project after a six-second countdown.





.webp)

Discussion (0)
Leave a comment