14 KiB
14 KiB
Responsive Design Patterns
Modern responsive design patterns and techniques for creating flexible, accessible layouts that work across all devices.
Mobile-First Approach
Start with mobile design and enhance for larger screens.
Why Mobile-First?
Benefits:
- Forces focus on essential content
- Better performance (smaller base CSS)
- Progressive enhancement mindset
- Easier to add features than remove them
Basic Pattern:
/* Base (mobile) styles - no media query */
.container {
width: 100%;
padding: 1rem;
}
/* Tablet and up */
@media (min-width: 768px) {
.container {
padding: 2rem;
}
}
/* Desktop and up */
@media (min-width: 1024px) {
.container {
max-width: 1200px;
margin: 0 auto;
padding: 3rem;
}
}
Breakpoints
Standard Breakpoints
:root {
/* Mobile first - these are MIN widths */
--breakpoint-sm: 640px; /* Small tablets, large phones */
--breakpoint-md: 768px; /* Tablets */
--breakpoint-lg: 1024px; /* Laptops, desktops */
--breakpoint-xl: 1280px; /* Large desktops */
--breakpoint-2xl: 1536px; /* Extra large screens */
}
/* Usage */
@media (min-width: 768px) {
/* Tablet and up */
}
@media (min-width: 1024px) {
/* Desktop and up */
}
Custom Breakpoints
/* Content-based breakpoints */
@media (min-width: 400px) {
/* When content needs it, not arbitrary device size */
}
/* Prefer rem-based breakpoints for accessibility */
@media (min-width: 48rem) { /* 768px at 16px base */
/* Scales with user's font size preferences */
}
Container Queries (Modern)
/* Respond to container size, not viewport */
.card-container {
container-type: inline-size;
container-name: card;
}
@container card (min-width: 400px) {
.card {
display: grid;
grid-template-columns: 200px 1fr;
}
}
Responsive Typography
Fluid Typography
/* Scales smoothly between min and max */
h1 {
font-size: clamp(2rem, 5vw + 1rem, 4rem);
/* Min: 2rem (32px) */
/* Preferred: 5vw + 1rem */
/* Max: 4rem (64px) */
}
/* More examples */
.text-sm {
font-size: clamp(0.875rem, 0.85rem + 0.125vw, 1rem);
}
.text-base {
font-size: clamp(1rem, 0.95rem + 0.25vw, 1.125rem);
}
.text-lg {
font-size: clamp(1.125rem, 1.05rem + 0.375vw, 1.25rem);
}
Responsive Line Height
body {
/* Tighter on mobile, looser on desktop */
line-height: 1.5;
}
@media (min-width: 768px) {
body {
line-height: 1.6;
}
}
Layout Patterns
1. Stack Layout
Everything stacks vertically on mobile, side-by-side on larger screens.
.stack {
display: flex;
flex-direction: column;
gap: 1rem;
}
@media (min-width: 768px) {
.stack {
flex-direction: row;
}
}
2. Sidebar Layout
Sidebar stacks on mobile, side-by-side on desktop.
.sidebar-layout {
display: grid;
gap: 2rem;
}
@media (min-width: 768px) {
.sidebar-layout {
grid-template-columns: 250px 1fr;
}
}
/* Flexible sidebar */
@media (min-width: 768px) {
.sidebar-layout--flexible {
grid-template-columns: minmax(200px, 300px) 1fr;
}
}
3. Grid Layout
Responsive column count.
.grid {
display: grid;
gap: 1rem;
/* Mobile: 1 column */
grid-template-columns: 1fr;
}
@media (min-width: 640px) {
.grid {
/* Tablet: 2 columns */
grid-template-columns: repeat(2, 1fr);
}
}
@media (min-width: 1024px) {
.grid {
/* Desktop: 3-4 columns */
grid-template-columns: repeat(4, 1fr);
}
}
/* Auto-responsive grid (no media queries!) */
.grid-auto {
display: grid;
gap: 1rem;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
/* Creates as many columns as fit, minimum 250px each */
}
4. Holy Grail Layout
Classic three-column layout that adapts to mobile.
.holy-grail {
display: grid;
gap: 1rem;
min-height: 100vh;
/* Mobile: stack everything */
grid-template:
"header" auto
"main" 1fr
"sidebar1" auto
"sidebar2" auto
"footer" auto
/ 1fr;
}
@media (min-width: 768px) {
.holy-grail {
/* Desktop: traditional layout */
grid-template:
"header header header" auto
"sidebar1 main sidebar2" 1fr
"footer footer footer" auto
/ 200px 1fr 200px;
}
}
.header { grid-area: header; }
.sidebar-1 { grid-area: sidebar1; }
.main { grid-area: main; }
.sidebar-2 { grid-area: sidebar2; }
.footer { grid-area: footer; }
5. Card Layout
Responsive cards that adapt their internal layout.
.card {
display: grid;
gap: 1rem;
/* Mobile: stack image and content */
grid-template:
"image" auto
"content" 1fr
/ 1fr;
}
@media (min-width: 640px) {
.card {
/* Tablet+: side-by-side */
grid-template:
"image content" 1fr
/ 200px 1fr;
}
}
.card__image { grid-area: image; }
.card__content { grid-area: content; }
6. Switcher Pattern
Switch between horizontal and vertical based on available space.
.switcher {
display: flex;
flex-wrap: wrap;
gap: 1rem;
}
.switcher > * {
/* Grow to fill, but switch to vertical when < 400px */
flex-grow: 1;
flex-basis: calc((400px - 100%) * 999);
/* Clever calc that breaks at threshold */
}
7. Pancake Stack
Header, main, footer layout that adapts height.
.pancake {
display: grid;
grid-template-rows: auto 1fr auto;
min-height: 100vh;
}
.header { /* auto height */ }
.main { /* fills available space */ }
.footer { /* auto height */ }
Responsive Images
1. Flexible Images
img {
max-width: 100%;
height: auto;
display: block;
}
2. Art Direction (Different Images per Breakpoint)
<picture>
<source media="(min-width: 1024px)" srcset="large.jpg">
<source media="(min-width: 768px)" srcset="medium.jpg">
<img src="small.jpg" alt="Description">
</picture>
3. Resolution Switching (Same Image, Different Sizes)
<img
srcset="
small.jpg 400w,
medium.jpg 800w,
large.jpg 1200w
"
sizes="
(min-width: 1024px) 800px,
(min-width: 768px) 600px,
100vw
"
src="medium.jpg"
alt="Description"
>
4. Background Images
.hero {
background-image: url('small.jpg');
background-size: cover;
background-position: center;
}
@media (min-width: 768px) {
.hero {
background-image: url('medium.jpg');
}
}
@media (min-width: 1024px) {
.hero {
background-image: url('large.jpg');
}
}
@media (min-width: 1024px) and (-webkit-min-device-pixel-ratio: 2) {
.hero {
background-image: url('large@2x.jpg');
}
}
5. Aspect Ratio
/* Modern aspect ratio */
.image-container {
aspect-ratio: 16 / 9;
overflow: hidden;
}
.image-container img {
width: 100%;
height: 100%;
object-fit: cover;
}
/* Fallback for older browsers */
.image-container-fallback {
position: relative;
padding-bottom: 56.25%; /* 16:9 aspect ratio */
}
.image-container-fallback img {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
object-fit: cover;
}
Responsive Navigation
1. Mobile Menu (Hamburger)
<nav class="nav">
<div class="nav__brand">Logo</div>
<button class="nav__toggle" aria-expanded="false" aria-controls="nav-menu">
<span class="sr-only">Menu</span>
<span class="hamburger"></span>
</button>
<ul class="nav__menu" id="nav-menu">
<li><a href="/">Home</a></li>
<li><a href="/about">About</a></li>
<li><a href="/contact">Contact</a></li>
</ul>
</nav>
.nav {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1rem;
}
/* Mobile: hidden menu */
.nav__menu {
display: none;
position: absolute;
top: 100%;
left: 0;
right: 0;
background: white;
flex-direction: column;
padding: 1rem;
}
.nav__menu[aria-expanded="true"] {
display: flex;
}
.nav__toggle {
display: block;
}
/* Desktop: visible menu */
@media (min-width: 768px) {
.nav__menu {
display: flex;
position: static;
flex-direction: row;
gap: 2rem;
}
.nav__toggle {
display: none;
}
}
2. Priority+ Navigation
Show important items, collapse others into "More" menu.
.priority-nav {
display: flex;
gap: 1rem;
}
.priority-nav__item {
flex-shrink: 0; /* Don't shrink */
}
.priority-nav__more {
margin-left: auto; /* Push to end */
}
/* Hide items that don't fit */
@media (max-width: 768px) {
.priority-nav__item:nth-child(n+4) {
display: none; /* Hide items 4+ */
}
}
Responsive Tables
1. Horizontal Scroll
.table-container {
overflow-x: auto;
-webkit-overflow-scrolling: touch; /* Smooth scrolling on iOS */
}
table {
min-width: 600px;
width: 100%;
}
2. Stacked Table (Card View)
<table class="responsive-table">
<thead>
<tr>
<th>Name</th>
<th>Email</th>
<th>Role</th>
</tr>
</thead>
<tbody>
<tr>
<td data-label="Name">John Doe</td>
<td data-label="Email">john@example.com</td>
<td data-label="Role">Developer</td>
</tr>
</tbody>
</table>
/* Desktop: normal table */
@media (min-width: 768px) {
.responsive-table {
display: table;
}
}
/* Mobile: stacked cards */
@media (max-width: 767px) {
.responsive-table,
.responsive-table thead,
.responsive-table tbody,
.responsive-table tr,
.responsive-table th,
.responsive-table td {
display: block;
}
.responsive-table thead {
display: none; /* Hide table header */
}
.responsive-table tr {
margin-bottom: 1rem;
border: 1px solid #ddd;
border-radius: 0.5rem;
padding: 1rem;
}
.responsive-table td {
display: flex;
justify-content: space-between;
padding: 0.5rem 0;
border-bottom: 1px solid #eee;
}
.responsive-table td:last-child {
border-bottom: none;
}
.responsive-table td::before {
content: attr(data-label);
font-weight: bold;
margin-right: 1rem;
}
}
Responsive Forms
1. Single Column to Multi-Column
.form {
display: grid;
gap: 1rem;
/* Mobile: single column */
grid-template-columns: 1fr;
}
@media (min-width: 768px) {
.form {
/* Desktop: two columns */
grid-template-columns: repeat(2, 1fr);
}
.form__field--full {
/* Some fields span both columns */
grid-column: 1 / -1;
}
}
2. Touch-Friendly Inputs
input,
button,
select,
textarea {
/* Minimum 44x44px touch target */
min-height: 44px;
padding: 0.75rem 1rem;
font-size: 1rem; /* Prevents zoom on iOS */
}
@media (min-width: 768px) {
input,
button,
select,
textarea {
/* Can be smaller on desktop */
min-height: 40px;
font-size: 0.875rem;
}
}
Responsive Utilities
Show/Hide at Breakpoints
/* Hide on mobile */
.hidden-mobile {
display: none;
}
@media (min-width: 768px) {
.hidden-mobile {
display: block;
}
}
/* Show only on mobile */
.visible-mobile {
display: block;
}
@media (min-width: 768px) {
.visible-mobile {
display: none;
}
}
/* Hide on desktop */
@media (min-width: 1024px) {
.hidden-desktop {
display: none;
}
}
Responsive Spacing
.section {
/* Mobile: smaller padding */
padding: 2rem 1rem;
}
@media (min-width: 768px) {
.section {
/* Tablet: medium padding */
padding: 4rem 2rem;
}
}
@media (min-width: 1024px) {
.section {
/* Desktop: larger padding */
padding: 6rem 3rem;
}
}
/* Or use fluid spacing */
.section-fluid {
padding: clamp(2rem, 5vw, 6rem) clamp(1rem, 3vw, 3rem);
}
Testing Responsive Design
Browser DevTools
// Common viewport sizes to test
const viewports = [
{ width: 375, height: 667, name: 'iPhone SE' },
{ width: 390, height: 844, name: 'iPhone 12 Pro' },
{ width: 428, height: 926, name: 'iPhone 14 Pro Max' },
{ width: 768, height: 1024, name: 'iPad' },
{ width: 1024, height: 768, name: 'iPad Landscape' },
{ width: 1280, height: 720, name: 'Desktop' },
{ width: 1920, height: 1080, name: 'Full HD' },
];
Responsive Testing Checklist
- Test all breakpoints
- Test between breakpoints (awkward sizes)
- Test portrait and landscape
- Test zoom levels (100%, 200%, 400%)
- Test with real devices when possible
- Test touch interactions on mobile
- Test with different font sizes
- Test with slow network (images, fonts)
Performance Considerations
Lazy Loading
<!-- Lazy load images below the fold -->
<img src="image.jpg" loading="lazy" alt="Description">
<!-- Eager load above-the-fold images -->
<img src="hero.jpg" loading="eager" alt="Hero image">
Conditional Loading
// Load component only on larger screens
if (window.matchMedia('(min-width: 768px)').matches) {
import('./DesktopComponent.js').then(module => {
// Initialize desktop component
});
}
Font Loading
@font-face {
font-family: 'CustomFont';
src: url('font.woff2') format('woff2');
font-display: swap; /* Show fallback while loading */
}
Modern CSS Features
1. CSS Grid Auto-Fill
.grid {
display: grid;
gap: 1rem;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
/* Automatically creates columns, minimum 250px */
}
2. Flexbox Gap
.flex-container {
display: flex;
flex-wrap: wrap;
gap: 1rem; /* No more margin hacks! */
}
3. Container Queries
.card {
container-type: inline-size;
}
@container (min-width: 400px) {
.card__title {
font-size: 1.5rem;
}
}
4. Aspect Ratio
.video-container {
aspect-ratio: 16 / 9;
}
5. Logical Properties
/* Better for RTL/LTR support */
.element {
margin-block-start: 1rem; /* margin-top */
margin-block-end: 1rem; /* margin-bottom */
margin-inline-start: 1rem; /* margin-left in LTR, margin-right in RTL */
margin-inline-end: 1rem; /* margin-right in LTR, margin-left in RTL */
}
Resources
- MDN: Responsive Design
- This is Responsive
- Responsive Design Patterns
- CSS-Tricks: Complete Guide to Grid
- CSS-Tricks: Complete Guide to Flexbox
"The best design is the one that works everywhere, for everyone."