Notifications

No notifications

/Phase 1

Responsive Design

Responsive Design — One Codebase, Every Screen

Responsive design ensures your site looks and works great on any device — from phones to ultra-wide monitors. The core tools are media queries, fluid units, and modern CSS features like container queries.

Mobile-First Approach

Write base styles for small screens first, then add complexity with min-width media queries:

/* Base = mobile */
.grid { display: flex; flex-direction: column; }

/* Tablet and up */ @media (min-width: 768px) { .grid { flex-direction: row; } }

Common Breakpoints

BreakpointTargetMedia Query
480pxSmall phones@media (min-width: 480px)
768pxTablets@media (min-width: 768px)
1024pxLaptops@media (min-width: 1024px)
1280pxDesktops@media (min-width: 1280px)

Fluid Typography

Use clamp() to scale font sizes smoothly between a minimum and maximum:

h1 { font-size: clamp(1.5rem, 4vw, 3rem); }

Container Queries

Container queries style children based on their parent's size (not the viewport):

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

@container (min-width: 400px) { .card { flex-direction: row; } }

Responsive Images

<img
  srcset="photo-400.jpg 400w, photo-800.jpg 800w"
  sizes="(max-width: 600px) 100vw, 50vw"
  src="photo-800.jpg"
  alt="Responsive photo"
/>

The browser picks the best source for the device's screen size and resolution.

On this page

Detailed Theory

Why Responsive Design Exists

In 2007 every website assumed a 1024-pixel desktop monitor. Then phones happened. Today over half of all web traffic is mobile, screens range from a 320 px watch face to an 8K TV, and you don't know which one your user is on. Responsive design means writing one layout that adapts to all of them — instead of building a separate "mobile site".

Step Zero: The Viewport Tag

If you forget this single line, mobile browsers will pretend they're a 980 px desktop and shrink everything. Add it to every page's :

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

That's it. Now your CSS actually sees the real device width.

Mobile-First Mindset

The trick is to design for the smallest screen first, then progressively add layout for bigger ones. Why? Because a small phone forces you to keep things simple, and "adding columns when there's space" is much easier than "removing columns when there's not".

/* Base styles — phone */
.card { padding: 12px; font-size: 1rem; }

/* When the screen gets bigger, enhance */ @media (min-width: 768px) { .card { padding: 24px; font-size: 1.125rem; } }

Notice we use min-width, not max-width. That's the mobile-first signal: "starting from this width, do extra stuff."

Media Queries 101

A media query says "apply these styles only when the condition is true."

@media (min-width: 768px) { /* ≥ tablet */ }
@media (min-width: 1024px) { /* ≥ small laptop */ }
@media (min-width: 1280px) { /* ≥ desktop */ }
@media (prefers-color-scheme: dark) { /* user wants dark mode */ }
@media (prefers-reduced-motion: reduce) { /* user is sensitive to motion */ }

Common Breakpoints

There's no "official" set, but a sensible default mirrors Tailwind:

NameMin widthRoughly
sm640 pxLarge phone / small tablet
md768 pxTablet
lg1024 pxSmall laptop
xl1280 pxDesktop
2xl1536 pxBig monitor

Don't invent breakpoints based on devices ("iPhone 14 is 390 px so I'll target 390 px"). Pick breakpoints based on where *your design* breaks.

Flexible Sizing Beats Breakpoints

Before reaching for a media query, ask if a fluid technique can do the job for free.

max-width for Containers

.container { width: 100%; max-width: 1200px; margin-inline: auto; }

The container is fluid up to 1200 px, then stops growing. One rule, zero media queries.

Images that Behave

img, video { max-width: 100%; height: auto; display: block; }

Images shrink to fit their parent and never overflow. Set this once, forget about it.

Auto-Fit Grids

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

Cards reflow from 1 column on a phone to 5 columns on a desktop without a single @media.

Units That Help You Be Fluid

UnitDescription
%Percent of parent — flexible widths
remRelative to root font-size — typography & spacing
vw / vh1% of viewport — hero sections
min(), max(), clamp()Mix fixed + fluid in one value

The clamp() Function — Fluid Without Breakpoints

clamp(min, preferred, max) reads as "be the *preferred* value, but never below *min* and never above *max*."

/* Font scales smoothly from 1rem on a phone to 2rem on a big screen */
h1 { font-size: clamp(1.5rem, 4vw, 3rem); }

/* Container width that's fluid but bounded */ .wrapper { width: clamp(320px, 90vw, 1200px); }

This is the modern shortcut for "fluid typography" and replaces piles of media queries.

Beginner Mistakes to Skip

1. Hard-coding pixel widths on cards or sidebars. Use max-width instead. 2. Forgetting the viewport meta tag — site looks fine on Chrome DevTools, broken on a real phone. 3. Pixel media queries based on phones instead of where the design breaks. 4. Tiny tap targets. Buttons should be at least 44 × 44 px so fingers can hit them. 5. Hover-only interactions. Touch devices don't have hover; provide a tap or visible button.

Intermediate: Container Queries — Components That Adapt to Themselves

Media queries respond to the *viewport*. Container queries respond to the *parent's size*. Why does this matter? Because the same card component might sit in a 300 px sidebar on one page and a 900 px main column on another — and you want it to look right in both, automatically.

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

@container (min-width: 400px) { .card { display: grid; grid-template-columns: 100px 1fr; } }

The card decides its own layout based on the room *it* has, not the page.

Intermediate: Responsive Images with srcset

Don't ship a 4 MB hero image to a phone:

<img
  src="hero-800.jpg"
  srcset="hero-400.jpg 400w, hero-800.jpg 800w, hero-1600.jpg 1600w"
  sizes="(max-width: 600px) 100vw, 800px"
  alt="…"
/>

The browser picks the smallest file that still looks crisp. Free performance win.

For art-direction (different *crops* for phone vs desktop), use :

<picture>
  <source media="(min-width: 800px)" srcset="hero-wide.jpg" />
  <img src="hero-square.jpg" alt="…" />
</picture>

Intermediate: Honour User Preferences

Modern OSes expose user settings; CSS can read them.

@media (prefers-color-scheme: dark) {
  :root { background: #111; color: #eee; }
}

@media (prefers-reduced-motion: reduce) { * { animation: none !important; transition: none !important; } }

Your site instantly feels considerate without users changing a thing.

Advanced: The New Viewport Units

vh on mobile has a long-running gotcha — it includes the area behind the address bar, so 100vh overflows. CSS now has:

UnitMeaning
svh*Small* viewport — when address bar is showing
lvh*Large* viewport — when address bar is hidden
dvh*Dynamic* viewport — adjusts as the bar appears/disappears

.hero { min-height: 100dvh; } /* always exactly the visible area */

Advanced: Logical Properties for Internationalisation

If your site might support Arabic, Hebrew, or vertical Japanese, use logical properties so layout flips automatically:

.card { padding-inline: 1rem; margin-block: 1rem; }

inline = direction of text (left/right in English, right/left in Arabic). block = perpendicular (top/bottom).

Advanced: @supports for Progressive Enhancement

Use a fancy property only if the browser supports it; otherwise fall back gracefully:

.card { display: block; }

@supports (display: grid) { .card { display: grid; } }

Testing Checklist

  • Open Chrome DevTools → Toggle Device Toolbar — try iPhone SE (smallest), iPad, and a 1440 px desktop.
  • Set browser font size to 200% (Ctrl + scroll). Layout should still work — that's why we use rem.
  • Run [Lighthouse](https://developer.chrome.com/docs/lighthouse) → Mobile audit. Aim for green on Accessibility & Performance.
  • Test on a real phone over a slow connection. Browser DevTools lie about touch and lie about network.

Practice Path

1. Take a desktop-only landing page and convert it to mobile-first using only min-width media queries. 2. Replace every fixed font-size: 32px with a clamp() expression and watch text scale smoothly. 3. Build a card component that switches from stacked (icon above text) to inline (icon beside text) using a container query, not a media query. 4. Add prefers-color-scheme: dark and prefers-reduced-motion support to one of your existing projects.