Page MenuHomePhabricator

[Spike] Determine best approach for focusing from the sidebar button into the sidebar
Open, MediumPublic0 Estimated Story PointsSpike

Description

Question we are trying to answer

In T261802, we moved the sidebar out of the header (where it was adjacent to the sidebar button) and into the .mw-workspace-container. Although this move helped with the search in header work (T249363) and will likely be necessary when we make the header sticky, it had at least one disadvantage. Because the DOM order changed, users are no longer able to tab (or use another focus management technique) directly from the sidebar button into the sidebar. Currently, they are forced to tab through the header contents before reaching the sidebar.

What is the best approach to bring this behavior back? We might have to use JavaScript to achieve this.

Acceptance Criteria

  • A POC patch is made demonstrating the proposed approach

Sign-Off Criteria

  • Create a task for the implementation using the proposed approach as a reference

Related Objects

StatusSubtypeAssignedTask
ResolvedGoalovasileva
ResolvedJdlrobson
Resolvedovasileva
ResolvedSpikeovasileva
ResolvedSpikephuedx
Resolvedovasileva
ResolvedSpikeVolker_E
OpenSpikeNone
ResolvedSpikeovasileva
Resolvedovasileva
ResolvedBUG REPORTmatmarex
Resolvedovasileva
ResolvedJdlrobson
Resolvedphuedx
Resolvednray
ResolvedMayakp.wiki
ResolvedMayakp.wiki
Stalledovasileva
DeclinedNone
ResolvedEdtadros
OpenNone
OpenNone

Event Timeline

Jdlrobson added a subscriber: Jdlrobson.

Spikes are analysis so moving this to upcoming for scheduling.

Jdlrobson triaged this task as Medium priority.Sep 17 2020, 11:06 PM

The technique of modifying focused elements with JS is typically called focus trapping (or looping). There's a good writeup on medium of the general implementation here: Focus Trapping for Accessibility (A11Y) . It generally involves checking which element is focused and changing the next focused element during a keydown event.

What I hadn't realized before is that this trick doesn't work on mobile! This is because keyboard events aren't emitted on mobile.

iOS & Android aren't our primary concern since Vector is a desktop skin, but it would be nice to make this as mobile friendly as possible. As an alternative, the author of the above article suggests that instead of switching the focused element, you mark all other elements you don't want focused with aria-hidden=true.

Another option would be to manage all the tabindex values ourselves (or at least the first three). We could hardcode some tabindex values to positive integers like:

  1. jump-to-content link tabindex="1"
  2. menu button tabindex="2"
  3. sidebar menu tabindex="3"

I did a quick check to see what happens when the sidebar is closed in that scenario, and it seems like it is skipped correctly, even with a positive tabindex value.

Besides the two approaches considered above, which can be summarized as:

  1. JS implemented focus-looping (with considerations for mobile devices... somehow).
  2. HTML implemented Positive-integer tabindex values.

There is also the option of moving the sidebar HTML back into the header, right after the menu button, so that it retains the natural tab-friendly DOM order. After discussing this option briefly with @nray, two issues were raised: 1.) given a flexible height of the header, positioning the sidebar below it at all times could be challenging, and 2.) This would have ramifications for an eventual sticky header, for example if the design were to be a sticky header but not a sticky sidebar, that could be problematic.

The first issue could be resolved quite easily by setting position:relative on the header and positioning the sidebar with position:absolute; top:100%; so that the top of the sidebar is always stuck to the bottom of the header, regardless of it's height.

The second issue however, of how this DOM order will interplay with the sticky header, needs some more consideration.

^^ cc @alexhollender

When we implement the sticky header, how important is it that we NOT make the sidebar sticky as well (if at all)? If we don't make the sidebar sticky, what should happen when a user scrolls past the sidebar and then clicks the sidebar button in the sticky header? For example in this scenario:

Is visual feedback that the sidebar has opened/closed important in this scenario?

For reference: MiBkdW1teSB3aXRoIGBmb2N1c2luYCBldmVudC4=

I've been exploring the feasibility of solving this via the DOM order change, i.e. moving the sidebar back inside the header element.

I wanted to figure out what kind of implications this change would have on future design ideas, namely having a sticky or scrolling header.

Based on this (initial) design prototype: https://di-language-switcher-3.web.app/Latitude
I made a codepen that illustrates the mechanics of that design https://codepen.io/j4n/pen/oNLyRyK

In that implementation, there are 2 distinct header elements, one for the top of the page and one on scroll. With this setup, I was unable to reasonably maintain the position and visibility of the sidebar -- stuck to the side of the page -- when it's containing header was moved or hidden. In this codepen, I try to hide the top header with just opacity, but the CSS to do that gets complicated pretty quickly. My impression from these experiments is that changing the DOM order by placing the sidebar inside the header might constrain the design possibilities in future, so I'm less keen on this solution.

So as it stands, I'm leaning towards favouring a JS-based focus-looping solution for this problem, or maybe the positive tabindex solution. Neither of these are great options, but I'm interested to hear from @Volker_E on which one he thinks might be less bad. IMO the positive tabindex values may be at least easier to implement, though could lead to conflicts in the future.

Given the low effort of implementing the positive tabindex approach (changing a few lines in the Mustache templates), I recommend we move forward with that approach because although it's considered an anti-pattern, it effectively solves this issue for mobile as well as non-JS users. If it becomes a maintenance concern in the future, then we can re-evaluate a JS based focus-looping technique.

can this task be resolved/decliend @Jdrewniak or is there something more to be done?

My comment above about using positive tab-index values was incorrect. We would have to change that attribute for every element on the page for that to work.

This is still an accessibility issue and I guess we will have to use javascript to fix it. That'll probably involve manually setting the focused element to the sidebar content when someone clicks on the sidebar button.

The details of how exactly we want the focus to be handled should still be fleshed out though. For example I'm wondering if the default state of the sidebar, open/closed, should have any effect on this focus order.

Sorry I don't have all the context here so I might be missing something here, but I'm wondering if it even makes sense to make the sidebar accessible from within the header. My initial impression is the sidebar shouldn't be keyboard accessible before the logo, the search bar, and the "Personal tools" navigation (aka primary nav). Placing the sidebar inside the header would force the users to tab through the sidebar links before getting to the rest of the header (unless they know to go backwards and hide the menu), and focusing on the sidebar would skip the user through the header.

This definitely seems tricky, in some ways we want the sidebar to behave like a menu or a modal, but in other ways we want it to be an independent section (i.e. sticky sidebar). The only other solution I thought of was to move the sidebar button to the end of the header, and change the visual order via CSS (i.e flex-order). This would keep the sidebar button inside the header and ensure the next tabable element is the sidebar while maintain the existing visual design. However, changing the display order is generally a big accessibility no no, so it wouldn't be ideal to rely on it.