Day 11: Accessibility Basics – ARIA, Alt Text & Keyboard Navigation
We’ve built a clean semantic layout, embedded media, connected pages, and structured forms. Now let’s make sure everyone can actually use them.
There’s a persistent myth that accessibility is a post-launch checklist, a plugin you install, or a legal hurdle you clear after shipping. It’s none of those things. Accessibility is a structural habit baked into your HTML from line one. When you skip it, you don’t just exclude users—you build fragile markup that breaks on mobile, fails search engine crawlers, and frustrates power users who rely on keyboards or screen readers.
In this post, we’ll cover the foundational patterns that make your HTML truly usable. You’ll learn exactly when to use ARIA (and when to avoid it), how to enforce predictable keyboard navigation, how to audit alt text strategically, and how to verify your work using built-in DevTools and free testing workflows. We’re keeping this focused on core attributes and early-stage habits. Complex focus traps, live regions, and framework-specific a11y patterns will come later. Today is about building a solid, testable baseline.
🎯 What You’ll Learn Today
By the end of this post, you will:
- Understand the First Rule of ARIA and why native HTML always wins
- Apply essential
aria-*attributes (aria-label,aria-expanded,aria-hidden,role) correctly - Enforce predictable keyboard navigation with
tabindex, focus indicators, and skip links - Audit and fix
alttext using purpose-driven rules (informative, functional, decorative) - Verify accessibility using DevTools, automated linters, and basic screen reader testing

1. The Mindset & The First Rule of ARIA
ARIA stands for Accessible Rich Internet Applications. It’s a set of attributes that explicitly tell assistive technology how to interpret UI elements that native HTML doesn’t fully describe on its own. But before you reach for any aria-* attribute, memorize this rule:
Don’t use ARIA if a native HTML element already does the job.
<button>, <nav>, <input>, and <img alt=""> are inherently accessible. They carry built-in keyboard support, focus management, and screen reader announcements. ARIA is a patch for semantic gaps, not a replacement for proper markup. Misusing it actually breaks accessibility more often than omitting it entirely.
🔹 Micro-Exercise 1: Semantic vs. ARIA Patch Create a new file named a11y.html with the standard HTML5 boilerplate. Inside <body>, add:
<!-- Native (preferred) -->
<button>Click Me</button>
<!-- ARIA patch (acceptable only if native isn't possible) -->
<div role="button" tabindex="0" aria-label="Custom Click">Click Me</div>
Code language: HTML, XML (xml)
Save and open in your browser. Press Tab to move focus between them. Both receive focus, but notice how the native <button> already has browser default focus styling and built-in keyboard behavior (Space/Enter triggers it). The <div> requires manual tabindex and won’t respond to keyboard events without JavaScript. Open DevTools → More Tools → Accessibility → Tree view. Both announce as “button,” but the native one inherits everything automatically. Remember this hierarchy.
2. Core ARIA Patterns for Real UI
Beginners overcomplicate ARIA. In practice, four attributes cover 80% of early projects:
aria-label: Names an element when visible text isn’t present (icon buttons, close×).aria-expanded="true/false": Tells screen readers whether a dropdown/accordion is open or closed.aria-hidden="true": Removes decorative or non-interactive elements from the accessibility tree so they’re skipped entirely.role: Assigns semantic meaning when HTML falls short (role="dialog",role="alert",role="navigation").
🔹 Micro-Exercise 2: Apply Core Attributes Keep a11y.html open. Replace the current content inside <body> with this structure:
<main id="main-content">
<h1>Accessible Menu Demo</h1>
<button id="menu-btn" aria-expanded="false" aria-controls="dropdown">
<span aria-hidden="true">☰</span> Menu
</button>
<ul id="dropdown" hidden>
<li><a href="#">Profile</a></li>
<li><a href="#">Settings</a></li>
</ul>
</main>
Code language: HTML, XML (xml)
Save, refresh. Open DevTools → Accessibility pane. Verify the button announces “Menu, button, collapsed.” The hamburger icon is skipped (aria-hidden). The aria-controls explicitly links the button to the dropdown. The hidden attribute keeps it off-screen until you toggle it via JS later. This is how you make custom UI readable without bloating markup.
3. Keyboard Navigation Fundamentals
Roughly 30% of power users and virtually all assistive tech users navigate entirely by keyboard. HTML’s default Tab order follows DOM order. Break it, and you break accessibility.
tabindex="0": Adds a custom element to the natural tab flow.tabindex="-1": Makes an element programmatically focusable (via JS) but removes it from sequential tab order.- Visible focus: Never remove
outlinewithout providing a clear:focus-visiblereplacement. It’s a usability requirement and a WCAG baseline. - Skip links: Hidden until focused, they jump users past repetitive headers straight to
<main>.
🔹 Micro-Exercise 3: Enforce Keyboard Flow Add this to the very top of <body> in a11y.html (before <main>):
<a href="#main-content" class="skip-link">Skip to main content</a>
Code language: HTML, XML (xml)
Add this <style> block inside <head>:
<style>
.skip-link {
position: absolute;
left: -9999px;
top: auto;
padding: 0.5rem 1rem;
background: #000;
color: #fff;
z-index: 100;
}
.skip-link:focus {
left: 0;
}
:focus-visible {
outline: 3px solid #0d6efd;
outline-offset: 2px;
}
</style>
Code language: HTML, XML (xml)
Save, refresh, press Tab. Watch the skip link appear. Press Enter. Focus jumps directly to #main-content. This takes 30 seconds to implement and saves users minutes of navigation fatigue. :focus-visible ensures keyboard users get a clear indicator while mouse users avoid the distracting ring.
4. Alt Text Revisited: The Audit Framework
We covered alt basics on Day 5, but real-world a11y requires a consistent audit mindset. The question isn’t “describe it”—it’s “what’s its purpose here?”
- Informative: Conveys content → write a concise, specific description.
- Functional: Acts as a link or button → describe the action (
alt="Search"). - Decorative: Purely visual →
alt=""(empty string, never omitted).
🔹 Micro-Exercise 4: Audit & Fix Add two images inside your <main> tag, right below the dropdown:
<!-- Informative -->
<img src="https://via.placeholder.com/300x150?text=Q3+Growth+Chart" alt="Bar chart showing 42% growth in Q3 2026">
<!-- Decorative -->
<img src="https://via.placeholder.com/300x10?text=Divider" alt="" role="presentation">
Code language: HTML, XML (xml)
Save, refresh. Open DevTools → Accessibility → select each <img>. Verify the first announces its description, the second is completely skipped. Run the page through the axe DevTools browser extension. You should see zero contrast or missing alt violations. This is your baseline for any media-heavy page.
5. Beginner Pitfalls & Quick Fixes
Let’s address the traps that break accessibility in Week 3:
- ARIA overuse: Slapping
roleandaria-*on everything → Fix: use native HTML first. Only patch when semantics genuinely fail. - Broken tab order: Negative
tabindexor JS focus traps without escape → Fix: keep DOM order logical, test withTab/Shift+Tabmanually. - Invisible focus:
outline: nonewithout replacement → Fix: always pair with:focus-visibleusing high-contrast borders or shadows. - Relying solely on automated linters: Tools like axe/Lighthouse catch ~30% of issues → Fix: combine automated scans with manual keyboard navigation and screen reader testing.
- Debug tip: Chrome DevTools → Accessibility tab → click “Show all nodes” → verify every interactive element has a computed name and role. If it says
genericornull, it’s invisible to assistive tech.
6. Synthesis Exercise: Build & Test an Accessible Toggle
Combine everything into one cohesive, production-ready file. Keep a11y.html open and make these final adjustments:
- Ensure your
<main>hasid="main-content"and the skip link points to it. - Verify the menu button uses
aria-expanded,aria-controls, andaria-hiddenon the icon. - Add
tabindex="0"to any custom interactive elements you might add later. - Apply
:focus-visibleglobally in your<style>block (already done). - Test the full page:
- Press
Tabrepeatedly. Verify logical focus order (skip link → heading → button → links → images) - Click the toggle. Watch
aria-expandedchange in DevTools Elements panel - Run axe DevTools extension. Fix any flagged issues
- (Optional) Turn on VoiceOver (Mac) or NVDA (Windows). Navigate using
Tab+ VoiceOver/NVDA keys. Listen for clear, contextual announcements.
- Press
Stretch prompt: Temporarily remove aria-expanded="false" from the button. Test with a screen reader. How does the announcement change? Why does explicit state management matter for dynamic UI, even when the visual toggle works perfectly?
Key Takeaways
- Native HTML is inherently accessible. Use ARIA only when semantics fall short-
aria-label,aria-expanded, andaria-hiddencover most early UI needs - Keyboard flow depends on DOM order,
tabindex, and visible:focus-visiblestyles altstrategy is purpose-driven: informative, functional, or empty for decorative- Verify with DevTools Accessibility pane, automated linters, and manual keyboard/screen reader tests
What’s Next
In the next post, we’ll finally consolidate everything we’ve learned into the HTML Project: Build Semantic Static Webpage. You’ll combine structure, media, forms, accessibility, and semantic layout into a single, production-ready page that passes real-world validation checks.
Preview question: When reviewing your final HTML page before considering it “complete,” what 3 accessibility checks will you run first? Jot down your list. We’ll apply it next.
← Day 10: Semantic HTML Layout Elements | Day 11 | Day 12: HTML Project — Build Semantic Static Webpage →





Leave a Reply