CSS: Responsive Design
Make layouts that adapt to any screen size using media queries and flexible units.
Mobile-First vs Desktop-First
There are two approaches to writing responsive CSS. Mobile-first starts with styles for small screens and uses min-width queries to add complexity for larger screens. Desktop-first starts with desktop styles and uses max-width queries to simplify for smaller screens.
Mobile-first (recommended)
Mobile-first is the industry standard. Start with a single-column layout and progressively enhance it for larger screens. It results in leaner CSS and better performance on mobile.
- Mobile-firstbase styles are for small screens; min-width queries layer on desktop styles
- Desktop-firstbase styles are for large screens; max-width queries strip back for mobile
- Mobile-first produces lighter CSS, mobile devices download only what they need
- Most CSS frameworks (Tailwind, Bootstrap) follow mobile-first conventions
- Always include the viewport meta tag: width=device-width, initial-scale=1
Viewport Meta Tag
Without this tag, mobile browsers zoom out to simulate a desktop screen.
<!-- Required in every responsive HTML page -->
<meta name="viewport" content="width=device-width, initial-scale=1">Media Queries (@media)
A @media rule applies CSS only when a condition is true, such as the screen being wider than a certain size. Everything inside the curly braces only takes effect when the condition matches.
@media
Media queries check conditions like screen width, height, orientation, or color scheme. The most common condition is min-width for mobile-first breakpoints.
- @media (min-width: 768px)applies when viewport is 768px or wider
- @media (max-width: 767px)applies when viewport is narrower than 768px
- @media (orientation: landscape)applies when device is in landscape
- @media printapplies when printing the page
- Styles inside a media query override styles outside it when the condition is met
Media Query
The box changes color and layout at different screen widths.
/* Mobile: base styles */
.box {
background: steelblue;
color: white;
padding: 16px;
border-radius: 4px;
font-size: 0.9rem;
}
/* Tablet and above */
@media (min-width: 600px) {
.box {
background: seagreen;
font-size: 1rem;
padding: 24px;
}
}Breakpoints (Common Screen Sizes)
A breakpoint is the screen width at which the layout changes. There are no official breakpoints, but these values cover the most common device sizes and are used by most frameworks.
Common Breakpoints
These are widely used breakpoints. Choose breakpoints based on where your content breaks, not specific devices, devices vary too much to target individually.
- 480pxsmall phones (portrait)
- 640pxlarge phones / small landscape
- 768pxtablets (portrait)
- 1024pxtablets (landscape) / small laptops
- 1280pxdesktops
- 1536pxlarge / wide screens
Breakpoints
One column on mobile, two on tablet, three on desktop.
/* Mobile: 1 column */
.layout {
display: grid;
grid-template-columns: 1fr;
gap: 10px;
}
/* Tablet: 2 columns */
@media (min-width: 600px) {
.layout { grid-template-columns: repeat(2, 1fr); }
}
/* Desktop: 3 columns */
@media (min-width: 900px) {
.layout { grid-template-columns: repeat(3, 1fr); }
}
.item { background: steelblue; color: white; padding: 16px; border-radius: 4px; text-align: center; }min-width and max-width Queries
min-width applies styles when the screen is at least that wide (mobile-first). max-width applies styles when the screen is at most that wide (desktop-first). You can also combine them to target a range.
min-width vs max-width
Prefer min-width for mobile-first development. Avoid mixing min-width and max-width queries in the same project, it creates confusing overrides.
- min-width: 768pxscreen is 768px or wider
- max-width: 767pxscreen is narrower than 768px
- min-width: 600px) and (max-width: 1024pxtargets only the range between 600px and 1024px
- Use min-width for mobile-first, start small, add complexity for larger screens
- Use max-width for desktop-first, start large, simplify for smaller screens
min-width vs max-width
Styles change at different viewport widths.
/* mobile-first base */
.note {
font-size: 0.85rem;
padding: 10px;
background: steelblue;
color: white;
border-radius: 4px;
}
/* tablet and above */
@media (min-width: 600px) {
.note { font-size: 1rem; padding: 14px; }
}
/* desktop and above */
@media (min-width: 900px) {
.note { font-size: 1.15rem; padding: 18px; background: seagreen; }
}Responsive Units (%, vw, vh)
Responsive units scale automatically with the screen without needing media queries. They are essential for fluid layouts that adjust smoothly rather than jumping at breakpoints.
Responsive Units
Percentage units are relative to the parent element. Viewport units (vw, vh) are relative to the browser window. Both are core tools for fluid layouts.
- %relative to the parent element's dimension
- vw1vw = 1% of the viewport width
- vh1vh = 1% of the viewport height
- vmin1% of the smaller viewport dimension (width or height)
- vmax1% of the larger viewport dimension
- 100vw = full viewport width; 100vh = full viewport height
Responsive units
Hero fills the full viewport height; the fluid box is 80% of its container.
.hero {
height: 100vh;
background: #1e293b;
color: white;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.2rem;
}
.fluid {
width: 80%;
margin: 20px auto;
background: steelblue;
color: white;
padding: 16px;
border-radius: 4px;
text-align: center;
}Flexible Images and Media
Images and videos are fixed-size by default. Without responsive rules they can overflow their containers on small screens. A few simple rules make all media flexible.
Flexible Images
The max-width: 100% rule is the foundation of responsive images. It prevents images from overflowing while allowing them to shrink freely on small screens.
- max-width: 100%image shrinks to fit its container but never grows beyond its natural size
- height: autopreserves the image aspect ratio when width changes
- object-fit: coverfills a fixed container while cropping to maintain ratio
- width: 100%image stretches to fill its container (can upscale)
- Apply max-width: 100%; height: auto globally to all images as a reset
Flexible images
Image scales down on narrow screens but never exceeds its natural size.
.card {
max-width: 400px;
margin: 0 auto;
border: 1px solid #ccc;
border-radius: 6px;
overflow: hidden;
}
.card img {
max-width: 100%;
height: auto;
display: block;
}
.card p {
padding: 10px 14px;
margin: 0;
font-size: 0.9rem;
}Container Queries (@container)
Container queries let you apply styles based on the size of a parent element rather than the viewport. This is more useful for reusable components that may appear in different-sized areas of a page.
@container
Container queries solve the problem where a component needs to respond to its own available space, not the screen size. The parent must be declared as a containment context.
- container-type: inline-sizemarks an element as a container to query against
- container-nameoptional name to target a specific container
- @container (min-width: 400px)applies styles when the container is at least 400px wide
- Browser support is now excellent (Chrome 105+, Firefox 110+, Safari 16+)
- Ideal for card components, sidebars, and reusable UI patterns
Container Queries
The card switches from stacked to side-by-side based on its container width.
.wrapper {
container-type: inline-size;
border: 1px solid #ccc;
border-radius: 6px;
margin-bottom: 12px;
}
.narrow { width: 240px; }
.wide { width: 100%; }
.card {
display: flex;
flex-direction: column;
gap: 8px;
padding: 12px;
}
.card img { border-radius: 4px; width: 80px; height: 80px; object-fit: cover; }
.card p { margin: 0; font-size: 0.9rem; }
/* When the container is at least 300px wide, go side-by-side */
@container (min-width: 300px) {
.card { flex-direction: row; align-items: center; }
}Responsive Typography (clamp())
clamp(min, preferred, max) creates fluid font sizes that scale smoothly between a minimum and maximum value. The preferred value is typically a viewport-relative unit like vw.
clamp() for typography
clamp() removes the need for multiple font-size media queries. The font scales fluidly with the viewport between the min and max values you set.
- clamp(1rem, 2.5vw, 2rem)never smaller than 1rem, never larger than 2rem, scales with viewport between
- min valuethe smallest the font will ever be (use rem)
- preferred valuethe ideal fluid size (use vw or a calc expression)
- max valuethe largest the font will ever be (use rem)
- Works for any length property: font-size, padding, gap, width, etc.
Responsive typography
Font sizes scale fluidly, no media queries needed.
.fluid-title {
font-size: clamp(1.5rem, 4vw, 3rem);
margin-bottom: 12px;
}
.fluid-body {
font-size: clamp(0.9rem, 2vw, 1.1rem);
line-height: 1.6;
max-width: 60ch;
}Testing Responsive Designs
Responsive designs must be tested at many screen sizes. The browser DevTools provide a device emulation mode that lets you simulate phones, tablets, and custom widths without needing physical devices.
Testing Tools and Tips
Always test in a real browser. Device emulation in DevTools is a good starting point but does not perfectly replicate real device behavior.
- Chrome / Edge DevToolsF12, then click the device toolbar icon to enter responsive mode
- Firefox Responsive Design ModeF12, then click the phone/tablet icon
- Resize the window manuallythe simplest way to check breakpoints
- outline: 2px solid red on *temporarily reveals all element boundaries without affecting layout
- Test at 320px (smallest phones), 768px (tablets), and 1280px (desktop) at minimum
- Check both portrait and landscape orientations for tablet and phone sizes
Debug layout outlines
Add this temporarily to see every element's boundaries without shifting the layout.
/* Temporary debugging rule, shows all element outlines */
* {
outline: 1px solid rgba(255, 0, 0, 0.3);
}Knowledge Check
1. What does mobile-first mean in CSS?
2. Which media query targets screens 768px wide and above?
3. What is the most common viewport meta tag needed for responsive design?
4. What does clamp(1rem, 2.5vw, 2rem) do for font-size?
5. What is the key difference between container queries and media queries?