The state of accessible web UI frameworks
Choosing a UI web framework or library is often difficult. There are plenty to choose from, so you have to take different factors into account: number and quality of the components, performance, ongoing development, license or language support. Way too often accessibility (a11y) doesn't make it high on that list, unfortunately.
I am fortunate to be part of a project that has to comply with WCAG 2.1. In other words, we are required by law to make our web application accessible. I've learned a lot in the last two years and became an accessibility advocate along the way.
In this post, I evaluate the accessibility of almost 30 popular CSS UI frameworks (also known as UI kits). I hope this will help you to make a decision, but also improve the frameworks themselves.
First, let me explain what I have based my ranking on. Web UI frameworks provide individual components with some default styling and markup examples. Hence, I've only checked visual properties (contrast ratio, focus indicator) and semantics (WAI-ARIA compliance).
This has two important implications:
- A fully accessible UI framework does not replace proper testing. You're still responsible for making your application accessible (descriptive labels, keyboard navigation, etc.).
- Some issues are straightforward to fix (like adjusting a color). Still, I review the out-of-the-box behavior, as people may not adjust the provided markup or styles.
The following is particularly important for UI component libraries, as they are meant to be widely reused:
Accessibility should be the default, not opt-in.
I see all the criteria as equally important. Hence, I have normalized the score on a scale from 0 (inaccessible) to 4 (fully accessible) per factor.
One of the core accessibility principles ensures that user interface components are operable and navigable:
2.4.7 Focus Visible: Any keyboard operable user interface has a mode of operation where the keyboard focus indicator is visible. (Level AA)
This makes sense: I cannot navigate a page with a keyboard if I can't see what element is currently focused. Fortunately, every browser provides a default focus indicator ("focus ring"). However, many designers do not like the default appearance, so they either remove the focus ring completely or provide an insufficient alternative (like a background color shade).
Can you tell, which button is active in the following example?
Here's how a custom focus indicator looks like in Bootstrap:
This leads to the following rating:
- ★★: Default browser focus indicator or sufficient alternative.
- ★☆: Insufficient focus ring.
- ☆☆: Focus ring removed.
The focus ring rating often differs between buttons and input controls (like text inputs or checkboxes), so I've checked both, leading to a final, cumulative rating (0 to 4 stars). For example, an insufficient button focus ring and a sufficient input focus ring results in 3 out of 4 points.
In the recent years, it became popular to use light font colors on light backgrounds. This may be considered good-looking or modern, but it makes reading the actual content much harder:
This does not only affect visually-impaired people, but anyone consuming the content in a bright environment as well. That's why WCAG specifies sufficient contrast ratios (depending on the font size). You can check the contrast ratio manually using contrast-ratio.com or the Chrome Dev Tools, but also with fully automatic tools like Lighthouse or pa11y.
Still, some CSS frameworks don't recognize the issue. Others, like Bootstrap, admit to use insufficient color contrasts. As mentioned earlier, I wish more libraries would be accessible-first.
For the contrast ratio review, I focused on the button components (the contrast between the foreground and background color for all buttons).
Normalized on a 4-star scale, my contrast-ratio rating looks as following:
- ★★★★: No colors fail WCAG AA.
- ★★☆☆: Some colors fail WCAG AA.
- ☆☆☆☆: Most colors fail WCAG AA.
Screen reader users heavily depend on semantics to understand the meaning of individual elements, like headlines, paragraphs or buttons. That's why following Semantic HTML is a common best practice:
<div id="header">Bad semantics</div> <header>Good semantics</header>
Semantic HTML offers a good basis, but it is limited for interactive UIs. That's where WAI-ARIA comes into play. It makes it possible to include additional semantics, e.g.:
- Describe complex widgets, e.g. Slider or Dialog.
- Tell if a widget is
- Provide custom labels or descriptions.
If you're implementing interactive widgets, the first place to look are the WAI-ARIA authoring practices.
For my review, I've chosen a Tabpanel component. Technically, it is straightforward (a bunch of buttons that replace some content on a mouse click). However, there is still a lot to consider to make a Tabpanel fully accessible:
That's how my WAI-ARIA compliance rating looks like:
- ★★★★: All required ARIA attributes.
- ★★☆☆: Some required ARIA attributes.
- ☆☆☆☆: Missing or wrong ARIA attributes.
Based on my review criteria, I've got the following results (sorted by the overall accessibility score):
|Library||Focus Ring||Contrast Ratio||WAI-ARIA|
|React Spectrum||4 out of 4||4 out of 4||4 out of 4|
|Reakit 1.3.8||4 out of 4||4 out of 4||4 out of 4|
|Lightning Design System 2.7.4||4 out of 4||4 out of 4||4 out of 4|
|Material UI 4.3.2||4 out of 4||4 out of 4||4 out of 4|
|Reach UI 0.15.0||4 out of 4||3 out of 4||4 out of 4|
|Carbon 10||3 out of 4||4 out of 4||4 out of 4|
|Foundation 6.5.0||3 out of 4||4 out of 4||4 out of 4|
|Clarity 0.13||3 out of 4||4 out of 4||4 out of 4|
|Instructure UI 5.32.0||4 out of 4||2 out of 4||4 out of 4|
|Elastic UI 5.0.1||4 out of 4||4 out of 4||2 out of 4|
|Grommet 2.7.11||4 out of 4||4 out of 4||2 out of 4|
|Atlaskit||4 out of 4||4 out of 4||2 out of 4|
|Polaris 3.0.0||4 out of 4||4 out of 4||2 out of 4|
|Fabric 9.6.1||4 out of 4||4 out of 4||2 out of 4|
|Blueprint 3.8.0||4 out of 4||2 out of 4||4 out of 4|
|Bootstrap 4.1.3||3 out of 4||2 out of 4||4 out of 4|
|Evergreen 4.5.0||3 out of 4||2 out of 4||4 out of 4|
|ng-lightning 2.0.1||4 out of 4||3 out of 4||2 out of 4|
|Chakra 0.5.0||4 out of 4||0 out of 4||4 out of 4|
|PrimeReact 3.3.3||4 out of 4||0 out of 4||4 out of 4|
|Base Web 9.33.0||1 out of 4||4 out of 4||2 out of 4|
|Ring UI 1.0.19||4 out of 4||2 out of 4||0 out of 4|
|Element 2.4.9||1 out of 4||0 out of 4||4 out of 4|
|Materialize 1.0.0||2 out of 4||2 out of 4||0 out of 4|
|Semantic UI 2.4.2||2 out of 4||2 out of 4||0 out of 4|
|Lucid 2.36.1||4 out of 4||0 out of 4||0 out of 4|
|UIkit 3.0.0||1 out of 4||2 out of 4||0 out of 4|
|Ant Design 3.10.7||1 out of 4||0 out of 4||2 out of 4|
|AT UI 1.3.3||0 out of 4||0 out of 4||0 out of 4|
Out of 29 UI libraries, only four can be considered fully accessible (according to my review criteria). What can we do to make accessibility a default rather than an afterthought?
Accessibility is about equal treatment, not special treatment.
It takes a lot of experience and time to make a website fully WCAG-compliant, but you can follow some basic guidelines to solve most of the issues. Tools like Lighthouse (Chrome Dev Tools → Audit or via web.dev) make it even easier.
Just like performance or security, accessibility doesn't just happen. You have to make it happen.