ScreenshotNeo

BlogHow-to

How to Improve Website Accessibility for Screen Readers

A practical developer checklist for semantic HTML, useful alt text, keyboard access, forms, testing, and ongoing screen-reader improvements.

By the ScreenshotNeo team1 October 20269 min read

Improve screen-reader accessibility in this order: build a semantic page structure, write useful names and alternatives, make every interaction keyboard-operable, label and validate forms, announce dynamic changes, then review with browsers, assistive technology, and representative users. A first-pass checklist can reveal obvious problems, but it is not proof of complete WCAG conformance.

1. Start with semantic structure

Screen readers use headings, landmarks, and native controls to let people scan and jump around a page. Use HTML elements for their meaning instead of styling generic div elements to look meaningful.

<header>
  <a href="/">Acme Docs</a>
  <nav aria-label="Primary">
    <a href="/guides">Guides</a>
    <a href="/reference">API reference</a>
  </nav>
</header>

<main id="main-content">
  <h1>Create an API key</h1>
  <p>Generate a key, then store it in your server environment.</p>

  <section aria-labelledby="security-heading">
    <h2 id="security-heading">Security settings</h2>
    <!-- section content -->
  </section>
</main>

<footer>
  <p>Copyright 2026 Acme</p>
</footer>

Use one descriptive h1 for the page topic, then nest h2 and h3 according to section relationships. Do not choose a heading only because its font size looks right. Give repeated regions distinguishing labels, such as <nav aria-label="Account"> and <nav aria-label="Primary">. Write informative page titles, headings, and labels; [WCAG 2.1 Success Criterion 2.4.6](https://www.w3.org/WAI/WCAG21/Understanding/headings-and-labels.html) states that headings and labels describe topic or purpose.

Prefer native elements: <button> for an action, <a> for navigation, <input> for input, and <dialog> or a carefully implemented modal pattern for dialogs. A clickable div starts with no keyboard behavior, focus semantics, or expected screen-reader role.

2. Write image alternatives that convey purpose

Ask what information or function the image contributes, then express that purpose in its alternative.

Image type Recommended alternative
Informative chart or diagram Summarize the data or relationship the image adds. Put longer detail in nearby text or a linked description.
Functional image inside a control Name the action, such as “Search,” rather than the appearance, such as “Magnifying glass.”
Decorative flourish Use alt="" or CSS so it is skipped.
Text embedded in an image Repeat the meaningful text in the alternative or, preferably, as real page text.
<!-- Informative -->
<img src="sales-by-region.png"
     alt="Bar chart: Europe led sales with 420 units, followed by Asia with 310 and North America with 280.">

<!-- Functional -->
<button type="submit">
  <img src="search.svg" alt="">
  <span>Search</span>
</button>

<!-- Decorative -->
<img src="ornament.svg" alt="">

Do not repeat adjacent visible text in an image alternative unless the image adds information. The [W3C/WAI Images Tutorial](https://www.w3.org/WAI/tutorials/images/) explains informative, functional, and decorative alternatives.

3. Make forms identifiable and recoverable

Every control needs an understandable, programmatically associated label. Add instructions for required, formatted, or unusual input. Errors should identify the field, explain the problem, and tell the user how to fix it. Announce successful completion as well as failure.

<form novalidate>
  <div>
    <label for="email">Work email</label>
    <input id="email" name="email" type="email"
           autocomplete="email" required
           aria-describedby="email-help email-error"
           aria-invalid="true">
    <p id="email-help">Use the address where we can send your receipt.</p>
    <p id="email-error" role="alert">Enter an email address, for example name@example.com.</p>
  </div>
  <button type="submit">Send receipt</button>
  <p id="form-status" role="status" aria-live="polite"></p>
</form>

Keep the visible label present; placeholder text is not a replacement because it disappears while typing and can have weak contrast. The [W3C/WAI Forms Tutorial](https://www.w3.org/WAI/tutorials/forms/) covers labels, instructions, grouping, and error handling.

4. Support keyboard operation and visible focus

Tab through the page without a mouse. Operate menus, dialogs, custom selects, carousels, drag alternatives, and checkout steps with the keyboard. Focus must never become trapped, lost, or invisible.

:focus-visible {
  outline: 3px solid #0b63ce;
  outline-offset: 3px;
}

/* Do not remove the outline globally. */
  • Use button and a instead of adding click handlers to noninteractive elements.
  • Ensure focus order follows the reading and task order; avoid positive tabindex values.
  • When opening a dialog, move focus into it, keep focus inside while open, and return focus to the invoking control on close.
  • Do not use keyboard event handlers that work only for a mouse-like gesture.
  • Provide an alternative for pointer-only actions such as dragging or hovering.

[W3C/WAI Easy Checks](https://www.w3.org/WAI/test-evaluate/easy-checks/) includes keyboard access and visible focus among its introductory checks.

5. Handle dynamic content and single-page applications

Screen readers need to know when content changes without a page navigation. Use a live region for short status messages, and move focus deliberately after major state changes.

<button id="save" type="button">Save settings</button>
<p id="status" role="status" aria-live="polite"></p>

<script>
const save = document.querySelector('#save');
const status = document.querySelector('#status');

save.addEventListener('click', async () => {
  save.disabled = true;
  status.textContent = 'Saving…';
  try {
    await fetch('/settings', { method: 'POST' });
    status.textContent = 'Settings saved.';
  } catch {
    status.textContent = 'Could not save settings. Check your connection and try again.';
  } finally {
    save.disabled = false;
  }
});
</script>

Use role="alert" for urgent errors and role="status" for nonurgent updates. Avoid putting a large changing region in a live region; it can interrupt reading. For route changes, update the document title and place focus on the new page heading or main landmark when appropriate.

6. Check media, tables, and reading order

  • Provide captions and, where needed, transcripts or audio descriptions for video.
  • Use table headers with th and an appropriate scope; do not use tables for layout.
  • Keep DOM order equal to the intended reading order. CSS visual reordering can confuse keyboard and screen-reader users.
  • Use sufficient contrast and do not communicate information by color alone.
  • Set the document language, for example <html lang="en">, and mark genuine language changes with lang.

7. A practical first-pass review

  1. Open a representative page in a browser and inspect the document title, language, landmarks, and heading outline.
  2. Turn off CSS or inspect the accessibility tree to check that content order and names still make sense.
  3. Tab through every task. Confirm visible focus, usable controls, no traps, and a logical order.
  4. Check every image, form field, error, success message, dialog, menu, and dynamic update.
  5. Repeat key tasks with at least one relevant screen reader and browser combination.
  6. Record issues by user impact, page or component, reproduction steps, expected behavior, and owner.

Easy Checks is a starting review. W3C/WAI says most checks can be performed in a browser, with extensions helping for some checks, but a basic pass does not demonstrate complete WCAG conformance. For complex or consequential experiences, involve people who use assistive technology during design and evaluation. Consider the pages and tasks covered, browser and screen-reader combinations, representative users, and whether the review examined both code semantics and visible interaction.

8. Troubleshooting common failures

Symptom Likely cause Fix
Headings cannot be navigated logically Visual styling was used instead of heading elements, or levels jump without a section relationship. Use real h1–h6 elements and restructure sections.
An image is announced twice The same information appears as both nearby text and nonempty alt text. Make the image decorative with alt="" when the nearby text is sufficient.
A button is announced as “clickable” or has no name A div or icon-only control lacks a semantic role and accessible name. Use a native button and visible text or an accurate aria-label.
Form errors are missed Error text is not associated with the field or announced after submission. Set aria-invalid, connect the message with aria-describedby, and announce the summary or status.
Focus disappears after opening a modal Focus was not moved into the dialog or returned afterward. Store the invoking element, focus the first meaningful control, constrain focus while open, and restore it on close.
Screen reader reads stale content A single-page app changed the view without updating title, focus, or status. Announce the change, update the title, and move focus to the new context when needed.
Keyboard users cannot reach a feature Interaction depends on hover, pointer events, or a custom widget with incomplete key handling. Provide keyboard equivalents and test the complete task using only the keyboard.

9. Performance, reliability, and maintenance

Semantic HTML usually reduces JavaScript and improves resilience. Keep client-side enhancements progressive so that essential reading and submission still work when scripts fail. Avoid excessive live-region updates and long focus jumps, which can make announcements noisy. Test loading, error, empty, permission, and offline states; accessibility problems often appear outside the successful path.

Maintain accessibility as a component contract. Document each component’s name, keyboard behavior, focus behavior, states, and announcements. Add automated checks for missing labels, invalid ARIA, and contrast where practical, then keep manual keyboard and screen-reader reviews for behavior automation cannot judge. Recheck after changing routing, dialogs, forms, navigation, or content templates.

10. Capture visual evidence without replacing assistive-technology testing

Screenshot comparison can help reviewers spot clipped focus indicators, missing error summaries, poor zoom layouts, and visual order changes across representative pages. It cannot verify accessible names, reading order, keyboard operation, or announcements, so treat screenshots as evidence for visual regressions alongside semantic and user testing.

Or skip the browser setup

If you need repeatable page images for review documentation or visual regression, ScreenshotNeo provides a single screenshot API request. Before capture it accepts cookie or consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing status. Its MCP server lets Claude, Cursor, and other MCP clients call take_screenshot, get_page_info, and capture_pdf.

See the ScreenshotNeo documentation for options such as full-page capture, CSS selectors, custom CSS and JavaScript, waits, blocking, headers, cookies, device presets, PDF output, caching, signed links, async jobs, bulk capture, and usage reporting.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

There is a free plan with 1,000 screenshots per month and no card. Paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.

FAQ

Can valid HTML alone make a site accessible?

No. Semantic structure is foundational, but users also need useful alternatives, keyboard operation, understandable forms, visible focus, correct dynamic announcements, and testing with relevant assistive technology.

Should every image have alt text?

Every image needs an intentional alternative decision. Informative and functional images need useful text; decorative images generally use empty alt text.

Is an automated accessibility score enough?

No. Automated tools can identify many code and contrast issues, but they cannot judge every name, task flow, announcement, or content alternative. Combine automation with keyboard, screen-reader, and user review.

How many screen readers and browsers should a team test?

There is no universal matrix. Choose combinations used by your audience and risk profile, cover representative tasks, and expand testing when a feature uses complex widgets, custom announcements, or unusual input methods.

Do screenshots prove screen-reader accessibility?

No. They can document visual states such as focus and errors, but only semantic inspection, keyboard checks, assistive-technology testing, and user involvement can evaluate the broader experience.