I did not want accessibility to become a large audit performed just before release. I wanted the smallest feedback loop that could catch a component problem while somebody was still working on it.
Start where components are already reviewed
Storybook was already where Design and Engineering met around component behaviour. That made it the right place to expose accessible names, keyboard states and automated violations beside the implementation.
The aim was not to claim full compliance. It was to shorten the distance between introducing a problem and seeing it.
Build a deliberately small baseline
I started with checks the team could understand and act on.
- Run automated accessibility checks for every relevant story.
- Represent focus, disabled, error and loading states explicitly.
- Review keyboard order and visible focus manually.
- Test accessible names and relationships, not only colour contrast.
- Keep failures visible in the same pull request as the component change.
Know what automation cannot prove
Automated tools can find missing attributes, invalid relationships and many contrast problems. They cannot tell whether a workflow makes sense, whether focus moves somewhere useful or whether an announcement arrives at the right moment.
I treat automation as a release gate for detectable errors and manual review as product judgement, not as competing approaches.
Make failure actionable
A red score without context becomes noise. Each failure needs the affected story, the rule, the DOM location and a route to reproduce it.
The best pipeline is not the one with every possible check. It is the one the team trusts enough to keep running.
The check appears beside the component.
Storybook runs the automated accessibility rules beside the rendered component while the team is already reviewing it. This captured Badge story reports no detectable violations; that is a useful release baseline, not a claim of complete accessibility.

Published Badge story · 0 violations · 3 passes · captured from the live Storybook
The same check now protects every change.
The Storybook test runner opens the real stories in a browser, executes the accessibility rules and fails the pull request when it finds a detectable violation. Manual judgement still covers the parts automation cannot understand.
- 01Story changesComponent behaviour becomes reviewable.
- 02Browser rendersVitest runs the real Storybook project.
- 03axe checksDetectable violations fail the run.
- 04PR is gatedThe failure stays beside the change.
What you can reuse.
Not just a conclusion: these are the working artefacts behind the argument. Open them, question them and adapt what is useful.
Component-level gate
Storybook is configured to fail the test run when detectable violations appear.
Read the a11y configPull-request workflow
The same browser checks run automatically on pull requests and changes to main.
Inspect the workflowLive reproduction
Open the published story and inspect the Accessibility panel beside the component.
Reproduce the check