030: Responsive Design
Learning outcomes
By the end, you can explain responsive design as a strategy; configure the viewport; create fluid wrappers, media, typography, Flexbox, and Grid; distinguish intrinsic responsiveness from breakpoints; prioritize content mobile-first; and test beyond device presets.
Prerequisites and retrieval
Retrieve fluid constraints, normal flow, Flexbox wrapping, and Grid auto-fit/minmax(). Identify which portfolio rules already respond without media queries. Responsive work begins with resilient defaults, not a list of phone models.
Terminology
- Responsive web design: Design approach letting layout adapt fluidly across devices, viewports, and user contexts. — Source: MDN: Responsive design
- Viewport: The browser area used to lay out the document, configured via viewport meta. — Source: MDN: Viewport meta tag
- Mobile-first: Start with simple narrow-space defaults, then enhance when space supports it (course term).
- Fluid layout: Sizes adapting continuously to available space using %/vw/clamp instead of fixed widths. — Source: MDN: Responsive design
- Intrinsic layout: Content and available space driving layout outcomes (auto-fit/minmax). — Source: CSS Sizing 3: Intrinsic sizes
- Breakpoint: Condition where design needs a discrete change (course term).
- Reflow: Content reflowing to remain readable without page-level two-dimensional scrolling (WCAG 1.4.10). — Source: WCAG 2.2: Reflow
- Responsive media: Images/video scaling with their container and ideally switching resources (srcset/sizes). — Source: MDN: Responsive images
- Container query (
@container): "A conditional rule that applies styles based on the size of a containing element, not the viewport." — Source: CSS Containment Module Level 3: Container Queries — deferred to 038; this lesson focuses on viewport and intrinsic responsiveness - Intrinsic sizing vs extrinsic: "Intrinsic sizing is driven by content; extrinsic sizing is imposed from outside (fixed width)." — Source: CSS Sizing Level 3: Intrinsic vs Extrinsic
Mental model: a resilient system, not three screenshots
A design is responsive when relationships survive variation: narrow and wide windows, zoom, larger default fonts, long translations, keyboard focus, reduced motion, portrait/landscape, and unexpected content. “Desktop, tablet, mobile” screenshots are samples, not the system.
Build in this order:
- Semantic source order and normal flow.
- Fluid sizes with useful min/max constraints.
- Flexible media and readable measure.
- Intrinsic Flexbox/Grid behavior.
- Media queries only where content demonstrates a need.
Mobile-first is not “design only for phones.” Narrow defaults force content priority and usually create simpler CSS. Wider conditions progressively add columns, spacing, or alignment without undoing rigid desktop assumptions.
Beginner example: responsive portfolio without queries
Ensure the document head contains:
<meta name="viewport" content="width=device-width, initial-scale=1">
Without this, mobile browsers may use a wider layout viewport and scale the page down. Do not add maximum-scale=1 or user-scalable=no; users need zoom.
html { box-sizing: border-box; }
*, *::before, *::after { box-sizing: inherit; }
body {
margin: 0;
color: rgb(30 41 59);
background: rgb(248 250 252);
font-family: system-ui, sans-serif;
line-height: 1.6;
}
.site-shell {
inline-size: min(100% - 2rem, 70rem);
margin-inline: auto;
}
img,
svg,
video {
display: block;
max-inline-size: 100%;
block-size: auto;
}
.hero {
padding-block: clamp(2.5rem, 8vw, 7rem);
}
.hero h1 {
max-inline-size: 14ch;
font-size: clamp(2.25rem, 1.6rem + 3vw, 5rem);
line-height: 1.05;
}
.project-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(100%, 18rem), 1fr));
gap: clamp(1rem, 3vw, 1.5rem);
}
The shell subtracts 2rem for edges and caps at 70rem. Images never exceed containers. Hero spacing and type scale within bounds. The grid creates as many useful tracks as fit. Drag the viewport one pixel at a time: changes are continuous, and cards rearrange based on their minimum rather than a device name.
Keep body text at a stable readable size. Fluid display text is optional and bounded; do not use viewport units alone.
Intermediate example: responsive image resources
CSS can resize an image, but it does not reduce downloaded bytes. HTML can let the browser choose a source:
<img
src="images/library-800.jpg"
srcset="images/library-480.jpg 480w,
images/library-800.jpg 800w,
images/library-1280.jpg 1280w"
sizes="(min-width: 48rem) 33vw, 100vw"
width="1280"
height="720"
loading="lazy"
alt="Library finder showing search results and opening hours">
srcset describes candidate intrinsic widths. sizes approximates the rendered slot: around one-third viewport on wider grids, full viewport on narrow layouts. The browser combines slot, density, cache, and other factors to choose. src remains a fallback. Width/height reserve aspect ratio; loading="lazy" can defer below-the-fold project images, but do not lazy-load the likely largest above-the-fold hero image.
For art direction, <picture> can supply a different crop at a condition, but use it only when composition meaningfully changes. Pure resolution switching belongs in srcset.
Content and component decisions
Responsive design is also editorial. Navigation labels should be concise but meaningful. Project cards need robust titles, variable descriptions, and visible actions. Do not hide essential content just because the viewport is narrow. Reorder the HTML by importance rather than using CSS order.
Use Flexbox for a header that naturally wraps:
.site-header {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
gap: 1rem 2rem;
}
.nav-list { display: flex; flex-wrap: wrap; gap: 0.5rem; }
This may need no menu transformation for a small portfolio. A JavaScript-driven disclosure menu adds keyboard, focus, naming, and state requirements; do not add it merely because mobile screenshots traditionally show a hamburger icon.
Optional advanced example: user preference
If the portfolio has nonessential motion:
.project-card {
transition: transform 180ms ease, box-shadow 180ms ease;
}
.project-card:hover { transform: translateY(-0.2rem); }
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
scroll-behavior: auto !important;
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
The simplest option is no nonessential motion. If motion exists, reduce it based on user preference. Avoid motion that is necessary to understand content.
Mistakes, debugging, and DevTools
- Designing fixed desktop widths and shrinking afterward.
- Treating preset device widths as complete testing.
- Disabling zoom in the viewport meta element.
- Hiding important content on narrow screens.
- Using
100vwinside a page and causing horizontal overflow. - Sending one huge image despite rendering small thumbnails.
- Using viewport-only type sizes with no minimum/maximum.
- Rearranging visual order independently from focus/read order.
- Adding breakpoints where wrapping and intrinsic tracks already solve the problem.
Use responsive mode, but also resize a normal window. Test 320 CSS pixels, intermediate widths, landscape, 200% and 400% zoom, larger default font, long words, sparse and dense cards, keyboard navigation, and slow network. Find the first width where content becomes cramped or relationships fail; that observation can justify tomorrow's query.
Accessibility and performance
WCAG Reflow targets no loss and no page-level two-dimensional scrolling at an equivalent 320 CSS pixels wide, with exceptions for genuinely two-dimensional content. Text must resize to 200%. Targets need adequate size and spacing; focus should never be clipped. Layout should work in both orientations unless one is essential.
Responsive performance means sending proportionate resources. Compress images, use srcset/sizes, reserve dimensions, avoid unnecessary fonts, and keep critical above-the-fold content promptly discoverable. Test throttled network and CPU rather than assuming a small viewport means a fast connection.
Deep dive: responsive design has several independent axes
Responsive design is not only viewport width. Test changes in:
- available inline size;
- text size and zoom;
- content length and localization;
- input method;
- user motion/contrast/color preferences;
- image/network constraints;
- orientation;
- component container size.
A layout that works at 375px but breaks at 200% zoom on a 1280px viewport is not robust.
Deep dive: intrinsic responsiveness before queries
Start with tools that adapt continuously:
.shell {
width: min(100% - 2rem, 72rem);
margin-inline: auto;
}
.cards {
display: grid;
grid-template-columns:
repeat(auto-fit, minmax(min(100%, 17rem), 1fr));
gap: 1rem;
}
.hero h1 {
font-size: clamp(2rem, 1.2rem + 4vw, 4.5rem);
}
Only add a media query when a relationship needs a discrete change.
Worked example: responsive media card
<article class="media-card">
<img
class="media-card__image"
src="course-640.jpg"
srcset="
course-320.jpg 320w,
course-640.jpg 640w,
course-960.jpg 960w"
sizes="(min-width: 48rem) 40vw, 100vw"
alt="Laptop showing a CSS layout exercise"
width="960"
height="640"
>
<div class="media-card__content">
<h2>CSS Layout</h2>
<p>Practice Flexbox and Grid with real constraints.</p>
</div>
</article>
.media-card {
display: grid;
gap: 1rem;
}
.media-card__image {
width: 100%;
aspect-ratio: 3 / 2;
object-fit: cover;
border-radius: 0.75rem;
}
HTML chooses an appropriate image resource. CSS controls layout/crop. Responsive design is a cooperation between markup and styling.
Deep dive: touch and pointer assumptions
Do not hide essential actions behind hover:
.card__actions {
opacity: 1;
}
A hover enhancement can be optional:
@media (hover: hover) and (pointer: fine) {
.card {
transition: transform 160ms ease;
}
.card:hover {
transform: translateY(-2px);
}
}
The card remains complete without hover.
Worked example: responsive table strategy
Tables can legitimately need horizontal scrolling:
<div class="table-scroll" tabindex="0">
<table>
...
</table>
</div>
.table-scroll {
overflow-x: auto;
max-inline-size: 100%;
}
Do not convert tabular data into unrelated cards only to avoid scrolling if the row/column relationships matter. The responsive solution should preserve information architecture.
If a scroll wrapper is keyboard-focusable for discoverability, give it an accessible label when needed and ensure the focus treatment is visible.
Deep dive: localization stress test
Try:
- English button:
Save - German-like expansion:
Änderungen speichern - long user-generated name;
- Arabic/Hebrew direction;
- 200% text size.
Avoid fixed widths that assume English copy:
/* fragile */
.button {
width: 100px;
}
Prefer content-aware sizing:
.button {
min-block-size: 2.75rem;
padding-inline: 1rem;
}
Responsive test matrix
For each major component, record:
| Test | Question |
|---|---|
| 320 CSS px | Does content reflow without page-level two-axis scrolling? |
| 200% zoom | Does content remain operable and readable? |
| long content | Does the component grow/wrap instead of clipping? |
| keyboard | Are focus order and indicators usable? |
| touch | Are controls large enough and not hover-dependent? |
| slow image/font | Is layout stable while assets load? |
| reduced motion | Are non-essential animations reduced? |
| high contrast | Are borders/focus states still perceivable? |
Treat this as a product test, not a final screenshot check.
Tiered exercises
Checkpoint: build a responsive test matrix
Create rows for 320px, an arbitrary middle width, a wide window, 200% text, 400% zoom, landscape, keyboard-only input, and a slow connection. Create columns for navigation, hero, cards, images, form controls, focus, and page overflow. Record pass/fail observations rather than “looks good.” Fix failures in the simplest shared rule before adding a breakpoint.
Content variation belongs in the matrix too. Test zero optional cards, one card, many cards, a three-line heading, a long email address, a missing image, and translated navigation. Intrinsic layout often handles these cases automatically. When it does not, identify whether the failure comes from content breaking, a minimum size, source order, or a genuinely discrete relationship change.
Responsive design is complete only when resource behavior matches visual behavior. Use the Network panel to compare image candidates at narrow and wide slots and at different pixel densities. A visually fluid 200px thumbnail that downloads a multi-megabyte 3000px source is responsive in layout but not in delivery.
Keep a short decision log during testing: observation, responsible rule, smallest fix, and regression checks. For example, “At 37rem the third navigation label wraps alone; wrapping remains readable, so no breakpoint is needed.” Not every visual change is a failure. This record prevents later contributors from adding device-specific patches for behavior that was intentionally accepted.
Foundation: Add the viewport meta element, fluid shell, flexible media, and readable prose width. Verify no horizontal page scrollbar at 320px.
Core: Build an intrinsic auto-fitting project grid and bounded hero type/spacing. Test continuously rather than at three presets.
Stretch: Create responsive srcset for one project image and document its likely rendered slot. Add reduced-motion handling only if the portfolio includes nonessential transitions or animation.
<meta name="viewport" content="width=device-width, initial-scale=1">
<img src="images/library-800.jpg"
srcset="images/library-480.jpg 480w, images/library-800.jpg 800w, images/library-1280.jpg 1280w"
sizes="(min-width: 48rem) 33vw, 100vw"
width="1280" height="720" loading="lazy"
alt="Library finder showing search results and opening hours">
.site-shell { inline-size: min(100% - 2rem, 70rem); margin-inline: auto; }
img, svg, video { display: block; max-inline-size: 100%; block-size: auto; }
.hero { padding-block: clamp(2.5rem, 8vw, 7rem); }
.hero h1 { max-inline-size: 14ch; font-size: clamp(2.25rem, 1.6rem + 3vw, 5rem); }
.project-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 18rem), 1fr)); gap: clamp(1rem, 3vw, 1.5rem); }
Recap and exit questions
Responsive design is a layered strategy: semantic order, fluid constraints, flexible resources, intrinsic layout, then justified conditional changes. Test content and user variation, not just devices.
- Why is responsive design broader than media queries?
- What does the viewport meta element change?
- How does an auto-fit Grid respond without breakpoints?
- Why are
srcsetandsizesperformance features? - What observation justifies a breakpoint?
