How to Design Visible Focus States and Keyboard Navigation That Work for Everyone

A chalkboard with handwritten notes mapping out the order of steps in a lesson

Put your mouse in a drawer and try to use your own product for ten minutes. Press Tab and see where the cursor goes. Try to open a menu, pick an option, close a dialog, and submit a form. Most teams that do this for the first time find that they lose track of where they are within seconds, because nothing on the screen tells them.

That moment of being lost is what a large group of people experience every day. Keyboard users include people with motor disabilities, people who rely on switch devices or screen readers, power users who hate reaching for a trackpad, and anyone with a broken wrist or a dead mouse battery. Designing for them is not a niche exercise. It is a quality check on whether your interface has a coherent structure at all.

Chalkboard with handwritten numbered steps showing the order of a lesson
Photo by Atlantic Ambience on Pexels

What focus actually is and why designers forget it

Focus is the browser's record of which element will receive the next keystroke. When you press Tab, focus moves to the next interactive element. When you press Enter or Space, the focused element activates. Everything a keyboard user does depends on that single pointer, and the only way they can see it is if you draw it.

Designers tend to forget focus because design tools do not show it. A mockup has a default state, a hover state, and maybe a pressed state. Focus rarely appears in the file, so developers fall back on the browser default, or worse, remove it. The Mozilla Developer Network documents the focus model in detail, and it is worth ten minutes of reading before you design a single component.

The fix starts in the design file. Every interactive component needs a focus state drawn next to its hover and pressed states, reviewed with the same care. If it is not in the spec, it will not be in the build.

Never remove the outline without a replacement

The most common accessibility bug on the web is a single line of CSS: outline: none. Someone dislikes the default ring on buttons, strips it globally, and every keyboard user loses their place everywhere at once. The ring was ugly to a designer's eye and essential to the person using it.

You are free to restyle the indicator, and you should. A good focus ring fits the visual language of the product. What you cannot do is remove it and leave nothing in its place. If you override the outline, the replacement must be at least as visible as the one you took away.

A useful rule is to treat the default as a floor. If your custom style is clearly more noticeable than the browser default in every context where the element appears, ship it. If you are not sure, keep the default and move on to a harder problem.

Design a focus ring that survives every background

A focus indicator has to be seen against whatever sits behind it, and products have many backgrounds. A blue ring that looks sharp on white can vanish on a dark header, a photo, or a colored card. Pick one color and you will find the surface where it disappears.

The WCAG guidelines ask for a visible indicator with enough contrast against adjacent colors, and the newer success criteria on focus appearance describe the size and contrast a good indicator should have. You do not need to memorize the numbers to follow the spirit of them.

The most reliable technique is a two-tone ring: a solid inner line in one color and an outer line in a contrasting color. One of the two will always stand out, whether the background is light, dark, or busy. Add a small gap between the element and the ring using outline-offset so the indicator does not merge with the element's own border.

Use focus-visible to keep the ring where it helps

The classic complaint about focus rings is that they appear when you click a button with a mouse, which looks like a bug to people who never use a keyboard. For years that complaint drove the habit of removing outlines entirely. The :focus-visible pseudo-class solves it properly.

With :focus-visible, the browser shows the ring when its heuristics suggest the user needs it, such as keyboard navigation, and hides it for ordinary mouse clicks on buttons. Text inputs still show a ring in both cases, because typing always needs a visible position. The result is that mouse users see a cleaner interface and keyboard users keep their indicator.

Write your styles on :focus-visible rather than :focus, and keep a fallback in place for the few older browsers that need it. Test with the keyboard and the mouse, not just one of them, since the whole point is that the behavior differs.

Make the tab order match the reading order

Tab order follows the order of elements in the document, not the order they appear on screen. When designers use CSS to rearrange a layout, the visual order and the source order drift apart, and keyboard focus starts jumping around the page in a way that feels random.

The best fix is boring: write the markup in the order a person should read and use, and use CSS to position it. Avoid positive tabindex values entirely. A value like tabindex="5" pulls an element out of the natural sequence and creates a second, hidden ordering that nobody maintains. Use tabindex="0" to make a custom element focusable and tabindex="-1" when you need to move focus to something with a script but keep it out of the tab sequence.

Walk through each page and read the tab sequence out loud. If it does not sound like a sensible way to move through the screen, change the source order, not the tab index.

Give people a way to skip the repetition

A page with a long navigation bar forces keyboard users to tab through every link before they reach the content. On a site with thirty navigation items, that is thirty keystrokes on every page load. Mouse users never feel this cost, which is why it survives for so long.

A skip link fixes it. It is a link placed first in the document that jumps to the main content, usually hidden until it receives focus and then displayed at the top of the page. It is a few lines of markup and a few lines of CSS, and it removes a daily annoyance for a large number of people.

"A keyboard pass is the cheapest usability test there is. If a flow only works when someone can aim a pointer at it, the design depends on a hidden assumption, and we would rather find that in review than in a support ticket." - Dennis Traina, founder of 137Foundry

The same thinking applies to landmarks. Using real elements such as header, nav, main, and footer lets screen reader users jump between regions, and it costs nothing when you are already writing semantic markup.

A dense tangle of colored cables in a patch panel being sorted in order
Photo by cnrdmroglu on Pexels

Handle dialogs, menus, and custom widgets deliberately

Native elements come with keyboard behavior for free. A real button responds to Enter and Space, a select responds to arrow keys, and a link responds to Enter. The trouble starts when a team builds a custom dropdown out of div elements and has to recreate all of that by hand.

For anything custom, follow the established patterns in the WAI-ARIA Authoring Practices Guide. It documents the expected keys for menus, tabs, comboboxes, and dialogs, so your component behaves the way assistive technology users already expect. Inventing your own key bindings means every user has to learn them from scratch.

Modal dialogs deserve special attention. When one opens, focus should move inside it. While it is open, Tab should cycle within the dialog and not leak to the page behind. When it closes, focus should return to the element that opened it. Getting this wrong strands people in a screen they cannot leave,, and it is one of the most common defects we see in custom overlays.

Manage focus when the interface changes

Single-page apps break a basic assumption: the browser used to reload the page on navigation and reset focus to the top. When a route changes without a reload, focus stays on the link that was clicked, which may no longer exist. A keyboard user is left on nothing, with the next Tab starting from an unpredictable place.

After a route change, move focus deliberately, usually to the main heading of the new view, and make sure that heading can receive focus with tabindex="-1". Announce the change to screen readers using a polite live region or by updating the page title. These two small steps make a client-rendered app feel like a normal website.

The same rule covers content that appears or disappears. When an item is deleted from a list, focus should land on a sensible neighbor, not vanish. When an inline error appears, move focus or announce it. Whenever the element that had focus goes away, you owe the user a new position.

Test it with a real keyboard, every time

Automated checkers find missing labels and low contrast, but they cannot tell you whether a flow makes sense on a keyboard. Only a person pressing keys can do that. WebAIM publishes practical guidance on keyboard testing, including what to look for on each pass.

Build a short routine and add it to your definition of done. Unplug the mouse, load the page, and Tab through the whole thing. Can you always see where you are? Can you reach every control? Can you leave every dialog? Can you complete the main task without touching a pointer? Four questions, five minutes, and you will catch problems that automated tools never will.

Bring in real users when you can. A single session with someone who relies on a keyboard or screen reader every day will teach you more than a month of internal review. If you are planning a redesign and want engineering help making these patterns part of the component library from the start, 137Foundry's web development service builds this kind of accessible foundation into new interfaces.

Where to start on an existing product

If your product already ships, do not try to fix everything at once. Start with the flows that matter most: sign in, search, checkout or the equivalent, and the main creation action. Run the keyboard test on each, write down every place focus is invisible or lost, and fix those first.

Then address the shared components. Fixing the focus style on your button, link, and input components improves every screen at once, which is a far better return than patching pages one by one. Document the standard in your design system so new work inherits it by default.

Treat the result as a habit, not a project. Every new component gets a focus state in the design file, a keyboard check in review, and a note in the spec. Do that for a few months and keyboard accessibility stops being a special effort and becomes a normal property of the things you build. If you would like a second set of eyes on where your product stands, you can start from the 137Foundry services overview and tell us what you are working with.

Need help with Web Development?

137Foundry builds custom software, AI integrations, and automation systems for businesses that need real solutions.

Book a Free Consultation View Services