Closed domenic closed 2 years ago
I would say there are still cases like navbars where you will like the focus to be applied to the content after the navigation. For instance, I personally find it confusing in the case of Twitter to not switch focus to the main content we are visiting (Profile, notifications, etc). You can even mix some of these behaviors in the same app, so none of them would be desirable as a default. Given the mentioned research, it also seems like different users would want different focus patterns, so maybe there should be new aria attributes to be set by the developer on the page, or reuse existing HTML semantic elements like nav
and main
. Depending on the user settings on the browser, the focus will be set differently.
Otherwise, focusReset: "after-transition"
seems like a good default
Another possibility: is the currently focused element in the DOM? If so, preserve focus. Otherwise, reset focus. This is simpler and more straightforward than calculating visibility.
Re:navbars setting focus to their contents, I think this is something the app should do, because it knows best what new content the user probably wants to be focused to.
The cases where people move DOM off-page or hide it are often because it's expensive to re-render content, especially if that content was initially rendered on the server. But I think this makes the stronger argument for some browser-native and performant way to cache DOM, because it would make the semantics of focus easier if we knew that whatever was in the DOM was actually part of the page, and not just being cached due to browser limitations.
Another possibility: is the currently focused element in the DOM? If so, preserve focus. Otherwise, reset focus. This is simpler and more straightforward than calculating visibility.
I think this is one of the possibilities I mentioned in the OP: Change the default focus behavior for app history SPA navs, to "manual".
One note: currently a display:none
element would drop focus, which is convenient for focus management in a SPA. However, maybe this behavior could also be applied to content-visibility:hidden
. After all, does it make sense to keep focus in a content-visibility:hidden
DOM subtree?
Examples of cases where "after-transition"
is correct, on twitter.com:
So this is an example where on the same page you want both "manual"
behaviors (for the sidebar tabs) and "after-transition"
behaviors (for other tabs). Hmm.
The spec proposal doesn't say anything about focus handling, right? And I think that is expected. The API should expose reasonable events so that authors can then focus whatever element they want. Having some default behavior is likely being wrong in too many cases. (Given how hard it was to have reasonable focus handling for
The spec proposal doesn't say anything about focus handling, right?
That's not correct. As detailed in the OP of this thread, and in https://github.com/WICG/navigation-api#focus-management , the proposal attempts to provide better tools for managing focus, to address the accessibility problems with single-page apps outlined in this user research.
So we definitely need to do something. This proposal suggests that "something" is to provide a new mode, focusReset: "after-transition"
, which works as described in the explainer.
The question at hand in this issue is, what should be the default behavior. The Fable Tech Labs user research claims that current default behavior, of leaving focus on a potentially off-screen element, leads to bad experiences. This issue thread is questioning whether that conclusion holds or not, given counterexamples such as tab UIs.
Having some default behavior is likely being wrong in too many cases.
There's no option where we don't pick a default behavior. The question is picking the right one. The two simplest options are:
"manual"
, which does not reset focus automatically. (Leading to the problems discussed in the user research)"after-transition"
, which resets focus automatically after transition. (Leading to problems in tab-like UIs.)The OP also discusses some more complicated possibilities.
This Google search displays a carousel of the moons of Jupiter. Clicking on any of them performs an SPA nav. Focus stays on the clicked moon. Resetting focus to the body would be bad here.
I think in this case, the set of images are acting more like a row of pagination buttons/links or even breadcrumbs, on a meta level. See aria-current. The selected link/image could be marked with aria-current="page"
and that's the thing that gets an .active
CSS class, but it would still be fine to move the focus to the content (search results).
It could be useful to consider a new option: allowing a way to customize the definition of a route change (some existing art) with a specific API that SPAs opt-into (and build into their frameworks). That way they could provide an upgrade path for their users, and help them deprecate the tab-like UIs in a backwards-compatible way.
(Edit to add: I think by adding an API for SPAs to opt-into, it could be possible to otherwise leave focus resetting the way users expect it to work.)
There are opposing use cases and all of them are valid, so trying to identify the default behaviour that meets most user expectations most of the time, is probably the best bet.
An advantage of the after-transition focus reset is that it's what most people are likely to expect because it mimics standard page load behaviour, and keyboard users will have navigation strategies based on that expectation.
Sometimes that strategy will be to return to the point on the page where they actually wanted to be, which is where @domenic's use cases come in, and sometimes the strategy will be to navigate through the content as usual.
Setting aside developer effort for a moment, each of the proposed options will be inconvenient to users some of the time and OK for users some of the time.
So my suggestion would be to go with the option that mimics standard page load behaviour, on the basis that it's almost certainly the most common experience users will be used to, but which makes it reasonable for developers to place focus somewhere else if/when there's a valid use case - and I think that's the after-transition focus reset behaviour?
My expectations were similar to those of @smaug----. That focus remains as-is.
I see the argument for resetting by default though.
What happens to selection given https://github.com/whatwg/html/issues/7657?
That's not correct. As detailed in the OP of this thread, and in https://github.com/WICG/navigation-api#focus-management , the proposal attempts to provide better tools for managing focus, to address the accessibility problems with single-page apps outlined in this user research.
ok, I'm now lost what text I should be reading. I thought https://wicg.github.io/navigation-api/ is the current proposal and the rest are just random ideas which might be added to it. But I guess this issue is about that - whether to add something to the spec proposal.
The spec draft for focusReset and scrollRestoration is at https://github.com/WICG/navigation-api/pull/201 ; I guess I should merge the focusReset part (which is mostly done) to avoid any confusion, while I try to figure out the scrollRestoration parts.
After some offline discussion, we remain unsure about the correct default, but tentatively resolved to stick with "after-transition"
. In part, because it better matches cross-document (MPA) navigations; and in part because among the two that choice will more likely encourage developers to think about focus management, because they'll see focus getting reset.
Another interesting idea was to force people into choosing one, by making focusReset
a required member. However, that plays very poorly with multiple navigate
handlers; it precludes allowing something like a first navigate
handler that doesn't care about focusReset
behavior and wants to delegate that decision to a second navigate
handler.
So for now, we'll close this issue accepting the current behavior. We'll keep an eye out for how people feel about the behavior in the wild and see if there are things to improve. But in the end the worst-case scenario is that we guessed wrong and most router code ends up using { focusReset: "manual "}
. That isn't the end of the world.
The current explainer draft describes a new default focus behavior that single-page apps (SPAs) can opt into, by using the new navigation API. Translating the technical details there into an overview:
This default behavior is called the
"after-transition"
focus reset behavior.This was motivated by this post by @MarcySutton, which states
and by the desire to make the "new default" for SPA navs the same as how MPA navs behave: i.e., to reset focus. The reasoning being, resetting focus to the body (or designated element with
autofocus=""
) would ensure none of the bad outcomes Marcy describes would occur.However, upon testing some SPAs in the wild, I'm having second thoughts about this. Essentially, resetting the focus by default is hostile to "tab" type patterns. For example:
This Google search displays a carousel of the moons of Jupiter. Clicking on any of them performs an SPA nav. Focus stays on the clicked moon. Resetting focus to the body would be bad here.
twitter.com's logged-in view displays tabs along the sideline. Clicking on, e.g., "Profile", performs an SPA nav. And focus stays on the "Profile" link. Resetting focus to the body would be bad here.
Airbnb's experiences page (sample) has various filters, e.g. "Arts and culture", "Sports", "Tours". Clicking on these performs an SPA navigation (and updates the very end of the URL bar). Focus remains on the thing you clicked. Resetting focus to the body would be bad here.
I think there are many more examples.
So now I'm not sure what to do. Some ideas:
Stick with the current plan, and make people aware that these sorts of tab patterns will need to use the navigation API's
{ focusReset: "manual" }
option.Change the default focus behavior for navigation API SPA navs, to
"manual"
: which means, don't touch focus. The usual focus fixup rule will apply if you remove the currently-focused element from the DOM or make it inert or hide it withdisplay: none
. But if you translate it offscreen, or hide it behind something, or usevisibility: hidden
, then it will stay focused, with potentially confusing consequences for users. You can opt in to the no-longer-default reset behavior by using{ focusReset: "after-transition" }
.Slight tweak of the
"manual"
-as-default plan above, but with a modified version of the focus fixup rule where it also accounts forautofocus=""
. Essentially, have a navigation API SPA nav reset the "has autofocus happened yet?" flag, allowing easy customizability of what gets focused after whole-page-replacing SPA navs.A bigger tweak of the above, where we have some sort of "super focus fixup rule" which runs after navigation API SPA navs. E.g.: if the currently-focused element is not intersecting the viewport, or is occluded in some way, then reset focus. This seems scary because getting just the right amount of magic seems difficult.
Have the cleverness be based on the specific case where clicking an
<a>
causes a navigation API SPA nav. If that particular<a>
is still in the DOM and focusable after the SPA nav finishes, then don't reset focus. Otherwise, reset focus. (To be clear, this would reset focus in cases like<button onclick="navigation.navigate('foo')">
. Only<a>
s get special treatment; tracking navigations through arbitrary JavaScript is too much magic.)The most important question right now is what the right default should be. We will always have a
"manual"
mode available. And if we think that some of these clever modes are useful and people might want them in some cases, we can always implement them later. But we need to figure out the default---whether it's"manual"
,"after-transition"
, or something more clever---before we can ship an initial version of the navigation API.