Design that meets accessibility requirements is a mandatory concern at iAdvize. As we refactor our solution to be WCAG AA compliant, we’ve come across a hot debate: how should we handle the “focus” state in our UI? It sounded like simple enough question at first, yet it has sparked many conversations between designers, developers and product managers alike.
It has helped us grow and we did find a few solutions, but before we discuss those, let’s have a little reminder about WCAG requirements when it comes to keyboard navigation and focus rings.
2.1.1 Keyboard: All functionalities of the content is operable through a keyboard interface without requiring specific timings for individual keystrokes, except where the underlying function requires input that depends on the path of the user’s movement and not just the endpoints. (Level A)
2.4.7 Focus Visible: Any keyboard operable user interface has a mode of operation where the keyboard focus indicator is visible. (Level AA)
All the features in our solution must be operable through a keyboard interface (a “keyboard interface” could also be a head or mouth stick). Fulfilling this first requirement implies displaying a “focus” feedback, without which navigation wouldn’t be visible on screen, thus the second requirement: the focus indicator must be visible. Note that “visible” is relatively subjective. There is no notion of contrast ratio, colors, size, etc in the WCAG spec here. It should just be “visible”…
Our designers have expressed two requests.
First, their normalized design language for a focused element implies that when there is a focus ring, it must be styled with a box-shadow instead of the native outline. In our solution, most of our outlines were then removed with a nice `outline: none`. Outlinenone.com is here to remind us that when we write `outline: none`, we have to implement an alternative to be WCAG compliant. No outline should not mean no visible focus, and our box-shadows do just that.
Their second request was to hide the focus ring or indicator when the user is using a mouse. The design team doesn’t want to see a focus ring after a click on a button, as it seems redundant. We discovered that if you have a native button without custom CSS style, the button won’t show the focus after it’s clicked. But when you add custom CSS on it, even if that style has nothing to do with its focus, it will now show the native focus outline for the mouse input too 🤷♂️.
The first requirement is actually not as easy as it might seem, and the second one is the main purpose of this article.
We’ve charted out multiple solutions trying to satisfy both requirements while remaining accessible. Let’s walk through each of them.
It’s currently a W3C proposal for styling keyboard-only focus using CSS.
Until major browsers support it, you can use this robust polyfill. It is nice because it doesn’t require adding extra elements or altering the tabindex.
However, in some parts of our solution we have drastic constraints about our bundle size, so using a polyfill is not the best solution for us.
2. Event listener
CSS rules can then use that class to show or hide the appropriate focus styling on the relevant elements.
We have implemented this solution in a large part of our product. It is pretty easy to set-up, but it makes more sense when implemented at t the beginning of a project: the earlier this parent class is taken into account, the easier it is to build your consistent focus styles around it.
If you use only outline styling to implement the focus indicator strategy for your whole codebase, this solution is perfect… but if you use box-shadow or border, it may be more difficult to maintain. For example if you have an element featuring a box-shadow with and without focus, you will be forced to implement specific code for this element. Your focus styling strategy may conflict with your regular styles. It’s the crucial limit of this solution.
3. Button content
We can wrap the content of the original interactive element inside an additional inner element with tabindex=”-1" and remove focus styling for this inner element.
This solution meets our designers’ second requirement: keyboard users will see a focus outline (native or otherwise) where they should, but mouse users, hitting that inner element, never will.
If you are refactoring component by component, this solution may be useful. We used it to refactor our notifications system. Notifications are displayed in multiple apps and pages. Because our style is scoped to our component, this method ensures it will have the same behavior everywhere it’s called and we don’t need to inject event listeners or impose a parent class anywhere.
4. Blur after click
We can handle each click on a button and force a blur event on that element right after a click. In this example it’s after a form submission:
We’ve never used this technique. It’s the last resort solution, too ugly to be used by default…but it would certainly remove the focus indicator after an interaction has been performed, still fulfilling that second design requirement.
5. Pragmatic way
Achieving keyboard-only focus styles is surprisingly difficult, and until :focus-visible is universally supported without polyfill, it seems bound to stay difficult, or at least time-consuming.
There’s always the other option: spend no time on it and let the browser do its native job.
In some environments this solution might be good enough. Do not mess with the native outline behaviour with your CSS, do not develop JS-based catches and you’ll meet WCAG criterias. What you won’t meet though is product and design requests. It’s important that the whole team estimates how valuable that compromise is before acting on it.
Our problem is twofold: first we want to be WCAG-compliant with our focus indicators, but we also do not want them to appear outside of WCAG-centric use cases (in this case, keyboard navigation).
The hardest part was to reconcile fairly opposite needs. We needed a quick, fool-proof focus indicator method for accessibility, yet we also wanted a well-designed and thorough solution.
With that in mind, the solutions we’ve explored all feel like we’re making a compromise, because we are! We’ve however found with them a fairly robust toolset with which we can meet the necessary requirements, should they come from WCAG criterias or from our own product design team. Depending on their toll on a feature’s complexity, time management or overall maintainability, we know we can pick one of these tools and achieve our goals.
We didn’t change the world of focus management, we just want give a feedback on some techniques we discover and tried. Most of solutions are explained in this topic:
For more details about the WCAG spec: