Responsive layout should derive from content constraints instead of a list of device models. Intrinsic sizing, flexbox, grid, media queries, and container queries provide complementary controls.
Establish a Responsive Foundation #
The viewport declaration makes the CSS viewport match the device width:
<meta name="viewport" content="width=device-width, initial-scale=1">Do not add user-scalable=no or a low maximum-scale; page zoom is an
accessibility mechanism. Applications that position controls around an
on-screen keyboard may also need the visual viewport API, because the visible
area can change without the layout viewport changing.
Use border-box sizing and constrain replaced content:
html {
box-sizing: border-box;
}
*,
*::before,
*::after {
box-sizing: inherit;
}
img,
video,
svg {
display: block;
max-inline-size: 100%;
block-size: auto;
}Without these rules, padding can unexpectedly increase component width and intrinsically wide media can overflow a narrow viewport.
Center and Constrain Reading Content #
A readable column needs a maximum line length and small-screen gutters:
main {
inline-size: min(100%, 72ch);
margin-inline: auto;
padding-inline: 1rem;
}ch approximates the advance width of the 0 glyph. It is useful for prose
measure, while rem and pixels better express controls whose size should not
depend on a selected typeface. With the global border-box policy,
padding-inline creates writing-mode-aware gutters inside the constrained
width; it works for left-to-right, right-to-left, and vertical text without
choosing physical left and right properties.
Avoid a fixed min-width on the page’s primary content. A declaration such as
min-width: 480px forces horizontal scrolling on viewports narrower than
480 CSS pixels. min(), max(), and clamp() preserve constraints without
making unsupported assumptions about the viewport:
h1 {
font-size: clamp(2rem, 1.25rem + 3vw, 4rem);
}Choose Flexbox or Grid #
Flexbox arranges a one-dimensional sequence along a main axis. Grid defines rows and columns and can align items across both axes.
.toolbar {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 0.75rem;
}flex-direction determines the main axis. justify-* properties distribute
space along the main axis; align-* properties control the cross axis.
align-items sets a default for children, while align-self overrides one
item. flex-flow combines flex-direction and flex-wrap.
Grid suits cards whose columns should form automatically:
.cards {
display: grid;
grid-template-columns: repeat(
auto-fit,
minmax(min(18rem, 100%), 1fr)
);
gap: 1rem;
}The inner min() prevents an 18 rem minimum from overflowing a container
narrower than 18 rem. auto-fit collapses empty tracks, allowing populated
cards to consume the available width.
Use minmax(0, 1fr) when grid content must be allowed to shrink below its
intrinsic minimum:
.sidebar-layout {
display: grid;
grid-template-columns: minmax(12rem, 1fr) minmax(0, 3fr);
}Add Media Queries for Environmental Constraints #
Media queries should introduce a layout change when content no longer fits:
@media (width < 48rem) {
.sidebar-layout {
grid-template-columns: 1fr;
}
}Range syntax expresses the boundary directly. Common media features include
width, height, orientation, aspect-ratio, resolution,
prefers-color-scheme, prefers-reduced-motion, hover, and pointer.
Input features matter because a narrow touchscreen and a narrow desktop window do not offer identical interaction:
@media (hover: hover) and (pointer: fine) {
.menu-item:hover {
text-decoration: underline;
}
}
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
scroll-behavior: auto;
animation-duration: 1ms;
animation-iteration-count: 1;
transition-duration: 1ms;
}
}Do not use hover: none as proof that a device is a phone. Hybrid devices can
have several input mechanisms.
Use Container Queries for Reusable Components #
A component can appear in a wide page column or a narrow sidebar. Its own available space is therefore more useful than the viewport:
.card-region {
container-type: inline-size;
}
.card {
display: grid;
gap: 1rem;
}
@container (width >= 32rem) {
.card {
grid-template-columns: 10rem minmax(0, 1fr);
}
}Containment belongs on an ancestor of the queried component. Avoid querying the size of the same element whose styles the query changes because that creates circular sizing logic.
Container queries style descendants, not the query container itself. Child
items can still overflow through their intrinsic minimum sizes, so apply
min-inline-size: 0 or a suitable wrapping policy where content must shrink.
Test Layout Behavior #
Test at the widths where content begins to wrap, overflow, or leave excessive space rather than only at named device presets. Include:
- Long words, URLs, translated text, and large font settings.
- Keyboard focus and zoom to at least 200 percent.
- Reduced motion and both color schemes.
- Coarse and fine pointers.
- Empty, minimal, and maximum data sets.
A responsive layout remains valid across a continuous width range; it does not switch between a small number of screenshots.
Comments
Read or comment on GitHub (sign-in required to comment)