How to Make the HTML Page Responsive
Make any HTML page responsive with fluid CSS, Grid or Flexbox, accessible reflow checks, and a practical debugging checklist.
Direct answer: Make an HTML page responsive by letting its layout shrink, wrap, and reflow with CSS Grid or Flexbox, adding viewport-specific media queries, constraining images, and checking the result at narrow widths. Start with a mobile-friendly single-column flow, then add columns when there is enough space. Keep the source and keyboard order meaningful even when the visual layout changes.
WCAG 2.2 Success Criterion 1.4.10 (Reflow) requires vertically scrolling content to work at an equivalent width of 320 CSS pixels without losing information or functionality or requiring two-dimensional scrolling, except for parts whose meaning or use genuinely requires two dimensions. See the WCAG 2.2 specification and W3C reflow guidance.
1. Start with the responsive document foundation
Use the viewport meta tag, fluid containers, and a readable base size. Avoid fixed page widths and absolute positioning for primary content.
<!doctype html>
<html lang='en'>
<head>
<meta charset='utf-8'>
<meta name='viewport' content='width=device-width, initial-scale=1'>
<title>Responsive article</title>
<link rel='stylesheet' href='styles.css'>
</head>
<body>
<header class='site-header'>…</header>
<main class='page-shell'>
<article class='content'>…</article>
<aside class='sidebar'>…</aside>
</main>
</body>
</html>
:root { --gutter: clamp(1rem, 3vw, 3rem); --content-max: 72rem; }
* { box-sizing: border-box; }
html { overflow-x: hidden; }
body { margin: 0; color: #1f2937; background: #fff; font: 1rem/1.6 system-ui, sans-serif; }
.page-shell { width: min(100% - 2 * var(--gutter), var(--content-max)); margin-inline: auto; }
img, video, svg, canvas { display: block; max-width: 100%; height: auto; }
.content { min-width: 0; }
The min-width: 0 rule matters for grid or flex children: without it, long content can force a track wider than the viewport. W3C’s C37 technique describes fitting images to their layout region with max-width and height behavior.
2. Choose a layout that can reflow
Grid for page regions
.page-shell {
display: grid;
gap: 2rem;
grid-template-columns: minmax(0, 2fr) minmax(14rem, 1fr);
}
@media (max-width: 52rem) {
.page-shell { grid-template-columns: 1fr; }
.sidebar { order: 2; }
}
This follows the documented Grid-and-media-query approach in W3C technique C32. Choose the breakpoint where columns become cramped, rather than tying it to a particular device.
Flexbox for wrapping rows
.toolbar { display: flex; flex-wrap: wrap; gap: .75rem 1rem; align-items: center; }
.toolbar > * { flex: 0 1 auto; }
@media (max-width: 36rem) { .toolbar > * { flex-basis: 100%; } }
Flexbox can enlarge, shrink, and wrap regions. Use it for one-dimensional groups such as navigation or controls; see W3C technique C31. Do not use order to create a reading sequence that differs from the HTML sequence.
3. Make type, spacing, and controls fluid
h1 { font-size: clamp(1.8rem, 5vw, 3.5rem); line-height: 1.1; }
.container { padding-inline: clamp(1rem, 4vw, 3rem); }
.card-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 16rem), 1fr)); gap: clamp(1rem, 2vw, 2rem); }
button, input, select, textarea { font: inherit; }
button, a.button { min-block-size: 2.75rem; padding: .6rem 1rem; }
clamp() keeps values fluid between a minimum and maximum. The min(100%, 16rem) guard prevents a card’s minimum from creating overflow on narrow screens.
4. Handle images, media, and long content
- Use
max-width: 100%; height: autofor images. - Use
object-fit: coveronly when cropping is acceptable; usecontainwhen the whole image must remain visible. - Wrap wide data when possible. For a table that truly needs two dimensions, provide a labelled horizontal scroll region.
- Break long URLs with
overflow-wrap: anywhere; do not hide overflow on the whole content container.
.prose { overflow-wrap: anywhere; }
.table-scroll { overflow-x: auto; max-inline-size: 100%; }
.table-scroll table { min-inline-size: 40rem; }
.hero img { width: 100%; aspect-ratio: 16 / 9; object-fit: cover; }
5. Build navigation that works on small screens
Keep navigation in the DOM and toggle it with a real button. Expose state to assistive technology and preserve keyboard focus.
<button class='menu-button' aria-expanded='false' aria-controls='main-menu'>Menu</button>
<nav id='main-menu' hidden>
<a href='/docs'>Docs</a>
<a href='/pricing'>Pricing</a>
</nav>
<script>
const button = document.querySelector('.menu-button');
const menu = document.querySelector('#main-menu');
button.addEventListener('click', () => {
const open = button.getAttribute('aria-expanded') === 'true';
button.setAttribute('aria-expanded', String(!open));
menu.hidden = open;
});
</script>
6. Add media queries for real layout changes
/* Base: one column */
.dashboard { display: grid; gap: 1rem; }
@media (min-width: 48rem) {
.dashboard { grid-template-columns: minmax(0, 1fr) 18rem; }
}
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; scroll-behavior: auto !important; }
}
Use a small number of content-driven breakpoints. Resize continuously and inspect intermediate widths where wrapping often reveals bugs.
7. Check the page at narrow widths
- Set the viewport to 320 CSS pixels, then test 375, 768, and a wide desktop width.
- Look for horizontal scrolling, clipped focus rings, overlapping text, unreachable controls, and images that widen the layout.
- Zoom to 200% and repeat.
- Use accessibility tools and a keyboard. Confirm source order, headings, landmarks, labels, and focus order.
- Test long translations, large text, empty states, errors, and user-generated strings.
WCAG’s 320-pixel condition has scoped exceptions for content that needs two-dimensional space, such as a map, game, diagram, or some data tables. Apply the exception only to that component.
8. Common responsive bugs and fixes
| Symptom | Cause | Fix |
|---|---|---|
| Sideways scrolling | Fixed width, large minimum, or unbroken token | Use minmax(0, 1fr), min-width: 0, fluid widths, and overflow-wrap. |
| Image widens a card | Intrinsic dimensions exceed the track | Apply max-width: 100%; height: auto. |
| Flex items overflow | No wrapping or automatic minimum size | Add flex-wrap: wrap and min-width: 0. |
| Tiny phone text | Desktop size applied everywhere | Use a readable base size and clamp(). |
| Wrong keyboard order | Visual order differs from source order |
Reorder the HTML. |
| Inaccessible menu | CSS-only hiding or missing state | Use a button, aria-expanded, and aria-controls. |
| Table breaks page | Table is wider than viewport | Put only the table in a labelled overflow-x: auto wrapper. |
9. Performance and reliability notes
- Responsive CSS does not automatically make assets fast. Serve appropriately sized images, use modern formats where supported, and lazy-load below-the-fold media.
- Prefer CSS layout over resize handlers. If JavaScript must react to size, use
ResizeObserverand avoid expensive repeated work. - Keep component rules close to the component so overrides do not conflict.
- Run checks at multiple viewport widths, then manually inspect keyboard and zoom behavior. Screenshots reveal clipping but cannot prove semantics or focus order.
10. Or skip the browser setup
If you need rendered images of responsive states for documentation, visual regression, or previews, ScreenshotNeo captures a URL with one request. It supports viewport and device options, full-page or element captures, custom CSS and JavaScript, waiting rules, dark mode, retina scale, caching, and PDF output. Before capture it accepts cookie and consent banners and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and responses identify the result with X-Page-Verdict and X-Billed headers.
See the ScreenshotNeo API docs for all options.
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)
r.raise_for_status()
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(`HTTP ${res.status}`);
const fs = await import('node:fs/promises');
await fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));
An MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. Free usage is 1,000 shots per month with no card; paid plans start at $5 for 3,000 shots. Only clean shots are billed.
Create a free ScreenshotNeo account to get 1,000 screenshots each month with no card.
11. FAQ
Should every page become one column?
No. Start with one column where content is narrow, then add columns when they have room. Keep genuinely two-dimensional widgets scoped to their own region.
Is Grid or Flexbox better?
Use Grid for two-dimensional page regions and Flexbox for one-dimensional rows that need to wrap. Both work when constraints and source order are sound.
What width matters most?
Test 320 CSS pixels for the WCAG reflow condition, plus widths where your layout changes and widths common to your users.
Can a screenshot prove accessibility?
No. It can expose visual overflow and clipping. Keyboard order, semantics, focus, zoom, and screen-reader behavior require interaction and accessibility checks.


