โฟ Building for Everyone
You've learned to build beautiful, responsive, animated web pages. But here's a critical question: can everyone actually use them? Over 1 billion people worldwide live with some form of disability โ visual, auditory, motor, or cognitive. Web accessibility (often abbreviated a11y โ "a" + 11 characters + "y") ensures your sites work for all users, regardless of how they interact with the web.
And there's a second dimension: once your site is accessible to humans, can search engines understand it? SEO (Search Engine Optimization) is the art of helping Google, Bing, and other crawlers discover, parse, and rank your content. Accessibility and SEO aren't separate disciplines โ they're deeply intertwined. The same semantic HTML that helps screen readers also helps search engines.
<h1>โ<h6>), descriptive alt text, logical document structure, meaningful link text โ these all serve both screen reader users and search engine crawlers. Accessible sites naturally rank better.
๐ Why Accessibility Matters
Accessibility benefits far more people than you might think. Permanent disabilities are just one part of the picture โ consider the spectrum of ability:
| Type | Permanent | Temporary | Situational |
|---|---|---|---|
| Visual | Blind, low vision | Eye surgery recovery | Bright sunlight on screen |
| Motor | Paralysis, tremors | Broken arm | Holding a baby |
| Auditory | Deaf, hard of hearing | Ear infection | Noisy environment |
| Cognitive | Dyslexia, ADHD | Concussion | Distracted, multitasking |
When you design for accessibility, you design for everyone. Curb cuts were made for wheelchair users, but they help parents with strollers, delivery workers with carts, and travelers with suitcases. The same principle applies to the web โ accessible features benefit all users.
WCAG: The Standard
The Web Content Accessibility Guidelines (WCAG) are the global standard for web accessibility. WCAG 2.2 is organized around four principles โ POUR:
Information must be presentable in ways users can perceive. Text alternatives for images, captions for videos, sufficient color contrast.
Users must be able to operate the interface. Keyboard accessible, enough time, no seizure-triggering content, navigable.
Content and operation must be understandable. Readable text, predictable behavior, input assistance.
Content must be robust enough for diverse user agents, including assistive technologies. Valid markup, proper ARIA usage.
๐๏ธ Semantic HTML is Your Foundation
The single most impactful thing you can do for accessibility is use semantic HTML. Every semantic element carries built-in accessibility information that assistive technologies understand natively โ no ARIA needed.
<!-- โ BAD: div soup โ invisible to assistive tech -->
<div class="header">
<div class="nav">...</div>
</div>
<div class="main">
<div class="article">
<div class="title">My Article</div>
</div>
</div>
<!-- โ
GOOD: semantic elements โ accessible by default -->
<header>
<nav aria-label="Main navigation">...</nav>
</header>
<main>
<article>
<h1>My Article</h1>
</article>
</main>
Landmark Roles
Semantic landmarks create a navigational skeleton that screen reader users can jump between โ like a table of contents for the page structure:
| HTML Element | Implicit ARIA Role | Purpose |
|---|---|---|
<header> |
banner |
Site-wide header (when top-level) |
<nav> |
navigation |
Navigation links group |
<main> |
main |
Primary content (one per page) |
<aside> |
complementary |
Related but secondary content |
<footer> |
contentinfo |
Site-wide footer (when top-level) |
<section> |
region (if labeled) |
Generic section (needs aria-label) |
Heading Hierarchy
Screen reader users navigate by headings more than anything else. A logical heading hierarchy is crucial:
<!-- โ Skipping levels โ confuses screen readers -->
<h1>Page Title</h1>
<h4>Subsection</h4> <!-- Skipped h2 and h3! -->
<!-- โ
Logical nesting -->
<h1>Page Title</h1>
<h2>Section</h2>
<h3>Subsection</h3>
<h2>Another Section</h2>
Alt Text for Images
Every <img> needs an alt attribute. The content depends on the image's role:
<!-- Informative: describe what it shows -->
<img src="chart.png" alt="Bar chart showing 60% mobile, 30% desktop, 10% tablet traffic">
<!-- Decorative: empty alt (not missing โ empty!) -->
<img src="divider.svg" alt="">
<!-- Functional (inside a link): describe the action -->
<a href="/"><img src="logo.png" alt="Home โ Company Name"></a>
๐ท๏ธ ARIA Roles & Attributes
ARIA (Accessible Rich Internet Applications) fills the gap when HTML semantics alone aren't enough. ARIA doesn't change behavior โ it only changes what assistive technologies announce. Think of it as metadata for screen readers.
<button> is always better than <div role="button">. Native elements come with built-in keyboard handling, focus management, and screen reader support.
ARIA Categories
| Category | Examples | Purpose |
|---|---|---|
| Roles | role="alert", role="dialog", role="tablist" |
Define what an element is |
| States | aria-expanded, aria-checked, aria-disabled |
Describe the current state (dynamic) |
| Properties | aria-label, aria-describedby, aria-required |
Provide additional context |
Essential ARIA Attributes
<!-- aria-label: names an element (when no visible text) -->
<button aria-label="Close dialog">โ</button>
<!-- aria-labelledby: points to another element's text -->
<dialog aria-labelledby="dialog-title">
<h2 id="dialog-title">Confirm Delete</h2>
...
</dialog>
<!-- aria-describedby: extra description -->
<input type="password" aria-describedby="pw-hint">
<p id="pw-hint">Must be at least 8 characters</p>
<!-- aria-expanded: toggle state -->
<button aria-expanded="false" aria-controls="menu">Menu</button>
<ul id="menu" hidden>...</ul>
<!-- aria-live: announce dynamic content -->
<div aria-live="polite">3 results found</div>
<!-- aria-hidden: hide from assistive tech (decorative) -->
<span aria-hidden="true">๐ญ</span>
aria-live="polite" waits for the user to be idle before announcing. aria-live="assertive" interrupts immediately (use for urgent messages like errors). Dynamic content updates (search results, notifications, chat) should use live regions so screen readers announce changes.
โจ๏ธ Keyboard Navigation
Many users navigate the web without a mouse โ they use the keyboard, switch devices, or voice control. Every interactive element must be keyboard accessible. This is one of the most common accessibility failures.
Focus Management
Interactive HTML elements (<a>, <button>, <input>, <select>, <textarea>) are focusable by default. Non-interactive elements (<div>, <span>) are not โ and that's intentional.
| Key | Action |
|---|---|
| Tab | Move focus to next interactive element |
| Shift+Tab | Move focus to previous element |
| Enter | Activate link or button |
| Space | Activate button, toggle checkbox |
| Escape | Close dialog/modal/popup |
| Arrow keys | Navigate within widgets (tabs, menus, radio groups) |
tabindex
<!-- tabindex="0": add to tab order (in DOM order) -->
<div role="button" tabindex="0">Custom button</div>
<!-- tabindex="-1": focusable via JS, NOT in tab order -->
<div id="modal" tabindex="-1">Focus me programmatically</div>
<!-- โ NEVER use tabindex > 0 โ disrupts natural order -->
<input tabindex="3"> <!-- DON'T do this -->
Focus Indicators
Never remove focus outlines without providing an alternative. The :focus-visible pseudo-class is the modern solution โ it shows focus styles for keyboard users but not for mouse clicks:
/* โ NEVER do this */
*:focus { outline: none; }
/* โ
Custom focus style for keyboard users only */
:focus-visible {
outline: 3px solid #4f46e5;
outline-offset: 2px;
border-radius: 4px;
}
/* Remove default for mouse users */
:focus:not(:focus-visible) {
outline: none;
}
Skip Navigation Link
Keyboard users shouldn't have to Tab through the entire navigation on every page. A skip link lets them jump directly to the main content:
<!-- First element in <body> -->
<a href="#main-content" class="skip-link">Skip to main content</a>
<nav>...</nav>
<main id="main-content">...</main>
.skip-link {
position: absolute;
top: -40px;
left: 0;
background: #4f46e5;
color: #fff;
padding: 8px 16px;
z-index: 100;
transition: top 0.2s;
}
.skip-link:focus {
top: 0;
}
๐จ Visual Accessibility
Color Contrast
WCAG requires minimum contrast ratios between text and background. Low contrast text is unreadable for many users โ especially in bright sunlight or on low-quality screens:
| Level | Normal Text | Large Text (โฅ18pt / 14pt bold) |
|---|---|---|
| AA | 4.5:1 | 3:1 |
| AAA | 7:1 | 4.5:1 |
Don't Rely on Color Alone
About 8% of men and 0.5% of women are color blind. Never use color as the only way to convey information โ always pair it with text, icons, or patterns:
<!-- โ Color alone โ invisible to color-blind users -->
<span style="color: red;">Error</span>
<!-- โ
Color + icon + text -->
<span style="color: red;">โ Error: Email is required</span>
Text Readability
- Minimum body text: 16px (browser default). Never below 12px.
- Line height: 1.5โ1.8 for body text. Tight leading hurts readability.
- Line width: 50โ80 characters. Very long lines cause reading fatigue.
- Text must be resizable up to 200% without loss of content (WCAG 1.4.4). Use
rem/em, notpxfor font-size.
Reduced Motion
We covered prefers-reduced-motion in Module 10. As a reminder: always respect the user's motion preferences:
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
transition-duration: 0.01ms !important;
}
}
๐ Accessible Forms
Forms are where accessibility failures happen most often. Every input needs a label, every error needs to be announced, and the entire form must be keyboard-operable.
Labels: The #1 Rule
<!-- โ No label โ screen reader says "edit text, blank" -->
<input type="email" placeholder="Enter email">
<!-- โ
Explicit label โ proper association -->
<label for="email">Email address</label>
<input type="email" id="email" placeholder="you@example.com">
<!-- โ
Implicit label (wrapping) -->
<label>
Email address
<input type="email" placeholder="you@example.com">
</label>
Error Messages
<label for="pw">Password</label>
<input type="password" id="pw"
aria-describedby="pw-error"
aria-invalid="true">
<p id="pw-error" role="alert">
โ Password must be at least 8 characters
</p>
Grouping with fieldset/legend
<fieldset>
<legend>Shipping Address</legend>
<label for="street">Street</label>
<input id="street" type="text">
<label for="city">City</label>
<input id="city" type="text">
</fieldset>
๐งช Testing Accessibility
Automated tools catch about 30โ40% of accessibility issues. The rest require manual testing. A good accessibility workflow combines both:
Automated Testing
Built into Chrome. Audits accessibility, performance, SEO, and best practices. Good starting point.
Browser extension by Deque. Industry-standard engine with zero false positives. More detailed than Lighthouse.
Visual overlay that highlights issues directly on the page. Great for seeing problems in context.
Manual Testing Checklist
- Keyboard only: Unplug your mouse. Can you reach and operate everything with Tab, Enter, Space, Escape?
- Zoom to 200%: Is all content still readable? Nothing cut off or overlapping?
- Screen reader: Try macOS VoiceOver (Cmd+F5) or NVDA (Windows, free). Navigate your page โ does it make sense?
- Color contrast: Check all text/background combinations, especially light gray text.
- Focus indicators: Tab through the page. Can you always see where focus is?
๐ SEO Fundamentals
Search Engine Optimization ensures your content is discoverable. Search engines crawl your HTML, analyze its structure, and rank it based on relevance, quality, and technical correctness. Good SEO starts with good HTML.
HTML for SEO
- One
<h1>per page โ clearly describes the page topic. - Logical heading hierarchy โ helps crawlers understand content structure.
- Descriptive link text โ "Read our pricing guide" not "Click here".
- Alt text on images โ search engines can't "see" images without it.
- Semantic elements โ
<article>,<nav>,<main>help crawlers identify content regions. langattribute โ<html lang="en">tells search engines the content language.
The <title> Tag
The most important SEO element. It appears in browser tabs, search results, and social shares:
<!-- โ Vague, unhelpful -->
<title>Home</title>
<!-- โ
Descriptive, includes brand -->
<title>Web Accessibility & SEO Guide โ Web Foundations | Mehdi Tmimi</title>
Meta Description
<!-- 150-160 characters max. Appears in search results. -->
<meta name="description"
content="Learn web accessibility and SEO. Master ARIA roles, keyboard navigation, color contrast, meta tags, Open Graph, and structured data. Free course.">
Robots & Crawling
<!-- Allow indexing (default behavior) -->
<meta name="robots" content="index, follow">
<!-- Block indexing (private/admin pages) -->
<meta name="robots" content="noindex, nofollow">
<!-- Canonical URL โ prevents duplicate content -->
<link rel="canonical" href="https://example.com/page">
๐ท๏ธ Meta Tags & Open Graph
When someone shares your link on social media, Open Graph tags control what appears โ the title, description, image, and type. Without them, platforms guess (usually badly).
Open Graph Protocol
<!-- Essential Open Graph tags -->
<meta property="og:type" content="website">
<meta property="og:url" content="https://example.com/page">
<meta property="og:title" content="Page Title โ Site Name">
<meta property="og:description" content="A compelling 1-2 sentence description.">
<meta property="og:image" content="https://example.com/og-image.jpg">
<meta property="og:image:width" content="1200">
<meta property="og:image:height" content="630">
Twitter Cards
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="Page Title">
<meta name="twitter:description" content="Short description">
<meta name="twitter:image" content="https://example.com/og-image.jpg">
Favicons
<!-- Modern favicon setup -->
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
<link rel="manifest" href="/site.webmanifest">
๐ Structured Data (JSON-LD)
Structured data helps search engines understand your content beyond plain HTML. By adding JSON-LD (JavaScript Object Notation for Linked Data) to your page, you can tell Google exactly what your content represents โ an article, a recipe, a product, an event, a course โ and earn rich snippets in search results.
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Article",
"headline": "Web Accessibility & SEO Guide",
"author": {
"@type": "Person",
"name": "Mehdi Tmimi"
},
"datePublished": "2026-03-10",
"description": "Complete guide to web accessibility and SEO.",
"image": "https://example.com/article-image.jpg"
}
</script>
Common Schema Types
| Schema Type | Use For | Rich Result |
|---|---|---|
Article |
Blog posts, news articles | Enhanced listing with date/author |
Product |
E-commerce items | Price, availability, ratings |
Course |
Online courses | Course info in search |
FAQ |
FAQ pages | Expandable Q&A in search results |
BreadcrumbList |
Site navigation path | Breadcrumb trail in search |
โ๏ธ Exercises
Exercise 1: Audit a Page with Lighthouse
Open Chrome DevTools โ Lighthouse โ check "Accessibility". Run the audit on any public website. Note the score and the top 3 issues found. For each issue, identify the HTML fix.
Exercise 2: Keyboard Navigation Test
Unplug your mouse (or disable your trackpad). Navigate through any website using only Tab, Shift+Tab, Enter, Space, and Escape. Document: Can you reach all interactive elements? Can you always see where focus is? Can you operate all menus and buttons?
Exercise 3: Fix an Inaccessible Form
Start with this inaccessible form: inputs with no labels, placeholder-only, no error association, no fieldset grouping. Fix all the issues using proper <label>, aria-describedby, aria-invalid, <fieldset>/<legend>, and focus indicators.
Exercise 4: Add SEO & Open Graph Tags
Take any HTML page you've built and add: a descriptive <title>, meta description, canonical URL, complete Open Graph tags (og:title, og:description, og:image, og:url), Twitter Card tags, and JSON-LD structured data.
Exercise 5: Build an Accessible Navigation
Build a responsive navigation bar with: a skip-to-content link, proper <nav> landmark with aria-label, keyboard-operable hamburger menu (aria-expanded), focus trapping when the mobile menu is open, and :focus-visible styling. Test with keyboard only and VoiceOver/NVDA.
๐ Module Summary
Use native elements (<header>, <nav>, <main>, <button>). They carry built-in accessibility โ no ARIA needed.
aria-label, aria-labelledby, aria-describedby, aria-expanded, aria-live. Only use when native HTML isn't enough.
Everything must work without a mouse. Use :focus-visible, skip links, logical tab order. Never tabindex > 0.
4.5:1 contrast ratio (AA). Don't rely on color alone. Support text resizing to 200%. Respect prefers-reduced-motion.
Descriptive <title>, meta description, logical headings, canonical URLs, Open Graph, Twitter Cards.
JSON-LD for rich search results. Use Schema.org types: Article, Product, Course, FAQ, BreadcrumbList.