Some web browsers on some platforms have started to hide the focus ring (outline) until a user uses TAB key. But it is needed to change the focus programmatically in response to a keydown event, there is no way to tell the browser to show the focus ring.
My use case: https://jsfiddle.net/82fdoqL5/embedded/result/
My bug against Firefox: https://bugzilla.mozilla.org/show_bug.cgi?id=1585955#c8
cc @whatwg/a11y
Perhaps there are two things to be done first:
:focus-visible (in supporting browsers) since those browsers are already differentiating between a click and a key press.For the first item, just as Apple disables navigating by Tab until a user enables it, I don't think Microsoft's approach here is any more or less wrong. I would prefer neither be the case.
The example leans on a browser default. How does the proposed solution fix the browser default without getting a developer to write a listener?
If the developer has to be involved, then why not lean on :focus-visible and instead push browsers to support that now?
arguably, if you're working on doing programmatic focus, you'd probably want to define an explicit focus outline/style, particularly since browsers do this sort of visual optimisation.
and as soon as you define an explicit :focus style, the heuristic in browsers like Firefox etc will be overridden.
so this is almost the opposite situation of :focus-visible - that was needed to basically define a style for when browsers would normally (based on their heuristic) show the default focus outline, whereas defining :focus will always apply on focus, regardless of how that focus happened (e.g. as a result of a mouse click) and/or whether or not the browser would normally suppress the default focus outline or not.
@patrickhlauke, it is not possible, AFAIK, to use css style to define the same style as default:
outline: initial will not do anything
ah, so you want to explicitly force the default outline, without explicitly defining it.
[edit: so to be clear, i'd still say you should explicitly define :focus style, but then have a way of saying "the default that you'd do anyway", as a way of just forcing the browser to skip its heuristic, but nothing more]
this smells more like a CSS WG ask to be honest?
Interestingly I'm working on a Chrome bug on :focus-visible today around the same issue: https://crbug.com/1010549 (edit: looks like the bug was filed by @Yaffle, hi!)
As my colleague commented in that issue, the suggested heuristic for :focus-visible does encompass this case:
An example heuristic is to update modality on each style recalc: if the most recent user interaction was via the keyboard; and the key pressed was either Tab, Shift + Tab, or an arrow key;
then the modality is keyboard. Otherwise, the modality is not keyboard.
(Emphasis added.)
Agreed that there should be a way to reinstate the default focus ring if it has been disabled in favour of :focus-visible, for cases where the heuristic fails like this (even if this specific case is a bug).
Also agreed that this seems like a CSS issue more than an HTML issue.
CSS in principle is host-independent and I think the CSS WG still strives for that, so the host has to define when :focus-visible applies. (And you could imagine passing something to .focus() that makes it apply.)
this also goes beyond the .focus() / programmatic setting, as the very first click on that grid example in Firefox also fails to show the focus indication (and that's focus as a result of a mouse click, nothing to do with programmatic focus via JS).
@annevk
you could imagine passing something to
.focus()that makes [:focus-visible] apply.
That would be one way to address the issue; another would be to allow authors to reinstate the default focus style (per https://github.com/w3c/csswg-drafts/issues/4421).
I don't yet have an opinion on which of those directions would be preferable (or if indeed the answer is "both", anyway).
It would be interesting to think through the circumstances under which you'd want to use these things.
In this case, you'd be reacting to a keyboard event, so it'd be a choice between something like
onKeydown(e) {
friendElement.focus({ focusVisible: true });
}
and
onKeydown(e) {
friendElement.classList.add('default-focus-ring-on-focus`);
friendElement.addEventListener('blur', () => {
friendElement.classList.remove('default-focus-ring-on-focus');
}, true);
}
with css
.default-focus-ring-on-focus:focus {
outline: dotherightthing;
}
The second option certainly seems more fragile, but probably also more straightforward to spec, implement and understand.
From a primitives perspective, either way we need to define in HTML that elements have some kind of "focus visible" flag, that's to be set per platform / user agent conventions. And then we define that :focus-visible matches elements that have that flag set.
Given that needs to be there anway, the former of your two options does not seem that complex to me and avoids developers having to duplicate styles and be aware there's two states (:focus-visible and a custom class) for what is essentially a single state.
we need to define in HTML that elements have some kind of "focus visible" flag, that's to be set per platform / user agent conventions.
Could you elaborate on that?
The equivalent of https://html.spec.whatwg.org/#element-has-the-focus, but for :focus-visible.
just clarifying then that the user agent would also dynamically then need to change all those flags when its own heuristics determine that the user has interacted with the page with a keyboard, and potentially (depending on the heuristic) unset all those flags again on each element when the user starts using mouse/tapping a touchscreen again?
or would it make more sense to have this as a single flag for the page/document (which the browser then refers back to when an element does receive focus, to determine if it should then show the default focus outline or not based on the current heuristic state of "did the user use keyboard/do they need to see the default focus on this?")
Yeah, apologies for making it that concrete, it's probably more some kind of function that takes various things into account and returns a boolean. (Even if the keyboard is used it might still not make sense to show an outline around an element so it cannot really be a global decision. E.g., for a video element the user agent might want to highlight individual controls, but not the entire element.)
Most helpful comment
CSS in principle is host-independent and I think the CSS WG still strives for that, so the host has to define when
:focus-visibleapplies. (And you could imagine passing something to.focus()that makes it apply.)