Back to main website
What kitties pray for ...when they'er bored of string | Image copyright 2013 alejandro-vrl

Beginner's Deep Dive: Web Accessibility (A11y)

A hands-on overview of accessibility fundamentals you can apply today. Uses native HTML first, minimal ARIA, keyboard-first thinking.

What accessibility is (and why it matters)

Accessibility ensures everyone can use your site—keyboard users, screen-reader users, people who zoom, prefer reduced motion, or have color-vision differences. It's good ethics, good UX, and helps SEO through clearer semantics.

POUR principles:
  • Perceivable — alternatives like text, captions.
  • Operable — keyboard accessible, no traps.
  • Understandable — clear labels, instructions, errors.
  • Robust — semantic HTML that works with assistive tech.

The mindset (before code)

  • Use native HTML first; add ARIA only if native elements can't express it.
  • Make sure everything is usable by keyboard.
  • Keep focus visible and predictable.
  • Provide text alternatives for non-text content.
  • Don't rely on color alone to convey meaning.

1) Semantic HTML: the biggest win

<body>
  <header>…site header…</header>
  <nav aria-label="Primary">…links…</nav>
  <main id="content">…unique content…</main>
  <aside>…related info…</aside>
  <footer>…copyright…</footer>
</body>

Headings: one <h1> per page, then nest h2, h3 in order—do't skip levels for style.

<h1>Enchanted Yule Cards</h1>
<h2>Top Sellers</h2>
<h3>Krampus & Yule Cat</h3>

Links vs Buttons: links go to URLs; buttons do actions.

<a href="/cards/yule-cat" class="card">View details</a>
<button type="button" id="addToCart">Add to basket</button>

2) Images & icons (alt text that sells)

<img src="yule-cat-card-front.jpg"
     alt="Funny Yule card: Krampus dangling from the Yule Cat's mouth">
<img src="snowflakes.svg" alt="" aria-hidden="true">
<button type="button" aria-label="Add to basket">
  <svg aria-hidden="true" focusable="false">…</svg>
</button>

3) Color & contrast

Aim for contrast ratio ≥ 4.5:1 for body text (3:1 for large text). Don't communicate status by color alone.

<p><strong class="status-dot" aria-hidden="true">●</strong>
<span class="sr-only">Status:</span> In stock</p>
.sr-only{
  position:absolute; width:1px; height:1px; padding:0; margin:-1px;
  overflow:hidden; clip:rect(0,0,1px,1px); white-space:nowrap; border:0;
}

4) Keyboard accessibility & focus

:focus { outline: 3px solid currentColor; outline-offset: 2px; }
:focus-visible { outline: 3px solid; }

Skip link to jump to main content:

<a class="skip-link" href="#content">Skip to content</a>

5) Forms that make sense (labels, hints, errors)

<form novalidate>
  <div>
    <label for="email">Email</label>
    <input id="email" name="email" type="email"
           autocomplete="email" aria-describedby="email-hint email-err">
    <div id="email-hint" class="hint">We'll send updates here.</div>
    <div id="email-err" class="error" role="alert">Enter a valid email.</div>
  </div>
  <fieldset>
    <legend>Card finish</legend>
    <label><input type="radio" name="finish" value="matte"> Matte</label>
    <label><input type="radio" name="finish" value="gloss"> Gloss</label>
  </fieldset>
  <button type="submit">Checkout</button>
</form>

6) ARIA: when (and how) to use it

  • Use role="alert" for immediate, important messages.
  • Use aria-expanded, aria-controls for disclosure widgets.
  • Use aria-live="polite" for quieter updates.
<p id="cart-updates" aria-live="polite"></p>
<button id="add">Add to basket</button>
<script>
document.getElementById('add').addEventListener('click', () => {
  document.getElementById('cart-updates').textContent =
    'Added “Yule Cat Card” to basket.';
});
</script>

7) Disclosures/accordions (FAQ, product details)

<button aria-expanded="false" aria-controls="a1" id="q1">
  What paper stock is used?
</button>
<div id="a1" role="region" aria-labelledby="q1" hidden>
  300gsm card, smooth finish.
</div>
<script>
const btn = document.getElementById('q1');
btn.addEventListener('click', () => {
  const expanded = btn.getAttribute('aria-expanded') === 'true';
  btn.setAttribute('aria-expanded', String(!expanded));
  document.getElementById('a1').hidden = expanded;
});
</script>

8) Multimedia (video, motion)

@media (prefers-reduced-motion: reduce) {
  * { animation-duration: .001ms !important; animation-iteration-count: 1 !important;
      transition-duration: .001ms !important; }
}
<video controls>
  <source src="product-demo.mp4" type="video/mp4">
  <track kind="captions" src="product-demo.en.vtt" srclang="en" label="English">
  Your browser does not support the video tag.
</video>

9) Tables (specs, sizes)

<table>
  <caption>Card sizes and envelopes</caption>
  <thead>
    <tr><th scope="col">Size</th><th scope="col">Inches</th><th scope="col">Envelope</th></tr>
  </thead>
  <tbody>
    <tr><th scope="row">Standard</th><td>5" x 7"</td><td>White</td></tr>
  </tbody>
</table>

10) Page essentials

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Funny Pagan Yule Cards | Webliminal Arts</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
</head>

11) Single-page apps (React/Vue)

// After navigation:
document.querySelector('h1')?.focus(); // ensure h1 is focusable via tabindex="-1"
<h1 tabindex="-1">Checkout</h1>

12) Quick checklist

  • Keyboard access: Tab/Shift+Tab/Enter/Space/Escape/Arrows work as expected.
  • Visible focus for all interactive elements.
  • One h1, logical heading order.
  • Alt text on meaningful images; alt="" on decorative.
  • Labels on form fields; clear hints and error text.
  • Color contrast ≥ 4.5:1 for body text.
  • Skip link and correct landmarks.
  • No “div-buttons”. Use <button>/<a>.
  • Respects prefers-reduced-motion.
  • lang attribute and descriptive <title>.

13) Testing (quick and free)

  • Keyboard only: Try to complete a key task without a mouse.
  • Screen readers: NVDA (Windows), VoiceOver (macOS).
  • Automated checks: Chrome Lighthouse (Accessibility), axe DevTools.
  • Contrast: verify text and icons with a contrast checker.

14) Starter utilities

<div aria-live="polite" class="sr-only" id="global-updates"></div>
<script>
function announce(msg){
  const live = document.getElementById('global-updates');
  live.textContent = ''; // force change for some SRs
  setTimeout(() => live.textContent = msg, 50);
}
</script>
<div role="alert" class="toast" id="toast" hidden>Added to basket</div>
<script>
function showToast(text){
  const t = document.getElementById('toast');
  t.textContent = text;
  t.hidden = false;
  setTimeout(() => t.hidden = true, 3000);
}
</script>

15) Common anti-patterns

  • Using placeholder text instead of a real <label>.
  • Click handlers on non-interactive elements (e.g., <div>).
  • Removing focus outlines without replacement.
  • Icon-only buttons with no accessible name.
  • Auto-advancing carousels with no pause/controls.
  • Modals that don't trap focus or close with Escape.

Back to top

Side stuff...

You know like, cos’ you have to have a proper list of pages too!



Linx

Follow in the pawprints of those who have found the cream...

sponsored by

Branded Frog

Graphic Design and eCommerce solutions.


Branded Frog Graphic Design and eCommerce solutions