Skip to main content

Why focus indicators are crucial

Any interactive element must have a focus indicator to let keyboard-only users know where they are on the page. Focus indicators are essential for:
  • Keyboard navigation - Users who navigate with Tab/Shift+Tab need to see where they are
  • Motor disabilities - Users who cannot use a mouse rely entirely on keyboard navigation
  • Screen reader users - Visual focus indicators help sighted screen reader users track their position
  • Power users - Many users prefer keyboard navigation for efficiency
  • Temporary limitations - Broken mouse, touchpad issues, or situational constraints
Removing focus indicators without providing visible alternatives makes your website unusable for keyboard-only users.

The classic outline

By default, browsers provide a focus outline on interactive elements. While this outline might not match your design, it serves a critical accessibility function.
/* Browser default focus indicator */
button:focus {
  outline: 2px solid blue; /* Browser-specific styling */
}
Never use outline: none without providing an alternative focus indicator. This is one of the most common accessibility mistakes.

Common anti-pattern

/* BAD: Removes focus indicator with no alternative */
button:focus {
  outline: none;
}

Using :focus-visible

The :focus-visible pseudo-class is a modern solution that shows focus indicators only when needed - typically for keyboard navigation, not for mouse clicks.
/* Shows focus indicator only for keyboard navigation */
button:focus-visible {
  outline: 2px solid var(--brand-color);
  outline-offset: 2px;
}
:focus-visible is supported in all modern browsers. For older browser support, include both :focus and :focus-visible rules.

Different ways to show focus

This doesn’t mean that we MUST use the outline to show focus. There are all kinds of ways to show a focus indicator.
The Web A11y for Devs course demonstrates several creative approaches to focus indicators:

1. No indicator (bad example)

[focus-none]:focus-visible {
  outline: none;
}
This example demonstrates what NOT to do. Always provide a visible focus indicator.

2. Classic outline

The traditional approach using the outline property:
[focus-outline]:focus-visible {
  outline: 2px solid var(--brand-secondary);
}

3. Background color change

Change the background and text color on focus:
[focus-background] {
  transition:
    background 0.3s ease,
    color 0.3s ease;
}

[focus-background]:focus-visible {
  border-color: var(--brand-secondary);
  background: var(--brand-secondary);
  color: white;
}
When using transitions, remember to disable them for users who prefer reduced motion.

4. Box shadow

Use box-shadow for a softer, modern focus indicator:
[focus-shadow] {
  box-shadow: 0 0 1px 3px transparent;
  transition: box-shadow 0.3s ease;
}

[focus-shadow]:focus-visible {
  outline: none;
  box-shadow: 0 0 3px 3px var(--brand-secondary);
}

5. Text styling

Change text appearance for focus indication:
[focus-text]:focus-visible {
  color: var(--brand-secondary);
  text-decoration: underline;
}

6. Transform effect

Use subtle transforms to indicate focus:
[focus-transform] {
  transition: scale 0.1s ease;
}

[focus-transform]:focus-visible {
  scale: 1.05;
}

Interactive demo from the course

The introduction page includes a live demonstration of these different focus indicator styles:
<div id="focus-indicators" class="my-5 flex flex-wrap items-center gap-4">
  <button focus-none>No indicator</button>
  <button focus-outline>Classic outline</button>
  <button focus-background>Background</button>
  <button focus-shadow>Box shadow</button>
  <button focus-text>Text trickery</button>
  <button focus-transform>Transformation</button>
</div>
Try navigating these buttons with the Tab key to see how different focus indicators behave.

Best practices for focus indicators

Focus indicators must have a contrast ratio of at least 3:1 against adjacent colors (WCAG 2.2).
Focus indicators should be easily noticeable. Subtle differences in color or style may not be enough.
Use a consistent focus indicator style throughout your application so users know what to expect.
Combine color changes with other visual cues like outlines, borders, or transforms.
Use outline-offset to create space between the element and the focus indicator for better clarity.
button:focus-visible {
  outline: 2px solid blue;
  outline-offset: 2px;
}
Navigate your entire site using only the Tab key. Can you always see where you are?

Working with design teams

Always make sure your interactive components have visible focus indicators.
Try to have honest and transparent conversations with your design team to make sure focus indicators are properly covered for every feature moving forward.

Tips for design collaboration

1

Include focus states in designs

Ask designers to include focus states in their mockups alongside hover and active states.
2

Explain the requirement

Help designers understand that focus indicators are not optional - they’re an accessibility requirement.
3

Offer creative solutions

Share examples of beautiful, on-brand focus indicators that match your design system.
4

Test together

Demonstrate keyboard navigation to your design team. Seeing the experience firsthand helps build understanding.
5

Document standards

Create focus indicator guidelines in your design system documentation.

Focus management for custom components

When building custom interactive components, you need to manage focus properly:

Custom buttons

If you must use a <div> or <span> as a button (though you should prefer native <button> elements):
<div 
  role="button" 
  tabindex="0"
  class="custom-button"
>
  Click me
</div>
.custom-button:focus-visible {
  outline: 2px solid blue;
  outline-offset: 2px;
}
Custom buttons require additional work for keyboard support (Enter and Space key handlers) and accessibility. Always prefer native <button> elements when possible.
When opening a modal, trap focus within it:
function openModal() {
  const modal = document.getElementById('modal');
  const firstFocusable = modal.querySelector('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');
  
  modal.style.display = 'block';
  firstFocusable.focus();
}
Provide skip links to help keyboard users bypass repetitive content:
<a href="#main-content" class="skip-link">Skip to content</a>
.skip-link {
  position: absolute;
  top: -40px;
  left: 0;
  background: #000;
  color: white;
  padding: 8px;
  text-decoration: none;
  z-index: 100;
}

.skip-link:focus {
  top: 0;
}

Common focus indicator mistakes

Avoid these common mistakes that break keyboard accessibility:
  • Removing outlines globally - Never use * { outline: none; } in your CSS
  • Only styling :hover - Focus states need styling too, not just hover states
  • Insufficient contrast - Focus indicators that are barely visible don’t help users
  • Missing focus on custom components - All interactive elements need focus indicators
  • Breaking focus order - Using CSS positioning or JavaScript incorrectly can break the natural tab order
  • Forgetting focus within components - Accordions, tabs, and other composite widgets need internal focus management

Testing your focus indicators

Testing checklist

  1. Tab through your entire page - Can you see where focus is at all times?
  2. Check contrast - Do focus indicators have sufficient contrast (3:1 minimum)?
  3. Test on different backgrounds - Focus indicators should work on all background colors
  4. Test with zoom - Focus indicators should remain visible at 200% zoom
  5. Try different browsers - Focus styles can vary between browsers
  6. Use browser DevTools - Chrome and Firefox have accessibility inspectors that highlight focus
  7. Get feedback from keyboard users - Real user testing is invaluable

CSS example from the course

Here’s the complete CSS from the Web A11y for Devs course showing various focus indicator approaches:
#focus-indicators {
  button:focus-visible {
    outline: none;
  }

  [focus-none] {
    &:focus-visible {
      outline: none;
    }
  }

  [focus-outline] {
    &:focus-visible {
      outline: 2px solid var(--brand-secondary);
    }
  }

  [focus-background] {
    transition:
      background 0.3s ease,
      color 0.3s ease;

    &:focus-visible {
      border-color: var(--brand-secondary);
      background: var(--brand-secondary);
      color: white;
    }
  }

  [focus-shadow] {
    box-shadow: 0 0 1px 3px transparent;
    transition: box-shadow 0.3s ease;

    &:focus-visible {
      outline: none;
      box-shadow: 0 0 3px 3px var(--brand-secondary);
    }
  }

  [focus-text] {
    &:focus-visible {
      color: var(--brand-secondary);
      text-decoration: underline;
    }
  }

  [focus-transform] {
    transition: scale 0.1s ease;

    &:focus-visible {
      scale: 1.05;
    }
  }
}

Additional resources

Build docs developers (and LLMs) love