The show/hide interactions of Olivero’s header can be confusing and unexpected for blind people.

From @andrewmacpherson in #13

  • A disclosure button (show or hide) - in which case it wasn't hiding things correctly, and well-behaved buttons shouldn't really do anything until the user presses them.
  • A button to "pin" (or un-pin) the header behaviour. This would be about whether it should stay visible or not. A "stay put" button. This description made more sense to me eventually, but in this case it isn't a disclosure button, and the use of aria-expanded isn't inappropriate.

The trouble is, it can't really be both of these things, and it wasn't properly behaving like one or the other. So @mherchel and I talked about this in detail, and came up with a plan.

The button will control whether the header has a sticky position at the top of the viewport, versus a static position at the top of the document. THAT"S ALL IT WILL DO. This seems to be simplest behaviour, with fewest surprises.

Proposed Resolution

  • The header will continue to always be visible to the accessibility tree (no change)
  • Remove the focusin event that causes the menu to slide out when an item gains focus
  • We will continue to use a button element. It will have type="button", role="switch", and aria-checked="<state>" attributes
  • The button will not be removed via visiblity: hidden
  • Button will be visually hidden via opacity when at the top of the page.
  • When the button gains focus, opacity will be changed to 1
  • The header's "pinned" state, will be remembered (and respected) via localStorage (expired after 2 weeks). The default state for this is OFF.
  • The button's [initial] accessible name will be "Sticky header”
  • Remove ESC key event listener.
  • As a keyboard user, if i'm scrolled halfway down the page and I shift+tab back to the navigation, the browser needs to ensure that the header is visible, so I can see what I'm doing. This means that if the header is not sticky, make the browser scroll to where the navigation is visible (ideally this can be done without JS, but it can be done w JS if necessary)

Issue fork drupal-3186349

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

bnjmnm created an issue. See original summary.

bnjmnm’s picture

Issue tags: +Accessibility
mherchel’s picture

This seems like an issue with any element that gets fixed to the viewport. Is there any way around this?

Off the top of my head, the only fix would be some serious JS.

We could implement an event listener on focus, and when fired, we could compare the focused element to the the fixed nav bar, and if it's overlapping, we could scroll the page a bit. This seems very heavy-handed to me, and it would also require a getBoundingClientRect() on each focused element, which forces a reflow in the browser.

Thoughts on this?

mherchel’s picture

That would also only fix #2 above. I don't have any idea on how to fix #1. Thoughts on this?

bnjmnm’s picture

Making sticky elements accessible is difficult! I learned this the hard way in this Claro issue: #3070558: Implement bulk operation designs. The solution, although it seems to work, is as complex as you suspected it might be. It also hasn't progressed much since a patch with the solution was provided, which is understandable as the JS is pretty intense for any reviewer to take in.

@rain suggested another option that wasn't feasible for Claro at the time: provide a user-configurable option to disable sticky behavior. Such a feature would be implemented in this issue: #3067273: Provide form for customizable settings. The issue presents several implementation questions that must be answered before it can move forward. Most, if not all, the same questions would need to be answered for Olivero's implementation. Helping push that issue forward would likely make it much easier for Olivero to implement the same thing. Weighing in now may help that functionality get added in a way that makes it easy for Olivero to leverage.

andrewmacpherson’s picture

Title: Sticky header focus + speech control issues » Major accessibility problems with Olivero header show/hide feature
Issue summary: View changes
Priority: Normal » Major

Epic review ahead. I've experienced lots of WTF moments with the Olivero header, and it's taken me a long time to get my head around it.

First, a request for information: Can the Olivero design team please provide an explanation of how the header is intended to behave, and the reasoning behind it? I think it's very confusing. My overall impression is that it's a fancy sliding gee-whizz effect. I haven't seen the benefit of it yet... so what am I missing?

@bnjmnm's youtube video doesn't cover all the problems I found with it. There are more problems than covering operable controls. Here's my experience:

There is sometimes a button on the left-hand side. However it appears and disappears, and I'm not sure why it does that.
What's more, I can't figure out what the button is supposed to do. It has an X icon (close?) and swaps to a 3-lines icon (show the menu?). It doesn't seem to work though. If I click the X, the menu slides out of the way, but comes back as soon as I press TAB. If the intention is to hide the menu, it fails at that.

Here are detailed steps to relive my confusion...

Scenario 1

Keyboard users employ the TAB key to move between links on the page. Let's see how the menu behaves when I try that.

  1. Load a page. Press TAB to move through all the links on the page sequentially. The menu remains visible.
  2. After tabbing to something below the bottom viewport edge, a button appears! It's in the top-left corner of the page, and has an X-icon. The menu remains visible.
  3. I press shift-TAB to move backwards to reach this new button... but I overshoot. I pressed TAB one too many times, and now focus is on the skip-to-main-content link. (Ooops, that happens from time to time. My bad.)
  4. But the button has disappeared! Oh, never mind. Let's continue exploring the controls elsewhere on the page. Press TAB a lot of times...
  5. When I move focus to something below the viewport, the X-icon button appears again!
  6. Now I really want to know what the button does. So I shift-TAB backwards, and take it slowly. This time I don't overshoot. The button doesn't have a focus indicator, but I pressed shift-TAB once when focus was on the site name. So I'm pretty sure the button has focus.
  7. I hit the spacebar. The menu slides away to the left! The button changes from an X icon to a 3-lines icon. So that's what it does. It hides the menu. I press spacebar a few more time, and watch the menu appear and disappear.
  8. I decide to leave the menu hidden...
  9. Now I press TAB. I'm expecting to reach the links in the main content. But the menu appears!
  10. Try again. I use the button to hide the menu, but it just comes back again when I press TAB.
  11. Conclusion: there is a button to hide the menu, but it isn't always available. When it is available, it doesn't work. The menu won't stay hidden.

Scenario 2

Keyboard users don't always want to interact with controls. Sometimes they just want to read the page. We don't need the TAB key for that.

  1. Load a page, then press the page-down key. This is what you do if you just want to read a page (not use interactive controls).
  2. The page scrolls by approx 1 viewport worth. The menu slides away to the left. The 3-line button appears.
  3. Press page-up. When you reach the top of the page, the menu appears, and button disappears.
  4. You get the same results when using the arrow keys to scroll content. Here it's more fine-grained. The menu slides out of the way as soon as you move scroll a couple of lines worth.
  5. Note, I have not yet pressed the TAB key. Focus is still at the start of the page.
  6. Now I'm curious what the button does. I press the TAB key, to move through the first few controls on the page. I expect the 3-line button is one of these, because it is in the top-left corner.
  7. The first time I press the tab key, the viewport scrolls to the top of the page (expected), and my focus is on the skip-link. But the 3-line button has disappeared!
  8. From here onwards, the story works out the same as scenario 1.
  9. Conclusion: the menu will disappear and stay hidden if I just want to read the page, using the arrow/page keys. But if I put focus anywhere in the menu, then I can't hide the menu afterwards.

Scenario 3

A variation of the previous 2 scenarios. This time, I'll use the skip-link to avoid the header navigation. Let's see if that makes a difference.

  1. Load a page, then press TAB once. Focus is on the skip-to-main-content link. Press enter to activate it.
  2. The page scrolls, enough to move #main-content to the top of the viewport. The menu slides away to the left. The 3-line icon button is shown on the top left corner of the page.
  3. Keep pressing TAB to go through links in the main content. Just avoid shift-tabbing backwards to the header navigation. The menu remains hidden.
  4. Use the arrow and page keys too. The menu behaves like it did in scenario 2. It remains hidden, until you scroll close to the top of the page. It appears if you scroll to the top, and disappears if you scroll away.
  5. At no point have I ever seen the X-icon button.
  6. Now try to use the 3-line button. Focus is somewhere in the main page content at this stage, so shift-TAB until you get near the top of the page.
  7. The menu will appear, and focus will be on the last item in the header navigation. The button has changed to an X-icon.
  8. From this point onwards, the story plays out like scenario 1. I am unsuccessful in any attempt to hide the main navigation. It just keeps coming back again.
  9. Using the arrow/page keys to scroll no longer has any effect on the presence of the menu.
  10. Conclusion: I can refine what I learned from scenarios 1 and 2. If you ever interact with the main navigation, by putting focus in it, then you will never be able to hide the menu afterwards.

At this stage, I don't grok this at all. I have no idea what the point of this button is.

Bonus round: with a screen reader

This isn't greatly different, but a few extra details emerge.

  1. When the button appears and disappears, it's removed from the accessibility tree using CSS visibility: hidden.
  2. The button has aria-expanded. When it shows an X-icon, it is reported as expanded. When you close it, the icon changes to 3-lines and it is reported as collapsed. That sort of seems okay, but it's misleading because...
  3. If you press TAB to move away from the button, it opens up again, and the aria-expanded attribute becomes true. The user hasn't pressed the button to make the menu appear. It did that without the user asking for it.

Under the hood

  1. Both the button, and the content it supposedly controls, are nested inside the <header> landmark region. That's good.
  2. The button seems to be behaving well, and uses aria-expanded properly. Semantics-wise, it's good.
  3. The problem is that the content isn't actually being hidden. When the menu slides away to the left, it just has the spatial position changed using CSS translation, and opacity is made zero. The links are still in the accessibility tree, and the tabbing order.
  4. It also makes a liar of the button. The point of the aria-expanded is to inform users that additional content can be revealed or hidden. However this doesn't happen! The only thing that changes is the button state. As far as assistive technology and the tabbing order is concerned, nothing has actually changed. It doesn't hide/reveal additional content.
  5. The .header__inner has a focusin event listener which slides the menu in whenever focus enters it. That's the reason for the WTF of scenario 1. You close the menu, but as soon as you tab away, you trigger this focusin listener.

Conclusion

This disappearing menu behaves very badly. It's like a Jack-in-the-box with a broken latch; you can put the doll back in the box, but as soon as you take your hand off the lid, Jack pops up again.

What to do about it?

I understand what's going on at the code level. Yet I'm unsure exactly what to suggest to improve this, because I don't understand the design intention.

  1. Can we get rid of it? No fancy animation, or sticky header. Just leave the navigation at the top of the page where you'd expect it. Much simpler, no moving parts.
  2. Re: #5, specifically the idea of putting a user-preference in another place somewhere else. Please no, that sounds far too convoluted. A web page which needs user settings to be changed before you can operate it? That's not a very elegant or inclusive approach to accessibility.
  3. Just make the button work properly. When the user changes the state of the button, leave it that way until they say otherwise by hitting the button again. Specifically:
    • Hide the content properly, using CSS visibility: hidden to remove it from the accessibility tree and the tabbing order.
    • Remove the focusin event listener from the .header__inner.

    This will effectively honour a user's preference. Hide the menu, and it stays hidden. There's no need for a separate user preference somewhere else.

The part I'm not sure about is how the menu should behave before the user touches the button. We should go for whatever has the fewest surprises. So I'd lean towards having the menu visible, and not hiding it until the user touches the button. So there's a sticky header, and it hangs around until intentionally dismissed.

The appearing/disappearing behaviour of the button is confusing. Can't the button just be there all the time? That's closer to how the narrow/mobile menu behaves.

What about navigating between pages? Should the menu be shown/hidden based on the state it had on the previous page? Again, this is the option with the fewest surprises...

  • If they've never hidden it, it will still be there.
  • If they navigated to a new page by using the header menu, then that means it was open when they left the previous page. So it can be open when they arrive at the new page.
  • If they navigate to a new page by a link in the footer or main content, keep the state as they left it.

I re-organised the issue summary a little bit. Made the issue title broader.

jwitkowski79’s picture

Hi @andrewmacpherson and @bnjmnm!
Here's my response to the above questions around the intended behavior of the Olivero header and our case for keeping it (as long as we can make it fully accessible).

Intended functionality:

  • When at the top of the page, navigation will appear in its default state - always expanded
  • On scroll down, the navigation collapses into a hamburger nav on the left side of the screen. Animation will help signify to users the transition
  • Users can click the hamburger icon at any time to expand and access the navigation
  • On tablet: the navigation will appear as a hamburger icon in the upper right side by default

One of the main design principles was to make Olivero feel modern. This means taking advantage of the capabilities and strengths of modern browsers and interaction patterns. One way we felt we could apply this principle was to the header. As we’ve all seen, there are 3 common approaches to how headers interact with scroll interaction:

1. They are not fixed to the top of the screen
2. They are persistently fixed to the top of the screen on scroll down/up
3. They are fixed to the top of the screen only as users scroll up

Olivero was going to be a theme for a CMS which meant that the amount of content that a user could put on the page would be wildly varied. We wanted to go with a fixed header of some sort to help account for the worst-case scenarios of pages that present a lot of content..say blog or news articles, but we also didn’t want to introduce a fixed item on the page that would possibly impede on other fixed items that could be added to a page. We looked towards our design principle of making it feel modern..and took a fixed header one step further to help make the interaction feel modern and new, without it being something that users would have to relearn. We also came up with a way to have the navigation always persistent, yet not impede with content or possible other fixed items that could appear on the page. While the interaction itself seems new and novel, we introduced common design/UI patterns including:

- Hamburger nav icon to indicate navigation
- Nav always appears expanded when at the top of the page
- Animation on scroll down helps indicate how to expand the navigation
- Full navigation can always be accessed on the screen without scrolling up

andrewmacpherson’s picture

Issue summary: View changes

@jwitkowski79 - Thanks for the update in #7. I'm still digesting it.

The sticky header also seems involved in #3190120: Olivero: Focused level-2 nav items should always be in viewport during keyboard navigation.. There's a demonstration in comment #9 there. When a sub-nav has many items, and extends past the viewport, you tab through them and they will be scrolled into the viewport, until the sticky header engages. After that point, focus can't be seen on remaining sub-nav items because they are kept below the bottom viewport edge.

mherchel’s picture

Assigned: Unassigned » andrewmacpherson

@andrewmacpherson and I just got off of a long call to go over ideas. The plan is below:

Initial plan

  • The header will continue to always be visible to the accessibility tree (no change)
  • Remove the focusin event that causes the menu to slide out when an item gains focus
  • We will continue to use a button element. It will have type="button", role="switch", and aria-checked="<state>" attributes
  • The button will not be removed via visiblity: hidden
  • Button will be visually hidden via opacity when at the top of the page.
  • When the button gains focus, opacity will be changed to 1
  • The header's "pinned" state, will be remembered (and respected) via localStorage (expired after 2 weeks)
  • The button's [initial] accessible name will be "Sticky header"

Follow ups

  • There was discussion on adding a tooltip to the button. Not sure if we initially need this, or this can be a followup issue (maybe discuss with UX team?)
  • Determine if accessible names need to be changed to something more appropriate
  • Determine if the close "x" icon makes sense in this context
  • Testing with the National Federation of the Blind

Assigning to @andrewmacpherson on sign off of the above, and to input any other thoughts and rationale.

mherchel’s picture

Status: Active » Needs review
StatusFileSize
new11.26 KB

Changes from #9 are in this patch. I'm going to set up a Tugboat preview for this shortly.

mherchel’s picture

nod_’s picture

Issue tags: +JavaScript
andrewmacpherson’s picture

#7 thanks for these answers. They describe how it behaves, but I was still very confused about why it behaved the way it did, or what to call the behaviour.

To me, the behaviour had aspects of:

  • A disclosure button (show or hide) - in which case it wasn't hiding things correctly, and well-behaved buttons shouldn't really do anything until the user presses them.
  • A button to "pin" (or un-pin) the header behaviour. This would be about whether it should stay visible or not. A "stay put" button. This description made more sense to me eventually, but in this case it isn't a disclosure button, and the use of aria-expanded isn't inappropriate.

The trouble is, it can't really be both of these things, and it wasn't properly behaving like one or the other. So @mherchel and I talked about this in detail, and came up with a plan.

The button will control whether the header has a sticky position at the top of the viewport, versus a static position at the top of the document. THAT"S ALL IT WILL DO. This seems to be simplest behaviour, with fewest surprises.

Some more notes...

  • The default behaviour, on first visit or page-load, is for the sticky behaviour to be OFF. It won't ever become sticky unless the user turns it on. That's pretty much how things already are for pointer users, but now we're making that work properly for other users too.
  • The button will not add or remove content from the accessibility tree, so the top level links will still be easy for a screen reader user to find using landmark regions, list-all-links, or find-in-page tools.
  • The navigation will always be easy to find by moving to the top of the page, even when it isn't sticky.
  • A blind user with a screen reader may have no use for this button at all. However DO NOT try remove it from the accessibity tree, because speech control users will need it if the sticky behaviour is in their way.
  • So, we'll need to make the button name clear, so that blind users will understand this is solely a visual effect. Otherwise, it can seem like a button which apparently does nothing.

The issue summary needs updated with the new plan.

As @mherchel said, there are still some details to consider. I'll add some more notes later, but there's enough to proceed with implementation, and this is no longer a major hurdle to stabilizing Olivero.

It will be good to get a review of this from our project partners at the National Federation of the Blind.

mherchel’s picture

Issue summary: View changes
mherchel’s picture

Issue summary: View changes
mherchel’s picture

Issue summary: View changes
mherchel’s picture

Issue summary: View changes
mherchel’s picture

StatusFileSize
new18.37 KB

@andrewmacpherson and I discussed this at length yesterday. We added some success criteria including

- Ensuring that the sticky header is scrolled to (if not pinned open) when it gains focus
- Removing the Escape key event listener that closes the sticky header

In addition, this patch makes some naming changes

- Renames the component from wide-nav__button to sticky-header-toggle
- Also renames a lot of the functions within the JavaScript.

Tugboat preview for this patch is at https://3186349-9-header-a11y-3-mqzwznybm2eyxezqyxrlgwritodwggul.tugboat...

mherchel’s picture

Issue tags: +Needs reroll

This needs a re-roll, but still can use a review from accessibility maintainers (Tugboat link is still active)

mherchel’s picture

StatusFileSize
new17.39 KB
anmolgoyal74’s picture

StatusFileSize
new17.39 KB
new2.76 KB
rainbreaw’s picture

Status: Needs review » Reviewed & tested by the community

I'm happy to sign off on this as an accessibility topic maintainer. Menu access is second in DOM, and persistent based on user need.

One non-blocking consideration (perhaps for a separate issue), is that the X feels like an escape option. Since the user cannot escape, consider using a different icon to collapse this back.

andrewmacpherson’s picture

Issue tags: +Needs followup

I'm taking a close look at the implementation this weekend; basically, checking all the things @mherchel and I agreed during the (long!) calls we spent reviewing this. Leaving RTBC for now, and if I find anything that still needs fixing I'll try to file follow-ups rather than block the work done so far. (If I don't add any more comments in the next few days, assume that RTBC still stands.)

@rainbreaw - can you clarify which patch you tested? @mherchel has been maintaining Tugboat links for testing, but the last Tugboat link was #20, and another patch has appeared since in #21.

Re. #22:

One non-blocking consideration (perhaps for a separate issue), is that the X feels like an escape option. Since the user cannot escape, consider using a different icon to collapse this back.

The button is a role="switch" with an immediate on/off behaviour. An escape-key behaviour isn't appropriate, as there's no multi-step operation to abort.

The issue of whether an "X" close-icon was mentioned in #9 as a possible follow-up. We decided to leave it for now, since it's highly bikeshed-able. Now that 2 accessibility maintainers have noted it, I think we should have that follow up for sure, so I'll create that. I think it could be better as an in/out chevron-arrow, similar to the >> and << used by some application sidebars (like GitLab).

lauriii’s picture

Status: Reviewed & tested by the community » Needs work
Issue tags: -Needs reroll

I didn't review the code yet but I tested the patch manually. I'm not sure the behavior makes sense for users navigating with keyboard. They don't have the additional context from the aria attributes, meaning that they can only see the icons. I find it confusing that when I press the toggle button as I'm scrolled on top of the page, there's no visible action as a result of pressing the button, other than the icon switching.

mherchel’s picture

StatusFileSize
new236.34 KB

I brought this to the UX meeting at #3205869: Drupal Usability Meeting 2021-04-02. It was agreed that the major issue was that the button doesn't appear to do anything (other than turn into an X) when the user is scrolled all the way to the top.

@benjifisher came up with the idea of toggling the visibility of the drop shadow that appears under the header (visible when the header is pinned open). We demo'd this using Chrome Devtools, and everyone was happy! I think we have a solution.

mherchel’s picture

StatusFileSize
new91.58 KB
mherchel’s picture

Status: Needs work » Needs review
StatusFileSize
new19.18 KB
new1.82 KB

Box-shadow mentioned in #26 is added!

Tugboat link is https://3186349-header-a11y-5-wajnw8ii2xwh7dkyr5h6yadflnteujhf.tugboat.qa/

mherchel’s picture

StatusFileSize
new1.74 KB
new20.96 KB

This patch changes some selectors that are relevant to our Nightwatch tests (just added in #3205434: Add Nightwatch Test Coverage for Olivero), so we have to update the tests.

Note that I'm also changing the name of the test's file to match with the name of the component, and I'm adding an additional test that ensures that the sticky nav will remain open after page refresh.

mherchel’s picture

StatusFileSize
new849 bytes
new21.64 KB

Found and fixed a minor IE11 issue.

Latest Tugboat preview: https://3186349-header-a11y-6-nspxi1kiq6qtjilasl3gxrow2gnxg5jy.tugboat.qa/

proeung’s picture

Status: Needs review » Reviewed & tested by the community

@mherchel First off, awesome job tackling this issue! I know that there are a lot of back and forth conversations on how best to address this accessibility issue within the header show/hide feature and I'm glad that we finally have the latest patch and preview to test/review.

Overall, the code changes look good. I see all of the revised ARIA attributes coming through and the toggling of the drop-shadow is visible when the header is pinned to the top of the page. Also, the Nightwatch test looks to be passing as well. I think the patch in #30 is good to go and the issue can be marked as RTBC.

mherchel’s picture

Assigned: andrewmacpherson » Unassigned
StatusFileSize
new29.88 KB
new940 bytes

Removing some unrelated code that closed the search form when ESC is pressed (per @lauriii)

mherchel’s picture

StatusFileSize
new20.76 KB
new940 bytes

I added some code from another issue in the prev patch. Correct patch attached.

mherchel’s picture

  • lauriii committed bc8ea2e on 9.2.x
    Issue #3186349 by mherchel, anmolgoyal74, bnjmnm, andrewmacpherson,...
lauriii’s picture

Status: Reviewed & tested by the community » Fixed
Issue tags: -Needs followup

Committed bc8ea2e and pushed to 9.2.x. Thanks!

shaal made their first commit to this issue’s fork.

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.