Reputation: 7215
We have some buttons, which are styled with css and have some icons like this:
This button has as default outline property, so that everytime we click on it, it has an outline (in chrome blue):
to get rid of it, we can of course overwrite this property like:
outline: none
but then when we tab through and reach this button, it will also not have any outline, which is a bad practice for accesibility.
Can we achive this so that this outline appears only when we come on this button with tab, but not when we click on it?
Just as info: We have also some a tags which seems visually just like that, and with a tags we have exact this behaviour we want, outlines appear only when we tab on to that link, but not on click. We just want to have exact same behaviour with button tags also.
Upvotes: 16
Views: 9558
Reputation: 175
I found an easy way, that is pure html/css and does not use any javascript.
Wrap the content of the button inside an additional outer element (e.g. div) and make the wrapper focusable/tabable with tabindex="0"
The inner element gets tabindex="-1"
So instead of:
<button>Buttontext</button>
do this:
<div tabindex="0">
<button tabindex="-1">
Buttontext
</button>
</div>
In that way you can tab onto the outer div and the browser draws its focus-outline. When you click into the button, you select the inner element, which has no outline (outline: none
)
Upvotes: 0
Reputation: 15545
Can we achive this so that this outline appears only when we come on this button with tab, but not when we click on it?
Yes you can by using :focus-visible
.
If you want an outline to appear when button is tabbed and not clicked, set outline: none
on the button on :focus
but not on :focus-visible
:
button:focus:not(:focus-visible) {
outline: none;
}
Upvotes: 16
Reputation: 7215
Since none of the answers above really worked for me in all cases (most of them ignores at least the fact that one can again switch to mouse, and the boxshow must then also be removed from the button. It does not have to be removed always from the eventlistener), here is the answer for me:
If your html tags are more or less on the same level (so you don't have to create a listener for the whole page):
The previous element, whose 'tab' should switch focus to the button should have a keydown/keyup event which then runs:
if ($event.keyCode === '9' && !$event.shiftKey) {
$event.target.nextSibling.style.boxShadow = '0 0 5px 1px #305C73';
}
When you also want to support a shift+tab (back tabbing), which you should, you also need this for the next element:
if ($event.keyCode === '9' && $event.shiftKey) {
$event.target.previousSibling.style.boxShadow = '0 0 5px 1px #305C73';
}
The button itself show remove it by its own blur event:
$event.target.style.boxShadow = 'none';
The button itself can still set its outline to none:
outline: none
Since i asked the question independent of the JS-technology in background, the event handling parts are not included here. But in case of an angular app, one can use sth. like this:
<previousTag (keydown)="addBoxShadowBorButtonOnTab($event)">
<button (blur)="removeBowShadow($event)">
<nextTag (keydown)="addBoxShadowBorButtonOnShiftTab($event)">
In plain javascript this part is done like in the answers above with an EventListener.
If your tags are within other tags and you have a hierarchical DOM structure, or you cannot change your 'previous' and 'next' tags:
In my case we had sth. like this:
<li>
<previousTag>
<button>
</li>
<li>
<nextTag>
<li>
It is better to register a listener for keyup/keydown events for adding your boxShadow since you are then more independent of your HTML Structure and future DOM changes. So you can set your outline independent of from which elements you are coming from.
In my Angular app, it seems sth. like this:
@HostListener('document:keyup.shift.tab', ['$event'])
@HostListener('document:keyup.tab', ['$event']) onTabHandler(event: KeyboardEvent) {
const focusedElm = event.target;
if (focusedElm.id === 'my-heart-button') {
focusedElm.style.boxShadow = '0 0 5px 1px #305C73';
}
}
Removing the outline should still work exactly the same way like above.
This is the only way to ensure that the Buttons will always have an outline 'just during focusing on while tabbing' for accessibility, but they will not appear on a mouse click concerning aesthetics.
Upvotes: 1
Reputation: 4420
When you click on an element it casts :active
on it, so you want to chain :active
and :focus
together:
button:active:focus {
outline: none;
}
<button>Button</button>
As you said this doesnt work with additional css, in this case you have to implement a bit complicated solution, where you add class to body when user uses tab for a first time, otherwise you remove outlines all together
function handleFirstTab(e) {
if (e.keyCode === 9) { // the "I am a keyboard user" key
document.body.classList.add('user-is-tabbing');
window.removeEventListener('keydown', handleFirstTab);
}
}
window.addEventListener('keydown', handleFirstTab);
body:not(.user-is-tabbing) button:focus {
outline: none;
}
button {
background-color: red;
}
<button>Button</button>
Upvotes: 12
Reputation: 16251
Use .btn:focus{ outline:0; }
to remove outline
And use addEventListener
to target Tab
click and set box-shadow
if tab on your element
document.addEventListener('keydown', function(e) {
var elem=document.getElementById('heart');
if (e.key === 'Tab' && document.activeElement === elem) {
elem.style.boxShadow="0 0 0 0.2rem rgba(0,123,255,.25)";
}
else{
elem.style.boxShadow="none";
}
});
.btn {
width: 80px;
height: 80px;
transform: rotate(-46deg);
border: none;
background: url(https://image.flaticon.com/icons/svg/579/579268.svg);
}
.btn:focus{
outline:0;
}
<button class="btn" id="heart">
</button>
You can target tab
click by css with this plugin:https://github.com/ten1seven/track-focus
body[data-whatinput="keyboard"] .btn:focus {
box-shadow: 0 0 0 0.2rem rgba(0,123,255,.25);
}
Upvotes: 1
Reputation: 11281
If your icons are some kind of fonts, your best option is using text-shadow
instead:
button {
outline: none!important;
background: transparent;
border-color: transparent;
color: red;
font-size: 30px;
}
button:focus i.fa {
text-shadow: 1px 1px 5px rgba(255, 0, 0, 0.7);
}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/meyer-reset/2.0/reset.min.css">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
<button class="btn">
<i class="fa fa-heart-o"></i>
</button>
In this example, I used font-awesome-4.7 and generated the text-shadow
with text-shadow
generator.
Upvotes: 1