Accessibility on-demand with Chakra-ui and focus-visible

Keegan Famouss
5 min readNov 16, 2019

tl;dr The Chakra-ui component library overrides the default accessibility outline with it’s own box-shadow property. Use the focus-visible package on npm to override this css property instead of the normal outline: none; .

chakra-ui home page sales pitch

Chakra-ui is an awesome, new component library for React. It boasts accessibility and speed, alongside theme-ablility and reuse. What more can you ask from a component library in near 2020?

I just started a rebuild of an older AngularJS (v1) project in React and thought it’d be a great time to explore this new component library. Instant win! The components like Flex, Box, Text, Link, Button, Card, Drawer, Collapse, Input, Menu, Modal, Toast, etc. are exactly what you need to get a site up and running quickly to give your client and PM some immediate, visual feedback.

Just load Chakra’s default theme to <ThemeProvider /> and load their supplied <CSSReset /> component, and boom. You’ve got theme’d components all in place with the necessary css reset stuff for browser consistency.

But alas, in all this speedy-glory of getting my header in place using a few Flex and Link components, I go to navigate to my fancy ‘About us’ page and I see this:

Yes, that pesky blue accessibility ring that plagues the on-click of an <a> tag. Of course, I’ve got no problem with accessibility for accessibility-sake, but when you click something you obviously know where it is and we don’t need this blue ring haunting every user click throughout our UI.

At first I did a little bit of a whoopsy-daisy and went on a manhunt to disable outline (come on, only for this use-case not in general 😅).

a:focus {
outline: none;

But it didn’t work. Wheels spinning, I could see my css outline property being set and on closer inspection its actually being set by Chakra. So what gives?

The Chakra default theme applies its own box-shadow property on focus, in efforts to replace the default outline property and give you control over the look-and-feel.

Great! Thats both stylistically useful and easy to take care-of. We could just override the default theme property to shadows.outline: none but that means we’ve disabled it for every case. Clicking, Tabbing, etc. That’s not cool.

So how do we get rid of the on-click action? We just want this box-shadow to appear on keyboard focus, not click focus. Enter: yarn add focus-visible .

The focus-visible package gives us a handy poly-fill for the focus-visible css selector that is currently only implemented in Firefox. See caniuse:

This is a great feature and I hope these reds all turn to greens ASAP. But until then, we’ll use the focus-visible package to give us this functionality with just 3 steps. Lets do it!


  1. yarn add focus-visible to your project.
  2. import 'focus-visible/dist/focus-visible' somewhere high up. I put it in index.jsx:
//index.jsximport React from 'react';
import ReactDOM from 'react-dom';

import 'focus-visible/dist/focus-visible';
import App from './App';ReactDOM.render(<App />, document.getElementById('root'));

3. Use the @emotion/core global component to add the global css style needed for focus-visible, from their docs.

*You should already have @emotion/core installed from when you added @chakra-ui/core (per their installation instructions).

import { Global, css } from '@emotion/core'const GlobalStyles = css`
This will hide the focus indicator if the element receives focus via the mouse,
but it will still show up on keyboard focus.
.js-focus-visible :focus:not([data-focus-visible-added]) {
outline: none;
box-shadow: none;
//apply the GlobalStyles to your app:
const App: React.FC = () => {
return (
<ThemeProvider theme={theme}>
<Global styles={GlobalStyles} />
<YourApp />
export default App;

Explained: The Global component from emotion allows you to create global css styles (go-figure). You can simply use the css`` literal to write the needed styles given to us by the focus-visible docs. Here, we use the version to select [data-focus-visible-added] because that is what emotion adds when you tab onto an element. (you can test this by using the dev-tools, selecting an element and then tabbing onto it).

Wallah! Presto! Bingo-Bango! All is now good in the world of accessibility for accessibility-sake, and in the world of uninterrupted UX. Let’s confirm our 2 use cases are working as expected:

  1. Clicking one of our nav links (trust me I clicked it) does not show the blue accessibility ring, ie: Chakra’s box-shadow. We can verify this by also using the dev-tools to select an <a> and in the styles panel and force the :focus state. We see focus-visible is correctly setting our box-shadow to none.

2. Tabbing through our UI correctly gives us the box-shadow. And we can verify again with dev-tools that the focused nav item ‘Admin’ does still have the Chakra defined box-shadow and focus-visible is not overriding it.

Chakra-ui and focus-visible working together give us some amazing, accessibility out of the box, when we need it! We want all users to have a great UX and that means maintaining the accessibility rules and working around them as great engineers, which I know you are! 🍻