How to remove focus around buttons on click

My buttons all have a highlight around them after I click them. This is in Chrome.

UnselectedSelected

<button class="btn btn-primary btn-block">
    <span class="icon-plus"></span> Add Page
</button>

I am using Bootstrap with a theme, but I'm pretty sure that's not it: I was noticing this before on another project.

It goes away if I use an <a> tag instead of <button>. Why? If I wanted to use <button> how would I make it go away?


Solution 1:

I found this Q and A on another page, and overriding the button focus style worked for me. This problem may be specific to MacOS with Chrome.

.btn:focus {
  outline: none;
  box-shadow: none;
}

Note though that this has implications for accessibility and isn't advised until you have a good consistent focus state for your buttons and inputs. As per the comments below, there are users out there who cannot use mice.

Solution 2:

You want something like:

<button class="btn btn-primary btn-block" onclick="this.blur();">...

The .blur() method correctly removes the focus highlighting and doesn't mess up Bootstraps's styles.

Solution 3:

my understanding is that the focus is first applied following the onMouseDown event, so calling e.preventDefault() in onMouseDown may be a clean solution depending on your needs. This is certainly an accessibility friendly solution, but obviously it adjusts the behaviour of mouse clicks which may not be compatible with your web project.

I am currently using this solution (within a react-bootstrap project) and I do not receive a focus flicker or retained focus of buttons after a click, but I am still able to tab my focus and visually visualize the focus of the same buttons.

Solution 4:

2021 focus-visible Solution

Note 1: In each of the 3 options outlined below, buttons behave the same way (no focus ring on click), but selects and inputs vary slightly in their default behavior. Only Option 3 removes focus rings consistently around buttons, inputs, and selects. Please compare all approaches and make sure you understand the implications.

Note 2: Due to the cascading nature of CSS, the order of the CSS rules is important.

OPTION 1: Use the :focus-visible pseudo-class

The :focus-visible pseudo-class can be used to kill the unsightly outlines and focus rings on buttons and various elements for users that are NOT navigating via keyboard (i.e., via touch or mouse click).

Warning: As of 2021, the :focus-visible pseudo-class is ** widely supported across modern browsers but fails on fringe browsers**. If old-browser support is important, the Javascript polyfill in option 2 below is the closest approximation.

/**
 * Remove focus styles for non-keyboard focus.
 */
:focus:not(:focus-visible) {
  outline: 0;
  box-shadow: none;
}

/**
 * Cross-browser styles for explicit focus via 
 * keyboard-based (eg Tab) navigation or the
 * .focus-visible utility class.
 */
:focus,
.focus-visible:focus:not(:focus-visible) {
  outline: 0;
  box-shadow:
    0 0 0 .2rem #fff,
    0 0 0 .35rem #069;
}
<h3>Defaults:</h3>
<button>Foo</button>
<input type="button" value="Bar"/> 
<select><option>Baz</option></select>
<input type="text" placeholder="Qux"/>
<textarea placeholder="Quux" rows="1"></textarea>

<h3>Force focus on click:</h3>
<button class="focus-visible">Foo</button>
<input class="focus-visible" type="button" value="Bar"/> 
<select class="focus-visible"><option>Baz</option></select>
<input class="focus-visible" type="text" placeholder="Qux"/>
<textarea class="focus-visible" placeholder="Quux" rows="1">
</textarea>

OPTION 2: Use a .focus-visible polyfill

This solution uses a normal CSS class instead of the pseudo-class mentioned above and has wider browser support (in 2021). It requires either 1 or 2 Javascripts be added to your HTML; one for the official focus-visible polyfill and the other for older browsers that don't support classList.

Note: In Chrome, the polyfill seems to treat selects differently than the native :focus-visible pseudo-class.

/**
 * Cross-browser focus ring for explicit focus 
 * via keyboard-based (eg Tab) navigation or the
 * .focus-visible utility class.
 */
:focus {
  outline: 0;
  box-shadow:
    0 0 0 .2rem #fff,
    0 0 0 .35rem #069;
}

/**
 * Remove focus ring for non-explicit scenarios.
 */
:focus:not(.focus-visible) {
  outline: 0;
  box-shadow: none;
}
<h3>Defaults:</h3>
<button>Foo</button>
<input type="button" value="Bar"/> 
<select><option>Baz</option></select>
<input type="text" placeholder="Qux"/>
<textarea placeholder="Quux" rows="1"></textarea>

<h3>Force focus on click:</h3>
<button class="focus-visible">Foo</button>
<input class="focus-visible" type="button" value="Bar"/> 
<select class="focus-visible"><option>Baz</option></select>
<input class="focus-visible" type="text" placeholder="Qux"/>
<textarea class="focus-visible" placeholder="Quux" rows="1">
</textarea>

<!-- place this code just before the closing </html> tag -->
<script src="https://cdn.polyfill.io/v2/polyfill.js? 
features=Element.prototype.classList"></script>
<script src="https://unpkg.com/focus-visible"></script>

OPTION 3: Use a global key-navigation vs mouse-navigation state

An inverse solution to focus-visible, is to disable outlines on mousemove, and enable them on keydown -> "Tab". In this case, rather than specifying which elements shouldn't show an outline, you must specify which elements should.

document.addEventListener("mousemove", () => 
  document.body.classList.remove("focus-visible")
);

document.addEventListener("keydown", ({key}) => 
  (key === "Tab") && document.body.classList.add("focus-visible")
);
/**
 * Cross-browser focus ring for explicit focus 
 * via keyboard-based (eg Tab) navigation or the
 * .focus-visible utility class.
 */
:focus {
  outline: 0;
  box-shadow:
    0 0 0 .2rem #fff,
    0 0 0 .35rem #069;
}

/**
 * Remove focus ring for non-explicit scenarios.
 */
body:not(.focus-visible) :focus:not(.focus-visible) {
  outline: 0 !important;
  box-shadow: none !important;
}
<h3>Defaults:</h3>
<button>Foo</button>
<input type="button" value="Bar"/> 
<select><option>Baz</option></select>
<input type="text" placeholder="Qux"/>
<textarea placeholder="Quux" rows="1"></textarea>

<h3>Force focus on click:</h3>
<button class="focus-visible">Foo</button>
<input class="focus-visible" type="button" value="Bar"/> 
<select class="focus-visible"><option>Baz</option></select>
<input class="focus-visible" type="text" placeholder="Qux"/>
<textarea class="focus-visible" placeholder="Quux" rows="1">
</textarea>

A note about accessibility

Removing all focus rings a la :focus { outline: none; } or :focus { outline: 0; } is a known accessibility issue and is never recommended. Additionally, there are folks in the accessibility community who would rather you never remove a focus ring outline and instead make everything have a :focus style — either outline or box-shadow could be valid if styled appropriately.

Finally, some folks in the accessibility community believe developers should not implement :focus-visible on their websites until all browsers implement and expose a user preference which lets people pick whether all items should be focusable or not. I personally don't subscribe to this thinking, which is why I provided this solution that I feel is far better than the harmful :focus { outline:none }. I think :focus-visible is a happy medium between design concerns and accessibility concerns.

Resource:

  • https://css-tricks.com/keyboard-only-focus-styles/

Demo:

  • https://wicg.github.io/focus-visible/demo/

Solution 5:

Can't believe nobody has posted this yet.

Use a label instead of a button.

<label type="button" class="btn btn-primary btn-block">
<span class="icon-plus"></span> Add Page
</label>

Fiddle