ScreenshotNeo

BlogHow-to

How to Create a Responsive Web Design Landing Page

Build a landing page that keeps content usable from a 320px viewport to wide screens with accessible layout, images, testing, and performance checks.

By the ScreenshotNeo team1 October 202611 min read

A responsive landing page preserves its information and functionality while its layout adapts to the available viewport. Build the page with a mobile-friendly layout first, allow regions to reflow, keep images inside their containers, and test the result at narrow widths and high zoom.

WCAG 2.2 Success Criterion 1.4.10 (Reflow) requires vertically scrolling content to remain usable without loss of information or functionality and without two-dimensional scrolling at a width equivalent to 320 CSS pixels, except where two-dimensional layout is essential to the content. See the WCAG 2.2 standard and the W3C reflow explanation.

1. Define the page structure before writing CSS

Start by listing the content and actions the page must expose. A typical landing page may contain a header, hero, benefits, supporting proof, a primary action, and a footer. Treat this as a content outline rather than a required formula: the correct structure depends on what the page must communicate.

  • Give the page one clear primary action, such as starting a trial or requesting a demo.
  • Use real headings in a logical order so keyboard and screen-reader users can navigate sections.
  • Keep essential copy and controls in the document flow; do not hide them solely at narrow widths.
  • Make the navigation usable when the viewport is narrow. A menu button can reveal links, but it must be keyboard accessible and expose its expanded state.

2. Create a complete responsive landing page

The following file is runnable as-is. Save it as index.html and open it in a browser. It uses semantic HTML, CSS Grid, Flexbox, a responsive image source, and a small accessible menu script. The breakpoint is an implementation choice; adjust it after testing your actual content.

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Northstar — Ship better projects</title>
  <meta name="description" content="A practical project workspace for small teams.">
  <style>
    :root {
      --ink: #14213d;
      --muted: #526173;
      --surface: #ffffff;
      --soft: #eef3f8;
      --accent: #0b6e99;
      --accent-dark: #084b70;
      --line: #d6e0ea;
      --radius: 1rem;
      --max: 72rem;
    }

    *, *::before, *::after { box-sizing: border-box; }
    html { scroll-behavior: smooth; }
    body {
      margin: 0;
      color: var(--ink);
      background: var(--surface);
      font: 1rem/1.6 system-ui, -apple-system, Segoe UI, sans-serif;
    }
    img { display: block; max-width: 100%; height: auto; }
    a { color: var(--accent-dark); }
    .wrap { width: min(100% - 2rem, var(--max)); margin-inline: auto; }
    .skip-link { position: absolute; left: -9999px; }
    .skip-link:focus { left: 1rem; top: 1rem; z-index: 10; padding: .5rem .75rem; background: #fff; }

    .site-header { border-bottom: 1px solid var(--line); }
    .nav { display: flex; align-items: center; justify-content: space-between; min-height: 4.5rem; gap: 1rem; }
    .brand { font-weight: 800; color: var(--ink); text-decoration: none; }
    .nav-links { display: flex; align-items: center; gap: 1rem; }
    .nav-links a { text-decoration: none; }
    .menu-button { display: none; border: 1px solid var(--line); background: #fff; padding: .5rem .7rem; border-radius: .5rem; }

    .hero { padding: clamp(3rem, 8vw, 7rem) 0; background: var(--soft); }
    .hero-grid { display: grid; grid-template-columns: 1.1fr .9fr; gap: clamp(2rem, 6vw, 5rem); align-items: center; }
    h1, h2, h3 { line-height: 1.15; margin: 0 0 1rem; }
    h1 { font-size: clamp(2.4rem, 7vw, 5rem); max-width: 12ch; }
    h2 { font-size: clamp(1.8rem, 4vw, 3rem); }
    .lede { font-size: clamp(1.1rem, 2vw, 1.35rem); color: var(--muted); max-width: 55ch; }
    .actions { display: flex; flex-wrap: wrap; gap: .75rem; margin-top: 1.5rem; }
    .button { display: inline-block; padding: .75rem 1rem; border-radius: .6rem; font-weight: 700; text-decoration: none; border: 2px solid var(--accent); }
    .button.primary { color: #fff; background: var(--accent); }
    .button.secondary { color: var(--accent-dark); background: transparent; }
    .hero-card { background: #fff; border: 1px solid var(--line); border-radius: var(--radius); padding: 1rem; box-shadow: 0 .75rem 2rem #14213d18; }
    .hero-card img { width: 100%; border-radius: .6rem; }

    section { padding: clamp(3rem, 7vw, 6rem) 0; }
    .section-intro { max-width: 60ch; margin-bottom: 2rem; color: var(--muted); }
    .cards { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem; }
    .card { border: 1px solid var(--line); border-radius: var(--radius); padding: 1.25rem; }
    .card p { color: var(--muted); }
    .split { display: grid; grid-template-columns: repeat(2, 1fr); gap: 2rem; align-items: start; }
    .quote { border-left: .3rem solid var(--accent); padding: 1rem 1.25rem; background: var(--soft); }
    .signup { background: var(--ink); color: #fff; }
    .signup p { color: #d9e4ef; }
    .signup a { color: #fff; }
    footer { padding: 2rem 0; color: var(--muted); }

    @media (max-width: 48rem) {
      .menu-button { display: block; }
      .nav-links { display: none; width: 100%; flex-direction: column; align-items: flex-start; padding: 1rem 0; }
      .nav-links[data-open="true"] { display: flex; }
      .nav { flex-wrap: wrap; }
      .hero-grid, .split { grid-template-columns: 1fr; }
      .cards { grid-template-columns: 1fr; }
      h1 { max-width: 16ch; }
    }

    @media (prefers-reduced-motion: reduce) {
      html { scroll-behavior: auto; }
    }
  </style>
</head>
<body>
  <a class="skip-link" href="#main">Skip to content</a>
  <header class="site-header">
    <div class="wrap nav">
      <a class="brand" href="/">Northstar</a>
      <button class="menu-button" type="button" aria-expanded="false" aria-controls="nav-links">Menu</button>
      <nav id="nav-links" class="nav-links" aria-label="Primary" data-open="false">
        <a href="#benefits">Benefits</a>
        <a href="#proof">Proof</a>
        <a class="button primary" href="#signup">Start free</a>
      </nav>
    </div>
  </header>

  <main id="main">
    <section class="hero">
      <div class="wrap hero-grid">
        <div>
          <p>Project clarity for small teams</p>
          <h1>Ship work your customers can see.</h1>
          <p class="lede">Northstar keeps tasks, decisions, and launch notes in one focused workspace.</p>
          <div class="actions">
            <a class="button primary" href="#signup">Start free</a>
            <a class="button secondary" href="#benefits">See how it works</a>
          </div>
        </div>
        <div class="hero-card">
          <picture>
            <source media="(max-width: 48rem)" srcset="/images/dashboard-small.webp">
            <img src="/images/dashboard.webp" alt="Northstar project dashboard showing an organized launch plan" width="960" height="720" loading="eager" fetchpriority="high">
          </picture>
        </div>
      </div>
    </section>

    <section id="benefits">
      <div class="wrap">
        <h2>A calmer path from idea to launch</h2>
        <p class="section-intro">Keep the most important context close to the work so decisions remain easy to find.</p>
        <div class="cards">
          <article class="card"><h3>Plan clearly</h3><p>Turn a goal into a short sequence of owned tasks.</p></article>
          <article class="card"><h3>Share context</h3><p>Attach decisions and notes where the team will see them.</p></article>
          <article class="card"><h3>Review progress</h3><p>See what is blocked before it reaches launch day.</p></article>
        </div>
      </div>
    </section>

    <section id="proof" class="soft">
      <div class="wrap split">
        <div><h2>Built for focused teams</h2><p>Use a small, readable workflow that leaves room for the details your team actually needs.</p></div>
        <blockquote class="quote">“We can see the next decision without opening five different tools.”</blockquote>
      </div>
    </section>

    <section id="signup" class="signup">
      <div class="wrap">
        <h2>Make the next launch easier to run</h2>
        <p>Create a workspace and invite your team when you are ready.</p>
        <a class="button primary" href="/signup">Start free</a>
      </div>
    </section>
  </main>

  <footer><div class="wrap">© 2026 Northstar</div></footer>
  <script>
    const button = document.querySelector('.menu-button');
    const links = document.querySelector('#nav-links');
    button.addEventListener('click', () => {
      const open = button.getAttribute('aria-expanded') === 'true';
      button.setAttribute('aria-expanded', String(!open));
      links.dataset.open = String(!open);
    });
  </script>
</body>
</html>

3. Make layout regions reflow

CSS Grid and Flexbox are documented W3C techniques for creating layouts that reflow. They are implementation options, not requirements; the requirement is that content and functionality remain available. The examples below show two common patterns.

Grid for page regions

.hero-grid {
  display: grid;
  grid-template-columns: 1.1fr .9fr;
  gap: 3rem;
}

@media (max-width: 48rem) {
  .hero-grid { grid-template-columns: 1fr; }
}

This changes two columns into one column when space becomes constrained. See W3C Technique C32.

Flexbox for controls

.actions {
  display: flex;
  flex-wrap: wrap;
  gap: .75rem;
}

.button { min-inline-size: 10rem; }

Wrapping prevents a row of buttons from forcing horizontal scrolling. See W3C Technique C31.

4. Keep images inside their available region

An image wider than its container can create horizontal scrolling even when the rest of the layout reflows. Set max-width: 100% and let the height follow the intrinsic ratio. W3C describes this fitting approach in Technique C37.

img {
  display: block;
  max-width: 100%;
  height: auto;
}

.hero-card img {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
}

Use picture, srcset, or a responsive image service when different viewport sizes need different image files. Always provide useful alternative text for informative images; use an empty alt attribute for purely decorative images.

5. Handle navigation, forms, and interactive states

  • Use a real <button> for a collapsible menu, not a styled <div>.
  • Keep aria-expanded synchronized with the menu’s visible state and connect the button with aria-controls.
  • Ensure keyboard focus remains visible. Do not remove the browser outline without replacing it.
  • Allow labels and validation messages to wrap at narrow widths. Do not rely on placeholder text as the only label.
  • Make tap targets large enough for your audience and leave space between adjacent controls.
  • Check hover, focus, disabled, error, and success states at every layout width.

6. Test the 320 CSS pixel equivalent and zoom

  1. Open the page in browser developer tools and select a viewport equivalent to 320 CSS pixels wide.
  2. Scroll through every section. Confirm that text, images, controls, and legal links remain available.
  3. Look for horizontal scrolling caused by fixed widths, long unbroken strings, transforms, tables, or oversized media.
  4. Zoom to 200% and repeat the review. Content should reflow rather than disappear behind a second scrolling direction.
  5. Navigate with a keyboard from the address bar. Verify the menu, links, form controls, and focus indicators.
  6. Test a real phone and a wide desktop after the narrow-width review; intermediate widths often reveal awkward wrapping.

7. Check loading, interaction, and visual stability separately

Responsive layout and loading performance are related but separate checks. Google Search Central describes Core Web Vitals as measures of real-world loading performance, interactivity, and visual stability. Its user-experience targets are LCP of 2.5 seconds, INP of 200 milliseconds, and CLS of 0.1. Treat these as targets for a good experience, not as a guarantee of conversions.

  • LCP: optimize the largest above-the-fold element, often by sizing and compressing the hero image and avoiding render-blocking work.
  • INP: keep menu and form handlers small, defer nonessential JavaScript, and avoid long main-thread tasks.
  • CLS: reserve image space with width/height or aspect-ratio, and avoid inserting banners above existing content.

8. Troubleshooting responsive landing pages

Symptom Likely cause Fix
Horizontal scrolling on phones Fixed width, oversized image, long URL, or transformed element Find the overflowing element in developer tools; use fluid widths, max-width: 100%, and safe text wrapping.
Desktop navigation overlaps the hero Absolute positioning or a header height that assumes one line Keep the header in normal flow and allow links to wrap or collapse.
Buttons run off the screen Non-wrapping flex row or fixed button widths Use flex-wrap: wrap, flexible widths, and sufficient gaps.
Text is unreadable after zoom Fixed heights, clipped overflow, or viewport-locked typography Remove fixed heights from text containers and test at 200% zoom.
Menu opens visually but is inaccessible Click-only handler, missing focus management, or stale ARIA state Use a button, update aria-expanded, support keyboard activation, and keep the revealed links in the tab order.
Layout shifts while loading Images or embeds have no reserved dimensions Set intrinsic dimensions or an aspect-ratio before the resource loads.
Hero image is slow on mobile One large desktop asset is downloaded everywhere Provide appropriately sized sources with picture/srcset, compress them, and load below-the-fold images lazily.

9. Review checklist before publishing

  • At the 320 CSS pixel equivalent, all information and actions remain available.
  • No section requires two-dimensional scrolling unless its meaning requires it.
  • Images stay within their layout regions and have appropriate alternative text.
  • Grid and Flexbox regions reflow at the widths your content needs.
  • Menu and form interactions work with keyboard and touch.
  • Focus indicators, error messages, and contrast remain visible in every state.
  • Hero media has reserved space and an appropriate mobile source.
  • LCP, INP, and CLS are measured separately using the current page and realistic devices.
  • Capture screenshots at narrow, medium, and wide widths for regression review.

10. Or skip the browser setup

If you need repeatable screenshots of the finished page for QA, documentation, or review, ScreenshotNeo provides a single HTTP request. It accepts the cookie or consent banner like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each step can be turned off. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and the response identifies the result with X-Page-Verdict and X-Billed headers. Its MCP server also lets Claude, Cursor, and other MCP clients call take_screenshot, get_page_info, and capture_pdf.

See the ScreenshotNeo API documentation for all capture options.

cURL

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

Python

import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
    timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)

Node.js

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot failed: ${res.status}`);
const fs = await import('node:fs/promises');
await fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));

ScreenshotNeo supports full-page capture with lazy images loaded, CSS-selector element capture, dark mode, device presets or custom viewports, retina scale, custom CSS and JavaScript, click and wait actions, blocked resources, headers, cookies, user agents, authorization, timezone, geolocation, transparent backgrounds, resizing, selectable cache TTLs, signed links, asynchronous jobs with signed webhooks, bulk capture of up to 100 URLs per call, a usage API, and an OpenAPI specification. The parameter names used by other screenshot APIs also work, which can simplify migration.

The Free plan includes 1,000 shots per month with no card. Paid plans start at $5 for 3,000 shots; yearly billing gives two months free, and every feature is available on every plan. Create a free ScreenshotNeo account.

FAQ

Does responsive design require a particular framework?

No. The conformance outcome is usable content and functionality as the viewport narrows. Grid, Flexbox, or another approach can achieve that outcome.

What width must I support?

WCAG 2.2 Reflow uses an equivalent width of 320 CSS pixels for vertically scrolling content, with an exception for content that genuinely requires two-dimensional layout. Test wider and narrower real devices as well.

Should every image use a different mobile file?

No. Use responsive sources when an alternate size materially reduces the download. Regardless of source selection, the rendered image must fit its available region.

How do I know whether a layout problem is also a performance problem?

Measure them separately: inspect reflow, overflow, and keyboard access for layout; measure LCP, INP, and CLS for loading, interaction, and visual stability.

Can ScreenshotNeo capture only the hero section?

Yes. Pass a CSS selector to capture one element, or use full-page capture when you need the complete landing page.