Skip to content

Accessibility in Front-End Development: How, Not Why

The business case for accessibility has been made a thousand times. What is harder to find is the implementation detail, so this skips the argument and goes to the code.

Share
Abstract visualisation of multiple pathways converging on a single illuminated destination

The business case for accessibility is well established and not in dispute. More users, less legal exposure, better products. Every article on the subject makes it; most do it well, and a developer who reads one comes away agreeing but still not knowing what to type.

So this skips the argument. What follows is the implementation: which decisions matter, in roughly the order they matter, with the numbers where numbers exist.

One correction before starting, because it circulates widely and is wrong. Accessibility is not a search ranking factor. Google has said so directly. The confusion arises because accessible sites tend to share properties that do help ranking, such as semantic structure, descriptive alt text and good performance, but the causal claim is false. Accessibility matters because people cannot use your site otherwise, which is a stronger reason than a ranking bump that doesn't exist.

Who this is actually for

The mental model that produces bad accessibility work is "blind users with screen readers". That group matters, but it's a small fraction of the picture.

The World Health Organisation estimates 1.3 billion people, around 16 per cent of the global population, live with a significant disability. Most of those disabilities are not visual. Motor impairments that make a mouse impractical. Cognitive and attention differences are affected by layout and language. Hearing loss, which is what captions are for. Vestibular disorders triggered by motion.

Then there is the far larger group nobody counts: temporary and situational. A broken wrist. An eye infection. Bright sunlight on a phone screen. A noisy train with no headphones. Holding a child in one arm. The person using your site one-handed while distracted is helped by exactly the same things that help someone with a permanent motor impairment.

This matters practically because it changes what you build. Designing for screen readers alone produces sites with impeccable ARIA and unusable keyboard navigation, poor contrast and gratuitous animation. The broad approach is cheaper and helps far more people.

Worth knowing accurately, since this area has moved recently and much published guidance is out of date.

WCAG 2.2 is the current W3C recommendation, and Level AA is the practical target for essentially everyone. Most legislation still references 2.1 AA as the legal floor, and the gap between them is small, so building to 2.2 satisfies both.

In the EU, the European Accessibility Act became enforceable on 28 June 2025, a significant change. Unlike earlier EU rules, which covered public sector bodies, the EAA reaches private companies selling to EU consumers, including e-commerce, banking and transport, with exemptions for microenterprises. It applies to businesses outside the EU that sell into it, which catches a great many organisations that assume it does not apply to them.

In the US, the Department of Justice's ADA Title II rule sets WCAG 2.1 AA as the standard for state and local government, with deadlines that were extended in April 2026 to April 2027 for larger entities and April 2028 for smaller ones. Title III, which covers private businesses, has no regulation specifying a technical standard. Still, it generates thousands of federal lawsuits a year and a much larger volume of demand letters that settle quietly.

The practical read: if you build anything commercial with international reach, this is now a compliance question rather than a values question, and the values reason was already sufficient.

Semantic HTML does most of the work

The highest-leverage accessibility decision is using the element that already means what you want.

A button element is focusable, activates on Enter and Space, announces itself as a button, and appears in the browser's list of controls. A div with a click handler does none of that, and making it equivalent requires role="button", tabindex="0", and keyboard handlers for both Enter and Space. Developers reimplement this constantly and get it subtly wrong, usually by handling Enter and forgetting Space.

The same applies throughout. Use a with an href for navigation and button for actions, and the distinction is not stylistic: links can be opened in a new tab and appear in the links list; buttons cannot and do not. Use label bound to inputs, fieldset and legend for groups, table with th and scope for tabular data, nav, main and article for landmarks, and headings in strict order without skipping levels.

Native dialog and the popover attribute are worth adopting specifically because they handle focus and dismissal correctly by default, which hand-built modals rarely do.

If you find yourself adding ARIA to make a div behave like a control, stop and use the control.

ARIA, and the rule that governs it

The first rule of ARIA is that you should not use ARIA. That is the actual wording in the specification's authoring practices, and it means: if a native element does the job, use it, because ARIA changes only how something is announced, never how it behaves.

This is the point most often missed. role="button" on a div makes a screen reader say "button". It doesn't make it focusable, respond to keys, or work. You have described a control without building one, which is arguably worse than doing nothing, since the user is now told something exists that they cannot operate.

Where ARIA genuinely earns its place is communicating state and relationships that HTML cannot express:

<button aria-expanded="false" aria-controls="menu-1">Menu</button>
<ul id="menu-1" hidden>...</ul>

aria-expanded on disclosure controls, aria-current="page" on the active navigation item, aria-live regions for content that updates without a page change, aria-describedby to associate error messages with fields. These express things markup has no other way to say.

Two failure modes to avoid. Redundant roles, like role="button" on a button, add noise. And stale state, where aria-expanded says false while the menu is open, is worse than no ARIA at all, because assistive technology believes you.

Keyboard access and focus

Everything must work without a mouse. This is the single most testable thing on this list; it takes five minutes, and most sites fail it.

Press Tab through your interface and watch. Focus order should follow visual order; every interactive element should be reachable, and you should always be able to see where you are.

Three things reliably break.

Invisible focus. Removing the default outline because it looked untidy is common and is a serious barrier. If you dislike the default, replace it rather than delete it:

:focus-visible {
  outline: 3px solid var(--colour-focus);
  outline-offset: 2px;
}

:focus-visible is the right selector because it shows the ring for keyboard users and suppresses it for mouse clicks, which is the behaviour people actually wanted when they reached for outline: none.

Focus escaping modals. When a dialog opens, focus should move into it, stay within it while open, and return to the triggering element when it closes. Native dialog with showModal() does all of this. Hand-built modals usually let Tab wander into the page behind, where a screen reader user reads content they cannot see. The inert attribute on background content is the manual fix.

No skip link. A keyboard user should not tab through forty navigation items on every page. A skip link, visually hidden until focused, is a few lines and a significant improvement.

Forms

Forms are where accessibility failures cost people the most, because they are usually the point of the page.

Every input needs a programmatically associated label. Placeholder text is not a label: it disappears on focus, fails contrast requirements in most implementations, and is not reliably announced.

<label for="email">Email address</label>
<input id="email" type="email" aria-describedby="email-error" aria-invalid="true">
<p id="email-error">Enter an email address in the format name@example.com</p>

Errors need three things: association with the field via aria-describedby, identification as errors rather than colour alone, and text that says how to fix the problem rather than that one exists. "Invalid input" fails all three tests.

Group related controls with fieldset and legend, particularly radio buttons, where the question itself is otherwise never announced.

Colour and contrast

Numbers, since this is one of the few areas with hard thresholds.

WCAG AA requires 4.5:1 contrast for normal text against its background, 3:1 for large text, which means 18.66px bold or 24px regular and above. Non-text elements that convey meaning, including icons, form borders and focus indicators, need 3:1. AAA raises text to 7:1, which is worth targeting for body copy on content sites.

Beyond ratios, one rule: colour must never be the only carrier of information. Red borders on invalid fields need accompanying text. Chart series distinguished only by hue are unreadable for the roughly 8 per cent of men with colour vision deficiency. Links inside body text should be underlined, not merely coloured, because that is the only cue available to some readers.

Motion and target size

Two more with concrete requirements.

Respect the reduced motion preference. Vestibular disorders are real, and large transitions and parallax can cause genuine nausea:

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

Target size is a WCAG 2.2 addition: 24 by 24 CSS pixels minimum at AA, with a spacing exception, and 44 by 44 at AAA. I covered the practical side of this, including how to give a small icon a large hit area, in the piece on designing for mobile.

Alt text deserves more care than it usually gets, and the empty alt="" versus missing attribute distinction matters more than most people realise. I covered that properly in the image optimisation piece.

Testing, and the limits of automation

Automated tools catch roughly a third of accessibility issues. That figure is worth internalising, because it sets expectations correctly: a clean axe run means you have cleared the machine-checkable third, not that your site is accessible.

Automation is best at the category that dominates real-world failures: missing alt attributes, insufficient contrast, unlabelled form fields, empty links and buttons. Those few classes account for the overwhelming majority of detected errors on typical sites, which is why running a scanner remains worth doing despite its limits.

What it cannot assess is whether your alt text is meaningful, whether focus order makes sense, whether an error message is comprehensible, or whether a custom component is usable in practice.

So the practical programme is three layers. Run axe-core inside your existing end-to-end suite rather than as a separate quarterly ritual, which I covered in more detail in the testing tools piece. Do a keyboard-only pass on every new flow; it takes five minutes and catches more than the scanner. And test with an actual screen reader periodically: VoiceOver on macOS and iOS, NVDA on Windows, TalkBack on Android, all free and all revealing.

Overlay widgets do not work

One paragraph, because the marketing is persistent.

Accessibility overlays are JavaScript widgets that claim to make a site compliant by inserting a line of code. They do not. They cannot fix semantic structure, keyboard traps or meaningless alt text, because those problems live in your markup. Sites using them continue to be sued and to lose, and the largest vendor in the space drew a US Federal Trade Commission action over its compliance claims. Beyond not helping, overlays often interfere with the assistive technology users already have configured, making the experience worse for the people they claim to serve.

There is no line of code. There is only doing the work.

Where to start

If you have an existing site and no idea where you stand, in this order:

Run axe or Lighthouse to get the machine-checkable issues, then fix contrast, missing labels, missing alt text, and empty controls first, since they make up most of what is found and are the cheapest to resolve.

Do a keyboard pass on your three most important flows, probably navigation, search and checkout or signup. Fix focus visibility and anything unreachable.

Then move accessibility into the templates and components rather than treating it as remediation. A design system where the button component is correct once makes every future page correct by default, which is the only version of this that scales.

And add the automated check to CI so the ground you gain stays gained.

None of that requires a specialist or a budget line. Most of it is choosing the right element and not deleting the focus ring.