HTML (HyperText Markup Language) is the backbone of every webpage. It provides structure and meaning to content. Web Accessibility (A11y) ensures that this structure is usable by everyone — including people with visual, auditory, motor, or cognitive disabilities. In this guide you'll go from the very basics of an HTML document all the way to advanced ARIA techniques and accessibility testing.
Step 1 — Anatomy of an HTML Document
Every HTML page follows a standard skeleton. Understanding each part is the essential first step before writing a single tag.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content="A short description of this page for search engines" />
<title>My First Page — Site Name</title>
<link rel="stylesheet" href="styles.css" />
</head>
<body>
<h1>Hello, World!</h1>
<p>Welcome to my first webpage.</p>
<script src="app.js" defer></script>
</body>
</html>Key Parts Explained
- <!DOCTYPE html> — Tells the browser this is HTML5. Always the very first line.
- <html lang="en"> — The root element. lang attribute is critical for screen readers.
- <head> — Contains metadata: charset, viewport, title, CSS links. Not visible on page.
- <meta charset="UTF-8"> — Enables full Unicode character support (emojis, accents, etc.).
- <meta name="viewport"> — Makes the page responsive on mobile devices.
- <body> — Everything visible to the user goes here.
- defer on <script> — Loads JS after HTML is parsed, preventing render-blocking.
Step 2 — Essential HTML Tags
HTML has over 100 tags, but a small set covers 90% of your daily needs. Let's learn the most important ones.
<!-- Headings: h1 is the page title, use h2-h6 for sub-sections -->
<h1>Main Page Title</h1>
<h2>Section Heading</h2>
<h3>Sub-Section</h3>
<!-- Paragraphs -->
<p>This is a paragraph of text. Keep each paragraph focused on one idea.</p>
<!-- Links -->
<a href="https://example.com" target="_blank" rel="noopener noreferrer">
Visit Example (opens in new tab)
</a>
<!-- Images: alt text is mandatory for accessibility -->
<img src="cat.jpg" alt="A ginger cat sleeping on a red blanket" width="400" height="300" />
<!-- Unordered list -->
<ul>
<li>Apples</li>
<li>Bananas</li>
<li>Cherries</li>
</ul>
<!-- Ordered list -->
<ol>
<li>Preheat oven to 180°C</li>
<li>Mix flour and butter</li>
<li>Bake for 25 minutes</li>
</ol>
<!-- div: generic block container (no semantic meaning) -->
<div class="card">...</div>
<!-- span: generic inline container -->
<p>The price is <span class="highlight">₹499</span> only.</p>
<!-- Strong and emphasis -->
<p>This is <strong>important</strong> and this is <em>emphasized</em>.</p>Step 3 — HTML Forms
Forms are how users send data to your app. HTML provides a rich set of input types that trigger native mobile keyboards and built-in browser validation.
<form action="/submit" method="POST" novalidate>
<!-- Text input with associated label -->
<div class="field">
<label for="username">Username</label>
<input
type="text"
id="username"
name="username"
required
minlength="3"
maxlength="20"
placeholder="e.g. kuldeep123"
autocomplete="username"
/>
</div>
<!-- Email -->
<div class="field">
<label for="email">Email Address</label>
<input type="email" id="email" name="email" required autocomplete="email" />
</div>
<!-- Password -->
<div class="field">
<label for="password">Password</label>
<input type="password" id="password" name="password" required minlength="8" />
</div>
<!-- Number -->
<div class="field">
<label for="age">Age</label>
<input type="number" id="age" name="age" min="13" max="120" />
</div>
<!-- Date -->
<div class="field">
<label for="dob">Date of Birth</label>
<input type="date" id="dob" name="dob" />
</div>
<!-- Textarea -->
<div class="field">
<label for="bio">Bio</label>
<textarea id="bio" name="bio" rows="4" cols="40" maxlength="200"></textarea>
</div>
<!-- Select dropdown -->
<div class="field">
<label for="branch">Branch</label>
<select id="branch" name="branch" required>
<option value="">-- Select Branch --</option>
<option value="cs">Computer Science</option>
<option value="mech">Mechanical</option>
<option value="civil">Civil</option>
</select>
</div>
<!-- Radio buttons grouped with fieldset/legend -->
<fieldset>
<legend>Gender</legend>
<label><input type="radio" name="gender" value="male" /> Male</label>
<label><input type="radio" name="gender" value="female" /> Female</label>
<label><input type="radio" name="gender" value="other" /> Other</label>
</fieldset>
<!-- Checkbox -->
<label>
<input type="checkbox" name="terms" required />
I agree to the <a href="/terms">Terms & Conditions</a>
</label>
<!-- Submit button -->
<button type="submit">Create Account</button>
</form>Form Best Practices
- Always use <label for="id"> paired with the input's id — this is critical for screen readers and increases click target size.
- Use the correct input type= (email, tel, number, date) — triggers the right mobile keyboard.
- Add autocomplete attributes to help password managers and improve UX.
- Use fieldset + legend to group related inputs (like radio buttons).
- Never disable browser zoom — users with low vision rely on it.
Step 4 — Semantic HTML5 Elements
Semantic elements carry meaning beyond just visual presentation. They tell browsers, search engines, and assistive technologies what role each section plays on the page.
<body>
<!-- Site-wide header: logo, site name, main nav -->
<header>
<a href="/" class="logo">Education Academy</a>
<nav aria-label="Main navigation">
<ul>
<li><a href="/">Home</a></li>
<li><a href="/papers">Papers</a></li>
<li><a href="/learn">Learn</a></li>
</ul>
</nav>
</header>
<!-- Main content area — only ONE per page -->
<main>
<!-- A self-contained piece of content that makes sense on its own -->
<article>
<header>
<h1>Mathematics I — 2024 Paper</h1>
<p>Posted on <time datetime="2024-05-15">May 15, 2024</time></p>
</header>
<p>Question paper content...</p>
</article>
<!-- A thematic grouping of content -->
<section aria-labelledby="tips-heading">
<h2 id="tips-heading">Study Tips</h2>
<p>Tips content...</p>
</section>
</main>
<!-- Supplementary content related to main -->
<aside aria-label="Related topics">
<h2>Related Subjects</h2>
<ul>...</ul>
</aside>
<!-- Site-wide footer -->
<footer>
<p>© 2024 Education Academy</p>
</footer>
</body>Step 5 — HTML Tables
Tables should only be used for tabular data (like a question paper's marks table), never for layout. Proper table markup makes data readable by screen readers.
<table>
<caption>Marks Distribution — Mathematics I</caption>
<thead>
<tr>
<th scope="col">Unit</th>
<th scope="col">Topic</th>
<th scope="col">Marks</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>Differential Calculus</td>
<td>20</td>
</tr>
<tr>
<td>2</td>
<td>Integral Calculus</td>
<td>20</td>
</tr>
<tr>
<td>3</td>
<td>Matrices</td>
<td>20</td>
</tr>
</tbody>
<tfoot>
<tr>
<th scope="row" colspan="2">Total</th>
<td>60</td>
</tr>
</tfoot>
</table>Accessible Table Rules
- Always include a <caption> that describes what the table contains.
- Use <th> with scope="col" for column headers and scope="row" for row headers.
- thead, tbody, and tfoot help screen readers understand table structure.
- Never use tables for page layout — use CSS Flexbox or Grid instead.
Step 6 — Media Elements
HTML5 provides native elements for audio and video. Use them instead of third-party plugins.
<!-- Responsive image with multiple resolutions -->
<picture>
<source srcset="hero-wide.webp" media="(min-width: 800px)" type="image/webp" />
<source srcset="hero-mobile.webp" media="(max-width: 799px)" type="image/webp" />
<img src="hero-fallback.jpg" alt="Students studying in a library" width="1200" height="600" loading="lazy" />
</picture>
<!-- srcset for resolution switching -->
<img
src="photo-400.jpg"
srcset="photo-400.jpg 400w, photo-800.jpg 800w, photo-1200.jpg 1200w"
sizes="(max-width: 600px) 400px, (max-width: 1000px) 800px, 1200px"
alt="A professor teaching in a classroom"
/>
<!-- Video with captions -->
<video controls width="640" poster="thumbnail.jpg">
<source src="lecture.mp4" type="video/mp4" />
<source src="lecture.webm" type="video/webm" />
<track kind="captions" src="captions-en.vtt" srclang="en" label="English" default />
<track kind="captions" src="captions-hi.vtt" srclang="hi" label="Hindi" />
<p>Your browser doesn't support video. <a href="lecture.mp4">Download it</a>.</p>
</video>
<!-- Audio -->
<audio controls>
<source src="podcast.mp3" type="audio/mpeg" />
<source src="podcast.ogg" type="audio/ogg" />
<p>Your browser doesn't support audio.</p>
</audio>Step 7 — What is Web Accessibility (A11y)?
Web Accessibility means designing and developing websites so that people with disabilities can perceive, understand, navigate, and interact with them. The Web Content Accessibility Guidelines (WCAG) are the international standard.
WCAG Conformance Levels
- Level A — Minimum accessibility. Failure at this level blocks all users with certain disabilities.
- Level AA — The target for most websites. Covers the most common barriers. Required by ADA and EN 301 549.
- Level AAA — The highest level. Not required for entire sites — used for specific pages like financial services.
POUR Principles of Accessibility
- Perceivable — Information must be presentable in ways all users can perceive (e.g., alt text for images).
- Operable — UI components must be operable by keyboard, not just mouse.
- Understandable — Content and UI must be understandable (clear language, predictable behavior).
- Robust — Content must be interpreted reliably by assistive technologies (valid HTML, ARIA).
Step 8 — ARIA Roles and Labels
ARIA (Accessible Rich Internet Applications) extends HTML's semantics for dynamic content and custom UI components that HTML alone cannot describe.
<!-- aria-label: provides a text label when there's no visible label -->
<button aria-label="Close dialog">✕</button>
<!-- aria-labelledby: links to an existing element's text as the label -->
<section aria-labelledby="results-heading">
<h2 id="results-heading">Search Results</h2>
<!-- content -->
</section>
<!-- aria-describedby: points to additional description text -->
<input
type="password"
id="pwd"
aria-describedby="pwd-hint"
/>
<p id="pwd-hint">Must be at least 8 characters and include a number.</p>
<!-- aria-hidden: hides decorative elements from screen readers -->
<span aria-hidden="true">🎉</span>
<span>Congratulations!</span>
<!-- aria-live: announces dynamic content changes -->
<div role="status" aria-live="polite" aria-atomic="true">
<!-- Content injected by JS will be announced -->
Form submitted successfully!
</div>
<!-- role="alert" for urgent messages (interrupts current speech) -->
<div role="alert">Error: Invalid email address.</div>
<!-- aria-expanded: state of collapsible elements -->
<button
aria-expanded="false"
aria-controls="dropdown-menu"
>
Menu
</button>
<ul id="dropdown-menu" hidden>...</ul>
<!-- aria-required, aria-invalid for form validation -->
<input
type="email"
aria-required="true"
aria-invalid="true"
aria-describedby="email-error"
/>
<span id="email-error" role="alert">Please enter a valid email.</span>Step 9 — Keyboard Navigation
Many users — including people with motor disabilities and power users — navigate entirely by keyboard. Your site must be fully operable without a mouse.
<!-- Skip link: allows keyboard users to jump directly to main content -->
<a href="#main-content" class="skip-link">Skip to main content</a>
<header>... (long navigation) ...</header>
<main id="main-content">
<h1>Welcome</h1>
...
</main>.skip-link {
position: absolute;
top: -100%;
left: 1rem;
padding: 0.5rem 1rem;
background: #000;
color: #fff;
z-index: 9999;
border-radius: 0 0 4px 4px;
}
/* Show the skip link when it receives focus (Tab key) */
.skip-link:focus {
top: 0;
}Keyboard Navigation Rules
- Tab moves forward through focusable elements; Shift+Tab moves backward.
- Enter and Space activate buttons. Enter follows links.
- Arrow keys navigate within components like menus, tabs, and select dropdowns.
- Escape closes dialogs and dropdown menus.
- tabindex="0" adds an element to the natural tab order. tabindex="-1" allows programmatic focus only.
- Never use tabindex > 0 — it breaks the natural document flow.
- Always provide a visible :focus style. Never use outline: none without a replacement.
/* Provide a strong, visible focus indicator */
:focus-visible {
outline: 3px solid #005fcc;
outline-offset: 2px;
border-radius: 2px;
}
/* Remove outline only for mouse users (not keyboard) */
:focus:not(:focus-visible) {
outline: none;
}Step 10 — Color Contrast and Visual Accessibility
Approximately 8% of men and 0.5% of women have some form of color vision deficiency. Good contrast ensures text is readable regardless of color perception.
WCAG Contrast Requirements
- Normal text (< 18pt or < 14pt bold): minimum contrast ratio 4.5:1 (AA) or 7:1 (AAA).
- Large text (≥ 18pt or ≥ 14pt bold): minimum 3:1 (AA) or 4.5:1 (AAA).
- UI components and icons: minimum 3:1 against adjacent colors.
- Decorative images and disabled controls are exempt.
- Never convey information by color alone — always add a text label or icon.
Step 11 — Screen Reader Friendly Images
Images are meaningless to screen readers unless you provide alternative text. Writing good alt text is a skill — here's how to do it right.
<!-- Informative image: describe what it conveys, not what it looks like -->
<!-- BAD: -->
<img src="chart.png" alt="chart" />
<!-- GOOD: -->
<img src="chart.png" alt="Bar chart showing RTU exam pass rates from 2020-2024. Pass rate increased from 68% to 84%." />
<!-- Decorative image: use empty alt to hide from screen readers -->
<img src="divider-wave.svg" alt="" role="presentation" />
<!-- Functional image (used as a button/link): describe the action -->
<!-- BAD: -->
<a href="/home"><img src="logo.png" alt="logo" /></a>
<!-- GOOD: -->
<a href="/home"><img src="logo.png" alt="Education Academy — Go to homepage" /></a>
<!-- Complex image: use aria-describedby for a long description -->
<img
src="architecture-diagram.png"
alt="System architecture diagram"
aria-describedby="arch-desc"
/>
<p id="arch-desc" class="visually-hidden">
The diagram shows three layers: client (browser), API server, and database.
Arrows indicate bidirectional data flow between client and API, and
unidirectional flow from API to database.
</p>/* Visually hide but keep accessible to screen readers */
.visually-hidden {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border-width: 0;
}Step 12 — Accessible Forms
Forms are the most common source of accessibility failures. Error messages, field descriptions, and required indicators must all be programmatically associated with inputs.
<form novalidate>
<div class="field" id="email-field">
<!-- Label MUST be linked via for/id -->
<label for="email-input">
Email Address
<!-- Visually mark required fields -->
<span aria-hidden="true" class="required-star">*</span>
</label>
<!-- Hint text linked via aria-describedby -->
<p id="email-hint" class="hint">We'll never share your email.</p>
<input
type="email"
id="email-input"
name="email"
required
aria-required="true"
aria-describedby="email-hint email-error"
aria-invalid="true"
autocomplete="email"
/>
<!-- Error message: injected by JS on validation failure -->
<p id="email-error" role="alert" class="error">
Please enter a valid email address (e.g., name@example.com).
</p>
</div>
<!-- Group related options -->
<fieldset>
<legend>Preferred Contact Method <span aria-hidden="true">*</span></legend>
<label><input type="radio" name="contact" value="email" required /> Email</label>
<label><input type="radio" name="contact" value="phone" /> Phone</label>
<label><input type="radio" name="contact" value="sms" /> SMS</label>
</fieldset>
<button type="submit">Submit</button>
</form>Step 13 — Testing Accessibility
Automated tools catch about 30-40% of accessibility issues. Manual testing with real assistive technologies is essential to catch the rest.
- Run axe DevTools (free Chrome/Firefox extension) — fixes all flagged issues first.
- Open Chrome DevTools → Accessibility panel → inspect the accessibility tree for any element.
- Run Lighthouse (Chrome DevTools → Lighthouse tab) for an automated accessibility audit.
- Test keyboard-only navigation: Tab through your entire page without touching the mouse.
- Test with NVDA (free, Windows) or VoiceOver (built-in macOS/iOS) to hear how your page sounds.
- Test with browser zoom at 200% — layout must not break or content overflow.
- Test with Windows High Contrast mode — check that custom styles still convey meaning.
- Test on a real mobile device with a screen reader (TalkBack on Android, VoiceOver on iOS).