Skip to main content
Votre navigateur est trop ancien pour afficher correctement ce site. Veuillez passer à la dernière version de Chrome, Edge, Firefox ou Safari.
Retour au blog
KeyboardDesign

Visible focus without the ugly outline: fixing outline: none properly

Removing focus outlines is still one of the most common keyboard accessibility failures. How to design a focus style that looks intentional, survives sticky headers and works in forced-colours mode.

AccessiSight team
Accessibility engineering
1 octobre 2026

Somewhere in most stylesheets there is a line like *:focus { outline: none; }. It was usually added because a designer saw a blue ring after clicking a button and filed it as a bug. For anyone who navigates with a keyboard, that one line removes the only way to see where they are on the page.

What WCAG asks for

Three success criteria are involved, at different levels:

  • 2.4.7 Focus Visible (AA): "Any keyboard operable user interface has a mode of operation where the keyboard focus indicator is visible."
  • 2.4.11 Focus Not Obscured (Minimum) (AA, new in WCAG 2.2): when a component receives keyboard focus, it "is not entirely hidden due to author-created content".
  • 2.4.13 Focus Appearance (AAA, WCAG 2.2): the indicator is at least as large as a 2 CSS pixel thick perimeter of the component and has a contrast ratio of at least 3:1 between its focused and unfocused states.

You don't need AAA to use 2.4.13 as a design target. A 2px ring with 3:1 contrast is simply a focus style people can see.

Use :focus-visible, not :focus

The reason designers hated the ring is that browsers used to show it on mouse clicks too. :focus-visible fixes that: browsers apply it when focus should be shown, which in practice means keyboard use and text fields, not mouse clicks on buttons.

:focus-visible {
  outline: 3px solid #1a56db;
  outline-offset: 2px;
}

The offset keeps the ring clear of the element's own border, so it reads as a focus state rather than a thicker border.

One style for light and dark backgrounds

A single colour will fail contrast somewhere, on a dark hero or a brand-coloured button. A two-tone indicator works on almost anything:

:focus-visible {
  outline: 2px solid #ffffff;
  outline-offset: 0;
  box-shadow: 0 0 0 4px #111827;
}

Keep the outline even if the shadow is doing most of the visual work. In Windows forced-colours (high contrast) mode, box shadows are removed but outlines are kept and recoloured, so an indicator built only from box-shadow disappears for exactly the users who turned that mode on. If you really want no visible outline in normal mode, outline: 2px solid transparent still shows up in forced colours.

Sticky headers and cookie banners

2.4.11 exists because of a very common pattern: the user tabs down the page and the focused link slides underneath a sticky header or a cookie banner. The component has focus; nobody can see it. The fix for headers is usually one line:

html { scroll-padding-top: 5rem; } /* at least the header's height */

When the browser scrolls a focused element into view, it respects that padding. For banners and chat widgets pinned to the bottom, give the page matching bottom padding, or let the user dismiss them.

Testing it

Put the mouse away and press Tab from the top of the page. At every stop you should be able to answer "where am I?" without hunting. Watch for three things: an indicator that vanishes on some components (often custom buttons or cards), one that is too faint against the background, and focus that disappears behind fixed content.

AccessiSight does a real Tab walk through the page in a browser, records what each stop looks like focused and unfocused, and reports removed outlines with no replacement, indicators too faint to see, and focused elements covered by other content. It can't judge whether your indicator matches your brand. That part is yours.

References: Understanding 2.4.7, Understanding 2.4.11 and Understanding 2.4.13.