Responsive Design & Media Queries

Responsive Design & Media Queries

Responsive design means building websites that adapt to different screens, input methods, user preferences, and content sizes.

A responsive page should work on:

  • small phones,
  • large phones,
  • tablets,
  • laptops,
  • desktop monitors,
  • zoomed browsers,
  • landscape and portrait orientations,
  • and split-screen browser windows.

The goal is not to make one perfect layout for every device. The goal is to create layouts that remain readable, usable, and stable as available space changes.


1. Responsive Design Is Not Just Screen Size

Responsive design includes:

  • layout changes,
  • readable typography,
  • flexible images,
  • comfortable touch targets,
  • accessible zoom behavior,
  • support for different input devices,
  • and respecting user settings such as reduced motion or dark mode.
.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 1rem;
}

This grid responds without a media query because the columns are fluid.

phonetabletdesktop window

2. The Viewport Meta Tag

Responsive design starts in the document <head>.

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

Without this tag, many mobile browsers pretend the page is around 980px wide and zoom the site out. Text becomes tiny and users must pinch to read.

Parts:

  • width=device-width: use the real device viewport width.
  • initial-scale=1.0: start at normal zoom.

Do not disable zoom:

<!-- Bad: prevents users from zooming -->
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">

Users need zoom for accessibility. Let them zoom.


3. Mobile-First CSS

Mobile-first means writing the smallest layout first, then adding larger layouts with min-width.

.layout {
  display: grid;
  gap: 1rem;
}

@media (min-width: 768px) {
  .layout {
    grid-template-columns: 260px 1fr;
  }
}

Why mobile-first is recommended:

  • The base CSS is simpler.
  • Small screens get fewer overrides.
  • Larger screens progressively add complexity.
  • It matches how content should work: readable first, enhanced later.

Desktop-first uses max-width:

.layout {
  display: grid;
  grid-template-columns: 260px 1fr;
}

@media (max-width: 767px) {
  .layout {
    grid-template-columns: 1fr;
  }
}

Desktop-first can work, but it often requires undoing complex desktop styles for mobile.


4. Media Query Syntax

Media queries apply CSS only when conditions match.

@media (min-width: 768px) {
  .cards {
    grid-template-columns: repeat(2, 1fr);
  }
}

The general structure:

@media media-type and (condition) {
  selector {
    property: value;
  }
}

Most modern CSS omits screen because screen is the common default target.

@media (min-width: 1024px) {
  .page {
    max-width: 1200px;
  }
}

Range syntax is also available in modern CSS:

@media (600px <= width <= 900px) {
  .preview {
    background: #dbeafe;
  }
}

The classic equivalent:

@media (min-width: 600px) and (max-width: 900px) {
  .preview {
    background: #dbeafe;
  }
}

5. Common Media Features

Useful media features:

FeatureExampleUse
min-width@media (min-width: 768px)Mobile-first breakpoints
max-width@media (max-width: 767px)Desktop-first overrides
orientation@media (orientation: landscape)Landscape or portrait changes
hover@media (hover: hover)Hover-capable devices
pointer@media (pointer: coarse)Touch target adjustments
prefers-reduced-motion@media (prefers-reduced-motion: reduce)Reduce animation
prefers-color-scheme@media (prefers-color-scheme: dark)Theme preference
prefers-contrast@media (prefers-contrast: more)Higher contrast styles

Example for touch devices:

@media (pointer: coarse) {
  .button {
    min-height: 44px;
  }
}

Example for hover:

@media (hover: hover) {
  .card:hover {
    transform: translateY(-3px);
  }
}

This avoids hover-only interactions on touch devices.


6. Breakpoints

Breakpoints are widths where the layout needs to change.

Do not choose breakpoints only because a device exists. Choose them when your content starts to break.

Typical starting points:

LabelWidthCommon use
Small480pxLarger phones
Medium768pxTablets
Large1024pxLaptops
Extra large1280pxDesktops
Wide1536pxLarge monitors

Example:

.cards {
  display: grid;
  gap: 1rem;
}

@media (min-width: 640px) {
  .cards {
    grid-template-columns: repeat(2, 1fr);
  }
}

@media (min-width: 1024px) {
  .cards {
    grid-template-columns: repeat(3, 1fr);
  }
}

The right question is: "At what width does this layout become awkward?"

48076810241280Add breakpoints when the content needs them.

7. Fluid Layouts Before Breakpoints

Responsive design works best when the layout is fluid before breakpoints are added.

.container {
  width: min(100% - 2rem, 1200px);
  margin-inline: auto;
}

This means:

  • Use full available width minus 2rem.
  • Never exceed 1200px.
  • Center the container.

Fluid grid:

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

This often removes the need for several breakpoints.


8. Viewport Units

Viewport units are based on the browser viewport.

UnitMeaning
vw1% of viewport width
vh1% of viewport height
vmin1% of the smaller viewport dimension
vmax1% of the larger viewport dimension
svhsmall viewport height
lvhlarge viewport height
dvhdynamic viewport height

Use modern viewport height units for mobile layouts:

.hero {
  min-height: 100svh;
}

100vh can be awkward on mobile because browser address bars appear and disappear. svh, lvh, and dvh give more control.


9. Fluid Typography with clamp()

clamp() gives a value a minimum, preferred value, and maximum.

h1 {
  font-size: clamp(2rem, 6vw, 5rem);
}

Meaning:

minimum: 2rem
preferred: 6vw
maximum: 5rem

Use it for headings, spacing, and layout sizes.

.section {
  padding-block: clamp(3rem, 8vw, 7rem);
}
minimumfluidmaximumclamp(2rem, 6vw, 5rem)

clamp() is often better than many typography breakpoints.


10. Responsive Images

Images should not overflow their container.

img {
  max-width: 100%;
  height: auto;
}

For fixed-shape image cards:

.thumbnail {
  aspect-ratio: 16 / 9;
  object-fit: cover;
  width: 100%;
}

HTML can also provide multiple image sources:

<picture>
  <source srcset="hero-wide.jpg" media="(min-width: 900px)">
  <img src="hero-small.jpg" alt="Students learning CSS">
</picture>

Use srcset and sizes when you want the browser to choose the best image size:

<img
  src="course-800.jpg"
  srcset="course-400.jpg 400w, course-800.jpg 800w, course-1200.jpg 1200w"
  sizes="(min-width: 1024px) 33vw, (min-width: 640px) 50vw, 100vw"
  alt="Course preview">

11. Responsive Navigation

Navigation often changes shape across screen sizes.

.nav {
  display: flex;
  flex-direction: column;
  gap: 1rem;
}

@media (min-width: 768px) {
  .nav {
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
  }
}

Mobile navigation should be:

  • easy to tap,
  • keyboard accessible,
  • readable at zoom,
  • and not dependent on hover.

Touch targets should usually be at least around 44px high.

.nav-link {
  min-height: 44px;
  display: inline-flex;
  align-items: center;
}

12. Responsive Tables

Wide tables are hard on small screens. A simple solution is horizontal scrolling.

<div class="table-scroll">
  <table>
    ...
  </table>
</div>
.table-scroll {
  overflow-x: auto;
}

.table-scroll table {
  min-width: 640px;
}

Do not shrink table text until it becomes unreadable. Scrolling is often better than destroying the table structure.


13. Container Queries

Media queries respond to the viewport. Container queries respond to a component's parent container.

.course-card-wrapper {
  container-type: inline-size;
}

.course-card {
  display: grid;
  gap: 1rem;
}

@container (min-width: 460px) {
  .course-card {
    grid-template-columns: 160px 1fr;
  }
}

This lets the same component adapt whether it appears in a narrow sidebar or a wide main content area.

narrow container: stackedwide container: side by side

Container queries are covered again in the modern CSS chapter, but they are important for responsive design too.


14. Responsive Accessibility

A responsive site must remain accessible.

Check:

  • Text remains readable at 200% zoom.
  • Layout does not require horizontal scrolling for normal content.
  • Buttons and links are easy to tap.
  • Focus states remain visible.
  • Content order still makes sense.
  • Hidden menus are keyboard accessible.
  • Hover-only behavior has a touch and keyboard alternative.

Bad:

.card {
  height: 180px;
  overflow: hidden;
}

This may clip text when users increase font size.

Better:

.card {
  min-height: 180px;
}

Let content grow when needed.


15. Testing Responsive Design

Use multiple testing methods:

  1. Resize the browser manually.
  2. Use DevTools responsive mode.
  3. Test real mobile devices when possible.
  4. Zoom the page to 200%.
  5. Test keyboard navigation.
  6. Test landscape orientation.
  7. Test with slow network conditions.
  8. Check long words, long names, and real content.

Responsive bugs often appear between common device sizes, not exactly at common device sizes. Drag the viewport slowly and watch where the layout breaks.


16. Debugging Checklist

When a layout breaks on smaller screens, ask:

  1. Is the viewport meta tag present?
  2. Is any element wider than the viewport?
  3. Is a fixed width causing overflow?
  4. Would max-width: 100% fix images or media?
  5. Would minmax(0, 1fr) fix grid overflow?
  6. Would min-width: 0 fix flex overflow?
  7. Is text clipped by fixed height?
  8. Are breakpoints based on content, not device names?
  9. Can clamp() reduce breakpoint complexity?
  10. Would container queries make the component more reusable?
Check viewport tag and overflowCheck fixed widths, images, and mediaCheck flex/grid shrinking behaviorTest zoom, keyboard, and real content

17. Complete Example: Responsive Course Layout

.course-page {
  width: min(100% - 2rem, 1180px);
  margin-inline: auto;
  padding-block: clamp(2rem, 6vw, 5rem);
}

.course-hero {
  display: grid;
  gap: 1.5rem;
}

.course-title {
  font-size: clamp(2.25rem, 7vw, 5rem);
  line-height: 1;
}

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

.course-image {
  width: 100%;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  border-radius: 1rem;
}

@media (min-width: 900px) {
  .course-hero {
    grid-template-columns: minmax(0, 1fr) 360px;
    align-items: center;
  }
}

This layout uses:

  • fluid container width,
  • fluid section spacing,
  • fluid heading size,
  • responsive card grid,
  • responsive media,
  • and one breakpoint for the hero layout.

18. Mini Practice

Build a responsive card grid:

  • One column by default.
  • Two columns at 640px.
  • Three columns at 1024px.
  • Gap of 1rem.

Possible answer:

.cards {
  display: grid;
  gap: 1rem;
}

@media (min-width: 640px) {
  .cards {
    grid-template-columns: repeat(2, 1fr);
  }
}

@media (min-width: 1024px) {
  .cards {
    grid-template-columns: repeat(3, 1fr);
  }
}

Now try the fluid version without those breakpoints:

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

Both approaches are valid. The second is often simpler when exact column counts are not required.