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
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
b8bac3d
Simplify rigourously
VincentSmedinga Jul 15, 2024
e329c9a
Change menu prop from ReactNode to callback function
VincentSmedinga Sep 27, 2024
359edc6
Implement wrapping in two halves
VincentSmedinga Sep 27, 2024
285a869
Wrap Header and Footer stories in Screen
VincentSmedinga Sep 30, 2024
e4f6df5
Introduce component for buttons in page menu
VincentSmedinga Sep 30, 2024
73e88f9
Use Page Menu in Header (incorrectly for now)
VincentSmedinga Sep 30, 2024
29bb49f
Allow icon in Page Menu Button
VincentSmedinga Sep 30, 2024
834e72c
Allow Grid to render a different HTML element
VincentSmedinga Sep 30, 2024
c6efcef
Make Header render its own Grid
VincentSmedinga Sep 30, 2024
e6e5cf2
Make Leader render its children
VincentSmedinga Sep 30, 2024
1a28c0c
Include menu button in Page Menu
VincentSmedinga Sep 30, 2024
7fa2903
Rename `links` prop to `menu`
VincentSmedinga Sep 30, 2024
0615ff6
Update homepage example
VincentSmedinga Sep 30, 2024
9f69f31
Make Grid a decorator in Mega Menu stories for easier reuse
VincentSmedinga Sep 30, 2024
c8f8f91
Simplify white space in Header
VincentSmedinga Sep 30, 2024
ca7fa54
Allow logo to grow a bit in wide windows
VincentSmedinga Sep 30, 2024
6432adc
Hide secondary page menu items in narrow windows
VincentSmedinga Sep 30, 2024
5f547ec
Improve spacing in page menu
VincentSmedinga Sep 30, 2024
63b43c7
Merge branch 'develop' into poc/DES-884-single-line-header
VincentSmedinga Sep 30, 2024
9e9fd4b
Remove obsolete class name
VincentSmedinga Sep 30, 2024
f2f2796
Prefer class name over pseudo-selector
VincentSmedinga Sep 30, 2024
30fcd0d
Merge branch 'develop' into poc/DES-884-single-line-header
VincentSmedinga Oct 1, 2024
4664490
Use Text Level 5 for application name
VincentSmedinga Oct 1, 2024
89a8b10
Remove prop that prevents a Page Menu to wrap
VincentSmedinga Oct 1, 2024
b1f2dd6
Revert Header rendering its own Grid
VincentSmedinga Oct 2, 2024
8220373
Extract Item component for Page Menu
VincentSmedinga Oct 2, 2024
d131cee
Implement end-alignment on Page Menu alone
VincentSmedinga Oct 2, 2024
bfc1d5a
Remove beta warnings from Header, Mega Menu and Page Menu
VincentSmedinga Oct 1, 2024
bd11475
WIP Docs
VincentSmedinga Oct 2, 2024
54957de
Fix things
VincentSmedinga Oct 2, 2024
0798488
Use `any` to type ref for polymorphic components
VincentSmedinga Oct 2, 2024
f03164d
Finetune space lengths
VincentSmedinga Oct 3, 2024
a855c31
Fix gap token name for page menu item
VincentSmedinga Oct 3, 2024
41f6441
Rename mixin
VincentSmedinga Oct 3, 2024
aa53f36
Decrease line height for application name Heading
VincentSmedinga Oct 3, 2024
91c22ae
Revert "Make Grid a decorator in Mega Menu stories for easier reuse"
VincentSmedinga Oct 3, 2024
38d63cb
Merge branch 'develop' into poc/DES-884-single-line-header
VincentSmedinga Oct 3, 2024
12be155
Make Header render a header element
VincentSmedinga Oct 3, 2024
a34fc1b
Make Mega Menu render a nav element
VincentSmedinga Oct 3, 2024
f705b23
Let Grid and Grid Cell render any structural tag
VincentSmedinga Oct 7, 2024
a669021
Fix type import
VincentSmedinga Oct 7, 2024
8a64d5b
Simplify mega menu stories
VincentSmedinga Oct 8, 2024
32b2069
Deprecate Mega Menu component
VincentSmedinga Oct 8, 2024
b62fd4c
Implement Mega Menu as pattern in Amsterdam home page
VincentSmedinga Oct 8, 2024
b379529
Merge branch 'develop' into poc/DES-884-single-line-header
VincentSmedinga Oct 8, 2024
8ecf069
Fix empty class name
VincentSmedinga Oct 8, 2024
e6cdd94
Remove unnecessary keys from example
VincentSmedinga Oct 8, 2024
9657743
Implement hack to wrap all main navigation in one nav element
alimpens Oct 23, 2024
6891717
Finetune poc
alimpens Oct 23, 2024
3e20118
Add padding
alimpens Oct 23, 2024
5685c69
Add toggle button
alimpens Oct 23, 2024
0101037
Break up menu in subcomponents
alimpens Nov 1, 2024
fbfd1c1
Remove unused state
alimpens Nov 1, 2024
10511d5
Merge item and link
alimpens Nov 1, 2024
dd1c504
Move styling from inline to css
alimpens Nov 1, 2024
b5df56d
Cleanup, add MegaMenuSecondaryLinkList
alimpens Nov 1, 2024
9d595cd
Add minimal version for headers without mega menu
alimpens Nov 2, 2024
a16e9af
Fix stories
alimpens Nov 4, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 11 additions & 13 deletions packages/css/src/components/header/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,20 @@

# Header

Displays the City’s logo at the top of every page, and optionally a navigation menu.
Includes the name of the application if it is not the general website.
Establishes the City of Amsterdam as the official source of the website or application.
Contains the main and secondary navigation and optionally a site-wide title.

## Guidelines
## How to use

- The Header must be used on all websites and applications for the City of Amsterdam.
- It includes the logo of the City or the organization, the site title (except for the general website), and a menu with links to commonly used pages.
- The Header is important because it conveys our corporate identity and is the first thing people see.
Using it consistently helps users recognize and trust the website.
- It is the same on every page of the application.
- The page menu can contain a maximum of 5 items.
The last two will often be ‘Search’ and ‘Menu’.
- Labels should be short to ensure the menu fits on one line, even on medium-wide screens.
- An icon can be added to the end of a link to make its function easier to find.
The Header must be used on all websites and applications for the City of Amsterdam.
One objective of the Header is to convey our corporate identity.
Using it consistently helps users recognize and trust our websites.
Within a website, the Header must be the same on every page.

Both navigation menus in the Header support variations in the amount and grouping of links to accommodate websites of varying sizes and information architectures.
See [Mega Menu](https://designsystem.amsterdam/?path=/docs/components-navigation-mega-menu--docs) and [Page Menu](https://designsystem.amsterdam/?path=/docs/components-navigation-page-menu--docs) for more guidelines.

## References

- A Header is a [landmark](https://www.w3.org/TR/wai-aria-practices-1.1/#aria_landmark_roles) and can be use to group navigation elements.
- A Header is a [landmark](https://www.w3.org/TR/wai-aria-practices-1.1/#aria_landmark_roles) and can be used to group navigation elements.
- [WCAG 3.2.3](https://wcag.com/designers/3-2-3-consistent-navigation/) Consistent Navigation: Navigation menus that appear on multiple pages are consistent.
135 changes: 79 additions & 56 deletions packages/css/src/components/header/header.scss
Original file line number Diff line number Diff line change
Expand Up @@ -4,83 +4,106 @@
*/

@import "../../common/breakpoint";
@import "../page-menu/page-menu";

.ams-header {
align-items: center;
column-gap: var(--ams-header-column-gap);
display: flex;
flex-wrap: wrap;
padding-block: var(--ams-header-padding-block);
row-gap: 1.5rem;
row-gap: var(--ams-header-row-gap);
}

@media screen and (min-width: $ams-breakpoint-wide) {
column-gap: var(--ams-header-column-gap);
flex-wrap: nowrap;
}
.ams-header--has-mega-menu {
display: grid;
}

.ams-header__section {
align-items: center;
align-self: start;
column-gap: var(--ams-header-section-column-gap);
display: flex;
grid-column: 1;
grid-row: 1;
padding-inline-start: var(--ams-grid-padding-inline);
}

.ams-header__logo-link {
flex: none;
outline-offset: var(--ams-header-logo-link-outline-offset);
}

.ams-header__links {
display: none;
/* TODO Remove after updating Header line heights in DES-973. */
.ams-heading.ams-header__app-name {
line-height: 1.25;
}

@media screen and (min-width: $ams-breakpoint-medium) {
display: block;
flex: 10 0 auto;
}
.ams-mega-menu-navigation {
column-gap: var(--ams-header-column-gap);
display: flex;
flex-wrap: wrap;
grid-column: 1;
grid-row: 1;
inset-block-start: 0;
pointer-events: none;
row-gap: var(--ams-header-row-gap);
}

@media screen and (min-width: $ams-breakpoint-wide) {
order: 3;
}
.ams-header__navigation-lite {
align-items: center;
display: flex;
margin-inline-start: auto;
}

@mixin reset-list {
box-sizing: border-box;
margin-block: 0;
padding-inline-start: 0;
}

.ams-header__menu {
flex: 1;
padding-inline-start: var(--ams-page-menu-column-gap); // TODO Don’t use tokens of another component
text-align: end;
align-items: center;
column-gap: var(--ams-page-menu-column-gap);
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: flex-end;
list-style: none;
margin-inline-start: auto;
padding-inline-end: var(--ams-grid-padding-inline);
pointer-events: auto;
row-gap: var(--ams-page-menu-row-gap);

@include reset-list;
}

@mixin reset-item {
box-sizing: border-box;
}

@media screen and (min-width: $ams-breakpoint-wide) {
order: 4;
padding-inline-start: 0;
.ams-header__menu-item--secondary {
@media screen and (not (min-width: $ams-breakpoint-medium)) {
display: none;
}
}

.ams-header__app-name {
flex: 1 1 100%;

@media screen and (min-width: $ams-breakpoint-wide) {
min-inline-size: 0;
order: 2;

.ams-header__app-name-heading {
display: block;
inline-size: 100%;
line-height: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.ams-header__menu-link {
@include ams-page-menu-element;

&:hover {
text-decoration-line: var(--ams-page-menu-item-hover-text-decoration-line);
}
}

// Temporary – will move to Mega Menu and/or Icon Button
.ams-header__menu-button {
background-color: transparent;
background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'><path fill='%23004699' fill-rule='evenodd' d='M0 3.238h32V7.81H0V3.238zm0 10.476h32v4.572H0v-4.572zM0 24.19h32v4.572H0V24.19z'/></svg>");
background-position: center right;
background-repeat: no-repeat;
background-size: 1.1875rem 1.1875rem;
border: 0;
color: var(--ams-page-menu-item-color);
font-family: var(--ams-page-menu-item-font-family);
font-size: var(--ams-page-menu-item-font-size);
font-weight: var(--ams-page-menu-item-font-weight);
line-height: var(--ams-page-menu-item-line-height);
margin-block: 0;
margin-inline: 0;
padding-inline: 0 1.875rem;
text-align: center;
touch-action: manipulation;
.ams-mega-menu {
inline-size: 100%;
pointer-events: auto;
}

.ams-mega-menu--closed.ams-mega-menu--closed {
display: none;
}

.ams-mega-menu__secondary-link-list {
@media screen and (min-width: $ams-breakpoint-medium) {
display: none;
}
}
1 change: 1 addition & 0 deletions packages/css/src/components/logo/logo.scss
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
.ams-logo {
block-size: var(--ams-logo-block-size);
display: block;
min-block-size: var(--ams-logo-min-block-size);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shall we try a container query to show just the crosses on narrow screens?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds interesting, but what would you want the container query to test that a ‘regular’ window width query can’t? Is it to consider the presence of an app title?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The header has a max width so it would make sense I suppose. You could use a :has() selector in combination with a container (or normal) query to hide the logo text. So probably not in this file but that defies the purpose of having isolated components, so targeting logo in the header CSS is wrong but needed? That's probably why it should be a pattern and not a component?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let’s do the narrow logo in a separate PR.

}

.ams-logo__emblem {
Expand Down
7 changes: 4 additions & 3 deletions packages/css/src/components/page-menu/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@ A small set of links for use in the Header and Footer.
## Guidelines

- Limit the amount of menu items in the Header to 5 items or less, including ‘Search’ and ‘Menu’.
- The menu should fit on a single line and is right-aligned.
- The menu in the footer is left-aligned.
- The menu in the Header aligns to the end; the one in the Footer aligns to the start.
- Submenus are not allowed.
- The ‘Menu’ button opens the Mega Menu.
- The ‘Menu’ button in the Header opens the Mega Menu.
- On narrow screens, menu items other than ‘Search’ and ‘Menu’ move from the Page Menu to the Mega Menu.
- Labels should be short so that the menu wraps to multiple lines only if necessary.
- If a very well-known icon exists for the function of a link or button, it can be added at the end of it.

### Using links with routing libraries

Expand Down
55 changes: 41 additions & 14 deletions packages/css/src/components/page-menu/page-menu.scss
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
* Copyright Gemeente Amsterdam
*/

@import "../../common/breakpoint";
@import "../../common/text-rendering";

@mixin reset-list {
Expand All @@ -15,6 +16,17 @@
box-sizing: border-box;
}

@mixin reset-button {
dlnr marked this conversation as resolved.
Show resolved Hide resolved
background: none;
border: 0;
margin-block: 0; // [1]
margin-inline: 0; // [1]
padding-block: 0;
padding-inline: 0;

// [1] Remove the margin in older Safari.
}

.ams-page-menu {
align-items: center;
column-gap: var(--ams-page-menu-column-gap);
Expand All @@ -28,21 +40,26 @@
}

.ams-page-menu--align-end {
justify-content: end;
justify-content: flex-end;
margin-inline-start: auto;
}

.ams-page-menu--no-wrap {
flex-wrap: nowrap;
.ams-page-menu__item {
&--secondary {
@media screen and (not (min-width: $ams-breakpoint-medium)) {
display: none;
}
}
}

@mixin page-menu-item {
@mixin ams-page-menu-element {
color: var(--ams-page-menu-item-color);
column-gap: var(--ams-page-menu-item-column-gap);
display: inline-flex;
flex-direction: row;
font-family: var(--ams-page-menu-item-font-family);
font-size: var(--ams-page-menu-item-font-size);
font-weight: var(--ams-page-menu-item-font-weight);
gap: var(--ams-page-menu-item-gap);
line-height: var(--ams-page-menu-item-line-height);
outline-offset: var(--ams-page-menu-item-outline-offset);
text-align: center;
Expand All @@ -51,20 +68,30 @@
text-underline-offset: var(--ams-page-menu-item-text-underline-offset);
touch-action: manipulation;
white-space: nowrap;

@include text-rendering;

&:hover {
color: var(--ams-page-menu-item-hover-color);
}

svg {
color: currentColor;
}
}

.ams-page-menu__link {
@include page-menu-item;
@include text-rendering;
@include ams-page-menu-element;
@include reset-item;
}

.ams-page-menu__link:hover,
.ams-page-menu__button:hover {
color: var(--ams-page-menu-item-hover-color);
text-decoration-line: var(--ams-page-menu-item-hover-text-decoration-line);
&:hover {
text-decoration-line: var(--ams-page-menu-item-hover-text-decoration-line);
}
}

.ams-page-menu__link svg {
color: currentColor;
.ams-page-menu__button {
cursor: var(--ams-page-menu-button-cursor);

@include ams-page-menu-element;
@include reset-button;
}
2 changes: 1 addition & 1 deletion packages/react/src/Breakout/BreakoutCell.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ describe('Breakout cell', () => {
})

it('supports ForwardRef in React', () => {
const ref = createRef<HTMLDivElement>()
const ref = createRef<HTMLElement>()

const { container } = render(<Breakout.Cell ref={ref} />)

Expand Down
2 changes: 1 addition & 1 deletion packages/react/src/Column/Column.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ describe('Column', () => {
})

it('supports ForwardRef in React', () => {
const ref = createRef<HTMLDivElement>()
const ref = createRef<HTMLElement>()

const { container } = render(<Column ref={ref} />)

Expand Down
22 changes: 19 additions & 3 deletions packages/react/src/Grid/Grid.test.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { render } from '@testing-library/react'
import { render, screen } from '@testing-library/react'
import { createRef } from 'react'
import { Grid } from './Grid'
import { Grid, gridTags } from './Grid'
import type { GridPaddingSize } from './Grid'
import { ariaRoleForTag } from '../common/accessibility'
import '@testing-library/jest-dom'

const paddingSizes = ['small', 'medium', 'large']
Expand Down Expand Up @@ -87,8 +88,23 @@ describe('Grid', () => {
})
})

gridTags.forEach((tag) => {
it(`renders with a custom ${tag} tag`, () => {
const { container } = render(<Grid as={tag} aria-label={tag === 'section' ? 'Accessible name' : undefined} />)

let component: HTMLElement | null
if (tag === 'div') {
component = container.querySelector(tag)
} else {
component = screen.queryByRole(ariaRoleForTag[tag])
}

expect(component).toBeInTheDocument()
})
})

it('supports ForwardRef in React', () => {
const ref = createRef<HTMLDivElement>()
const ref = createRef<HTMLElement>()

const { container } = render(<Grid ref={ref} />)

Expand Down
Loading