Animated Position of Focus Ring

Avatar of Chris Coyier
Chris Coyier on (Updated on )

DigitalOcean provides cloud products for every stage of your journey. Get started with $200 in free credit!

Maurice Mahan created FocusOverlay, a “library for creating overlays on focused elements.” That description is a little confusing at you don’t need a library to create focus styles. What the library actually does is animate the focus rings as focus moves from one element to another. It’s based on the same idea as Flying Focus.

I’m not strong enough in my accessibility knowledge to give a definitive answer if this is a great idea or not, but my mind goes like this:

  • It’s a neat effect.
  • I can imagine it being an accessibility win since, while the page will scroll to make sure the next focused element is visible, it doesn’t otherwise help you see where that focus has gone. Movement that directs attention toward the next focused element may help make it more clear.
  • I can imagine it being harmful to accessibility in that it is motion that isn’t usually there and could be surprising and possibly offputting.
  • If it “just works” on all my focusable elements, that’s cool, but I see there are data attributes for controlling behavior. If I find myself needing to sprinkle behavior control all over my templates to accommodate this specific library, I’d probably be less into it.

On that last point, you could conditionally load it depending on a user’s motion preference.

The library is on npm, but is also available as direct linkage thanks to UNPKG. Let’s look at using the URLs to the resources directly to illustrate the concept of conditional loading:

<link 
  rel="stylesheet" 
  href="//unpkg.com/focus-overlay@latest/dist/focusoverlay.css" 
  media="prefers-reduced-motion: no-preference"
/>

<script>
const mq = window.matchMedia("(prefers-reduced-motion: no-preference)");

if (mq.matches) {
  let script = document.createElement("script");
  script.src = "//unpkg.com/focus-overlay@latest/dist/focusoverlay.js";
  document.head.appendChild(script);
}
</script>

The JavaScript is also 11.5 KB / 4.2 KB compressed and the CSS is 453 B / 290 B compressed, so you’ve always got to factor that into as performance and accessibility are related concepts.

Performance isn’t just script size either. Looking through the code, it looks like the focus ring is created by appending a <div> to the <body> that has a super high z-index value in which to be seen and pointer-events: none as to not interfere. Then it is absolutely positioned with top and left values and sized with width and height. It looks like new positional information is calculated and then applied to this div, and CSS handles the movement. Last I understood, those aren’t particularly performant CSS properties to animate, so I would think a future feature here would be to use animation FLIP to take advantage of only animating transforms.