Shrinking Button Outlines

I’ve always been a bit concerned about :focus styles for buttons. Some browsers barely do anything by default, making things less than obvious, whereas Firefox’s insistence on including an inner outline (-moz-focus-inner) is ugly. That is, ugly if you spot it at all.

I’m on board with Roger Johansson‘s approach of pairing :focus styles with :hover styles. In my own CSS, this usually means a change in background or font colour. However, I’m in the market for a technique which is better still at drawing the eye towards the focused “target” that is the button.

A little while back, Nikita Vasilyev came up with a really interesting solution which animated focus rings between elements as focus moved from one to another. Great stuff.

The only problem was, this is a javascript solution to a problem that really isn’t in the realm of javascript. Not in my opinion, anyway. If every accessibility improvement required 5k of javascript, well… the performance issue would make the product inaccessible. It was this inspiring post, nonetheless, which I recalled when I starting playing with the outline-offset property.

Outlines don’t stand out well when wrapped around rectangular buttons. At least, not the weedy 1px dotted outlines we use as standard on links. By using outline-offset, it’s easy to move the outline away from the edge, making it clearer.

button:focus {
   outline: 1px dotted;
   outline-offset: 3px;
}

Then, I thought “what about animating the outline offset?” It turns out the outline-offset property can be animated using a simple CSS transition and the animation can be triggered on :focus. Try shifting focus (using the TAB key) to the button below to see the effect.

button {
  outline: 2px solid transparent;
  outline-offset: 50px;
  transition: 1s ease all;
}

button:focus {
  outline-offset: 0;
}

I’m hoping this would make identifying focused button controls (as they’re focused) easier. Because the outline is not part of the click target of the button, you can also transition a thick, transparent outline to a tight, thinner one without blocking surrounding elements.

Now we just need to remove the transition for :active so that clicks don’t trigger the shrinking effect (below).

button:active {
   transition: none;
}

Some browsers (not many) don’t support outline-offset. For these browsers, the outline just changes from transparent to solid and we’re back to square one. Other browsers don’t support transitions. Fine, so it doesn’t move, but it still appears like it always did. What do you think?