Beautiful focus outlines

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.

Two texts on different backgrounds. The first text is white on a dark background, the second text is black on a light background. Both show a focused inline link in the text color

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.

Three buttons on a half white, half orange background. The first button has a white outline, the second button has an orange outline, and the third button has a black outline with an offset

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:

A comparison of two ā€œRead moreā€ buttons. The first button shows an outline that dips inward around the icon, creating an uneven border shape, while the second button maintains 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:

A comparison of two texts with a focused inline link. The left link has a rectangular outline, while the right link has a rounded outline

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.

Comparison of two rounded buttons. The left button's blue outline touches the button's edge directly, while the right button's outline is positioned a few pixels away from the edge, creating cleaner visual separation and better visibility.

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:

Comparison of two dark menus with full-width buttons. The left menu's orange focus outline extends beyond the viewport edge, while the right menu uses a negative outline offset to keep the focus indicator contained within the viewport width

Interactive example

Hereā€™s a comparison of elements with and without some of the mentioned properties:

See the Pen Pretty outlines by Thomas GĆ¼nther (@medienbaecker) on CodePen.

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.

Thanks to Andy Bell for the feedback about this technique. Using box-shadow like this for a double line effect can lead to issues in Windows High Contrast Mode. Learn more about this in his article Use transparent borders and outlines to assist with high contrast mode.

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:

See the Pen Hidden outline by Thomas GĆ¼nther (@medienbaecker) on CodePen.

The outline for the first link is not visible because the div inside it is a block-level element. The two other links work just fine.

Quick testing checklist

Hereā€™s how I usually test focus outlines:

  1. Put your mouse aside and navigate your site with the keyboard
  2. Check if you can always see your current position and if all interactive elements show an outline
  3. Test outline visibility and contrast against different backgrounds
  4. Look for visual issues like jagged edges or cut-off outlines
  5. Compare outline shapes across different elements and contexts
  6. Ensure the outline design integrates well with your siteā€™s visual language

The best test? Hand your keyboard to someone who regularly uses keyboard navigation and watch how they interact with your site. Youā€™ll learn a lot!

Real-world examples

Smart Transition

A screenshot of the Smart Transition website showing a white outline around a blue button with the text ā€œLoginā€

On the Smart Transition website I made sure to have consistent focus outlines across different elements. The outline color matches the text color and the offset ensures itā€™s always visible.

Verlage gegen Rechts

A screenshot of the Verlage gegen Rechts website showing a yellow outline with an offset around a green button on a green background

On the Verlage gegen Rechts website, we use random colors on every page load. The focus outline always uses the current text color and an offset. This way, the outline always has enough contrast.

GOV.UK

A screenshot of the UK government website showing a high-contrast yellow outline with a black border around the search field

The UK government website uses a high-contrast black outline with a yellow border. This combination works on any background and looks great in combination with their blue.

HanseWasser

A screenshot of the HanseWasser website showing a red dotted outline around a button with the text ā€œLƤuft mit unsā€

The HanseWasser website uses a red dotted outline matching their brand colors. They use this color consistently across different elements.

Aesop

A screenshot of the Aesop website showing a simple black outline around a button with the text ā€œEntdecken Sie den Classic Hair Care Duoā€

Aesop's website uses a simple black or white outline for focus states, depending on the background colour. This minimalistic approach fits their design language.

DW Learn German

A screenshot of the DW Learn German website showing a blue and green outline around a hamburger button

The DW Learn German website uses a blue and green outline for focus states. Interestingly, the focus outlines animate between elements. Not sure if this is a good idea, but itā€™s definitely special.

Final thoughts

Focus outlines are fundamental to an accessible web ā€“ theyā€™re not just a nice-to-have, but a core part of how many people navigate our websites. I hope this article helps you create better focus outlines in your projects by incorporating them into your design process.

If you have your own approach to focus outlines or interesting examples to share, Iā€™d love to hear about them ā€“ you can find me on Mastodon.