Responsive Design and Media Queries

Session Time: 120 minutes


Table of Contents

  1. Introduction: The "Write Once, Run Everywhere" of UI
  2. The Mobile-First Strategy
  3. Media Queries: Conditional Logic for Style
  4. Responsive Units vs. Fixed Units
  5. AI-Assisted Responsive Testing
  6. Lab: Designing for Every Device
  7. Wrap-Up and Reflection

Learning Objectives

Upon completion of this session, participants will be able to:

  • Design web layouts that adjust seamlessly to various screen sizes (mobile, tablet, desktop) using the Mobile-First approach.
  • Evaluate the effectiveness of media queries and breakpoints to handle content reflow.
  • Use AI to analyze responsive performance and suggest CSS refinements for fluidity.
  • Build a responsive landing page for an AI application that maintains visual balance on all devices.

Session Breakdown

Segment Topic Duration (minutes)
Introduction Why Responsive UI Matters 15
Concept I Mobile-First Philosophy & Viewports 20
Concept II Media Queries (Syntax & Logic) 25
Concept III Relative Units (rem, vh, %) 15
AI Tooling Simulating Devices with AI 15
Practical Lab Lab: Designing for Every Device 20
Conclusion Wrap-Up and Reflection 10
Total 120 minutes

1. Responsive Design : Concepts

While it's now in the distant past, building a successful online presence used to mean ensuring that a website worked correctly on a few desktop browsers. The mobile internet looked like this:

Motorolla Razr 2005

The mobile internet in 2005. Image: Wired

Today, users are more likely to visit websites using a mobile browser than a desktop browser. Because of this, modern websites and applications are typically designed to work across many devices by utilizing responsive design.

What is responsive design?

Responsive design is a technique for designing and developing websites and apps that adapt to different screen sizes and devices. It allows us to create apps that feel at home on whatever device the app is viewed on. Specifically, the most critical criterion to respond to is the width of the device's screen.

When we implement responsive design, we also consider and accept the limitations of different devices. Making apps respond to mobile devices may mean exposing fewer features to mobile users. Similarly, building features designed for touch input will limit those features to devices with touch input.

Whether you're aware or not, you encounter examples of responsive design every time you use the internet! Common examples include things like:

  • Navbar responsiveness: The navbar of a desktop site might be horizontal, with links spanning the width of the page, while a mobile site might have a hamburger menu that expands into a vertical list when clicked.
  • Image size: Desktop sites can account for large image sizes, but mobile sites need smaller or adaptive images in order to fit the screen without any unintended cropping.

Why implement responsive design?

Non-traditional interfaces have skyrocketed in popularity, and users access the web from all kinds of devices. Responsive design ensures that more people can use our applications. Furthermore, it enables them to have a positive experience using our apps, regardless of how they interact with them.

That's not to say there are no challenges to implementing responsive design. By its nature, implementing responsive design requires more planning, more code, and more testing. The benefits typically outweigh the challenges, and it is an essential consideration for any website or app.


2. Responsive design in the real world

Again, you encounter responsive design on almost any site you visit on the web, but these are solid examples:

Shopify offers an example of using responsive design to elevate a message.

When you go to their site, whether on a mobile device with a five-inch screen or a desktop device with a 34-inch screen, the first thing you see is a call to action.

A call to action is a prompt that urges a user to take a specific action. Typically, a call to action will lead to the user becoming a customer. In this case, the call to action asks the user to sign up for a trial.

Some of the techniques Shopify used to accomplish this are as follows:

  • Responsive image: The background image for mobile is much smaller than on a desktop.
  • Responsive nav bar: The nav stretches across the screen in the desktop view. The nav is initially hidden on mobile devices, but users can press the hamburger icon to show it.
  • Layout orientation: The "Start free trial" and "Watch the Shopify story" buttons are positioned horizontally across from each other on the desktop. On mobile, they are stacked vertically. It's typical to display most content vertically for mobile devices, given the limited width of their screens.

Shopify

Another example of a responsive site is one you've become familiar with - GitHub.

GitHub's responsive design goes further than just design considerations. Their responsive design goes as far as hiding certain features on mobile devices. This works for GitHub's users because the likelihood of someone wanting to create a repo on a mobile device is minimal.

While their users may not create repositories from their mobile devices, they will likely use its social features. Therefore, these features are given more prominence.

GitHub

Many approaches can be taken to implement responsive design, and the final implementation will ultimately depend on the goals of the application in question.


3. Mobile-first design philosophy

Two approaches can be followed when writing CSS for a website:

  1. Write the starting CSS for a large desktop screen, then apply styling targeting smaller screens as the screen width decreases.
  2. Write the starting CSS for a mobile screen, then apply styling as the width increases. This is referred to as mobile-first design.

Typically, a mobile-first approach (#2 above) is preferable for a few reasons:

  • Mobile-first helps encourage thought about what content is the most important - and ensures we know to prioritize that content. Once we know the minimum requirements, we can add additional features for other devices that can handle them.
  • Although not ideal, a design based on small screen width is usable on larger screens. However, the reverse is often not the case. If necessary, we can rely on just the design for smaller screens, knowing it will cover everything.

The first step to enabling a better experience on mobile

Unlike desktop browsers that essentially render pixel-by-pixel, mobile browsers render to a virtual viewport. The virtual viewport enables pinch-to-zoom on mobile browsers. Mobile browsers scale down page content to fit it into the viewport by default.

This default results in tiny text that may be hard to read but does at least make older sites usable with pinch-to-zoom. This was necessary when mobile devices with fully-featured browsers were first released since websites weren't built to work on mobile devices.

So, how do you make a website feel native on a mobile device?

Viewport meta tag

The following should look familiar from the boilerplate HTML code you've used before:

<meta name="viewport" content="width=device-width, initial-scale=1.0">

This HTML informs the browser not to scale the page on a mobile device. With this HTML present, mobile devices instead display page content based on the device's width (as specified by width=device-width) and do not zoom out initial-scale=1.0.

This viewport meta tag is so essential that it is one of the few items automatically included in VS Code's HTML boilerplate. Every app you write will have this meta tag.

Below is an example of a page with the viewport meta tag on the left and the same page without it on the right:

Meta viewport tag & No meta viewport tag


4. Media Queries

Media queries are a CSS feature that allows us to apply different rules to a page depending on a device's characteristics. For example, we can use media queries to change the layout of our page for different screen sizes. We could even hide or show specific elements when printing the page with a printer.

To use a media query, we wrap the CSS rules we want to apply conditionally in an @media block. The @media block specifies the conditions that must be met for the CSS rules to apply.

body {
  background-color: lightsalmon;
}

@media (min-width: 600px) {
  body {
    background-color: lightgreen;
  }
}

We can also combine multiple media queries to create more complex conditions. For example, the following media query will apply the CSS rules inside the block to all desktop devices with a screen width of less than 1200 pixels:

@media (min-width: 768px) and (max-width: 1199px) {
  /* CSS rules for with a screen width of greater than 768px 
     and fewer than 1200 pixels */
}

Responsive Units vs. Fixed Units

Learning objective: Use relative units to ensure scalability.

Hardcoding pixels (px) is the enemy of responsiveness.

Unit Type Description Analogy
px Absolute Fixed dots on screen. Hardcoded constant (300)
% Relative Percentage of the parent element. parent_width * 0.5
rem Relative Based on the root HTML font size. Global variable scaling
vw Relative 1vw = 1% of the Viewport Width. screen.width * 0.01

Best Practice:

  • Use rem for font sizes and padding (accessibility).
  • Use % or fr (Grid) for layout widths.
  • Use px only for very specific details (like a border line).

Breakpoints

Devices

Breakpoints are the specific screen sizes or conditions we use to create media queries. Common breakpoints include:

  • Mobile: 320px - 480px
  • Tablet: 481px - 767px
  • Desktop: 768px - 1199px
  • Large desktop: 1200px+

While the above values are good general guidelines, it's important to recognize that desktop users can resize their browser window to any width. Also, nothing stops a phone manufacturer from making a phone that doesn't conform to the above rules. For example, the iPhone 16 Pro Max has a viewport width of over 800 pixels in landscape orientation, putting it firmly in our supposed desktop viewport territory.

Therefore, you should focus your energy on making breakpoints that make sense for your application, and you should always choose breakpoints based on a site's content.

The easiest way to do this is to drag your browser's width in and out, making it thinner and wider. Look at the website as you do so. If you feel the app becomes difficult to use at any point due to its layout at that screen width, add a media query that targets that screen width and write CSS that corrects the issue.

Order matters

The order in which we write media queries matters. For example, if two media queries apply to the same device, the browser will use the second one that matches, even if it's not what you might have intended.

Let's say we wanted to create a design where we intend to apply these rules:

  • The background of the body element should be lightsalmon when the viewport is smaller than 600 pixels in width.
  • The background should be lightgreen when the viewport is between 600 pixels and 800 pixels in width
  • The background should be gainsboro when the viewport is 800 pixels wide or more.

This CSS would help accomplish these goals:

body {
  background-color: lightsalmon;
}

@media (min-width: 600px) {
  body {
    background-color: lightgreen;
  }
}

@media (min-width: 800px) {
  body {
    background-color: gainsboro;
  }
}

If the viewport is 900px wide, the background will be gainsboro, as intended. But the cascade still applies to media queries, so if we start moving our rules around, then things will start to break:

@media (min-width: 600px) {
  body {
    background-color: lightgreen;
  }
}

@media (min-width: 800px) {
  body {
    background-color: gainsboro;
  }
}

body {
  background-color: lightsalmon;
}

Because the last body rule in the document applies universally and sets the background color to lightsalmon, the background color will always be lightsalmon, no matter how wide the browser viewport is.

Here's another example:

body {
  background-color: lightsalmon;
}

@media (min-width: 800px) {
  body {
    background-color: gainsboro;
  }
}

@media (min-width: 600px) {
  body {
    background-color: lightgreen;
  }
}

Here, we've moved the main body rule back to the top and moved the media query for screens 600 pixels and larger to the bottom. This change makes it so that when the viewport width is 600 pixels or larger, the background color will be lightgreen (and lightsalmon when the viewport is less than 600 pixels). Again, because of the cascade, the background color will never be gainsboro.

Note we could use multiple media queries to make the media query for screens 600 pixels and larger a little more specific so that it doesn't apply to all screens above 600 pixels, like so:

body {
  background-color: lightsalmon;
}

@media (min-width: 800px) {
  body {
    background-color: gainsboro;
  }
}

@media (min-width: 600px) and (max-width: 799px) {
  body {
    background-color: lightgreen;
  }
}

When the screen is 800 pixels or larger, the background color will change to gainsboro, as we originally intended.

The big takeaway is that, like always, the order in which you write your CSS matters. However, media queries add a layer of complication you must also consider. Media queries should always appear after all other CSS, and it's important to test your implementation of them.

Practical Example : Hamburger nav bar using Media Query

Let's create a responsive nav bar for a shopping site with a non-functioning hamburger button using media queries! You've interacted with a hamburger button before, even if you haven't heard of the term. It's an icon that looks like this: ☰.

Hamburger buttons are frequently used when the viewport's width isn't enough to accommodate the entire width of all the elements inside a nav bar. They are a classic pattern widely understood by users, so their use is prevalent, even outside the web.

To set up the hamburger button on our site, we want the nav bar to appear as a horizontal row of <p> elements when the screen is 550 pixels or wider and as a hamburger menu when it's smaller than 550 pixels. Remember, nothing here is intended to function. That's why we use regular <p> elements even though a nav bar typically holds links.

Next, add the following inside of your <body> tags. Feel free to copy this from here. The only item of note is the two <div> elements we're creating inside the <nav> element. Eventually, we'll make it so only one of these <div> elements shows at a time.

<nav>
  <div id="destinations">
    <p>Home</p>
    <p>About</p>
    <p>Shop</p>
    <p>Contact</p>
    <p>Climate Pledge</p>
  </div>
  <div id="destinations-mobile">
    <p>🍔</p>
  </div>
</nav>

We will use a mobile-first design. Therefore, the CSS we write outside our media queries will target smaller screens. We'll then write new CSS in our media queries that targets larger screens. Let's start with some CSS to handle the <nav> we just created:

nav {
  font-size: 18px;
  background-color: white;
  padding: 0 16px;
}

Since we're implementing a mobile-first design, our base CSS will hide the <div> element with an id of destinations.

#destinations {
  display: none;
}

This rule removes the element with the id of destinations from the flow of the document and hides it from the user.

Next, we add a media query that will show the <div> element with an id of destinations and hide the <div> with an id of destinations-mobile when we hit our breakpoint. We're only interested in changing what's shown in the nav bar as the screen widens. This media query will accomplish our goal:

@media (min-width: 550px) {
  #destinations {
    display: flex;
    gap: 16px;
  }

  #destinations-mobile {
    display: none;
  }
}

We only add CSS declarations for the properties we want to change. There's no reason to repeat any of the CSS above the media query.

Shrink the window smaller than 550 pixels in width, then extend it past 550 pixels in width. You should see a change in what is shown in the nav bar! In general, this is a good practice for testing your media queries. If, at any point, while shrinking/extending the browser window, you see that the page becomes difficult to use, it may be a good idea to add additional breakpoints or adjust the one you wrote.

Result

If you'd like to learn how to make this functional, head to the Functional Hamburger Nav lesson for a walkthrough.


5. Responsive Design : Best Practices

In addition to shrinking/expanding your browser window's width to test its responsive layout, viewing your app on various other devices is a good practice. Each browser/device has different quirks, so it will become important to see how they handle your app in a production environment.

Key areas to focus on

When conducting tests, zero in on:

  • Layout: Does it align with your design objectives?
  • Content: Is all your content visible and easily accessible?
  • Navigation: How effortless is it to move around the website?
  • Forms: Are they fully functional?
  • Interactivity: Do clickable items behave as expected?

❓ Which of these would "testing the animation of a hamburger menu" fall under?

Using Chrome's Device Toolbar for Testing

While testing on the actual devices your audience uses is ideal, it's often not feasible. Tools like the Device Toolbar in Chrome provide a viable alternative. The Device Toolbar in Chrome DevTools simulates various screen sizes and resolutions. To activate it:

  1. Open Developer Tools (⌘ Command + Shift + C on Mac or Control + Shift + C on Windows and Linux)
  2. Click on the Device Toolbar icon to the left of the Elements tab
  3. Select a device from the drop-down or input custom dimensions
  4. Refresh the page to see how your website behaves under different conditions.

🎓 You Do

Chrome's browser simulation for mobile devices has come a long way, but it isn't perfect. Find a mobile device in the drop-down that most closely matches your personal mobile device.

  1. Navigate to one of your favorite websites using the simulator. Take a look at the UI.
  2. Now, navigate to that same site from your mobile device.

Are there any differences in how the page is displayed?

Progressive enhancement

Progressive enhancement is a way to design websites that work for everyone, regardless of their device or browser. To use this strategy, developers start by creating a basic version of the website with all the essential content and features in a way that is accessible to all. Once this is complete, they add features and enhancements for users with more powerful devices and browsers.

Benefits of Progressive Enhancement

  • Universal Accessibility: All users receive the essential content and basic functionalities.
  • Reduced Load Times: Users with older hardware or slower internet connections benefit from faster load times for essential features.
  • Easier Maintenance: Since the basic version needs to be maintained, it simplifies long-term site upkeep.

6. Responsive design using Flexbox

In this lesson, we'll add content and responsiveness to the main content of our site using only Flexbox!

In index.html, add the following code below the closing </header> tag:

<main>
  <div class="brand-text">
    <h1>Happy Fashion</h1>
  </div>
  <div class="card-container">
    <div class="title-text">
      <p id="card-title">Featured Items:</p>
    </div>
    <div class="item-card">
      <img
        src="https://cdn-icons-png.flaticon.com/128/6516/6516824.png"
        alt="boots icon"
      />
      <p class="item-desc">Cowboy Boots</p>
    </div>
    <div class="item-card">
      <img
        src="https://cdn-icons-png.flaticon.com/128/2390/2390064.png"
        alt="pants icon"
      />
      <p class="item-desc">Belted Dress</p>
    </div>
    <div class="item-card">
      <img
        src="https://cdn-icons-png.flaticon.com/128/2806/2806170.png"
        alt="shirt icon"
      />
      <p class="item-desc">Varsity Jacket</p>
    </div>
    <div class="item-card">
      <img
        src="https://cdn-icons-png.flaticon.com/128/1974/1974214.png"
        alt="socks icon"
      />
      <p class="item-desc">Baseball Hat</p>
    </div>
  </div>
</main>

We've now added a few cards with links to icons from Flaticon, a fantastic resource for finding high-quality, free icons and assets to enhance your projects. However, it's important to note that Flaticon has strict attribution requirements, so properly credit them in your work.

Our output should look like this:

Unstyled cards

Moving on, let's style our .brand-text <div>. In css/style.css, add the following code at the bottom of your stylesheet:

/* New code below: */
.brand-text {
  color: #463f3a;
  margin-top: 25px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}

Having transformed our .brand-text <div> into a Flexbox and centered the <h1> element within it, let's now focus on styling our .card-container <div>. This <div> houses each card, and our styling rules will make it responsive without needing a media query.

Add the following code:

.card-container {
  width: 100%;
  margin: 10px;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
}

The key element here is flex-wrap: wrap, which instructs our Flexbox to display its contents as a grid if the screen width permits and as a column when the user's screen is too narrow to accommodate more than one card.

The flex-wrap property in CSS controls how flex items are wrapped within a flex container. Setting flex-wrap: wrap allows the flex items to wrap onto multiple lines when the container's width is insufficient to accommodate all items in a single row. This ensures that the items remain responsive and their layout is adjusted based on the available screen space.

Our output looks funky right now, but it will look much better after we style the individual cards!

Unstyled flexwrap

Add some rules for each card:

.item-card {
  width: 300px;
  margin: 15px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 30px 40px;
  border-radius: 15px;
  background-color: #d9dbdd;
}

.title-text {
  width: 100%;
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: center;
}

img {
  width: 70%;
  margin: 15px;
}

That looks better!

Styled cards

We applied additional Flexbox styling to each card to achieve this look, defined a fixed width, incorporated padding and margin, and applied a border-radius. We also styled the caption text for each card and set a fixed size for the <img> elements within each card.


7. A Media Query Free Future

Media queries have a few downsides - we end up re-writing a fair amount of CSS (hello bugs!) for many different devices, many of which we never see our content on. In addition, this process leads to potentially widely different user experiences, some of which could even cause our sites to break.

New CSS tools to help us write fewer media queries have recently gotten browser support, such as clamp(), min(), max(), and minmax().

MDN has extensive documentation on these CSS functions - follow the links above to check them out.

Even with their browser support, these CSS math functions are still officially in a draft state, so we don't cover them in-depth. However, you should try implementing one (we'd recommend clamp()) just to test the waters!


8. AI-Assisted Responsive Testing

Learning objective: Use AI to analyze responsive performance across devices.

Testing on every device is hard. You can use AI tools to simulate or code-review your responsiveness.

Prompting for Responsiveness

If you have a desktop layout, you can ask an LLM to "responsify" it.

Prompt Example:

"I have a Flexbox row of 3 cards. Here is the CSS. Write the media queries necessary to stack them vertically on mobile and display them as a 2x2 grid on tablets. Use a mobile-first approach."

Analyzing Visual Balance

You can describe the issue to an AI to get logic fixes.

"On mobile, my H1 header is breaking onto three lines and taking up too much vertical space. Suggest a clamp() function or responsive typography strategy to fix this."

(Note: clamp(min, preferred, max) is a powerful CSS function for fluid sizing).


9. Lab: Designing for Every Device

Learning objective: Build a responsive landing page that adapts across desktop, tablet, and mobile.

Scenario: You are launching "VisionAPI," a service that lets users upload images for object detection. You need a landing page.

Step 1: Mobile-First Setup

  1. Add the viewport meta tag to your HTML.
  2. Write the CSS for the "Features" section assuming a mobile screen (iPhone width).
    • The cards should be stacked vertically (flex-direction: column).
    • Font size should be readable (e.g., 1rem).

Step 2: Tablet Breakpoint (768px)

  1. Add a media query: @media (min-width: 768px) { ... }.
  2. Inside this query, switch the layout to a 2-column grid.
  3. Adjust padding to utilize the extra whitespace.

Step 3: Desktop Breakpoint (1024px)

  1. Add a media query: @media (min-width: 1024px) { ... }.
  2. Switch the layout to a single row (3 columns).
  3. Increase the font size of the main Headline (h1) to make it impactful.

Step 4: AI Optimization

  1. Task: Copy your CSS into an AI Chatbot.
  2. Prompt: "Check this CSS for redundant code. Are there any rules inside the media queries that are unnecessary because they inherit from the base styles? Suggest a cleaner structure."
  3. Implement the AI's suggestions to "DRY" (Don't Repeat Yourself) your code.

10. Wrap-Up and Reflection

Group Discussion:

  1. Why do we write min-width queries for Mobile-First, but max-width queries for Desktop-First?
  2. How does the rem unit help with accessibility (e.g., if a user changes their browser's default text size)?
  3. Did the AI find redundancies in your media queries?

Resources