Skip to main content

What is Responsive Design?

Responsive design makes websites work well on all device sizes - from phones to tablets to desktops. Key techniques:
  • Media Queries - Apply styles based on screen size
  • Flexible Layouts - Flexbox & Grid that adapt
  • Responsive Units - rem, em, %, vw, vh
  • Mobile-First - Design for small screens first

Media Queries

Apply styles based on device characteristics.

Basic Syntax

@media type and (condition) {
  /* Styles */
}

Common Media Queries

/* Styles for screens 768px and wider */
@media screen and (min-width: 768px) {
  .container {
    max-width: 720px;
  }
}

Standard Breakpoints

Common device breakpoints:
/* Mobile phones */
@media (max-width: 480px) { }

/* Tablets portrait */
@media (min-width: 481px) and (max-width: 768px) { }

/* Tablets landscape / Small laptops */
@media (min-width: 769px) and (max-width: 1024px) { }

/* Laptops / Desktops */
@media (min-width: 1025px) and (max-width: 1200px) { }

/* Large desktops */
@media (min-width: 1201px) { }

Project Breakpoints

/* From the tutorial project */

/* Tablet and smaller */
@media screen and (max-width: 768px) {
  /* Tablet optimizations */
}

/* Small mobile */
@media screen and (max-width: 480px) {
  /* Phone optimizations */
}

Mobile-First vs Desktop-First

Desktop-First (max-width)

/* Base: Desktop styles */
.container {
  width: 1200px;
}

/* Then override for smaller screens */
@media (max-width: 768px) {
  .container {
    width: 100%;
  }
}
/* Base: Mobile styles */
.container {
  width: 100%;
}

/* Then enhance for larger screens */
@media (min-width: 768px) {
  .container {
    width: 720px;
  }
}

@media (min-width: 1200px) {
  .container {
    width: 1200px;
  }
}
Mobile-first is recommended because:
  1. Forces you to prioritize content
  2. Better performance on mobile (loads less CSS)
  3. Progressive enhancement approach
  4. Most traffic is mobile

Real Examples from the Project

Header - Tablet Responsive

/* Base (mobile) styles */
.header__container {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--spacing-md);
}

/* Tablet and below */
@media screen and (max-width: 768px) {
  .header__container {
    flex-wrap: wrap;  /* Allow wrapping */
    padding: var(--spacing-sm) var(--spacing-md);
  }
  
  /* Logo smaller */
  .header__logo-text {
    font-size: var(--font-size-xl);
  }
  
  /* Search takes full width */
  .header__search {
    order: 3;              /* Move to end */
    flex-basis: 100%;      /* Full width */
    max-width: none;
    margin-top: var(--spacing-sm);
  }
  
  /* Nav spacing tighter */
  .header__nav-list {
    gap: var(--spacing-sm);
  }
}

Visually Hidden Text (Accessible)

@media screen and (max-width: 768px) {
  /* Hide cart label text but keep for screen readers */
  .header__cart-label {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
  }
}
This technique hides content visually but keeps it accessible to screen readers - better than display: none.

Hero Section

/* Base */
.hero {
  padding: var(--spacing-2xl) var(--spacing-lg);
}

.hero__title {
  font-size: var(--font-size-3xl);
}

/* Tablet */
@media screen and (max-width: 768px) {
  .hero {
    padding: var(--spacing-xl) var(--spacing-md);
  }
  
  .hero__title {
    font-size: var(--font-size-2xl);  /* Smaller title */
  }
}
/* Base */
.header__nav-list {
  display: flex;
  align-items: center;
  gap: var(--spacing-md);
}

/* Small mobile */
@media screen and (max-width: 480px) {
  .header__nav-list {
    flex-direction: column;  /* Stack vertically */
    align-items: flex-end;
    gap: var(--spacing-xs);
  }
}

Grid - Single Column Mobile

/* Base - responsive grid */
.products__grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: var(--spacing-lg);
}

/* Force single column on small mobile */
@media screen and (max-width: 480px) {
  .products__grid {
    grid-template-columns: 1fr;
  }
  
  .benefits__grid {
    grid-template-columns: 1fr;
  }
}
/* Base */
.footer__bottom {
  display: flex;
  justify-content: space-between;
  gap: var(--spacing-md);
}

/* Tablet */
@media screen and (max-width: 768px) {
  .footer__bottom {
    flex-direction: column;  /* Stack */
    text-align: center;
  }
}

Media Query Types

Screen (Most Common)

@media screen and (max-width: 768px) {
  /* Styles for screens */
}

Print

@media print {
  .no-print {
    display: none;  /* Hide on print */
  }
  
  body {
    color: black;
    background: white;
  }
}

All (Default)

@media (max-width: 768px) {
  /* Applies to all media types */
}

User Preference Media Queries

prefers-color-scheme (Dark Mode)

/* Light mode (default) */
:root {
  --color-bg: #FFFFFF;
  --color-text: #333333;
}

/* Dark mode */
@media (prefers-color-scheme: dark) {
  :root {
    --color-bg: #1a1a1a;
    --color-text: #f0f0f0;
  }
}

/* Usage - automatically adapts! */
body {
  background-color: var(--color-bg);
  color: var(--color-text);
}

prefers-reduced-motion (Accessibility)

/* Normal animations */
.element {
  transition: transform 300ms ease;
}

/* Respect user preference for reduced motion */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}
Always include prefers-reduced-motion - some users get motion sickness from animations.

Other Preference Queries

/* High contrast mode */
@media (prefers-contrast: high) {
  .button {
    border: 3px solid currentColor;
  }
}

/* Reduced transparency */
@media (prefers-reduced-transparency) {
  .element {
    opacity: 1 !important;
  }
}

Orientation

/* Portrait (taller than wide) */
@media (orientation: portrait) {
  .gallery {
    grid-template-columns: repeat(2, 1fr);
  }
}

/* Landscape (wider than tall) */
@media (orientation: landscape) {
  .gallery {
    grid-template-columns: repeat(4, 1fr);
  }
}

Responsive Units

Relative Units

/* Relative to root font-size (usually 16px) */
html {
  font-size: 16px;  /* 1rem = 16px */
}

.element {
  font-size: 1.5rem;    /* 24px */
  padding: 2rem;        /* 32px */
  margin: 0.5rem;       /* 8px */
}
Use rem for most sizing - it respects user’s browser font size settings (accessibility!).

Responsive Typography

Fixed Sizes with Media Queries

h1 {
  font-size: 1.5rem;  /* Mobile */
}

@media (min-width: 768px) {
  h1 {
    font-size: 2rem;  /* Tablet */
  }
}

@media (min-width: 1200px) {
  h1 {
    font-size: 2.5rem;  /* Desktop */
  }
}

Fluid Typography with clamp()

h1 {
  /* min, preferred, max */
  font-size: clamp(1.5rem, 4vw, 3rem);
  /* Never smaller than 1.5rem, never larger than 3rem */
  /* Scales smoothly in between based on viewport */
}

Responsive Images

CSS Approach

img {
  max-width: 100%;  /* Never overflow container */
  height: auto;     /* Maintain aspect ratio */
}

HTML Approach (Better!)

<!-- Different images for different sizes -->
<picture>
  <source media="(min-width: 1200px)" srcset="large.jpg">
  <source media="(min-width: 768px)" srcset="medium.jpg">
  <img src="small.jpg" alt="Description">
</picture>

<!-- Different resolutions -->
<img 
  srcset="image-1x.jpg 1x, image-2x.jpg 2x" 
  src="image-1x.jpg" 
  alt="Description"
>

Container Queries (Modern!)

Style based on container size, not viewport.
/* Define container */
.card-container {
  container-type: inline-size;
}

/* Query the container */
@container (min-width: 400px) {
  .card {
    display: flex;  /* Side-by-side when container is wide */
  }
}
Container queries are new but very powerful - they let components be truly modular and reusable.

Responsive Grid Patterns

Auto-Responsive Grid (No Media Queries!)

.grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
  gap: 20px;
}

/* Automatically adjusts columns based on available space:
 * 1200px screen: 4 columns
 * 800px screen: 3 columns
 * 500px screen: 2 columns
 * 300px screen: 1 column
 */

With Media Query Override

.grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
  gap: 20px;
}

/* Force single column on very small screens */
@media (max-width: 480px) {
  .grid {
    grid-template-columns: 1fr;
  }
}

Responsive Flexbox

Wrapping Layout

.row {
  display: flex;
  flex-wrap: wrap;  /* Items wrap to next line */
  gap: 20px;
}

.column {
  flex: 1 1 300px;  /* Grow, shrink, min 300px */
}

Changing Direction

.container {
  display: flex;
  flex-direction: row;
}

@media (max-width: 768px) {
  .container {
    flex-direction: column;  /* Stack on mobile */
  }
}

Reordering with order

.header {
  display: flex;
}

.logo { order: 1; }
.nav { order: 2; }
.search { order: 3; }

@media (max-width: 768px) {
  .search {
    order: 1;      /* Move search first on mobile */
    flex-basis: 100%;  /* Full width */
  }
}

Responsive Spacing

Variable Spacing

:root {
  --section-padding: var(--spacing-xl);  /* 32px */
}

@media (max-width: 768px) {
  :root {
    --section-padding: var(--spacing-md);  /* 16px */
  }
}

.section {
  padding: var(--section-padding);  /* Automatically responsive! */
}

Viewport-Based Spacing

.section {
  padding: clamp(1rem, 5vw, 3rem);
  /* Min 1rem, max 3rem, scales with viewport */
}

Testing Responsive Design

Browser DevTools

  1. Open DevTools (F12)
  2. Click device toolbar icon (Ctrl+Shift+M)
  3. Select device or enter custom dimensions
  4. Test at different breakpoints

Common Test Sizes

  • 320px - iPhone SE (small phone)
  • 375px - iPhone X/11/12/13 (standard phone)
  • 768px - iPad (tablet portrait)
  • 1024px - iPad Pro (tablet landscape)
  • 1366px - Laptop
  • 1920px - Desktop

Best Practices

Use mobile-first approach
Test on real devices when possible
Use relative units (rem, %, vw) over px
Keep breakpoints simple (2-3 main breakpoints)
Make touch targets at least 44x44px on mobile
Test with real content (not Lorem ipsum)
Don’t hide important content on mobile
Don’t target specific devices (they change constantly)
Avoid horizontal scrolling

Common Responsive Patterns

Hamburger Menu

/* Mobile: hamburger icon */
.nav-toggle {
  display: block;
}

.nav-menu {
  display: none;  /* Hidden by default */
}

.nav-menu.active {
  display: flex;
  flex-direction: column;
}

/* Desktop: full navigation */
@media (min-width: 768px) {
  .nav-toggle {
    display: none;
  }
  
  .nav-menu {
    display: flex;
    flex-direction: row;
  }
}

Responsive Table

/* Mobile: Cards */
@media (max-width: 768px) {
  table, thead, tbody, tr, th, td {
    display: block;
  }
  
  tr {
    margin-bottom: 1rem;
    border: 1px solid #ddd;
  }
}

Off-Canvas Sidebar

.sidebar {
  position: fixed;
  left: -250px;  /* Hidden off-screen */
  width: 250px;
  transition: left 300ms;
}

.sidebar.active {
  left: 0;  /* Slide in */
}

/* Desktop: always visible */
@media (min-width: 1024px) {
  .sidebar {
    position: static;
    left: 0;
  }
}

Complete Responsive Component

/* Product Card - Fully Responsive */

/* Base (Mobile) */
.product-card {
  background-color: var(--color-white);
  border-radius: var(--border-radius-md);
  padding: var(--spacing-md);
}

.product-card__image {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
}

.product-card__title {
  font-size: var(--font-size-base);
  margin: var(--spacing-sm) 0;
}

/* Tablet */
@media (min-width: 768px) {
  .product-card {
    padding: var(--spacing-lg);
  }
  
  .product-card__title {
    font-size: var(--font-size-lg);
  }
}

/* Desktop */
@media (min-width: 1200px) {
  .product-card:hover {
    transform: translateY(-8px);
    box-shadow: var(--shadow-lg);
  }
}

/* Reduced Motion */
@media (prefers-reduced-motion: reduce) {
  .product-card {
    transition: none;
  }
  
  .product-card:hover {
    transform: none;
  }
}

Viewport Meta Tag (Required!)

Always include in your HTML <head>:
<meta name="viewport" content="width=device-width, initial-scale=1.0">
Without this meta tag, mobile browsers will render at desktop width and zoom out - your media queries won’t work!

Quick Reference

Media Query Conditions

  • min-width - At least this wide
  • max-width - At most this wide
  • orientation - portrait | landscape
  • prefers-color-scheme - light | dark
  • prefers-reduced-motion - reduce | no-preference

Responsive Units

  • rem - Relative to root font-size
  • em - Relative to parent font-size
  • % - Relative to parent
  • vw/vh - Viewport width/height
  • vmin/vmax - Smaller/larger of vw or vh

Functions

  • clamp(min, preferred, max) - Responsive sizing
  • min(value1, value2) - Use smaller value
  • max(value1, value2) - Use larger value
  • calc(expression) - Calculate values

Next Steps

Flexbox

Build responsive layouts with Flexbox

Grid

Create responsive grids

Build docs developers (and LLMs) love