Responsive Design & Accessibility

TL;DR

Responsive design makes your site work on any screen size (mobile-first + media queries). Accessibility makes it work for every user (semantic HTML + ARIA + keyboard navigation). Both are non-negotiable for production websites.

The Big Picture

Over 60% of web traffic comes from mobile devices. Around 15% of the world’s population has some form of disability. Responsive design and accessibility aren’t nice-to-haves — they’re the baseline for professional frontend work.

Responsive design and accessibility: mobile-first approach flows to tablet and desktop, WCAG principles support visual, motor, cognitive, and auditory accessibility
Explain Like I'm 12

Responsive design is like water — it takes the shape of whatever container (screen) you pour it into. A phone gets a single column. A tablet gets two columns. A desktop gets the full layout.

Accessibility is like wheelchair ramps. Buildings have stairs, but ramps let everyone in — people in wheelchairs, parents with strollers, delivery workers with carts. Making websites accessible helps everyone, not just people with disabilities.

Mobile-First Design

Mobile-first means writing your base CSS for small screens first, then adding complexity for larger screens with min-width media queries. This ensures mobile users get a fast, clean experience.

/* Base: mobile styles (no media query needed) */
.container {
  padding: 1rem;
  max-width: 100%;
}

.hero-title {
  font-size: 1.5rem;
}

.nav-links {
  display: none; /* Hidden on mobile — use hamburger menu */
}

/* Tablet: 768px and up */
@media (min-width: 768px) {
  .container {
    padding: 2rem;
    max-width: 768px;
    margin: 0 auto;
  }

  .hero-title {
    font-size: 2.5rem;
  }

  .nav-links {
    display: flex;
    gap: 2rem;
  }
}

/* Desktop: 1024px and up */
@media (min-width: 1024px) {
  .container {
    max-width: 1200px;
  }

  .hero-title {
    font-size: 3.5rem;
  }
}
Tip: Use min-width (not max-width) for mobile-first. Common breakpoints: 640px (large phones), 768px (tablets), 1024px (laptops), 1280px (desktops). But design to the content, not to device sizes.

Responsive Images

Images are often the largest assets on a page. Serve the right size for each device to save bandwidth and improve load times.

<!-- Basic responsive image -->
<img src="photo.jpg"
     alt="Team working together"
     loading="lazy"
     style="max-width: 100%; height: auto;">

<!-- Responsive with multiple sizes -->
<img srcset="photo-400w.jpg 400w,
             photo-800w.jpg 800w,
             photo-1200w.jpg 1200w"
     sizes="(max-width: 600px) 400px,
            (max-width: 1000px) 800px,
            1200px"
     src="photo-800w.jpg"
     alt="Team working together"
     loading="lazy">

<!-- Art direction: different crops for different screens -->
<picture>
  <source media="(max-width: 600px)" srcset="photo-portrait.jpg">
  <source media="(max-width: 1000px)" srcset="photo-square.jpg">
  <img src="photo-landscape.jpg" alt="Team working together" loading="lazy">
</picture>
Info: Always include loading="lazy" on images below the fold. The browser will defer loading until the image is near the viewport, saving bandwidth and improving initial page load time.

Responsive Typography

Use relative units and clamp() for text that scales smoothly across screen sizes.

/* Fixed sizes — bad (doesn't scale) */
h1 { font-size: 48px; }

/* Relative units — better */
h1 { font-size: 2.5rem; } /* relative to root font size */

/* clamp() — best (fluid scaling with min/max limits) */
h1 {
  font-size: clamp(1.5rem, 4vw, 3.5rem);
  /* minimum: 1.5rem, scales with viewport, maximum: 3.5rem */
}

body {
  font-size: clamp(1rem, 1vw + 0.75rem, 1.25rem);
  line-height: 1.6;
}
Tip: clamp(min, preferred, max) is the modern way to do fluid typography. No media queries needed. The middle value usually uses vw (viewport width) units so text scales with the screen.

WCAG Accessibility Basics

The Web Content Accessibility Guidelines (WCAG) define four principles (POUR):

PrincipleMeaningExamples
Perceivable Users can perceive the content Alt text on images, captions on video, sufficient color contrast
Operable Users can navigate and interact Keyboard navigation, skip links, no time limits
Understandable Content and UI are clear Clear labels, error messages, consistent navigation
Robust Works with assistive technology Valid HTML, ARIA attributes, semantic elements
Warning: Accessibility lawsuits are increasing. In the US, the ADA applies to websites. In the EU, the European Accessibility Act takes effect in 2025. WCAG 2.1 Level AA is the standard most regulations reference.

Semantic HTML for Accessibility

The single most impactful thing you can do for accessibility is use semantic HTML. Screen readers use the HTML structure to navigate.

<!-- Skip link: first focusable element on the page -->
<a href="#main" class="skip-link">Skip to content</a>

<!-- Landmark regions that screen readers announce -->
<header>...</header>  <!-- "Banner" landmark -->
<nav>...</nav>        <!-- "Navigation" landmark -->
<main id="main">      <!-- "Main" landmark -->
  <article>...</article>
</main>
<aside>...</aside>    <!-- "Complementary" landmark -->
<footer>...</footer>  <!-- "Content info" landmark -->

<!-- Headings: create a navigable outline -->
<h1>Page Title</h1>         <!-- One per page -->
  <h2>Section</h2>           <!-- Never skip levels -->
    <h3>Subsection</h3>

<!-- Buttons vs Links -->
<button>Save</button>         <!-- Actions -->
<a href="/about">About</a>   <!-- Navigation -->

<!-- Forms: labels are mandatory -->
<label for="email">Email</label>
<input type="email" id="email" required>
Info: Screen reader users navigate by landmarks and headings. A page with only <div> elements is like a book with no chapter titles — usable but extremely frustrating to navigate.

ARIA Roles & Attributes

ARIA (Accessible Rich Internet Applications) fills accessibility gaps that semantic HTML can’t cover. Use it as a supplement, not a replacement.

<!-- Modal dialog -->
<div role="dialog" aria-labelledby="modal-title" aria-modal="true">
  <h2 id="modal-title">Confirm Action</h2>
  <p>Are you sure you want to delete this item?</p>
  <button>Cancel</button>
  <button>Delete</button>
</div>

<!-- Live region: announces changes to screen readers -->
<div aria-live="polite" id="status"></div>

<!-- Icon button: needs a label -->
<button aria-label="Close menu">
  <svg>...</svg> <!-- X icon -->
</button>

<!-- Expandable section -->
<button aria-expanded="false" aria-controls="details-panel">
  Show Details
</button>
<div id="details-panel" hidden>
  <p>Additional details here...</p>
</div>

<!-- Loading state -->
<div aria-busy="true" aria-live="polite">
  Loading results...
</div>
Warning: The first rule of ARIA is don’t use ARIA if a native HTML element exists. <button> is always better than <div role="button">. Native elements have keyboard handling, focus management, and screen reader support built in.

Keyboard Navigation

Many users navigate with keyboards only — people with motor disabilities, power users, and screen reader users. Every interactive element must be keyboard accessible.

/* Make focus visible — NEVER remove it without a replacement */
:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
}

/* Hide the default outline only for mouse users */
:focus:not(:focus-visible) {
  outline: none;
}

/* Skip link (visually hidden until focused) */
.skip-link {
  position: absolute;
  top: -100%;
  left: 0;
  padding: 0.5rem 1rem;
  background: var(--color-primary);
  color: white;
  z-index: 1000;
}

.skip-link:focus {
  top: 0;
}
// Trap focus inside a modal
function trapFocus(modal) {
  const focusable = modal.querySelectorAll(
    'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
  );
  const first = focusable[0];
  const last = focusable[focusable.length - 1];

  modal.addEventListener('keydown', function(e) {
    if (e.key !== 'Tab') return;

    if (e.shiftKey && document.activeElement === first) {
      e.preventDefault();
      last.focus();
    } else if (!e.shiftKey && document.activeElement === last) {
      e.preventDefault();
      first.focus();
    }
  });

  first.focus();
}
Tip: Test your site by unplugging your mouse and navigating with only Tab, Shift+Tab, Enter, Escape, and arrow keys. If you get stuck or can’t tell where focus is, there’s an accessibility issue.

Color Contrast

Low contrast text is unreadable for users with low vision, color blindness, or in bright sunlight.

WCAG LevelNormal TextLarge Text (18px+ bold / 24px+)
AA (minimum)4.5:13:1
AAA (enhanced)7:14.5:1
/* Good contrast examples */
body {
  color: #1a1a2e;       /* dark text */
  background: #ffffff;   /* white bg — contrast ratio: 16:1 ✅ */
}

/* Don't rely on color alone to convey information */
.error-field {
  border: 2px solid #dc2626;  /* red border */
  /* ALSO add an icon or text — don't rely on color alone */
}
.error-message {
  color: #dc2626;
  /* Prefix with icon: ⚠️ Error: ... */
}
Info: Use browser DevTools to check contrast. Chrome DevTools shows contrast ratios in the color picker. The Lighthouse accessibility audit flags contrast issues automatically.

Performance Essentials

Performance is part of accessibility — slow sites are unusable on slow connections. These are the key metrics and techniques.

Core Web VitalWhat It MeasuresGood Score
LCP (Largest Contentful Paint)How fast the main content loads< 2.5 seconds
INP (Interaction to Next Paint)How fast the page responds to interaction< 200ms
CLS (Cumulative Layout Shift)How much the layout shifts during load< 0.1
<!-- Prevent CLS: set dimensions on images -->
<img src="hero.jpg" width="1200" height="600" alt="..." loading="lazy">

<!-- Prevent CLS: reserve space for dynamic content -->
<div style="min-height: 60px;">
  <!-- Header loaded via JavaScript -->
</div>

<!-- Preload critical resources -->
<link rel="preload" href="/css/style.css" as="style">
<link rel="preload" href="/fonts/inter.woff2" as="font" type="font/woff2" crossorigin>

<!-- Defer non-critical JavaScript -->
<script src="/js/analytics.js" defer></script>

<!-- Non-blocking font loading -->
<link rel="preload" href="fonts.css" as="style" onload="this.rel='stylesheet'">
Tip: Run Lighthouse in Chrome DevTools regularly. It audits Performance, Accessibility, SEO, and Best Practices in one click, with specific recommendations.

Test Yourself

What does “mobile-first” mean and why is it preferred over desktop-first?

Mobile-first means writing base styles for small screens, then using min-width media queries to add complexity for larger screens. It’s preferred because: (1) mobile users are the majority of web traffic, (2) smaller screens force you to prioritize content, (3) mobile CSS is simpler (fewer columns, simpler layouts), and (4) it’s easier to add complexity than to undo it.

What are the four WCAG principles (POUR)?

Perceivable — users can perceive content (alt text, contrast, captions). Operable — users can navigate and interact (keyboard, skip links). Understandable — content and UI are clear (labels, error messages). Robust — works with assistive tech (valid HTML, ARIA).

What is the first rule of ARIA?

The first rule of ARIA is don’t use ARIA if a native HTML element already provides the functionality. For example, use <button> instead of <div role="button">, because native elements come with built-in keyboard handling, focus management, and screen reader support.

What are Core Web Vitals and why do they matter?

Core Web Vitals are three metrics Google uses to evaluate page experience: LCP (Largest Contentful Paint, < 2.5s), INP (Interaction to Next Paint, < 200ms), and CLS (Cumulative Layout Shift, < 0.1). They matter because: (1) Google uses them as ranking signals, (2) they directly correlate with user experience and conversion rates.

How does clamp() work for responsive typography?

clamp(min, preferred, max) sets a value that scales fluidly between a minimum and maximum. For typography: font-size: clamp(1rem, 2vw + 0.5rem, 2rem) means the font is at least 1rem, scales with viewport width, and caps at 2rem. No media queries needed — the browser handles smooth scaling.

Interview Questions

How would you make a website accessible for screen reader users?

Key steps: (1) Use semantic HTML (header, nav, main, article). (2) Add alt text to all meaningful images. (3) Use proper heading hierarchy (h1 > h2 > h3, no skipping). (4) Include a skip link. (5) Label all form inputs. (6) Use ARIA only when HTML falls short (modals, live regions). (7) Make focus visible and trap focus in modals. (8) Don’t rely on color alone to convey information.

What is the difference between srcset and <picture>?

srcset lets the browser choose the best resolution of the same image based on screen size/density. <picture> provides art direction — completely different images for different contexts (e.g., a landscape crop on desktop, portrait crop on mobile). Use srcset for resolution switching, <picture> when the image composition needs to change.

What causes Cumulative Layout Shift (CLS) and how do you prevent it?

CLS is caused by elements changing size or position after the page renders. Common causes: (1) images without dimensions, (2) fonts swapping (FOUT), (3) dynamically injected content (ads, banners), (4) late-loading CSS. Fixes: (1) set width/height on images, (2) use font-display: swap with metric-matched fallback fonts, (3) reserve space with min-height for dynamic content, (4) inline critical CSS.