Responsive design fundamentals
Responsive design creates websites that work well on any screen size — from small phones to large monitors. The foundation is the viewport meta tag (tells mobile browsers how to render), then CSS techniques like media queries, flexible layouts (flexbox/grid), and fluid typography. Modern responsive design prioritizes mobile-first development: start with mobile styles, then enhance for larger screens.
The mobile-first approach
Mobile-first means writing CSS for the smallest screens first, then adding media queries for larger screens. This approach encourages simpler, more performant code and ensures mobile users get a solid experience.
/* Mobile-first: styles for small screens first */
.container {
display: flex;
flex-direction: column; /* stack vertically on mobile */
padding: 15px;
font-size: 16px;
}
.sidebar {
order: 2; /* sidebar below content on mobile */
width: 100%;
margin-top: 20px;
}
.content {
order: 1;
width: 100%;
}
/* Enhance for tablet and up */
@media (min-width: 768px) {
.container {
flex-direction: row;
padding: 20px;
gap: 20px;
}
.sidebar {
order: 2;
width: 300px;
flex: 0 0 auto;
margin-top: 0;
}
.content {
flex: 1;
}
}
/* Enhance for desktop and up */
@media (min-width: 1200px) {
.container {
max-width: 1400px;
margin: 0 auto;
padding: 40px;
}
}Viewport meta tag
The viewport meta tag tells mobile browsers how to render your page. Without it, mobile devices assume the page is desktop-sized and zoom out, making text tiny.
<!-- Required in HTML head --> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <!-- Breakdown --> <!-- width=device-width: make page width match device width --> <!-- initial-scale=1.0: start at 100% zoom, not zoomed out --> <!-- Optional additions --> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=5.0, user-scalable=yes"> <!-- maximum-scale: limit zoom to 5x --> <!-- user-scalable: allow users to zoom (always include) -->
Media queries
Breakpoint | Device type | Common widths |
|---|---|---|
320px - 480px | Mobile phones | iPhone SE, older phones |
481px - 768px | Tablets | iPad, Android tablets |
769px - 1024px | Tablets landscape | iPad landscape, small laptops |
1025px+ | Desktop/monitors | Large screens, TVs |
/* Mobile-first: base styles for all devices */
.button {
padding: 12px 20px;
font-size: 16px;
width: 100%; /* full width on mobile */
}
/* Small tablet and up */
@media (min-width: 481px) {
.button {
width: auto; /* auto width on larger screens */
padding: 10px 20px;
font-size: 15px;
}
}
/* Tablet and up */
@media (min-width: 769px) {
.button {
padding: 12px 24px;
font-size: 16px;
}
}
/* Desktop and up */
@media (min-width: 1025px) {
.button {
padding: 14px 28px;
font-size: 18px;
}
}
/* Using max-width (desktop-first approach, less common) */
@media (max-width: 768px) {
.button {
width: 100%;
}
}Responsive patterns without media queries
Modern CSS offers ways to be responsive without explicit media queries using flexible units, min/max functions, and flexbox/grid.
/* Flexible layout with flexbox (no media query needed) */
.grid {
display: flex;
flex-wrap: wrap;
gap: 20px;
}
.item {
flex: 1 1 250px; /* min 250px, grows to fill space */
min-width: 0;
}
<!-- On narrow screens: 1 item per row
On medium: 2-3 items per row
On wide: 4+ items per row
All without media queries! -->
/* Responsive grid with auto-fit */
.responsive-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
}
<!-- Automatically adjusts columns based on space -->
/* Fluid typography */
.heading {
font-size: clamp(20px, 5vw, 48px);
/* min 20px, preferred 5% viewport width, max 48px -->
/* Scales smoothly with viewport */
}
/* Responsive padding/margin */
.section {
padding: clamp(20px, 5vw, 60px);
/* min 20px, preferred 5vw, max 60px */
}Container queries (modern alternative)
Container queries let elements respond to their container's size rather than viewport size. This is more powerful than media queries for components.
/* Define a container context */
.card-container {
container-type: inline-size; /* track container width */
}
/* Query the container size */
@container (min-width: 300px) {
.card {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
}
}
@container (max-width: 299px) {
.card {
display: flex;
flex-direction: column;
}
}
<!-- The card layout changes based on container width, not viewport -->
<!-- Works great for reusable components -->
/* Named containers */
.page {
container-type: inline-size;
container-name: page;
}
@container page (min-width: 800px) {
.main {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
}
}Responsive typography
/* Fluid font sizing with clamp() */
html {
/* Scale font size between 14px and 20px based on viewport */
font-size: clamp(14px, 2vw, 20px);
}
h1 {
font-size: clamp(28px, 8vw, 64px);
/* Small screens: 28px, scales up, max 64px */
}
h2 {
font-size: clamp(24px, 6vw, 48px);
}
p {
font-size: clamp(14px, 2vw, 18px);
line-height: 1.6;
}
/* Media query approach (older) */
@media (max-width: 768px) {
h1 { font-size: 28px; }
h2 { font-size: 24px; }
}
@media (min-width: 769px) and (max-width: 1200px) {
h1 { font-size: 40px; }
h2 { font-size: 32px; }
}
@media (min-width: 1201px) {
h1 { font-size: 64px; }
h2 { font-size: 48px; }
}
<!-- clamp() approach scales smoothly -->
<!-- Media query approach jumps at breakpoints -->Images in responsive design
/* Images scale with container */
img {
max-width: 100%;
height: auto; /* maintain aspect ratio */
display: block; /* remove inline spacing -->
}
/* Responsive images with srcset (HTML) */
<img
src="image-400w.jpg"
srcset="image-800w.jpg 2x, image-1200w.jpg 3x"
alt="Description"
>
<!-- Browser chooses image based on device pixel ratio -->
/* Picture element for art direction */
<picture>
<source media="(min-width: 1200px)" srcset="large.jpg">
<source media="(min-width: 768px)" srcset="medium.jpg">
<img src="small.jpg" alt="Description">
</picture>
/* CSS for background images */
.hero {
background-image: url('hero-400w.jpg');
}
@media (min-width: 768px) {
.hero {
background-image: url('hero-800w.jpg');
}
}
@media (min-width: 1200px) {
.hero {
background-image: url('hero-1600w.jpg');
}
}