Every front-end developer has dealt or will deal with this scenario: your boss, client or designer thinks the outline applied by browsers on focused elements does not match the UI, and asks you to remove it. Or you might even be looking to remove it yourself.
So you do a little research and find out that this is strongly discouraged, because the focus outline is there for a reason: it provides visual feedback for keyboard navigation (using the Tab key), letting users who can’t use a mouse or have a visual impairment know where they are on the screen.
That doesn’t mean you’re stuck with this outline, though. Instead of removing it, you can simply replace it with something else. That way, you’ll keep your interface accessible and get more flexibility on how it looks, so you can better match your UI.
You can start by removing the default browser outline by selecting the focused state of the element and applying outline: none
. Then, you may choose from each of the options ahead to replace it:
Change the background color
This works best for elements that can be filled, such as buttons. Select the focused state of the element and apply a contrasting background color to it. The higher the contrast the better because subtle changes may not be strong enough visual cues, particularly in cases where with color blindness and low-vision.
In the example below, both background and border color change; you may pick either or both.
Click or focus with the Tab key to view how this state looks.
See the Pen
Elements replacing native outline focus with background color by Lari (@larimaza)
on CodePen.
Change the text color
If the element has any text, you can select the focused state and change its color. This also works for icons applied with mask-image
; you can select the icon as a descendant of the focused element and change its background color, like the example button below.
See the Pen
Elements replacing native outline focus with text and icon color by Lari (@larimaza)
on CodePen.
Again, contrast is key. You may also consider using an underline on text links and making it part of the changed state because, as the Web Content Accessibility Guidelines state:
Color is not used as the only visual means of conveying information, indicating an action, prompting a response, or distinguishing a visual element. (Level A)
Understanding Success Criterion 1.4.1
Apply a box shadow
The box-shadow
property can do exactly the same job as the outline, except it’s much more powerful — you can now control its color, opacity, offset, blur radius and spread radius. And if a border-radius
is specified, the box shadow follows the same rounded corners.
See the Pen
Elements replacing native outline focus with box shadow by Lari (@larimaza)
on CodePen.
You can get really creative with this technique (seriously though, don’t do this):
See the Pen
Elements replacing native outline focus with insane box shadow by Lari (@larimaza)
on CodePen.
This works for virtually any type of focusable element, like toggles, checkboxes, radio buttons and slides.
See the Pen
Toggle and radio button replacing native outline focus with box shadow by Lari (@larimaza)
on CodePen.
Increase the element’s size
As an alternative to color change, you may also resort to subtle size modification as focus feedback. In this example, we’re using transform: scale
.
See the Pen
Elements replacing native outline focus with transform scale by Lari (@larimaza)
on CodePen.
The key here is subtlety. Extreme size changes may cause content reflow, not to mention a poor experience for those who prefer reduced motion.
Replicate existing hover styles
If the element already has a contrasting hover style, you can simply take that style and apply it for the focused state as well. This is a rather elegant solution, as you don’t have to add any new colors or outlines to the interface.
Here’s an example where both the focus and hover states adopt a high contrast to the background of an element’s default style:
See the Pen
Elements replacing native outline focus with hover styles by Lari (@larimaza)
on CodePen.
Bonus: Customize the default outline
Everything we’ve looked at so far takes the assumption that we want to remove the focus outline altogether. We don’t have to! In fact, it’s a border that we can customize.
button:focus {
outline: 3px dashed orange;
}
That’s shorthand and could have been written this way if we want to fine-tune the styles:
button:focus {
outline-width: 3px;
outline-style: dashed;
outline-color: orange;
}
One additional superpower we have is the outline-offset
property, which is separate from the outline
shorthand property but can be used alongside it to change the position of the focus ring:
button:focus {
outline: 3px dashed orange;
outline-offset: 10px;
}
Conclusion
You can mix and match all of these options to get custom styles that look appropriate for each component type within your interface.
And it’s worth repeating: Don’t forget to use stark color contrasts and other visual cues in addition to color when adopting custom focus states. Sure, we all want an experience that aligns with our designs, but we can adhere to good accessibility practices in the process. The W3C recommends this tool to test the contrast of colors values against the WCAG guidelines.
The main reason why people tend to remove the focus styles isn’t because of keyboard focus, but mouse focus — that is, the way a clicked element retains its focus state, leaving buttons with an outline or other effect after they have been used. For those times, I recommend simply adding
e.target.blur()
to the event handler function, which will un-focus the button after it is clicked.Or, for an even better solution, I like to do:
What that does is check if the X coordinate of the event is 0 before un-focusing. Unless the mouse was at the very far left of the screen when the user clicked (which it really shouldn’t ever be, because of padding and whatnot) the coordinate will be greater than 0. Keyboard events, on the other hand, will always return 0. So this will un-focus for mouse events, but not for keyboard events, allowing users who are navigating using a keyboard or other device to retain their navigation position.
What happens when you click on an input when you use the above code?
In a few chrome releases we will hopefully be able to do that in pure CSS:
:focus:not(:focus-visible){ outline: none;}
(Firefox fallback that works right now):
:focus:not(:-moz-focusring){ outline: none;}
Be very careful how you use
e.target.blur()
. As you acknowledge, a keyboard-only user may have to start over from the top of the page (most browsers will pick up from where they left off). For controls whose state change is important (such as a toggle), then the state change may not be announced to screen reader users either.The second solution is better, but be sure to test with screen readers as well as keyboard (both mobile and desktop).
Using
e.target.blur
in this way will have serious consequences for MIXED use of keyboard and pointer. To the extent that I’d say this is rather hostile to users.Normally you can click somewhere with a mouse, and keyboard tabbing will follow from that point onwards. So you can do things such as:
Click to open a disclosure group (e.g.
details/summary
), then press tab to go to the first control contained within.Click the first control in a form, then use the tab key to proceed through the rest of the inputs.
Click any radio button in a radio group, then use arrow keys to select the correct one.
Calling
blur()
typically resets the keyboard tabbing position back to the start of the page. This confounds people making mixed use of keyboard and pointer:Keyboard users who find a pointer difficult or uncomfortable to use, may occasionally resort to using one anyway, despite it not being their preferred way to interact. Maybe the focus indicator is hard to see, or the tabbing order is confusing. Sometimes it’s just to avoid a large number of TAB key presses. In particular, keyboard-only users might employ the “mouse keys” operating system feature to issue a click, then continue with the tab key.
Some people who mainly use the pointer, but switch to keyboard tabbing when dealing with form controls. Anyone who has ever worked as a data entry clerk is likely to have picked up this habit.
Using
e.target.blur()
in this way is worse than usingoutline: none
in my view. The CSS rule only kills the visible focus indicator, but doesn’t interfere with the actual focus state, so the keyboard tabbing position isn’t disrupted.In Understanding WCAG Success Criterion 2.5.6: Concurrent Input Mechanisms it says “Users should be able to switch input mechanisms at any point should the user determine that certain tasks and interactions are more easily accomplished by using an alternative input mechanism” (emphasis mine). I think that resetting the keyboard tabbing position to the start of the page goes against the spirit of this (although in a strict reading, I don’t think it fails the normative text of the success criterion.)
As already mentioned, using
blur()
here can still be problematic. Indeed, using:focus-visible
(and polyfilling it for now with https://github.com/WICG/focus-visible) is the way to go.Now that’s a cool trick, thanks! :)
The point above from Alex is true, but I’m not sure forcing the element to .blur() is a good choice. Firefox, at least, seems inconsistent as to which element will get focus when mixing a after a click. And it still “flashes” the focus ring on click. I’d also be a little worried about forced-blurs causing some event handler to fire more often than expected, or screen readers to be unhappy without more testing–though that’s just speculation. You play with an example on https://codepen.io/gillibrand/pen/povoZKE
You can probably better handle this with document level onclick/onkeydown handlers that are smart about adding and removing a “show-keyboard-focus” class to the body so the :focus selectors only match when that class is active.
Hey, I only came here to find :focus{outline: none} because I didn’t know how you’ve done it. It helped, thanks