Viewport and Mobile-First Approach
Viewport and Mobile-First Approach
The Viewport Meta Tag
The viewport meta tag tells mobile browsers how to render the page. Without it, mobile browsers assume a desktop-width viewport (typically 980px) and scale the page down, making text tiny and unreadable.
<meta name="viewport" content="width=device-width, initial-scale=1.0">
width=device-widthsets the layout viewport width to match the device's screen widthinitial-scale=1.0sets the initial zoom level to 100%
Common mistakes that harm accessibility:
maximum-scale=1.0prevents users from zooming — this is an accessibility violation under WCAG 2.1 SC 1.4.4user-scalable=nodisables pinch-to-zoom entirely — a critical barrier for low-vision users
Why Mobile-First Strategy?
Mobile-first means writing base CSS for the smallest screens, then progressively enhancing for larger viewports with min-width media queries. This approach has three key advantages:
- Performance: Mobile devices receive only the essential CSS they need. Extra rules for larger screens are fetched only when the viewport is wide enough to need them.
- Progressive Enhancement: Starting simple and adding complexity prevents broken layouts on small screens. A desktop-first approach often leaves gaps when viewed on mobile.
- Browser Support:
min-widthqueries are better supported in older browsers thanmax-widthqueries.
Mobile-First CSS Example
/* Base: mobile (single column) */
.card-grid {
display: grid;
grid-template-columns: 1fr;
gap: 1rem;
padding: 1rem;
}
/* Tablet: 2 columns */
@media (min-width: 768px) {
.card-grid {
grid-template-columns: repeat(2, 1fr);
gap: 1.5rem;
padding: 1.5rem;
}
}
/* Desktop: 3 columns */
@media (min-width: 1024px) {
.card-grid {
grid-template-columns: repeat(3, 1fr);
max-width: 1200px;
margin: 0 auto;
}
}
The base styles define the mobile layout. Each min-width breakpoint adds capabilities for wider screens. This keeps the mobile experience lean and fast while enabling richer layouts on tablets and desktops.
Common Breakpoint Values
/* Mobile: 0 - 767px (base styles, no query needed)
Tablet: 768px - 1023px */
@media (min-width: 768px) { /* ... */ }
/* Desktop: 1024px - 1439px */
@media (min-width: 1024px) { /* ... */ }
/* Large desktop: 1440px+ */
@media (min-width: 1440px) { /* ... */ }
Avoid targeting specific devices (iPhone, iPad). Instead, design for breakpoints that reflect your content's needs. Content will naturally break at different points depending on layout complexity.
Fluid Typography and Spacing
Fluid Typography and Spacing
Why Fluid Over Fixed?
Fixed font sizes create awkward gaps between breakpoints. A heading sized at 24px on mobile and 48px on desktop stays 24px until the exact breakpoint, then jumps to 48px. Fluid typography scales smoothly between a minimum and maximum size, eliminating these jumps entirely.
Fluid Type with clamp()
The clamp() function takes three arguments: a minimum value, a preferred value (often using viewport units), and a maximum value. It returns the preferred value as long as it stays within the min/max bounds.
html {
/* Scales from 16px at 320px viewport to 20px at 1200px viewport */
font-size: clamp(1rem, 0.875rem + 0.5vw, 1.25rem);
}
h1 {
font-size: clamp(1.75rem, 1rem + 3vw, 3.5rem);
line-height: 1.2;
margin-bottom: 0.5em;
}
h2 {
font-size: clamp(1.25rem, 1rem + 1.5vw, 2.25rem);
line-height: 1.3;
}
p {
font-size: clamp(1rem, 0.95rem + 0.25vw, 1.125rem);
line-height: 1.6;
}
The 0.875rem + 0.5vw portion is the preferred value — it grows with the viewport width. At 320px wide, this equals roughly 16px; at 1200px, it reaches about 20px. The browser handles the interpolation automatically.
Fluid Spacing Scale
Define a consistent spacing scale using CSS custom properties and clamp():
:root {
--space-xs: clamp(0.25rem, 0.2rem + 0.25vw, 0.5rem);
--space-sm: clamp(0.5rem, 0.4rem + 0.5vw, 1rem);
--space-md: clamp(1rem, 0.8rem + 1vw, 2rem);
--space-lg: clamp(2rem, 1.5rem + 2.5vw, 4rem);
--space-xl: clamp(3rem, 2rem + 5vw, 6rem);
}
.section {
padding: var(--space-lg) var(--space-md);
}
.card {
padding: var(--space-sm);
margin-bottom: var(--space-md);
}
This ensures spacing scales proportionally across all screen sizes without requiring media queries for every element.
Container Queries (Modern Component-Responsive Design)
Container queries let components respond to their parent container's width rather than the viewport. This makes components truly reusable across different layout contexts.
.card-container {
container-type: inline-size;
container-name: card;
}
@container card (min-width: 400px) {
.card {
display: flex;
gap: 1rem;
}
.card img {
width: 200px;
flex-shrink: 0;
}
.card-content {
flex: 1;
}
}
A card component might display stacked on mobile but side-by-side when its container is wide enough — regardless of whether that container is in a narrow sidebar or a wide main area.
Responsive Images and Media
Responsive Images and Media
The <picture> Element for Art Direction
The <picture> element lets you serve completely different images based on viewport width, format support, or pixel density. This is called art direction — showing a tightly cropped close-up on mobile and a wide landscape on desktop.
<picture>
<source srcset="/images/hero-1200.webp" media="(min-width: 1024px)" type="image/webp">
<source srcset="/images/hero-800.webp" media="(min-width: 640px)" type="image/webp">
<source srcset="/images/hero-400.webp" type="image/webp">
<img src="/images/hero-800.jpg" alt="Mountain landscape"
width="1200" height="600" loading="lazy">
</picture>
The browser evaluates <source> elements in order and uses the first matching one. The <img> fallback is required and is what screen readers and older browsers see.
Responsive <img> with srcset and sizes
The srcset attribute lets the browser choose the optimal image based on device pixel ratio and viewport width:
<img
src="/images/photo-800.jpg"
srcset="/images/photo-400.jpg 400w,
/images/photo-800.jpg 800w,
/images/photo-1200.jpg 1200w"
sizes="(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 33vw"
alt="Descriptive alt text"
loading="lazy"
decoding="async"
>
wdescriptors tell the browser the actual pixel width of each image filesizestells the browser how wide the image will render at different viewport widths- The browser downloads the best match — a 2x Retina display at 640px viewport might select the 1200w image
Always include loading="lazy" for below-the-fold images and decoding="async" to avoid blocking the main thread.
Responsive Video with Aspect Ratio
Maintain consistent video proportions without relying on JavaScript or padding hacks:
.video-wrapper {
position: relative;
padding-bottom: 56.25%; /* 16:9 aspect ratio */
height: 0;
overflow: hidden;
}
.video-wrapper iframe {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
border: 0;
}
Or use the modern aspect-ratio property for native support:
.video-wrapper {
aspect-ratio: 16 / 9;
width: 100%;
}
Responsive Tables
Data tables break on narrow screens. Common solutions:
.table-wrapper {
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}
@media (max-width: 640px) {
table { font-size: 0.875rem; }
th, td { padding: 0.5rem; white-space: nowrap; }
}
For complex tables, consider redesigning the layout entirely — stack rows into card-like blocks on mobile rather than requiring horizontal scrolling.
Quiz
1. Why is the viewport meta tag important for mobile web development?
2. Which CSS function allows you to create typography that scales smoothly between a minimum and maximum size based on viewport width?
3. What is the key difference between media queries and container queries?
Flashcards
Question
What does the viewport meta tag do and why should you avoid `user-scalable=no`?
Click to reveal answer
Answer
The viewport meta tag tells mobile browsers how to render the page width and zoom level. `user-scalable=no` disables pinch-to-zoom, which is a WCAG accessibility violation (SC 1.4.4) that prevents low-vision users from enlarging content.
Question
Explain the three arguments of the `clamp()` function in fluid typography.
Click to reveal answer
Answer
`clamp(minimum, preferred, maximum)` — The minimum is the smallest allowed value. The preferred value (often using vw units) scales with the viewport. The maximum is the largest allowed value. The browser returns the preferred value as long as it stays within bounds.
Question
Why use `min-width` media queries instead of `max-width` for mobile-first design?
Click to reveal answer
Answer
Mobile-first design starts with base styles for small screens and progressively enhances for larger ones. `min-width` queries add styles as the viewport grows, ensuring mobile devices receive only essential CSS. `max-width` queries work in reverse (desktop-first), which can result in incomplete or broken mobile layouts if desktop styles aren't explicitly overridden.
Revision Notes
Key Takeaways
- 1. Always include the viewport meta tag with `width=device-width, initial-scale=1.0` and never use `user-scalable=no` or `maximum-scale=1.0` as they violate accessibility standards
- 2. Write mobile-first CSS using `min-width` media queries — start with base styles for small screens and progressively add complexity for larger viewports
- 3. Use `clamp()` for fluid typography and spacing to create smooth scaling between minimum and maximum values without needing multiple breakpoints
- 4. Use the `picture` element for art direction (different crops for different sizes) and `srcset` with `w` descriptors for resolution switching (same image at different pixel densities)
- 5. Container queries (`container-type: inline-size`) let components respond to their parent container rather than the viewport, making them more reusable across different layout contexts
Interview Tips
- • When asked about responsive design, always mention the viewport meta tag first — it is the foundation without which no other technique works correctly on mobile
- • Explain mobile-first as a performance strategy, not just a layout convention — mobile devices receive minimal CSS upfront, reducing parse time and paint time
- • Be ready to compare `srcset` with the `picture` element: `srcset` handles resolution switching (same image, different sizes), while `picture` handles art direction (completely different images or crops)
- • Discuss container queries as the modern evolution of responsive components — they solve the problem where a card looks great in the main content but breaks in a sidebar
- • Know the accessibility implications: always allow user zoom, provide skip links for keyboard navigation, and use `aria-expanded` on toggle buttons
Cheat Sheet
Responsive Design Cheat Sheet
Viewport Meta Tag
<meta name="viewport" content="width=device-width, initial-scale=1.0">
Mobile-First Media Query
/* Base: mobile styles */
@media (min-width: 768px) { /* Tablet */ }
@media (min-width: 1024px) { /* Desktop */ }
Fluid Typography
font-size: clamp(1rem, 0.875rem + 0.5vw, 1.25rem);
Fluid Spacing
--space-md: clamp(1rem, 0.8rem + 1vw, 2rem);
padding: var(--space-md);
Container Query
.container { container-type: inline-size; }
@container (min-width: 400px) { /* styles */ }
Responsive Image
<img srcset="img-400.jpg 400w, img-800.jpg 800w"
sizes="(max-width: 640px) 100vw, 50vw"
src="img-800.jpg" alt="...">
Responsive Video
.video { aspect-ratio: 16 / 9; width: 100%; }
Common Breakpoints
- Mobile: base (0–767px)
- Tablet:
min-width: 768px - Desktop:
min-width: 1024px - Large:
min-width: 1440px