ScreenshotNeo

BlogGuides

Advanced Techniques for Building Responsive Web Apps

Build responsive web apps with fluid layouts, container queries, responsive images, and practical accessibility and performance checks.

By the ScreenshotNeo team4 October 202611 min read

Build responsive web apps by starting with a narrow, content-first layout, using CSS Grid and Flexbox for flexible structure, and adding breakpoints only when the content needs to change. Use viewport media queries for page-wide composition, container queries for components that adapt to their allocated space, and srcset, sizes, and intrinsic image dimensions to deliver suitable images without layout shifts. Then check accessibility, behavior across input modes, and real-user performance.

This guide walks through a runnable example and the decisions behind it. The goal is a layout that remains usable across viewports and input capabilities, rather than a set of fixed designs for named devices. The core recommendations are covered in web.dev’s responsive design guide.

1. Set the viewport and define the content constraints

Include the viewport declaration so mobile browsers use the device’s layout width. Avoid disabling zoom: users may need to enlarge content. Set a readable maximum width for long-form content, allow media to shrink within its parent, and use flexible spacing rather than fixed page widths.

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Responsive app example</title>
  <style>
    *, *::before, *::after { box-sizing: border-box; }
    body { margin: 0; font: 1rem/1.5 system-ui, sans-serif; color: #18212b; }
    img, video, iframe { max-inline-size: 100%; }
    img, video { block-size: auto; }
    .shell { inline-size: min(100% - 2rem, 72rem); margin-inline: auto; }
  </style>
</head>
<body>
  <main class="shell">
    <h1>Responsive app example</h1>
    <p>A layout should follow its content and the space available.</p>
  </main>
</body>
</html>

The viewport declaration belongs in the document head. Do not use user-scalable=no or a restrictive maximum scale to stop zooming. The general constraint max-inline-size: 100% prevents media from forcing horizontal overflow; specific components may need their own sizing and cropping rules.

2. Build flexible page layouts before adding breakpoints

Use Grid when you need rows and columns that align as tracks, and Flexbox when items need to distribute or wrap along one axis. Start with the narrow layout, then widen the viewport and add a breakpoint when the content becomes cramped, overly spaced, or harder to use. A breakpoint is a content decision, not a device model.

<style>
  .dashboard {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: clamp(1rem, 3vw, 2rem);
  }
  .dashboard__main, .dashboard__aside { min-inline-size: 0; }
  .card-list {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 16rem), 1fr));
    gap: 1rem;
  }
  @media (min-width: 52rem) {
    .dashboard { grid-template-columns: minmax(0, 2fr) minmax(16rem, 1fr); }
  }
</style>
<main class="shell dashboard">
  <section class="dashboard__main" aria-labelledby="recent-title">
    <h1 id="recent-title">Recent projects</h1>
    <div class="card-list">
      <article><h2>Project one</h2><p>Summary of the work.</p></article>
      <article><h2>Project two</h2><p>Summary of the work.</p></article>
    </div>
  </section>
  <aside class="dashboard__aside" aria-label="Account overview">
    <h2>Overview</h2><p>Account details and actions.</p>
  </aside>
</main>

minmax(0, ...) and min-inline-size: 0 allow grid children to shrink below their min-content width. Without them, long text, code, or a wide child can unexpectedly widen a track. The auto-fit card grid makes as many columns as fit while retaining a minimum card width. The 52rem breakpoint above is only an example: choose a value by resizing until this particular composition needs two columns.

Keep document order aligned with reading and keyboard order. Avoid rearranging content visually in a way that makes the focus sequence confusing. Use relative units such as rem, percentages, and viewport-based values where they suit the relationship; clamp() can bound fluid type or spacing so it does not grow without limit.

3. Adapt at the right scope: viewport, input, or container

Viewport media queries are appropriate when the overall page composition changes. Feature queries address interaction capability: a large screen does not guarantee a mouse, and a small screen does not guarantee touch. A size container query is useful when a reusable component must adapt to its parent region, such as a card appearing in either a sidebar or a wide content column. See MDN’s container query guide.

<style>
  .card-region { container-type: inline-size; }
  .product-card {
    display: grid;
    grid-template-columns: 1fr;
    gap: 1rem;
  }
  @container (min-width: 34rem) {
    .product-card { grid-template-columns: 10rem minmax(0, 1fr); align-items: start; }
  }
  .product-card img { inline-size: 100%; block-size: auto; }

  .action { min-block-size: 2.75rem; }
  @media (hover: hover) and (pointer: fine) {
    .action:hover { text-decoration: underline; }
  }
  @media (pointer: coarse) {
    .action { min-block-size: 3rem; }
  }
</style>
<div class="card-region">
  <article class="product-card">
    <img src="item.jpg" width="640" height="480" alt="Blue ceramic mug on a table">
    <div><h2>Ceramic mug</h2><p>A short product description.</p><a class="action" href="/mug">View details</a></div>
  </article>
</div>

The container establishes the query context; the card’s own width then controls its layout. A component used outside a query container will not receive that container-driven change, so ensure the surrounding markup supplies the context. Where container query support does not meet your project’s browser requirements, retain a layout that works with Grid or Flexbox alone and add an appropriate fallback strategy.

Use hover and pointer queries as enhancements. Keep essential actions available without hover and ensure controls remain usable with keyboard input. These features describe capabilities, not a particular device category.

4. Deliver responsive images, not just smaller images

CSS containment makes an image fit the layout, but does not by itself let the browser choose a smaller source file. Provide candidates with srcset and describe the rendered slot with sizes. Include intrinsic width and height so the browser can reserve the aspect ratio before the file decodes. Use picture when the image or crop should change for art direction. These techniques are detailed in web.dev’s responsive images guide.

<img
  src="/images/team-960.jpg"
  srcset="/images/team-480.jpg 480w,
          /images/team-960.jpg 960w,
          /images/team-1440.jpg 1440w"
  sizes="(min-width: 72rem) 40rem,
         (min-width: 52rem) 55vw,
         calc(100vw - 2rem)"
  width="1440"
  height="960"
  alt="The product team reviewing a design together"
>

sizes should approximate the image’s CSS slot at each layout width. If it says the image is much narrower than it really renders, the browser may select a candidate that looks soft; if it overstates the slot, it can download a larger file than needed. The browser also considers pixel density and other factors, so candidate selection is not a simple fixed mapping from one viewport to one file.

For a truly different crop on narrow screens, use picture with a media-qualified source and a fallback image:

<picture>
  <source
    media="(max-width: 40rem)"
    srcset="/images/team-portrait-640.jpg 640w, /images/team-portrait-960.jpg 960w"
    sizes="calc(100vw - 2rem)"
  >
  <img
    src="/images/team-wide-1200.jpg"
    srcset="/images/team-wide-800.jpg 800w, /images/team-wide-1200.jpg 1200w"
    sizes="(min-width: 72rem) 40rem, 60vw"
    width="1200" height="675"
    alt="The product team reviewing a design together"
  >
</picture>

For a designed crop of one source, CSS object-fit: cover and object-position may be enough. Use picture when the content or composition itself changes. Informative images need meaningful alternative text; decorative images should have alt="". A missing alt attribute does not mark an image decorative.

5. Load images according to their role

Defer below-the-fold images with lazy loading. Do not lazy-load the prominent image users need immediately, and use high fetch priority only for a genuinely critical image because it may compete with other resources.

<!-- Prominent image: load normally; high priority only if it is truly critical. -->
<img src="/images/hero-1200.jpg" width="1200" height="675" alt="Analytics dashboard on a laptop">

<!-- A below-the-fold image can be deferred. -->
<img src="/images/article-detail.jpg" width="800" height="600" alt="Close-up of a device prototype" loading="lazy">

Do not mark every image high priority or preload every candidate. First identify the resource that matters to the initial view, then confirm behavior with performance measurements. For a responsive image, keep candidate selection and layout dimensions accurate regardless of its loading priority.

6. Check accessibility and behavior across input modes

Responsive layouts can change wrapping, visibility, and interaction. Check the experience with zoom, keyboard navigation, orientation changes, and both coarse and fine pointers. Tailor specific devices and assistive technology to the audiences and browsers your app supports; there is no universal test matrix for every application.

  • Confirm there is no page-level horizontal scrolling at narrow widths, while allowing intentional two-dimensional content such as a data table to have its own accessible scrolling treatment.
  • Zoom text and page content; ensure controls and labels remain visible and reachable.
  • Tab through interactive controls in a logical order and verify visible focus indication.
  • Check actions without hover, and test pointer interactions at the capabilities your app supports.
  • Rotate orientation and resize continuously across breakpoints; watch for content jumps, overlap, and clipped controls.
  • Confirm image alternatives communicate informative content and decorative images are ignored by assistive technology.

7. Measure field performance and visual stability

Appearance in a few screenshots is not a substitute for measuring loading, interactivity, and layout stability with real users. Current Core Web Vitals guidance uses Largest Contentful Paint (LCP), Interaction to Next Paint (INP), and Cumulative Layout Shift (CLS). The recommended good thresholds are LCP within 2.5 seconds, INP at or below 200 milliseconds, and CLS at or below 0.1. Evaluate the 75th percentile separately for mobile and desktop. See web.dev’s Web Vitals guidance for definitions and current recommendations.

Intrinsic image dimensions help reserve space and reduce unexpected shifts. Responsive candidates can reduce unnecessary image transfer, but the result depends on the chosen candidates and accurate sizes. Measure your own app: inspect field data by device category, then use lab tools and layout inspection to find likely causes. Recheck current thresholds and definitions before using them as release criteria, since guidance is maintained over time.

8. A practical implementation and review sequence

  1. Write down the content hierarchy, essential actions, and supported browser policy.
  2. Add the viewport declaration, sensible maximum content width, and media constraints.
  3. Build the narrow layout with Grid or Flexbox, preserving logical reading and focus order.
  4. Resize until the content calls for a composition change; add the smallest necessary breakpoint.
  5. Use container queries for components whose available parent width matters more than the viewport.
  6. Provide image candidates and accurate slot sizes, intrinsic dimensions, and appropriate alternative text.
  7. Defer suitable below-the-fold images; prioritize only critical content.
  8. Review zoom, overflow, keyboard access, orientation, and input capability.
  9. Measure field LCP, INP, and CLS by mobile and desktop segments and investigate regressions.

9. Troubleshooting responsive layouts

Symptom Likely cause Fix
Mobile content appears scaled down like a desktop page Missing or incorrect viewport declaration Add width=device-width, initial-scale=1 in the document head.
A grid is wider than the screen despite fractional columns A child’s min-content width is forcing the track wider Use minmax(0, 1fr) tracks and set min-inline-size: 0 on the relevant grid or flex child; handle long unbreakable content deliberately.
A card query never changes its layout No ancestor establishes a query container, or the queried width is not reached Add a suitable container such as container-type: inline-size to the intended wrapper and check that the component receives enough inline space.
An image is blurry on a large or high-density display Candidate files do not cover the rendered slot or sizes understates it Provide suitable larger candidates and make sizes match the actual CSS slot.
Images cause content to jump while loading Space was not reserved before image decoding Set intrinsic width and height, or otherwise reserve the correct aspect ratio.
The initial prominent image appears late It may be lazy-loaded or competing with too many prioritized resources Do not lazy-load the initial prominent image; only prioritize it when it is genuinely critical and check competing requests.
Content is inaccessible when hovering is unavailable An essential action or information appears only on hover Make it available without hover and verify keyboard and coarse-pointer interaction.
A breakpoint works on one device but fails at another width The breakpoint was chosen for a device label instead of the content Resize around the point the layout becomes unusable and base the change on that constraint.
Zoom causes controls to overlap or disappear Fixed dimensions or layout assumptions do not allow content to reflow Use flexible sizing, allow wrapping, and retest controls at enlarged text and zoom levels.

10. Screenshot responsive states consistently

Visual review across several viewport widths can help catch overflow, unexpected wrapping, and breakpoint regressions. For a repeatable check, keep the tested URL, viewport, device scale, and page state consistent; screenshots show appearance, so pair them with keyboard and accessibility checks and field performance data. A screenshot is not proof that every interaction or assistive technology path works.

Or skip the browser setup

If you need screenshots while reviewing responsive states, ScreenshotNeo is a website screenshot API and MCP server. One GET request returns an image or PDF; see the ScreenshotNeo documentation for options and setup.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
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 request failed: ${res.status}`);
const image = Buffer.from(await res.arrayBuffer());
await import('node:fs/promises').then(fs => fs.writeFile('shot.webp', image));

ScreenshotNeo can accept cookie and consent banners and remove more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each step can be turned off. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and response headers report the page verdict and billing status. Its MCP server provides take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients. The free plan includes 1,000 screenshots per month without a card; paid plans start at $5 for 3,000 screenshots.

Sign up for 1,000 free screenshots a month, with no card required.

Frequently asked questions

Should I design mobile-first?

Starting narrow is a practical way to establish essential content and actions first. The key is to make breakpoints respond to content constraints, regardless of the initial design workflow.

Do I need container queries if I already use media queries?

Use them when a component’s layout depends on the width of its parent region. Media queries remain useful for page-wide changes and interaction capabilities.

Does a responsive layout automatically improve performance?

No. Flexible CSS improves fit; responsive image candidates can help deliver appropriate resources. Measure loading and field performance to see the effect in your app.

Is a screenshot enough to validate responsiveness?

No. It can reveal visual layout problems at a chosen state and size, but it does not check keyboard access, zoom usability, or real-user performance by itself.