DEV Community

Static Pages Studio
Static Pages Studio

Posted on

Six landing pages as single HTML files: what I kept, what I cut

I wanted landing page templates that anyone could open with a double click. No npm install, no build step, no framework version to keep up with. So I built six of them as single HTML files: a SaaS page, a portfolio, a mobile app page, an agency site, a waitlist and a newsletter.

Here are the rules I followed and the trade-offs that came with them.

1. All the theming lives in one :root block

Each file starts with a short list of CSS custom properties:

:root {
  --bg: #0b0d10;
  --ink: #f4f6f8;
  --muted: #9aa4b2;
  --line: #232a33;
  --accent: #4ade80;
  --radius: 14px;
  --maxw: 1120px;
}
Enter fullscreen mode Exit fullscreen mode

Everything else reads from these. To rebrand a page you change --accent and maybe --bg, and the buttons, borders, highlights and charts all follow. I kept each palette small on purpose. Once you have twelve color variables, nobody knows which one to touch.

2. clamp() instead of a pile of breakpoints

Headlines scale with the viewport and stop at sensible limits:

h1 { font-size: clamp(38px, 6vw, 64px); }
Enter fullscreen mode Exit fullscreen mode

That removed most of the media queries I would otherwise have written. The few that remain handle layout changes, like collapsing a two-column grid:

@media (max-width: 760px) {
  .feature-grid { grid-template-columns: 1fr; }
}
Enter fullscreen mode Exit fullscreen mode

3. Scroll reveals that don't hide content

The fade-in on scroll uses an IntersectionObserver, and the hidden starting state only exists inside a prefers-reduced-motion: no-preference query:

@media (prefers-reduced-motion: no-preference) {
  .reveal { opacity: 0; transform: translateY(18px); transition: .6s; }
  .reveal.in { opacity: 1; transform: none; }
}
Enter fullscreen mode Exit fullscreen mode
const io = new IntersectionObserver(entries => {
  entries.forEach(e => {
    if (e.isIntersecting) { e.target.classList.add('in'); io.unobserve(e.target); }
  });
}, { threshold: 0.12 });
document.querySelectorAll('.reveal').forEach(el => io.observe(el));
Enter fullscreen mode Exit fullscreen mode

If someone has reduced motion turned on, the content is just there. The same rule covers the looping animations: the waveform bars and the agency marquee stop.

4. Mockups drawn with CSS, not images

The dashboard in the SaaS hero and the phones in the app page are plain divs with borders, gradients and aspect-ratio. The files stay small (6 to 14 KB each) and there are no image paths to break when you move them around. The downside is that you can't drop in a real product screenshot without some editing. For a real product you'd probably want to do that anyway.

5. The forms are demos, and they say so

This is the part I changed after the first version. The waitlist and newsletter forms check the email format in the browser, but a static file has nowhere to send the data. My first draft showed a "You're on the list!" message anyway, which is a lie to whoever fills it in. Now it says it's a demo, and the README explains the two ways to make it real: paste your email tool's embed code, or point the form at a backend like Formspree:

<form action="https://formspree.io/f/your-id" method="POST">
  <input type="email" name="email" required>
  <button type="submit">Get early access</button>
</form>
Enter fullscreen mode Exit fullscreen mode

What I'd do differently

  • Placeholder copy with made-up stats ("Trusted by 1,200+ teams") looks nice in a demo, but it's easy to forget to remove before launch. Next time I'd mark it visibly, maybe with a dashed outline.
  • A few people will want Tailwind or React versions. Single HTML files are a deliberate choice, but they aren't for everyone.

Disclosure

I built these with AI assistance, then checked each page in a browser and edited it by hand. The pack is for sale (39 EUR) if you'd rather not start from scratch: Founder Landing Kit on Gumroad. The techniques above work fine on their own if you'd rather build your own.

Do you still reach for plain HTML for landing pages, or has everything moved to a framework for you?

Top comments (0)