CSS Responsive Design
Responsive design makes a website work well on any screen size, from a small phone to a large desktop monitor. The core tools are media queries, flexible layouts (Flexbox and Grid), and fluid units.
Viewport meta tag
Add this tag inside <head> on every page. Without it, mobile browsers render the page at desktop width and then scale it down, which breaks media queries.
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
</head>
width=device-width sets the viewport to the device’s actual screen width. initial-scale=1.0 prevents the browser from zooming in or out on load.
Mobile-first approach
Write your base CSS for mobile screens first, then use min-width media queries to layer in styles for larger screens. Mobile CSS is usually simpler, so starting there keeps your default styles clean.
/* Base styles: mobile (no media query needed) */
.nav {
flex-direction: column;
}
/* Tablet and up */
@media (min-width: 768px) {
.nav {
flex-direction: row;
}
}
The alternative, desktop-first, uses max-width queries to narrow styles down to smaller screens. Either approach works, but mobile-first is the more common convention.
Media queries
A media query applies CSS only when a condition is true, usually a screen width.
/* Mobile: single column (default, no media query) */
.grid {
display: grid;
grid-template-columns: 1fr;
}
/* Tablet and up */
@media (min-width: 768px) {
.grid {
grid-template-columns: repeat(2, 1fr);
}
}
/* Desktop and up */
@media (min-width: 1024px) {
.grid {
grid-template-columns: repeat(3, 1fr);
}
}
Common breakpoints used in practice (these are starting points, not rules):
480px: small phones768px: tablets1024px: small desktops1280px: large desktops
You can also query for height, orientation, or resolution:
@media (orientation: landscape) {
.hero {
min-height: 50vh;
}
}
@media (min-resolution: 2dppx) {
/* Styles for high-density (retina) screens */
}
Responsive units
Fixed pixel values don’t scale with the user’s font size preferences. These units do.
| Unit | Relative to |
|---|---|
em | Parent element’s font size |
rem | Root (html) font size |
% | Parent element’s dimension |
vw | 1% of viewport width |
vh | 1% of viewport height |
html {
font-size: 16px; /* 1rem = 16px */
}
h1 {
font-size: 2rem; /* 32px */
margin-bottom: 1em; /* relative to h1's own font size */
}
.container {
width: 90%; /* 90% of the parent */
max-width: 1200px;
}
.hero {
height: 100vh; /* full viewport height */
}
Prefer rem for font sizes. It respects the user’s browser font size setting and makes your whole site scale consistently.
clamp() for fluid typography
clamp() lets a value scale smoothly between a minimum and maximum based on the viewport width. No media queries needed.
h1 {
font-size: clamp(1.5rem, 4vw, 3rem);
}
The three arguments are: minimum, preferred (scales with viewport), maximum. Here the heading is never smaller than 1.5rem or larger than 3rem, and scales fluidly between those limits.
min() and max()
min() picks the smaller of two values. max() picks the larger.
.container {
/* Full width on small screens, capped at 1200px on large screens */
width: min(100%, 1200px);
}
.text {
/* At least 1rem, or 2vw if that is larger */
font-size: max(1rem, 2vw);
}
These are useful alternatives to media queries for simple responsive sizing.
Container queries
Media queries respond to the viewport size. Container queries respond to the size of a parent element. This is useful for reusable components that appear in different-sized contexts.
/* Mark the parent as a container */
.sidebar {
container-type: inline-size;
}
/* Apply styles when the container is at least 400px wide */
@container (min-width: 400px) {
.card {
flex-direction: row;
}
}
Container queries are supported in all modern browsers. They are especially useful in component-based projects where you don’t know ahead of time how wide a container will be.
Responsive images
img {
max-width: 100%;
height: auto;
}
max-width: 100% stops images from overflowing their container. height: auto maintains the original aspect ratio.