-
Notifications
You must be signed in to change notification settings - Fork 8
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat!: Update behaviour of Header and related components #1607
base: develop
Are you sure you want to change the base?
Conversation
e4a4c98
to
359edc6
Compare
99ce081
to
285a869
Compare
I’ve considered 3 approaches for the structure of HTML and components for the header, mega menu and search bar. For simplicity, the examples below do not contain the search bar, and only the menu button is specified for the Page Menu. My requirements were:
Approach 1: one explicit grid to contain everything <Grid paddingVertical="medium">
<Grid.Cell span="all">
<Header menu={<PageMenu.Button>Menu</PageMenu.Button>} />
</Grid.Cell>
<nav className="ams-visually-hidden ams-display-contents">
<Grid.Cell>
<Columns>…</Columns>
</Grid.Cell>
<Grid.Cell>…</Grid.Cell>
</nav>
</Grid> + One grid, no Approach 2: two explicit grids <Grid paddingVertical="medium">
<Grid.Cell span="all">
<Header menu={<PageMenu.Button>Menu</PageMenu.Button>} />
</Grid.Cell>
</Grid>,
<Grid as="nav" className="ams-visually-hidden" paddingBottom="large">
<Grid.Cell>
<Columns>…</Columns>
</Grid.Cell>
<Grid.Cell>…</Grid.Cell>
</Grid> + Existing and logical API Approach 3: two implicit grids <Header menu={<PageMenu.Button>Menu</PageMenu.Button>} />,
<MegaMenu className="ams-visually-hidden ams-display-contents" key={2}>
<Grid.Cell>
<Columns>…</Columns>
</Grid.Cell>
<Grid.Cell>…</Grid.Cell>
</MegaMenu>,
+ Simple outer API: Header and MegaMenu I chose approach 2. |
const toggleFeature = (feature: Feature) => | ||
setVisibleFeature((currentFeature) => (feature === currentFeature ? undefined : feature)) | ||
|
||
const visibilityClass = (feature: Feature) => (feature === visibleFeature ? null : 'ams-visually-hidden') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is ams-visually-hidden
the way to go? Yes you'll hide it but for the visually impaired, they will get the menu read out loud to them everytime. It should probably just use display: none
and the menu (toggle) button should have aria-controls="mega-menu-id"
so that the menu gets read to them when they toggle the menu.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Even Copilot agrees:
Using "visually hidden" to hide the megamenu may be a poor decision due to the following reasons:
- Accessibility Issues: "Visually hidden" is intended for content that is hidden from sighted users but accessible to screen readers. Using it to hide interactive elements like a megamenu can confuse screen reader users as they might access the menu without visual context.
- User Experience: Sighted users may face unexpected behavior if the megamenu is hidden visually but still interactable. This can lead to confusion and a poor user experience.
- Best Practices: The repository guidelines suggest visually available content should also be accessible to non-visual user agents and vice versa. Hiding the megamenu only visually contradicts this practice.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pretty cool it even refers to your own documentation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Correct, I haven’t implemented all accessibility features yet; I wanted to get the team’s feedback on this approach and API first. We will ensure that both sighted and non-sighted people can navigate properly. Screen reader users will want to hear the items, or they can use the Skip Link.
Someone suggested the Mega Menu must be in the DOM for search engines. That could well be – we should research that. Of course, we could employ other strategies like XML sitemaps, but I don’t expect they’ll allow us to hide the main menu for crawlers entirely.
I do remove the Search bar from the DOM, so I have considered it.
Pretty cool it even refers to your own documentation
Yes, it is!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Search engines execute javascript nowadays and are able to crawl the site without the help of the navigation. The child links of the Google search results are based on relevance, not the sitemap or main nav. Forcing visually impaired users to always use the skip link is not the way to go.
"emblem": { "color": { "value": "{ams.color.primary-red}" } }, | ||
"min-block-size": { "value": "2.5rem" }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a conflict with block-size
which has a min size of 1rem.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The two are intended to cooperate: the minimum height remains ‘40px’ as it was, but the logo is allowed to grow to ‘56px’. The result is 33% top padding, 33% logo, and 33% bottom padding (vertically).
packages/react/src/Header/Header.tsx
Outdated
</Heading> | ||
)} | ||
</div> | ||
<div className="ams-header__section">{menu}</div> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be a nav
, I think. The main navigation section is the Page Menu and the Mega Menu together.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, but I think it would be best to let PageMenu
render a nav
as its root tag. This would add that semantics to the footer menu as well.
I think the Page Menu and the Main Menu should be separate navigation sections. Screen reader users can then select which menu they want to explore.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the Page Menu and the Main Menu should be separate navigation sections
I think that's a little odd, semantically. The entire thing is our main navigation section imo, I don't really see why we should separate them. The mega menu is just a sub menu of the main menu.
It also goes against the whole 'use landmark roles sparingly' thing.
See also these examples, although the last 2 use the nav
tag a bit less sparingly than I would:
}, | ||
} | ||
|
||
export const WithAppNameLinksAndMenuButton: Story = { | ||
export const WithAppNameMenuAndMenuButton: Story = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we want to add a story here for the header + mega menu? It'd be nice to see the whole thing together, and I think I would look for it here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I want to make that the first entry in a new ‘Patterns’ section. Header + Mega Menu is a composition with some state: a pattern.
(The confusion may be that our Header component is just Logo + Heading + Page Menu, while visually the Main Menu and the Grid whitespace around it would conceptually belongs to the (application) header as well. I considered renaming Header to Masthead or Navigation Bar or something.)
Another reason is that this will give us room to present a couple of variations for larger and smaller sites, based on the navigation of some of our current sites.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this depends on where we'll end up with our semantic definition of the Header. I think our Header contains our main navigation, which is comprised of the Page Menu and the Mega Menu. If we go with that definition, then the Mega Menu is part of the Header.
const toggleFeature = (feature: Feature) => | ||
setVisibleFeature((currentFeature) => (feature === currentFeature ? undefined : feature)) | ||
|
||
const visibilityClass = (feature: Feature) => (feature === visibleFeature ? undefined : 'ams-visually-hidden') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just tested this with a screen reader, it's kinda of annoying that it always reads out the mega menu, even if it's collapsed. It also messes up your tab index. I think we should just use display: none
here, same as gov.uk. Do you have a link to the comment where someone said this was a good idea?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The ‘SEO’ comment is kind of concise :)
Robbert has more on making header and navigation accessible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ha, that's a little hard to put into context. One thing I can comment on: Using display: none
is not the same as not having the menu in the DOM. I agree it should be in the DOM, but visually hidden causes too many a11y issues imo.
As suggested in the description, I’ve started extracting separate, more focused PRs.
I’m converting this PR to a draft. We may continue reviewing here, but let’s not merge it. |
This implements the updated design for the Header.
It’s a significant PR that contains breaking changes for various components. It might be best to review this PR globally for the general approach and then extract smaller, focused PRs. This makes the detailed reviews more pleasant, updates the change log correctly, and helps our users understand and implement the changes.
Review my comment at the bottom to see the three approaches I considered and how I chose the winner.
UX agrees with the changes this PR introduces to the design – most notably, the generous white space – and will update Figma accordingly.
The big chunks that could be the new PRs:
These things remain to be done, preferably in separate PRs as well:
AppHeader
in the Amsterdam.nl home page to a patternOther tasks: