Focus outlines highlight the active interactive element on a web page. Theyāre crucial for accessibility, especially for keyboard users and those with motor impairments. If you ever tried navigating a website without a mouse, you can imagine how frustrating it is when you canāt see where you are. IfĀ not, imagine not seeing your cursor. Or just try it yourself by tabbing through your favorite website.
Unfortunately, focus outlines are often overlooked in web design. Clients and designers might not even notice them, leaving developers to handle design and implementation. Some might even suggest removing focus outlines for a cleaner aesthetic š±
But why make them pretty?
Focus outlines are often treated as a purely technical requirement ā something developers handle while designers and clients focus on the ārealā design elements. Yet theyāre essential for many users and can be a key part of your siteās visual identity.
When we treat focus outlines as essential design elements rather than afterthoughts, we create interfaces that are both beautiful and inclusive.
Default or custom?
You can use browser defaults or create custom outlines for interactive elements.
Most accessibility experts recommend custom outlines for better visibility. Looking at the default outlines across different browsers, I have to agree. Theyāre inconsistent and often too subtle.
Want to see how different browsersā default focus outlines look like? Thereās a great comparison of default focus styles for different elements across browsers on the ally.js website. It shows how inconsistent and subtle default focus styles can be.
By designing and testing custom focus outlines, youāre automatically making sure to test your site with a keyboard. This can uncover other accessibility issues you might miss otherwise.
Hereās my starting point for custom focus outlines:
*:focus-visible {
outline-color: currentColor;
outline-style: solid;
outline-offset: .25rem;
outline-width: .25rem;
}
I like to set each property separately so itās more readable. Especially here, when you need to adjust specific properties for invividual elements/backgrounds, it makes things a bit clearer. Depending on your design, you might want to tweak the color, style, offset, and width.
Using :focus-visible
instead of :focus
:focus-visible
is a neat pseudo-class that only applies focus styles when users navigate with the keyboard. This way mouse users wonāt see a lingering focus outline when they click on an element.
/* This means: show outline for keyboard users only */
button:focus-visible {
outline: 2px solid blue;
}
/* This means: show outline for all users */
button:focus {
outline: 2px solid blue;
}
Fun fact: before :focus-visible
was introduced, many developers simply removed focus outlines altogether with outline: none
so they wouldnāt show up when clicking with a mouse. Oops!
Want to learn more about the difference between
:focus
and:focus-visible
?
Check out the MDN Web Docs on :focus vs :focus-visible.
Whatās currentColor
?
Using currentColor
sets the outline color to the current text color. This way, the outline will always match the text color, no matter where itās used.
Itās a good starting point but you will most definitely have to adjust it for different elements or backgrounds. For example you could have a button a text color that is identical to the background color behind it. In this case, you might want to use a different color for the outline.
Making focus outlines accessible
Before diving deep into designing better focus outlines, letās talk about what makes them actually accessible. The Web Content Accessibility Guidelines (WCAG) have some requirements that help us create focus indicators for everyone. Compared to other accessibility requirements, these are pretty easy to meet:
- The focus outline is at least 2px wide
- The focus outline color has a contrast ratio of at least 3:1
This surprisingly is a AAA requirement, AA only mentioning a āvisibleā focus indicator.
I personally think it would be very helpful to have a more concrete requirement in AA, too.
More about that in Ericās article WCAG 2.2 misses mark on defining visible focus.
Improving the outline shape
Now that weāve covered the basics, letās talk about how to make focus outlines look better.
Jagged outlines
When elements contain children of varying heights or positions, outlines can appear uneven or jagged. This happens because outlines naturally follow the outline of all elements in their box model.
In the screenshot below, you can see how the outline dips around the icon in the first button, creating an uneven shape. The second button shows the desired result ā a clean, consistent rectangular outline:
To do this, we can turn the element into a block element or set overflow: hidden
on it. This forces the outline to follow the containerās border box rather than hugging the internal contents.
The overflow: hidden
approach is often the easiest solution, but be careful when using it with elements that need to extend beyond their container (like dropdowns or tooltips). On the other hand, changing the display property can have unintended side effects.
Rounded corners
One great thing about outline
is that it respects border-radius
. If you have a button with rounded corners, the outline will follow the same shape.
Sometimes I even set a border-radius
just for the outline shape, for example for icons (to make it circular) or for inline links:
Some browsers already apply a radius to the outline by default. If you want to have a consistent radius (or none) across all browsers, you have to set it explicitly.
Outline offset
The outline-offset
property is also pretty awesome and I use it a lot. Maybe you noticed me using it in the examples above.
It sounds simple but it can make a big difference, when the outline is no longer touching the element. Especially when your elementās border or background is similar to the outline color.
Setting an offset can also help create more consistent shapes, especially when elements have different sizes. It should however always be preferred to increase the actual size of the element, rather than using an offset to make it appear larger. This way, the element is easier to interact with and the outline is more predictable.
Sometimes you might even want to set a negative offset to move the outline inside the element. For example when you have full-width elements whose outline should not extend beyond the viewport:
Interactive example
Hereās a comparison of elements with and without some of the mentioned properties:
Different backgrounds
You can set the outline color to currentColor
and be done with it. But most of the time it makes sense to have more consistent outlines across elements and only adjust it when necessary. Use CSS custom properties to manage outline colors:
:root {
--outline-color: #ffffff;
}
[data-dark-background] {
--outline-color: #000000;
}
*:focus-visible {
outline-color: var(--outline-color);
}
I often use data attributes to mark elements with dark backgrounds (or the other way around). This way, I can adjust the outline color for elements inside them without changing the CSS every time.
Double line effect
You can combine outline
with box-shadow
to create a double line effect:
*:focus-visible {
outline: 2px solid white;
box-shadow: 0 0 0 4px black;
}
This creates a solid outline with a shadow around it, giving the appearance of a double line.
Yes, itās a workaround. Yes, it sucks if you already use box-shadows for something else. But unfortunately thereās no native support for multi-coloured outlines yet.
Why thereās no native support yet
There are ongoing discussions in the CSS Working Group about introducing native support for multi-coloured outlines. One proposal involves a new stripes()
function:
/* ā ļø Potential future syntax */
*:focus-visible {
outline: 2px stripes(white, black);
}
This proposed feature would make it easier to create high-contrast, accessible outlines without workarounds. However, browser implementation is still uncertain. You can follow the discussion on this topic in the W3C GitHub repository: Two different colours for a ādoubleā style outline.
For now, we have to use the outline
and box-shadow
combination. Because I donāt like this hack, Iām mostly using different outline colors for different elements/backgrounds.
For a deeper dive into the potential future of multi-colored borders and outlines, check out Josh Tumathās article: Stripes: The border function you never knew you needed.
Applying the hover styles to focus
When you have a hover effect on an element, itās a good idea to apply the same styles to the focus state. It makes it even more obvious, which element is currently active. And it just looks nice.
.my-button:hover, .my-button:focus-visible {
background-color: var(--color-highlight);
color: white;
}
This is especially important for things like dropdowns or tooltips, where the hover state controls the visibility of the element.
Animating focus outlines
I am a big fan of purposeful animations. While animations can make focus states more noticeable, they should be used thoughtfully. You can animate properties like the offset for a subtle bounce effect or transition the width. Hereās how you can use CSS animations to create a bouncing outline:
*:focus-visible {
outline-color: currentColor;
outline-style: solid;
outline-offset: .25rem;
outline-width: .25rem;
}
@media (prefers-reduced-motion: no-preference) {
*:focus-visible {
animation: outline-bounce .5s;
}
}
@keyframes outline-bounce {
0% { outline-offset: .25rem }
50% { outline-offset: .5rem }
100% { outline-offset: .25rem }
}
Animations can provide additional visual feedback, and enhance the user experience. However, they can also be distracting, cause motion sickness, and add complexity to your codebase.
If you decide to animate focus outlines, always respect prefers-reduced-motion
. Remember that simple, static outlines are often the most effective solution ā only add animations if they genuinely improve usability.
Some of the real-world examples Iāve collected further down, use animations for focus outlines.
Where is my outline?
There are some common pitfalls when working with focus outlines. Sometimes, you might not see your beautiful outline at all. Donāt panic. Here are some solutions to these issues:
Overflow issues
You might have cut off outlines and not even notice it. This happens easily for things like scroll containers. The outline is cut off because the scroll container doesnāt have enough space to show it.
There are two solutions to this problem:
/* Potential issue */
.scroll-container {
overflow-x: auto;
overflow-y: clip;
}
/* Solution 1: Ensure outline is visible */
.scroll-container:focus-visible {
outline-offset: -.1rem;
}
/* Solution 2: Add padding and negative margin to scroll container */
.scroll-container {
padding: .1rem;
margin: -.1rem;
}
Outlines and inline elements
Sometimes, outlines on your links just wonāt show up even though youāve set it correctly. Maybe because you put a block-level element inside your inline anchor element. This is not allowed in HTML and browsers will not display the outline.
Hereās an interactive demo with a link that has a block-level element inside it: