Responsive Design
A responsive layout adapts to the space it gets. The main tools:
- Fluid layout first: Flexbox and Grid with
fr,%,minmax()andclamp()often need no breakpoints at all. - Media queries, written mobile-first: base styles for small screens,
@media (width >= 768px)adds to them. - Container queries adapt a component to the size of its container, not the viewport, so the same card works in a sidebar and in the main column.
- Responsive images:
srcsetandsizeslet the browser pick a file of the right size;<picture>switches between different images.
It all needs the viewport meta tag.
Details
Media Queries
/* Mobile-first: no query for the base, then min-width steps */
.grid {
grid-template-columns: 1fr;
}
@media (width >= 768px) {
.grid {
grid-template-columns: 1fr 1fr;
}
}
- Range syntax
(width >= 768px),(400px <= width < 800px)replacesmin-width/max-width, and makes it clear whether the boundary is included. - Mobile-first keeps the simplest styles as the base, and phones do not have to
undo desktop styles; desktop-first (
max-width) works too, but mixing both leads to gaps and overlaps at the boundaries. - Breakpoints in
emfollow the user's default font size, so the layout switches before the text gets cramped.
Units
| Unit | Relative to | Use for |
|---|---|---|
px | nothing (a CSS pixel, not a device pixel) | borders, shadows, small fixed details |
em | the element's font size (for font-size itself: the parent's) | padding that scales with the text |
rem | the root (<html>) font size | font sizes, spacing |
% | the containing block (for width; see Box Model) | fluid widths |
vw, vh | 1% of the viewport width / height | full-screen sections, fluid type |
svh, lvh, dvh | 1% of the small / large / dynamic viewport height | full-height layouts on mobile |
cqi, cqw, cqh | 1% of the query container's inline size / width / height | sizes inside a component |
ch | the width of 0 in the current font | line length (max-inline-size: 65ch) |
- On mobile,
100vhis the large viewport: the height with the browser toolbars hidden, so the bottom of a100vhscreen is under the toolbar.100dvhfollows the toolbars as they show and hide;100svhis the height with them shown and never jumps. remfor font sizes respects the user's browser font setting;pxfont sizes ignore it. See Web Accessibility.emcompounds: a list inside a list withfont-size: 0.8emgets smaller and smaller.
clamp(), min(), max()
clamp(min, preferred, max): the preferred value, but not belowminand not abovemax. Fluid type without breakpoints:font-size: clamp(1rem, 0.9rem + 0.5vw, 1.25rem).min(a, b)takes the smaller:width: min(100%, 1200px)is a container that is fluid on small screens and limited on large ones.max(a, b)takes the larger:padding: max(16px, 4vw).- Keep a
rempart in a fluid font size:font-size: 5vwalone does not grow when the user zooms.
Container Queries
.card-list {
container-type: inline-size; /* this element can be queried */
container-name: cards; /* optional */
}
@container cards (width > 400px) {
.card {
grid-template-columns: 120px 1fr;
}
}
- An element queries its nearest ancestor container, never itself:
container-typegoes on the wrapper. inline-sizeapplies size containment along the inline axis: the container's width must not depend on its content.sizecontains both axes, so the container needs an explicit height.- Units
cqi/cqwsize things relative to the container. - Style queries
@container style(--variant: compact)check the value of a custom property on an ancestor (the parent when no container is named; nocontainer-typeneeded). Browser support: Baseline 2026, newly available.
Responsive Images
<!-- Same image, different sizes: the browser chooses -->
<img
src="photo-800.jpg"
srcset="photo-400.jpg 400w, photo-800.jpg 800w, photo-1600.jpg 1600w"
sizes="(width >= 768px) 50vw, 100vw"
width="800"
height="600"
alt="A dog on the beach"
/>
srcsetwithwlists files and their real widths;sizestells the browser how wide the image will be on the page. The browser multiplies by the pixel density and picks a file; it may also take a smaller one on a slow network or reuse a larger one from the cache.srcsetwithx(logo.png 1x, logo@2x.png 2x) is for images of a fixed size.<picture>with<source media>is for art direction: a different crop on mobile. With<source type="image/avif">it offers formats with a fallback. The browser must take the first matching<source>; the<img>inside is required and carriesaltand the size.- Lazy loading and the LCP image: see Loading Performance.
User Preferences and Input
| Query | True when |
|---|---|
(hover: hover) and (pointer: fine) | the primary input is a mouse or a trackpad |
(pointer: coarse) | the primary input is a finger: make targets larger |
(any-hover: hover) | at least one input can hover (a tablet with a mouse) |
(prefers-color-scheme: dark) | the system uses a dark theme |
(prefers-reduced-motion: reduce) | the user asked for less motion: drop large animations |
Hover effects without (hover: hover) stick on touch screens after a tap.
Example
.container {
width: min(100% - 2rem, 1200px); /* 1rem gutters on phones, 1200px max */
margin-inline: auto;
}
h1 {
font-size: clamp(1.75rem, 1.2rem + 2vw, 3rem);
}
.hero {
min-height: 100svh; /* fits the screen with the browser toolbars shown */
}
/* A global switch; per-component queries are more precise */
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
Follow-up Questions
-
Media query or container query? A media query for the page layout (number of columns, navigation); a container query for a reusable component that can be placed in areas of different width.
-
Why do
vwunits cause horizontal scroll on desktop?100vwincludes the vertical scrollbar; use100%for full-width blocks. -
srcsetor<picture>?srcsetwhen it is the same image at different resolutions and the browser may choose;<picture>when the content or format must change and the choice is yours. -
How do you make an image responsive?
max-width: 100%; height: auto: it shrinks with the container and never grows past its natural size;width/heightattributes reserve the space. -
Responsive vs adaptive design? Responsive is one fluid layout that reflows; adaptive is a set of fixed layouts switched at breakpoints or chosen by the server per device.
Common Mistakes
- Breakpoints for specific devices (375px, 414px) instead of where the layout actually breaks.
height: 100vhon mobile: the bottom is hidden under the browser toolbar.srcsetwithwdescriptors but nosizes: the browser assumes100vwand downloads too large a file.- Font sizes in
pxor purevw: they ignore the user's font settings or zoom. - Using
user-scalable=noto "fix" the mobile layout. - Hiding a large image on mobile with
display: none: it is still downloaded; use<picture>orsrcset.
See Also
- HTML: viewport meta tag
- CSS Grid
- CSS Transitions and Animations:
prefers-reduced-motion - Loading Performance
- Web Vitals: CLS and image sizes