Skip to content
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

Draft
wants to merge 58 commits into
base: develop
Choose a base branch
from

Conversation

VincentSmedinga
Copy link
Contributor

@VincentSmedinga VincentSmedinga commented Sep 27, 2024

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:

  • Allow Grid and Grid Cell to render more tags
  • Allow the Logo to grow a bit
  • Introduce Item and Button for Page Menu
  • Implement new Header design
  • Use the new Header in Home Page example

These things remain to be done, preferably in separate PRs as well:

  • complete a11y – at least some accessible names are missing
  • render a narrow logo of just the St. Andrew’s Crosses
  • extract the AppHeader in the Amsterdam.nl home page to a pattern
  • test the updated Header design with the menus of existing sites
  • remove the Mega Menu component

Other tasks:

  • Include missing license headers (test files and some others)
  • Use HTMLElement and HTMLDivElement correctly
  • Decorate all stories with a Screen

@github-actions github-actions bot temporarily deployed to demo-DES-884-single-line-header September 30, 2024 08:32 Destroyed
@github-actions github-actions bot temporarily deployed to demo-DES-884-single-line-header September 30, 2024 08:39 Destroyed
@github-actions github-actions bot temporarily deployed to demo-DES-884-single-line-header September 30, 2024 09:28 Destroyed
@github-actions github-actions bot temporarily deployed to demo-DES-884-single-line-header September 30, 2024 09:39 Destroyed
@github-actions github-actions bot temporarily deployed to demo-DES-884-single-line-header September 30, 2024 12:08 Destroyed
@github-actions github-actions bot temporarily deployed to demo-DES-884-single-line-header September 30, 2024 14:41 Destroyed
@github-actions github-actions bot temporarily deployed to demo-DES-884-single-line-header September 30, 2024 18:53 Destroyed
@github-actions github-actions bot temporarily deployed to demo-DES-884-single-line-header September 30, 2024 19:14 Destroyed
@github-actions github-actions bot temporarily deployed to demo-DES-884-single-line-header October 8, 2024 08:21 Destroyed
@VincentSmedinga
Copy link
Contributor Author

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:

  1. Optimal HTML, accessibility and SEO
  2. Use existing components where possible
  3. A ‘natural’ API; not too many tricks

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 as props
+ Mega Menu is a pattern, not a component
+/- An independent component for CSS multi-columns
- Requires display: contents, unsure about browser support

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
+ Mega Menu is a pattern, not a component
+ Doesn’t need display: contents
+ Easy to increase bottom padding for menu alone
+/- An independent component for CSS multi-columns
- A bit more HTML (just one tag actually)

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>,

Header renders <Grid><Grid.cell span="all"><header class="ams-header" ref><Logo /><Heading />{menu}</header></Grid.Cell></Grid>

MegaMenu renders <Grid as="nav" className="ams-mega-menu" paddingBottom="large">{children}</Grid>

+ Simple outer API: Header and MegaMenu
+ MegaMenu.Section could implement CSS multi-col locally
- Two components render a Grid as well; dependencies, weird classes and refs
- Strange API: Grid.Cell as children of MegaMenu
- Requires display: contents, unsure about browser support

I chose approach 2.

const toggleFeature = (feature: Feature) =>
setVisibleFeature((currentFeature) => (feature === currentFeature ? undefined : feature))

const visibilityClass = (feature: Feature) => (feature === visibleFeature ? null : 'ams-visually-hidden')
Copy link
Contributor

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.

Copy link
Contributor

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:

  1. 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.
  2. 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.
  3. 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.

Copy link
Contributor

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

Copy link
Contributor Author

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!

Copy link
Contributor

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" },
Copy link
Contributor

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.

Copy link
Contributor Author

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).

@github-actions github-actions bot temporarily deployed to demo-DES-884-single-line-header October 8, 2024 14:04 Destroyed
</Heading>
)}
</div>
<div className="ams-header__section">{menu}</div>
Copy link
Contributor

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.

Copy link
Contributor Author

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.

Copy link
Contributor

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 = {
Copy link
Contributor

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.

Copy link
Contributor Author

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.

Copy link
Contributor

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')
Copy link
Contributor

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?

Copy link
Contributor Author

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.

Copy link
Contributor

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.

@VincentSmedinga
Copy link
Contributor Author

VincentSmedinga commented Oct 15, 2024

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.

@VincentSmedinga VincentSmedinga marked this pull request as draft October 15, 2024 20:53
@github-actions github-actions bot temporarily deployed to demo-DES-884-single-line-header October 23, 2024 13:17 Destroyed
@github-actions github-actions bot temporarily deployed to demo-DES-884-single-line-header October 23, 2024 14:06 Destroyed
@github-actions github-actions bot temporarily deployed to demo-DES-884-single-line-header November 1, 2024 13:29 Destroyed
@github-actions github-actions bot temporarily deployed to demo-DES-884-single-line-header November 1, 2024 15:22 Destroyed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

3 participants