Add a small polyfill to allow detecting if an element is focused by the keyboard vs the mouse. In effect only showing the focus state to users who need it. This way we can leave the default browser accessibility styles intact without worrying about how it affects the styles most users see.
This example is a self contained piece of code that will add a mouse-focus attribute to elements that can be targeted with css.
let mouseTarget = null;
const handleBlur = (event) => {
event.target.removeEventListener('blur', handleBlur);
event.target.removeAttribute('mouse-focus', true);
}
const handleFocus = (event) => {
event.target.removeEventListener('focus', handleFocus);
event.target.addEventListener('blur', handleBlur);
if (mouseTarget === event.target) {
event.target.setAttribute('mouse-focus', true);
}
};
document.body.addEventListener('mousedown', (event) => {
event.target.addEventListener('focus', handleFocus);
mouseTarget = event.target;
});
You can then conditionally disable focus outlines for users that don't need them, preserving the functional default accessibility styles when needed.
[mouse-focus] {
outline: none;
}
See demo: https://codepen.io/nealgranger/pen/NvRdVB.
TODO:
/cc @camelburrito
Adding a few ppl that can weigh in
@spacedino - for UX
@dvoytenko , @aghassemi, @jridgewell - Eng
This is interesting , i am afraid also very risky + performance degrading. We can wait for others to weigh in. We might be able to come up with something better.
IMO, if we standardize the default focus outlines across platforms, the UX would be significantly better.
The performance impact should be very minimal. There only two event handlers created at any moment. My example approach is much faster than walking the dom tree to bind handlers to each element in advance.
I think the task of trying to standardize focus styles across all clients is a rabbit hole you don't want to fall into. That also makes assumptions on behalf of the consumer about what focus styles should look like, and would likely be something they try to override anyway.
I really prefer the approach of giving the consumer the necessary tools to create finer-grained focus styles, but not enforcing anything.
@10xjs This is great to add. This has come up a lot in a11y related discussions since many devs just remove outline for this reason and break a11y.
https://developers.google.com/web/fundamentals/accessibility/accessible-styles#input_modality
There is a spec (:focus-ring) for this already in draft and there is a polyfill we can use ( https://github.com/wicg/focus-ring/ ).
The polyfill is fairly active on github and a11y experts ( @alice, @robdodson , etc.. ) are very much involved in improving it which by itself is more than enough reasons to use the polyfill instead of writing ours.
Here is modified version of their demo (I added link) that uses the polyfill https://codepen.io/aghassemi/pen/brwWRV
The main difference between your prototype and the polyfill is the naming ([mouse-focus] vs .focus-ring) and handling of some types of inputs. Polyfill still does the focus on mouse for text inputs. I suspect the heuristics change as discussions and data points pours in.
The approach they use is interesting, they attempt to capture focus events from users that need accessibility styles - instead of what I am doing buy capturing users that _don't_ need accessibility styles.
Detecting events that don't need focus styles (mouse / touch) is a lot easier than trying to cover all of the possible cases that do need focus. I suspect that calling.focus() on an element with the focus-ring polyfill won't be detected.
With the pattern recommended for .focus-ring, default styles are disabled _unless_ keyboard focus is detected. This has the chance of failing to a non-accessible state, unlike my example, which fails to an accessible state.
@10xjs good points. Given the focus-ring spec is trying to solve this problem generally for the web platform, I think providing this feedback to the standards community would be very helpful. Do you mind starting a discussion issues on https://github.com/wicg/focus-ring/ with your suggestions?
I suspect that calling.focus() on an element with the focus-ring polyfill won't be detected.
This works, so long as a key was recently pressed.
https://github.com/WICG/focus-ring/blob/master/src/focus-ring.js#L78-L83
The implementation you've suggested is definitely cool, kind of an inverted version of :focus-ring. I don't think we plan to change our implementation because we're trying to closely mirror the behavior of the :focus-ring pseudo class itself. For what it's worth, we do have a few large partners either using the polyfill directly or running their own forks and we haven't had any major issues reported yet.
This is awesome鈥攁 discussion we've been having for a while to try to get both good visD and usability.
Would be great to have a clear description of all the users that rely on focus states, to make sure we're not forgetting key users, and making sure that whatever signals we're using to infer that they do or don't need focus states are accurate.
@aghassemi I've just discovered https://github.com/ten1seven/what-input. This is a an established implementation of my idea that follows strict accessibility guidelines.
This issue has been automatically marked as stale because it has not had recent activity. It will be closed in 7 days if no further activity occurs. Thank you for your contributions.
Most helpful comment
@10xjs This is great to add. This has come up a lot in a11y related discussions since many devs just remove outline for this reason and break a11y.
https://developers.google.com/web/fundamentals/accessibility/accessible-styles#input_modality
There is a spec (:focus-ring) for this already in draft and there is a polyfill we can use ( https://github.com/wicg/focus-ring/ ).
The polyfill is fairly active on github and a11y experts ( @alice, @robdodson , etc.. ) are very much involved in improving it which by itself is more than enough reasons to use the polyfill instead of writing ours.
Here is modified version of their demo (I added link) that uses the polyfill https://codepen.io/aghassemi/pen/brwWRV
The main difference between your prototype and the polyfill is the naming (
[mouse-focus]vs.focus-ring) and handling of some types of inputs. Polyfill still does the focus on mouse for text inputs. I suspect the heuristics change as discussions and data points pours in.