Skip to main content

Course Progress

Loading...

Media Queries for Responsive Design

Duration: 45 minutes
Module 1: CSS Layout & Responsive Design

Learning Objectives

  • Master the concepts in this lesson
  • Apply knowledge through practice
  • Build practical skills
  • Prepare for next topics

What Are Media Queries?

Media queries are the backbone of responsive web design. They allow you to apply different CSS styles based on various device characteristics, most commonly the viewport width. Think of media queries as the "if statements" of CSS—they let you create conditional styles that activate only when certain conditions are met.

Imagine you're a chameleon crossing different colored surfaces—you need to change your appearance based on your environment. Media queries give your website this adaptability, automatically adjusting its appearance to match the viewing environment.

                    graph TD
                    A[CSS Rules] --> B{Media Query}
                    B -->|"Screen width < 768px"| C[Mobile Styles]
                    B -->|"Screen width >= 768px and < 1024px"| D[Tablet Styles]
                    B -->|"Screen width >= 1024px"| E[Desktop Styles]
                    style A fill:#f9f,stroke:#333,stroke-width:2px
                    style B fill:#bbf,stroke:#333,stroke-width:1px
                    style C fill:#dfd,stroke:#333,stroke-width:1px
                    style D fill:#fdd,stroke:#333,stroke-width:1px
                    style E fill:#ddf,stroke:#333,stroke-width:1px
                

A Brief History of Media Queries

To fully appreciate media queries, we should understand their origins:

CSS2 and Media Types (1998)

CSS2 introduced the concept of "media types" with the @media rule, allowing you to target different types of media like screen, print, handheld, etc. This was the first step toward responsive design, but it lacked the ability to target specific characteristics of each device.

/* CSS for screens */
@media screen {
  body { font-size: 14px; }
}

/* CSS for print */
@media print {
  body { font-size: 12pt; }
}

Media Queries in CSS3 (2009)

CSS3 expanded the @media rule to include "media features" like width, height, orientation, and resolution. This was a game-changer, allowing developers to create truly responsive designs.

Responsive Web Design (2010)

Ethan Marcotte's landmark article "Responsive Web Design" popularized the use of media queries as a key component of responsive design, alongside fluid grids and flexible images.

Level 4 Media Queries (Present)

Media Queries Level 4 introduced new features like hover, pointer, and prefers-color-scheme, extending responsive design beyond just screen size to include user preferences and input methods.

Media Query Syntax

Media queries have a specific syntax that allows you to target devices based on their characteristics:

@media [media-type] ([media-feature]) {
  /* CSS rules to apply when conditions match */
}

Media Types

Media types specify the broad category of device. The most common ones are:

  • all: Matches all devices (default if not specified)
  • screen: Computer screens, tablets, phones
  • print: Print preview mode and printed pages
  • speech: Screen readers
/* Styles for print only */
@media print {
  nav, footer { 
    display: none; /* Hide navigation and footer when printing */
  }
  
  body {
    font-size: 12pt; /* Use points for print */
    color: black;
  }
  
  a::after {
    content: " (" attr(href) ")"; /* Show URLs after links when printing */
  }
}

Media Features

Media features describe specific characteristics of the user agent or display device. The most commonly used ones for responsive design are:

width / min-width / max-width

The viewport width. Most frequently used for responsive breakpoints.

@media (min-width: 768px) { /* Styles for screens 768px and wider */ }
@media (max-width: 767px) { /* Styles for screens narrower than 768px */ }
@media (width: 1024px) { /* Styles for screens exactly 1024px wide */ }

height / min-height / max-height

The viewport height.

@media (min-height: 600px) { /* Tall viewports */ }

orientation

Whether the device is in portrait or landscape mode.

@media (orientation: portrait) { /* Device is taller than it is wide */ }
@media (orientation: landscape) { /* Device is wider than it is tall */ }

aspect-ratio

The ratio of width to height of the viewport.

@media (aspect-ratio: 16/9) { /* Exactly 16:9 aspect ratio */ }
@media (min-aspect-ratio: 1/1) { /* Wider than tall */ }

resolution

The pixel density of the device.

@media (min-resolution: 2dppx) { /* High-DPI screens like Retina displays */ }

prefers-color-scheme (Level 5)

User's preference for light or dark color themes.

@media (prefers-color-scheme: dark) { /* Dark mode styles */ }
@media (prefers-color-scheme: light) { /* Light mode styles */ }

prefers-reduced-motion (Level 5)

User's preference for reduced motion (accessibility feature).

@media (prefers-reduced-motion: reduce) { 
  /* Styles with reduced or no animations */ 
}

hover

Whether the user's primary input mechanism can hover.

@media (hover: hover) { /* Styles for devices with hover capability */ }
@media (hover: none) { /* Styles for touch-only devices */ }

Combining Media Queries with Logical Operators

Media queries can be combined using logical operators to create more complex conditions:

AND Operator

Use the and keyword to combine multiple conditions (all must be true):

@media screen and (min-width: 768px) and (max-width: 1023px) {
  /* Styles for tablets only (between 768px and 1023px) */
}

OR Operator

Use a comma to create an OR relationship (at least one must be true):

@media (max-width: 600px), (orientation: portrait) {
  /* Styles for either small screens OR devices in portrait orientation */
}

NOT Operator

Use not to negate a media query:

@media not screen {
  /* Styles for anything that's not a screen (print, speech, etc.) */
}

ONLY Operator

Use only to prevent older browsers from applying styles (rarely needed today):

@media only screen and (min-width: 768px) {
  /* Styles that should only apply to screens, not to older browsers
     that might misinterpret the query */
}
                    graph TD
                    A[Media Query] --> B{Does it match?}
                    B -->|"Yes"| C[Apply Styles]
                    B -->|"No"| D[Ignore Styles]
                    
                    subgraph "Logical Operators"
                    E[AND] --> F["Both conditions must be true"]
                    G[OR] --> H["Either condition can be true"]
                    I[NOT] --> J["Negates a condition"]
                    end
                

Designing Your Breakpoints

One of the most important decisions in responsive design is where to set your breakpoints—the viewport widths at which your design changes.

Two Approaches to Breakpoints

Device-Based Breakpoints

Traditionally, developers set breakpoints based on common device sizes:

  • Mobile: 320px - 480px
  • Tablets: 481px - 768px
  • Laptops: 769px - 1024px
  • Desktops: 1025px - 1200px
  • Large Screens: 1201px and above

This approach can be useful as a starting point, but it has limitations. New devices with different screen sizes are constantly being released, and even the same device can have different effective screen sizes depending on browser settings, zoom level, and orientation.

Content-Based Breakpoints (Recommended)

A more future-proof approach is to set breakpoints based on where your content naturally breaks or looks awkward:

  1. Start with a mobile-first design
  2. Gradually widen your browser window
  3. When content looks stretched, awkward, or broken, that's a natural breakpoint
  4. Add a media query and adjust the design
  5. Continue expanding and adding breakpoints as needed

This approach ensures your design looks good at any size, not just on specific devices.

Common Bootstrap Breakpoints

For reference, here are the breakpoints used by Bootstrap 5, one of the most popular CSS frameworks:

Breakpoint Class infix Dimensions
X-Small None <576px
Small sm ≥576px
Medium md ≥768px
Large lg ≥992px
Extra large xl ≥1200px
Extra extra large xxl ≥1400px
Common Breakpoint Visualization 0px 576px 768px 992px 1200px 1400px + Mobile Small Tablets Tablets Laptops Desktops Large Displays (default) sm md lg xl xxl Mobile-First: Enhance with min-width queries → ← Desktop-First: Reduce with max-width queries

Mobile-First vs. Desktop-First Media Queries

There are two main approaches to writing media queries for responsive design:

Desktop-First Approach

Start with styles for large screens and use max-width media queries to override styles for smaller screens.

/* Base styles for desktop */
.container {
  display: flex;
  justify-content: space-between;
}

/* Override for tablets */
@media (max-width: 992px) {
  .container {
    flex-wrap: wrap;
  }
}

/* Override for mobile phones */
@media (max-width: 576px) {
  .container {
    flex-direction: column;
  }
}

Mobile-First Approach

Start with styles for small screens and use min-width media queries to add enhancements for larger screens.

/* Base styles for mobile */
.container {
  display: flex;
  flex-direction: column;
}

/* Enhancement for tablets */
@media (min-width: 576px) {
  .container {
    flex-direction: row;
    flex-wrap: wrap;
  }
}

/* Enhancement for desktop */
@media (min-width: 992px) {
  .container {
    flex-wrap: nowrap;
    justify-content: space-between;
  }
}

Why Mobile-First is Preferred

Performance Benefits

Mobile devices typically have slower connections and less processing power. Starting with minimal styles ensures faster loading on mobile.

Progressive Enhancement

You ensure the core content and functionality works on all devices, then enhance the experience for more capable devices.

Focused Design

Starting with mobile constraints forces you to focus on what's essential, resulting in more streamlined designs.

Future-Friendly

As new devices with different screen sizes continue to emerge, a mobile-first approach ensures your site will work across them.

The Suitcase Analogy

Think of designing a website like packing for a trip:

  • Desktop-First: You start with a large suitcase and gradually remove items to fit a smaller bag. The risk is forgetting what's truly important when you need to downsize.
  • Mobile-First: You start with a small backpack, only including essentials. Then, if you get a larger suitcase, you can add more items, but your essentials are always covered.

Practical Examples of Media Queries

Example 1: Responsive Navigation

A common pattern is to have a horizontal menu on desktop that transforms into a hamburger menu on mobile:

HTML

<nav class="main-nav">
  <a href="#" class="logo">Company Name</a>
  <button class="menu-toggle">☰</button>
  <ul class="nav-links">
    <li><a href="#">Home</a></li>
    <li><a href="#">About</a></li>
    <li><a href="#">Services</a></li>
    <li><a href="#">Contact</a></li>
  </ul>
</nav>

CSS (Mobile-First)

/* Mobile styles first (default) */
.main-nav {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  padding: 1rem;
  background-color: #333;
  color: white;
}

.logo {
  font-size: 1.5rem;
  text-decoration: none;
  color: white;
}

.nav-links {
  display: none; /* Hidden by default on mobile */
  width: 100%;
  margin: 1rem 0 0 0;
  padding: 0;
  list-style: none;
}

.nav-links.active {
  display: block; /* Shown when toggled */
}

.nav-links li {
  margin: 0.5rem 0;
}

.nav-links a {
  display: block;
  padding: 0.5rem 0;
  color: white;
  text-decoration: none;
}

.menu-toggle {
  display: block; /* Visible on mobile */
  background: none;
  border: none;
  color: white;
  font-size: 1.5rem;
  cursor: pointer;
}

/* Tablet and desktop styles */
@media (min-width: 768px) {
  .nav-links {
    display: flex; /* Always visible on desktop */
    width: auto;
    margin: 0;
  }
  
  .nav-links li {
    margin: 0 0 0 1.5rem;
  }
  
  .menu-toggle {
    display: none; /* Hidden on desktop */
  }
}

JavaScript

// Simple toggle functionality
document.querySelector('.menu-toggle').addEventListener('click', function() {
  document.querySelector('.nav-links').classList.toggle('active');
});

This example demonstrates:

  • Mobile-first approach with progressive enhancement
  • Menu is stacked vertically and hidden by default on mobile
  • Menu is horizontal and always visible on tablet/desktop
  • Toggle button is only visible on mobile

Example 2: Responsive Grid Layout

Creating a grid of items (like products or blog posts) that adjusts the number of columns based on screen size:

HTML

<div class="grid-container">
  <div class="grid-item">Item 1</div>
  <div class="grid-item">Item 2</div>
  <div class="grid-item">Item 3</div>
  <div class="grid-item">Item 4</div>
  <div class="grid-item">Item 5</div>
  <div class="grid-item">Item 6</div>
</div>

CSS (Mobile-First)

/* Base styles for mobile: 1 column */
.grid-container {
  display: grid;
  grid-template-columns: 1fr; /* One column by default */
  gap: 1rem;
  padding: 1rem;
}

.grid-item {
  background-color: #f0f0f0;
  padding: 2rem;
  border-radius: 0.5rem;
  text-align: center;
}

/* Tablet: 2 columns */
@media (min-width: 576px) {
  .grid-container {
    grid-template-columns: repeat(2, 1fr);
  }
}

/* Desktop: 3 columns */
@media (min-width: 992px) {
  .grid-container {
    grid-template-columns: repeat(3, 1fr);
  }
}

/* Large Desktop: 4 columns */
@media (min-width: 1200px) {
  .grid-container {
    grid-template-columns: repeat(4, 1fr);
    max-width: 1400px;
    margin: 0 auto;
  }
}

This example demonstrates:

  • Using CSS Grid for a responsive layout
  • Mobile-first approach with progressive enhancement
  • Gradually increasing the number of columns as screen size increases
  • Setting a maximum width and centering on very large screens

Example 3: Responsive Typography

Adjusting font sizes and line heights for different screen sizes:

CSS (Mobile-First)

/* Base typography for mobile */
html {
  font-size: 16px; /* Base font size */
}

h1 {
  font-size: 2rem; /* 32px on mobile */
  line-height: 1.2;
  margin-bottom: 1rem;
}

h2 {
  font-size: 1.5rem; /* 24px on mobile */
  line-height: 1.3;
  margin-bottom: 0.75rem;
}

p {
  font-size: 1rem; /* 16px on mobile */
  line-height: 1.6;
  margin-bottom: 1rem;
}

/* Tablet typography */
@media (min-width: 768px) {
  html {
    font-size: 18px; /* Base size increased to 18px */
  }
  
  h1 {
    font-size: 2.5rem; /* 45px on tablet */
  }
}

/* Desktop typography */
@media (min-width: 1200px) {
  html {
    font-size: 20px; /* Base size increased to 20px */
  }
  
  h1 {
    font-size: 3rem; /* 60px on desktop */
    line-height: 1.1;
  }
  
  h2 {
    line-height: 1.2;
  }
}

This example demonstrates:

  • Using relative units (rem) for scalable typography
  • Changing the base font size at different breakpoints
  • Adjusting not just size but also line height for better readability
  • Progressive enhancement of typography for larger screens

Modern Alternative: Fluid Typography

Instead of setting discrete font sizes at breakpoints, you can use CSS clamp() for smooth scaling:

h1 {
  font-size: clamp(2rem, 5vw + 1rem, 3rem);
  /* Minimum 2rem, scales with viewport, maximum 3rem */
}

Advanced Media Query Techniques

Feature Detection with @supports

Media queries can be combined with @supports to check if a browser supports certain CSS features:

@media (min-width: 768px) {
  @supports (display: grid) {
    /* Grid-based layout for modern browsers */
    .container {
      display: grid;
      grid-template-columns: repeat(3, 1fr);
    }
  }
  
  @supports not (display: grid) {
    /* Fallback for browsers without grid support */
    .container {
      display: flex;
      flex-wrap: wrap;
    }
    
    .container > * {
      width: 33.33%;
    }
  }
}

Container Queries (Experimental)

Container queries are a newer feature that allow styles to respond to the size of a parent container rather than the viewport. This is useful for reusable components:

/* Define a container that will be queried */
.card-container {
  container-type: inline-size;
}

/* Styles based on container width */
@container (min-width: 400px) {
  .card {
    flex-direction: row;
  }
}

@container (max-width: 399px) {
  .card {
    flex-direction: column;
  }
}

As of 2025, container queries have good browser support but may require polyfills for older browsers.

User Preference Media Queries

New media features allow you to detect user preferences for accessibility and customization:

/* Dark mode styles */
@media (prefers-color-scheme: dark) {
  body {
    background-color: #121212;
    color: #f0f0f0;
  }
}

/* Reduced motion for users with vestibular disorders */
@media (prefers-reduced-motion: reduce) {
  * {
    animation: none !important;
    transition: none !important;
  }
}

/* High contrast mode */
@media (prefers-contrast: high) {
  body {
    color: black;
    background: white;
  }
  
  a {
    color: blue;
    text-decoration: underline;
  }
}

Theming with CSS Custom Properties and Media Queries

Combining CSS variables with media queries for efficient theming:

:root {
  /* Base variables */
  --font-size-base: 16px;
  --spacing-unit: 1rem;
  --column-count: 1;
}

@media (min-width: 768px) {
  :root {
    --font-size-base: 18px;
    --spacing-unit: 1.5rem;
    --column-count: 2;
  }
}

@media (min-width: 1200px) {
  :root {
    --spacing-unit: 2rem;
    --column-count: 3;
  }
}

/* Using the variables */
body {
  font-size: var(--font-size-base);
  padding: var(--spacing-unit);
}

.grid {
  display: grid;
  grid-template-columns: repeat(var(--column-count), 1fr);
  gap: var(--spacing-unit);
}

Media Query Best Practices

Start with Mobile-First

Begin with styles for the smallest screen and use min-width queries to enhance for larger screens. This improves performance on mobile devices where resources are more constrained.

Use Relative Units

Prefer em, rem, and percentage units over fixed pixel values inside your media query styles. This makes your design more flexible and accessible.

Limit the Number of Breakpoints

Too many breakpoints can make your CSS harder to maintain. Aim for 3-5 major breakpoints and use them consistently throughout your project. Consider creating variables or a mixin in SCSS:

// SCSS breakpoint variables
$breakpoint-sm: 576px;
$breakpoint-md: 768px;
$breakpoint-lg: 992px;
$breakpoint-xl: 1200px;

// SCSS mixin
@mixin respond-to($breakpoint) {
  @if $breakpoint == sm {
    @media (min-width: $breakpoint-sm) { @content; }
  }
  @else if $breakpoint == md {
    @media (min-width: $breakpoint-md) { @content; }
  }
  @else if $breakpoint == lg {
    @media (min-width: $breakpoint-lg) { @content; }
  }
  @else if $breakpoint == xl {
    @media (min-width: $breakpoint-xl) { @content; }
  }
}

// Usage
.container {
  padding: 1rem;
  
  @include respond-to(md) {
    padding: 2rem;
  }
}

Use Print Media Queries

Don't forget about the print experience. Use @media print to optimize your page for printing:

@media print {
  /* Hide unnecessary elements */
  nav, footer, .sidebar, .ads, .comments, .share-buttons {
    display: none;
  }
  
  /* Ensure text is readable */
  body {
    font-size: 12pt;
    line-height: 1.5;
    color: black;
    background: white; /* Force background to white */
  }
  
  /* Break links apart */
  a {
    color: black;
  }
  
  a::after {
    content: " (" attr(href) ")";
    font-size: 90%;
  }
  
  /* Ensure images don't get cut off */
  img {
    max-width: 100% !important;
    page-break-inside: avoid;
  }
  
  /* Control page breaks */
  h1, h2, h3, h4, h5, h6 {
    page-break-after: avoid;
  }
  
  p, blockquote, table, figure {
    page-break-inside: avoid;
  }
}

Test on Real Devices

Browser dev tools are great for initial testing, but always test your media queries on actual devices when possible. Different devices have different browsers, screen ratios, and capabilities that can't always be simulated.

Don't Base Media Queries on Specific Devices

Avoid creating media queries targeted at specific devices (e.g., iPhone 15). These will quickly become outdated as new devices are released. Focus on ranges of screen sizes and content breakpoints instead.

Consider Viewport Height

Don't forget about the height of the viewport, especially for layouts with fixed headers or full-height sections:

/* Handle short viewports */
@media (max-height: 600px) {
  .hero-section {
    min-height: auto;
    padding: 2rem 0;
  }
  
  .fixed-header {
    position: static;
  }
}

Debugging Media Queries

Using Browser DevTools

Modern browsers provide excellent tools for working with responsive designs:

  • Device Emulation: Chrome and Firefox DevTools allow you to emulate specific devices or set custom viewport dimensions.
  • Media Query Inspector: In Chrome DevTools, you can see all the media queries in your CSS by looking for the colored bars in the "Styles" panel.
  • Responsive Design Mode: Firefox has a dedicated responsive design view (Ctrl+Shift+M) that includes rulers and device presets.

Visual Indicators

Adding temporary visual indicators can help you see when media queries are being applied:

/* For development only */
body::before {
  content: "Mobile (< 576px)";
  position: fixed;
  top: 0;
  left: 0;
  background: red;
  color: white;
  padding: 5px;
  z-index: 9999;
}

@media (min-width: 576px) {
  body::before {
    content: "Tablet (≥ 576px)";
    background: blue;
  }
}

@media (min-width: 992px) {
  body::before {
    content: "Desktop (≥ 992px)";
    background: green;
  }
}

Remember to remove these indicators before deploying to production!

Common Media Query Issues

  • Missing Viewport Meta Tag: Without <meta name="viewport" content="width=device-width, initial-scale=1.0">, mobile browsers will render pages at desktop width and scale them down.
  • Overlapping Media Queries: Be careful with overlapping ranges when using both min-width and max-width together.
  • CSS Specificity Conflicts: Later styles override earlier ones, so the order of your media queries matters.
  • Pixel Rounding Issues: Some browsers round pixel values differently, which can cause layouts to break at specific widths.

Practice Exercises

Exercise 1: Convert a Fixed Layout to Responsive

Start with this fixed-width CSS and convert it to a responsive design using media queries:

/* Fixed layout */
.container {
  width: 960px;
  margin: 0 auto;
}

.header {
  height: 100px;
}

.main-content {
  width: 700px;
  float: left;
}

.sidebar {
  width: 240px;
  float: right;
}

.footer {
  clear: both;
  height: 80px;
}

Your task:

  1. Make the container fluid with a max-width
  2. Use a mobile-first approach
  3. On mobile, stack the sidebar below the main content
  4. Switch to a two-column layout on tablet and above

Exercise 2: Create a Responsive Card Component

Design a product card that adapts to different screen sizes:

  • On mobile: Image on top, content below
  • On tablet: Image on the left (40%), content on the right (60%)
  • On desktop: Add a hover effect and slightly larger text

Exercise 3: Design for Multiple User Preferences

Create a simple page that adapts to the following user preferences using media queries:

  • Dark mode vs. light mode (prefers-color-scheme)
  • Reduced motion (prefers-reduced-motion)
  • Larger text for accessibility (prefers-reduced-transparency)

Conclusion

Media queries are an essential tool in modern web development, allowing you to create websites that provide an optimal experience across a wide range of devices. By mastering media queries, you can ensure your sites are accessible, performant, and user-friendly regardless of how they're accessed.

Key takeaways:

  • Media queries allow you to apply different styles based on device characteristics
  • A mobile-first approach using min-width queries is generally preferred
  • Focus on content-based breakpoints rather than specific devices
  • Modern media features go beyond screen size to address user preferences and accessibility
  • Combine media queries with other responsive techniques like fluid layouts and flexible images

In the next session, we'll explore CSS frameworks like Bootstrap that provide pre-built responsive components and simplify the process of building responsive websites.

Additional Resources