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)
Comments
Comment #2
bnjmnmComment #3
mherchelThis 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?
Comment #4
mherchelThat would also only fix #2 above. I don't have any idea on how to fix #1. Thoughts on this?
Comment #5
bnjmnmMaking 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.
Comment #6
andrewmacpherson commentedEpic 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.
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.
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.
#main-contentto 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.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.
visibility: hidden.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...aria-expandedattribute 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
<header>landmark region. That's good.aria-expandedproperly. Semantics-wise, it's good.aria-expandedis 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..header__innerhas afocusinevent 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 thisfocusinlistener.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.
visibility: hiddento remove it from the accessibility tree and the tabbing order.focusinevent 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...
I re-organised the issue summary a little bit. Made the issue title broader.
Comment #7
jwitkowski79 commentedHi @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:
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
Comment #8
andrewmacpherson commented@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.
Comment #9
mherchel@andrewmacpherson and I just got off of a long call to go over ideas. The plan is below:
Initial plan
focusinevent that causes the menu to slide out when an item gains focustype="button",role="switch", andaria-checked="<state>"attributesvisiblity: hiddenFollow ups
Assigning to @andrewmacpherson on sign off of the above, and to input any other thoughts and rationale.
Comment #10
mherchelChanges from #9 are in this patch. I'm going to set up a Tugboat preview for this shortly.
Comment #11
mherchelTugboat preview for #10 is at https://3186349-9-header-a11y-lwyb8q46i0nmytvqv085a9jv8fwby3pg.tugboat.qa/
Comment #12
nod_Comment #13
andrewmacpherson commented#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:
aria-expandedisn'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 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.
Comment #14
mherchelComment #15
mherchelComment #16
mherchelComment #17
mherchelComment #18
mherchel@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...
Comment #19
mherchelThis needs a re-roll, but still can use a review from accessibility maintainers (Tugboat link is still active)
Comment #20
mherchelre-roll done!
New Tugboat link at https://3186349-header-a11y-4-ojoizz5tydzuyngttx63x4wn8rfrijaj.tugboat.qa/
Comment #21
anmolgoyal74 commentedComment #22
rainbreaw commentedI'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.
Comment #23
andrewmacpherson commentedI'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:
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).Comment #24
lauriiiI 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.
Comment #26
mherchelI 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.
Comment #27
mherchelComment #28
mherchelBox-shadow mentioned in #26 is added!
Tugboat link is https://3186349-header-a11y-5-wajnw8ii2xwh7dkyr5h6yadflnteujhf.tugboat.qa/
Comment #29
mherchelThis 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.
Comment #30
mherchelFound and fixed a minor IE11 issue.
Latest Tugboat preview: https://3186349-header-a11y-6-nspxi1kiq6qtjilasl3gxrow2gnxg5jy.tugboat.qa/
Comment #31
proeung@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.
Comment #32
mherchelRemoving some unrelated code that closed the search form when ESC is pressed (per @lauriii)
Comment #33
mherchelI added some code from another issue in the prev patch. Correct patch attached.
Comment #34
mherchelFollowup from #9 added: #3207795: Potential header menu "X" close-icon usability issue in Olivero
Comment #36
lauriiiCommitted bc8ea2e and pushed to 9.2.x. Thanks!