Build a luckier tech stack today
Build a luckier tech stack today
How to Build an Accessible React Component Library for UK Public Sector Sites

How to Build an Accessible React Component Library for UK Public Sector Sites

On September 10, 2026

Every component you publish is a decision made on behalf of the teams who use it. Fix the focus ring once and a hundred services inherit the fix. Get a dialog wrong and you have shipped the same trap to every one of them. That leverage is the whole point of a component library, and it is also why accessibility work belongs at the library level rather than at the end of each project.

What follows is a working method for building a React library that holds up under WCAG 2.2 AA and the scrutiny that UK public sector services attract.

Know what you are being measured against

Three things set the bar. WCAG 2.2 AA is the technical standard. EN 301 549 is the harmonised European standard that UK accessibility regulations point to. And the Public Sector Bodies (Websites and Mobile Applications) Accessibility Regulations 2018 require public sector bodies to make their sites and apps accessible and to publish an accessibility statement — with exemptions and a disproportionate burden defence that apply in some cases. If it is unclear how the rules land on your organisation, ask your accessibility lead or legal team rather than guessing.

WCAG 2.2 added criteria that are easy to miss if you are still working from 2.1 habits: focus not obscured, target size, dragging movements, consistent help, redundant entry and accessible authentication. Build them into your components now. A date picker that only works by dragging, or a login form that blocks paste, will fail before anyone writes a line of page-level code.

Accessibility is a constraint on the component, not a task for the team consuming it.

Set up guardrails before the first component

Guardrails are cheaper than reviews. Wire these in on day one:

  • TypeScript with strict mode. It catches missing labels and sloppy props better than patience does.
  • eslint-plugin-jsx-a11y in your lint config, with failures breaking the build rather than producing warnings nobody reads.
  • axe-based unit tests using jest-axe or vitest-axe, run against every component in its default and error states.
  • A Storybook accessibility addon so reviewers see contrast and role problems while they are still cheap to fix.
  • Design tokens for colour with paired foreground and background values, so a 4.5:1 contrast ratio is built into the token rather than checked by eye.

Automated tooling catches a subset of real problems — useful, but never the whole picture. Keyboard and screen reader testing still decides whether a component ships.

Start from HTML, then add ARIA reluctantly

The most accessible React component is usually a thin wrapper around an element the browser already understands. A <button> gets keyboard activation, focus behaviour and screen reader announcements for free. A <div onClick> gets none of it, and the ARIA you add to compensate rarely covers everything.

Reach for semantic elements first: button, a, label, fieldset with legend, details and summary, dialog. Add ARIA only when no native element fits, and then check the authoring practices for that pattern. Never overwrite a role the browser has already assigned.

The components that carry the most risk

Form fields and errors

Every input needs a programmatically associated label. Hints go in aria-describedby, not in placeholder text. Errors appear inline next to the field and again in an error summary at the top of the form, which receives focus on submission and links to each problem field. Never signal an error with colour alone — use text and an icon as well.

Dialogs and menus

Move focus into the dialog on open, keep it inside while it is open, close on Escape, and return focus to the element that opened it. Prevent the page behind from being reachable. If a pattern needs three paragraphs of instructions to use correctly with a keyboard, it is the wrong pattern.

Disclosure and navigation

Accordions, tabs and mobile menus are where hidden content goes wrong. Use aria-expanded honestly, keep tab order matching visual order, and provide a skip link that becomes visible on focus. A skip link is a two-line component with an outsized effect on usability.

Targets and motion

Interactive elements need a target of at least 24 by 24 CSS pixels, with generous spacing for touch. Respect prefers-reduced-motion, and make sure nothing breaks at 400 per cent zoom or a 320-pixel viewport — content should reflow into one column rather than scroll sideways.

A definition of done for every component

  1. It can be used end to end with a keyboard alone, with a visible focus indicator that is never obscured.
  2. It has an accessible name, and announces state changes where relevant.
  3. It meets contrast requirements in every theme and state, including hover and disabled.
  4. It works at 400 per cent zoom and in Windows high contrast mode.
  5. It behaves correctly with reduced motion enabled.
  6. It appears in Storybook with a test for each state.
  7. It has been checked in at least one screen reader, such as NVDA on Windows or VoiceOver on macOS and iOS.

Document the accessibility contract

Props need more than types. Each component page should say what the component handles for you and what the consuming team still owns. "Pass a label; the component generates the ID and links the hint automatically" is useful. Silence is not.

Version carefully. A change that removes an accessible name or swaps a button for a div is a breaking change even if no prop signature moved. Use deprecation warnings, keep a changelog written for humans, and avoid quietly altering focus behaviour in a patch release — teams will notice, but only after it reaches production.

Testing that actually finds problems

Book an afternoon and use the library with the mouse unplugged. Then run it with a screen reader and your eyes closed, or at least turned away from the screen. Test on a low-end Android device with a slow connection, because that is what many users have. Recruit disabled users for research where you can, and pay them for their time. Their feedback tends to find the issues that no linter will.

Making it stick

Start with three components this sprint: a skip link, a form field with error handling, and a button. Get the guardrails running in continuous integration, write the definition of done into your pull request template, and treat accessibility bugs as bugs rather than enhancements. Add each new pattern only when someone can show it working with a keyboard and a screen reader.

Done properly, an accessible React library turns a hard, repeated problem into a habit. The teams consuming it stop thinking about focus management and start thinking about the service they are building — which is exactly where their attention belongs.

Photo: Simon / Pixabay

How to Write a Technical CV for UK Software Jobs

A practical guide to building a UK-style technical CV: the right sections, bullets that prove impact, correct British spelling, and the...

Common Mistakes in React State Management (and How to Fix Them)

A practical troubleshooting guide to the most common React state mistakes — prop drilling, needless re-renders, and context misuse — with fixes...