The outline: none Debate Has Been Over Since 2020

2026年8月29日3 次浏览来源:Dev.to阅读原文

A design review flags it every time: a button gets clicked, and a chunky blue ring slaps itself around the edge for half a second. "Can we remove that?

It looks broken." The engineer knows the one-line fix.

Ship it.

The review passes.

The button looks clean in every screenshot, in every browser, on every click.

Three weeks later an accessibility audit comes back with a line item: "Keyboard users cannot determine which element currently has focus." Nobody touched a mouse to find that bug.

They tabbed through the page with their eyes closed to the cursor, because that's how they use the page every day — and your clean button is now indistinguishable from every other pixel on the screen.

That's not a nitpick.

It's WCAG 2.4.7, a real success criterion, and on its own fails it every time.

The fix people reach for next is also wrong The instinct isn't to put the ring back — nobody wants the ugly version either.

It's to make it conditional: show it for keyboard users, hide it for mouse users, since a mouse click already gives you visual feedback that something happened.

Reasonable goal.

Before 2020, CSS had no way to ask "was this focus event a click or a keypress?" — so people answered the question in JavaScript instead: This is roughly what the WICG's own polyfill did under the hood, and plenty of design systems shipped a version of it.

It worked.

It also meant every page paid for a global event-listener setup, a class toggling on on every focus change, and a heuristic that could still drift out of sync — tab into a field, click somewhere unrelated, tab again, and the class-based guess doesn't always match what actually happened. 🎮 Try it yourself ▶️ Open the interactive playground → Runs right in your browser — poke at it and watch the concept react live.

The selector that made the JavaScript unnecessary Browsers already knew whether a focus event came from a keyboard or a pointer — they use that exact signal internally to decide their own default outline behavior. just exposes it to your CSS. still matches on every focus event, same as always — a mouse click, a tap, a keyboard tab, a call from your own JS. only matches the subset of those where the browser's own heuristic decides a visible indicator is warranted.

Tab to the button: it matches, the ring shows.

Click the button with a mouse: it doesn't, no ring, and no JavaScript had to watch for either.

It shipped in Chrome and Edge in October 2020 (Chrome 86), and the rest followed within about two years — by 2022 it worked in every evergreen browser without a flag or a polyfill.

The gotcha that trips people up on text fields Try the pattern above on a and a mouse click gives you no ring, exactly as expected.

Try it on a text and clicking it still shows the ring, even with your mouse.

That's not a bug in your CSS — the browser's heuristic treats form fields you're about to type into differently from buttons you just clicked.

A button click is a complete action; you got your feedback from the click itself.

A text field click is the start of typing, and you still need to see that the field is focused so you know where the caret is. accounts for that per element, not just per input device — which is exactly the nuance the old class-toggle approach couldn't express, because it only ever knew about the page as a whole. 🧠 Test yourself Think it clicked?

Take the 6-question quiz → Instant feedback, a hint on every question, and an explanation for each answer — right or wrong.

When outline: none alone is still fine There's one case where deleting the ring outright is the right call, not a shortcut: an element that's and only ever receives programmatic focus your own script manages — a modal you focus on open, then visibly indicate some other way (a border, a heading, an announced live region).

If a human never tabs to it and never clicks it, there's no keyboard user to leave stranded.

Everywhere a real person can land with Tab, is the answer, not a suppressed outline and a hope that nobody notices.

Go find the line in whatever you're shipping — or the class hack if your codebase predates 2021 — and swap it for the two rules above.

Which one does your codebase still have? 🚀 Want more like this?

Every guide, playground, and quiz lives on bestpractic.org — open it and sign up free so the next one finds you.

Thanks for reading!

Let's stay connected: ⭐ GitHub — follow me and star the projects: github.com/parsajiravand 💬 Discord — join the frontend best-practices community: discord.gg/d9KRhuAwQ 📸 Instagram — frontend best practices, daily: @bestpractice___

分享
Baike.dev

baike.dev helps you discover great languages, frameworks, databases, DevOps and cloud-native tools.

Quick links

About

Contribute

Found a great developer tool? Share it with the community.

Submit a tool
© 2026 baike.dev Developer EncyclopediaUpdated daily · Discover great developer tools