caduh

Web Accessibility Basics: 5 Easy Wins for Every Developer

4 min read

Low‑effort, high‑impact a11y fixes: alt text, semantic HTML & landmarks, color contrast (and non‑color cues), keyboard navigation & focus, and form labels/errors—with copy‑paste examples.

TL;DR

  • Write useful alt text (or alt="" when decorative).
  • Use semantic HTML and landmarks (<main>, <nav>, <header>, proper headings).
  • Meet color contrast and never rely on color alone to convey meaning.
  • Ensure full keyboard navigation and visible focus; manage focus on SPA changes and modals.
  • Give real labels to form controls and clear, programmatic error messages.

1) Images: alt text that helps (or stays silent)

Good rules

  • Describe the purpose of the image in context; skip “image of…”.
  • If purely decorative, use empty alt: alt="" (screen readers will ignore it).
  • Icons that act as buttons/links need an accessible name (aria-label, text, or <title> on inline SVG).
  • Don’t duplicate nearby captions; keep alt concise.

Examples

<!-- Content image with purpose -->
<img src="/team/ali.jpg" alt="Ali Karim, Backend Lead" />

<!-- Decorative / spacer -->
<img src="/bg/line.svg" alt="" />

<!-- Icon button -->
<button aria-label="Search">
  <svg aria-hidden="true" focusable="false">…</svg>
</button>

2) Semantic HTML & landmarks (structure = navigation)

  • Use real elements: <button> for actions, <a href> for navigation. Avoid div onClick.
  • Provide landmarks so users can jump around: <header>, <nav>, <main>, <aside>, <footer>.
  • Keep a single <h1> per page and a logical heading order (h2 under h1, etc.).
  • Add a Skip to content link as the first focusable item.

Examples

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

<header>…</header>
<nav aria-label="Primary">…</nav>
<main id="main">
  <h1>Account Settings</h1>
  <section aria-labelledby="profile">
    <h2 id="profile">Profile</h2>
    …
  </section>
</main>
<footer>…</footer>
<!-- Correct control types -->
<a href="/pricing">Pricing</a>   <!-- link → navigation -->
<button type="button">Add to cart</button> <!-- button → action -->

3) Color contrast & non‑color cues

  • Aim for contrast ratios that meet common guidance: 4.5:1 for normal text and 3:1 for large text (≥18.66px bold or ≥24px regular).
  • Don’t rely on color alone to signal state (e.g., errors, required fields). Add icons, text, underline, patterns, etc.
  • Keep a visible focus indicator; don’t remove outlines. Prefer :focus-visible for nicer styling.

Examples

/* Keep focus visible; customize instead of removing */
:focus { outline: 2px solid currentColor; outline-offset: 2px; }
:focus-visible { outline: 3px solid #005fcc; }

/* Link style: more than just color */
a { text-decoration: underline; }
<!-- Error with text & icon, not color alone -->
<p id="email-error" class="error">
  ⚠️ Enter a valid email address.
</p>

4) Keyboard navigation & focus management

  • Every interactive control must be reachable with Tab/Shift+Tab and operable with Enter/Space/Arrow keys as appropriate.
  • Never use tabindex > 0; use native order or tabindex="0" for custom widgets.
  • In modals, trap focus inside, restore focus to the trigger when closing, and allow Esc.
  • In SPAs, when route changes, move focus to the top of the new content (<h1> or container).

Examples

// Move focus after client-side route change
document.getElementById("main-heading")?.focus();
<!-- Modal skeleton -->
<div role="dialog" aria-modal="true" aria-labelledby="modal-title">
  <h2 id="modal-title" tabindex="-1">Confirm delete</h2>
  <button>Cancel</button>
  <button>Delete</button>
</div>

5) Forms: labels, hints, and errors that connect

  • Label every input; placeholder is not a label.
  • Use for + id or wrap the input with <label>.
  • Use aria-describedby to associate help text and error messages with the control.
  • Mark required fields with the required attribute (and indicate visually).
  • Group related options with <fieldset> + <legend>.

Examples

<label for="email">Email</label>
<input id="email" name="email" type="email" autocomplete="email" required
       aria-describedby="email-hint email-error" />
<div id="email-hint" class="hint">We’ll send receipts here.</div>
<p id="email-error" class="error" role="alert">Enter a valid email.</p>

<fieldset>
  <legend>Delivery options</legend>
  <label><input type="radio" name="delivery" value="standard" /> Standard</label>
  <label><input type="radio" name="delivery" value="express" /> Express</label>
</fieldset>

Quick testing (3 minutes)

  1. Tab through your page: Can you reach/see every interactive element?
  2. Zoom to 200%: Does layout still work and text remain readable?
  3. Disable CSS briefly: Does the document order and headings make sense?
  4. Run an automated check (e.g., Lighthouse/axe) to catch low‑hanging issues.
  5. Try a quick screen reader sanity check (VO/NVDA): can you reach the main content and operate key flows?

Pitfalls & fast fixes

| Pitfall | Why it hurts | Fix | |---|---|---| | Images with missing/verbose alt | Noise or missing info | Write purposeful alt or alt="" if decorative | | Clickable divs/spans | Not keyboard‑operable by default | Use <button>/<a> or add full keyboard semantics | | Hidden focus (outline: none) | Keyboard users get lost | Keep and style focus (:focus-visible) | | Low contrast text | Hard to read | Adjust colors to meet contrast guidance | | Placeholder as label | Disappears on input | Use a real <label>; keep hint text separate |


Quick checklist

  • [ ] Meaningful alt or alt="" for every <img>.
  • [ ] Proper landmarks and heading hierarchy; include a Skip to content link.
  • [ ] Contrast meets guidance; links are identifiable beyond color.
  • [ ] Full keyboard support and visible focus; manage focus on route changes & modals.
  • [ ] Labels on all inputs; errors announced and programmatically linked.

One‑minute adoption plan

  1. Add a Skip to content link and ensure <main> exists.
  2. Pass through all images: write alt or set alt="".
  3. Fix contrast on low‑contrast text and show a clear focus outline.
  4. Convert clickable <div>s into buttons/links.
  5. Wire up labels and error messaging on forms (with aria-describedby).