HTML Accessibility Basics: Build for Everyone
A practical introduction to web accessibility — semantic landmarks, alt text, labels, focus management, keyboard navigation, contrast, and when to reach for ARIA.
What you'll learn
- ✓How semantic landmarks help screen reader users navigate
- ✓The rules for writing useful alt text
- ✓How to associate labels with form controls
- ✓Keyboard navigation and visible focus styles
- ✓What contrast ratios you actually need to hit
- ✓When ARIA helps — and when it hurts
Prerequisites
- •Comfort with basic HTML — see Semantic HTML
Accessibility is not a feature you bolt on at the end. It is a property of the markup you write every day. The good news: most accessibility comes for free when you use the right HTML tag for the job. This post covers the handful of habits that catch the majority of real problems.
If semantic HTML is new to you, read Semantic HTML first — most of the work below builds on it.
Why accessibility matters
About one in five people uses some kind of assistive technology — screen readers, magnifiers, voice control, switch devices, or just a keyboard because a mouse is uncomfortable. An inaccessible site silently locks them out. It also tends to be a worse site for everyone: confusing focus, mystery icons, low contrast, and broken forms hurt sighted mouse users too.
Accessibility is also increasingly a legal requirement in many countries. But the strongest case is the simple one: your users are people, and people deserve to use the thing you made.
Landmarks: structure the page
Screen readers let users jump between landmarks — the major regions of a page. HTML gives you landmarks for free through semantic tags.
<body>
<header>
<nav><!-- main navigation --></nav>
</header>
<main>
<!-- the unique content of this page -->
</main>
<aside><!-- sidebar --></aside>
<footer><!-- closing content --></footer>
</body>
Each of <header>, <nav>, <main>, <aside>, and <footer> becomes a navigable landmark. A user can press a single key and jump straight to <main> — saving them the work of tabbing past every menu link on every page.
Rules of thumb:
- Exactly one
<main>per page. - One top-level
<h1>, then<h2>for major sections,<h3>for subsections. Do not skip levels for styling. - Do not wrap navigation in a
<div>when<nav>exists.
Alt text that actually helps
Every <img> needs an alt attribute. The question is what to put in it.
<!-- Meaningful image: describe what it conveys -->
<img src="/charts/revenue-2026.png"
alt="Revenue grew from $1.2M to $3.8M between Q1 and Q4 2026.">
<!-- Decorative image: use an empty alt -->
<img src="/swirl.svg" alt="">
<!-- Functional image: describe the action -->
<a href="/cart">
<img src="/cart.svg" alt="View shopping cart">
</a>
The rules:
- Meaningful images get a short description of what the image communicates, not what it looks like.
- Decorative images get
alt=""so screen readers skip them. Leavingaltoff entirely makes the reader announce the filename — much worse. - Functional images (icons that are links or buttons) get alt text describing the action.
Do not start with “Image of…” — the screen reader already announces “image.”
Try it. Open any page on your site. For each image ask: would a screen reader user miss anything if this image disappeared? If yes, write alt text. If no, set alt="".
Label every form control
A form control without a label is a mystery box. The fix is one line of HTML.
<!-- Best: <label> with for/id pair -->
<label for="email">Email address</label>
<input id="email" type="email" name="email">
<!-- Also valid: wrap the input -->
<label>
Email address
<input type="email" name="email">
</label>
Either form connects the visible text to the input. Clicking the label focuses the input, and a screen reader reads the label when the input is focused.
When you cannot show a visible label — for example, a search box with only an icon — use aria-label:
<input type="search" aria-label="Search articles" placeholder="Search…">
Do not rely on placeholder as a label. Placeholders disappear when the user starts typing, often have poor contrast, and are not announced consistently by assistive tech.
Focus you can see
Every interactive element — links, buttons, inputs, controls — must be reachable with the Tab key and must show a visible focus style when focused.
The browser default is a blue or black outline. Many sites remove it for aesthetics, then forget to add anything back. The result is a page that is invisible to anyone using a keyboard.
/* Bad: kills keyboard navigation */
:focus { outline: none; }
/* Good: a clear, custom focus style */
:focus-visible {
outline: 2px solid #2563eb;
outline-offset: 2px;
}
:focus-visible only shows the outline when the user is actually navigating with a keyboard, not when they click with a mouse — so you get a clean look for mouse users and a clear indicator for keyboard users.
Keyboard navigation
Tab through your own site with the mouse pushed aside. Ask yourself:
- Can I reach every interactive element?
- Is the focus order logical — does it follow the visual layout?
- Can I activate every button with Enter or Space?
- Can I close every modal with Escape?
- Is there a “Skip to content” link near the top so I can jump past the nav?
A skip link is a small but powerful pattern:
<a href="#main" class="skip-link">Skip to content</a>
<!-- … nav, header … -->
<main id="main">…</main>
.skip-link {
position: absolute;
left: -9999px;
}
.skip-link:focus {
left: 1rem;
top: 1rem;
background: #111;
color: #fff;
padding: 0.5rem 1rem;
}
The link is hidden until a keyboard user tabs to it, at which point it appears and offers a shortcut past the navigation.
Contrast you can read
Text needs enough contrast against its background to be readable. The WCAG guidelines define minimums:
- 4.5:1 for normal body text
- 3:1 for large text (about 18pt+ or 14pt+ bold)
- 3:1 for icons and interactive control borders
Browser devtools show contrast in the color picker. You can also paste foreground and background into a tool like the WebAIM contrast checker.
Soft greys on white look elegant but routinely fail. #999 on #fff is 2.85:1 — not enough. Bump it to #595959 or darker for body text.
/* Looks soft, fails WCAG */
.muted { color: #999; }
/* Looks similar, passes 4.5:1 */
.muted { color: #595959; }
When to reach for ARIA
ARIA — Accessible Rich Internet Applications — is a set of attributes that adds accessibility information to HTML. It is powerful and easy to misuse. The first rule of ARIA, written by the spec authors themselves, is: don’t use ARIA.
That sounds extreme, but the reasoning is sound: native HTML elements already carry the right semantics. A <button> is announced as a button, is focusable, and responds to Space and Enter. A <div role="button"> is none of those things by default — you have to add tabindex, keyboard handlers, focus styles, and ARIA state yourself, and most teams forget at least one.
Use ARIA when:
- You need to label something with no visible text (
aria-label). - You need to announce dynamic changes (
aria-live="polite"). - You are building a custom widget — tabs, comboboxes, tree views — for which no native element exists, and you have read the ARIA Authoring Practices for that pattern.
Do not use ARIA when:
- A native element would do the job.
<button>, not<div role="button">. - You are just trying to “make it accessible” without knowing what an attribute does. Bad ARIA is worse than no ARIA.
<!-- Bad: reinvented button -->
<div role="button" tabindex="0" onclick="save()">Save</div>
<!-- Good: real button -->
<button type="button" onclick="save()">Save</button>
Try it. Find a <div> or <span> on your site that you have wired up as a button. Replace it with a real <button>. Notice everything you no longer have to write yourself.
A quick audit checklist
Before you ship anything, run through this list:
- Page has one
<h1>and a sensible heading order. - Major regions use
<header>,<nav>,<main>,<footer>. - Every image has an
altattribute (empty for decorative). - Every form control has a label or
aria-label. - All interactive elements are reachable by Tab.
- Focus is clearly visible.
- Text contrast meets 4.5:1.
- Page is usable at 200% zoom.
- Site works without a mouse.
You will not get every item perfect on every page. The point is to make these questions a habit.
Recap
Most accessibility wins are HTML wins:
- Use semantic landmarks so screen reader users can navigate.
- Write alt text that describes meaning, not appearance.
- Label every form control with a real
<label>. - Keep focus visible and Tab order logical.
- Hit 4.5:1 contrast for body text.
- Prefer native elements. Use ARIA only when HTML cannot do the job.
Next steps
- Read Semantic HTML to deepen your landmark knowledge.
- Learn about HTML Forms for label and validation patterns.
- Try HTML Meta Tags and SEO — many of the same instincts apply to search engines.
Questions or feedback? Email codeloomdevv@gmail.com.