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

Improve nav semantics and functionality #73

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
40 changes: 31 additions & 9 deletions blocks/header/header.css
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,11 @@ header nav .nav-sections ul > li > ul > li {
font-weight: 400;
}

header nav .nav-sections .nav-drop-toggle {
display: none;
visibility: hidden;
}

@media (width >= 900px) {
header nav .nav-sections {
display: block;
Expand All @@ -193,28 +198,45 @@ header nav .nav-sections ul > li > ul > li {
}

header nav .nav-sections .nav-drop {
align-items: center;
display: flex;
position: relative;
padding-right: 16px;
cursor: pointer;
}

header nav .nav-sections .nav-drop::after {
header nav .nav-sections button.nav-drop-toggle {
align-items: center;
background-color: transparent;
border-radius: 0;
color: inherit;
display: flex;
flex-direction: column;
height: 24px;
line-height: inherit;
margin: 0 0 0 4px;
padding: 0;
position: relative;
right: 0;
top: 0;
visibility: visible;
width: 24px;
}

header nav .nav-sections .nav-drop-toggle::after {
content: '';
display: inline-block;
position: absolute;
top: 0.5em;
right: 2px;
display: flex;
transform: rotate(135deg);
width: 6px;
height: 6px;
border: 2px solid currentcolor;
border-radius: 0 1px 0 0;
border-width: 2px 2px 0 0;
margin-top: auto;
margin-bottom: auto;
}

header nav .nav-sections .nav-drop[aria-expanded='true']::after {
top: unset;
bottom: 0.5em;
header nav .nav-sections .nav-drop[aria-expanded='true'] .nav-drop-toggle::after {
margin-top: 0.5em;
transform: rotate(315deg);
}

Expand Down
53 changes: 36 additions & 17 deletions blocks/header/header.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,22 +72,19 @@ function toggleMenu(nav, navSections, forceExpanded = null) {
const expanded = forceExpanded !== null ? !forceExpanded : nav.getAttribute('aria-expanded') === 'true';
const button = nav.querySelector('.nav-hamburger button');
document.body.style.overflowY = (expanded || isDesktop.matches) ? '' : 'hidden';
nav.setAttribute('aria-expanded', expanded ? 'false' : 'true');
if (!isDesktop.matches) nav.setAttribute('aria-expanded', expanded ? 'false' : 'true');
toggleAllNavSections(navSections, expanded || isDesktop.matches ? 'false' : 'true');
button.setAttribute('aria-label', expanded ? 'Open navigation' : 'Close navigation');
// enable nav dropdown keyboard accessibility
const navDrops = navSections.querySelectorAll('.nav-drop');
console.log(navDrops);
if (isDesktop.matches) {
navDrops.forEach((drop) => {
if (!drop.hasAttribute('tabindex')) {
drop.setAttribute('tabindex', 0);
drop.addEventListener('focus', focusNavSection);
}
drop.addEventListener('focusin', focusNavSection);
});
} else {
navDrops.forEach((drop) => {
drop.removeAttribute('tabindex');
drop.removeEventListener('focus', focusNavSection);
drop.removeEventListener('focusin', focusNavSection);
});
}

Expand Down Expand Up @@ -168,6 +165,25 @@ async function buildBreadcrumbs() {
return breadcrumbs;
}


/**
* Click event handler for nav dropdowns
* @param {EventListenerObject} e Event object
* @param {Element} navSection The containing nav dropdown section
* @param {Element} navToggleButton The injected toggle button for the current section
* @param {Element} navSections All nav dropdown sections so they don't have to be re-queried
*/
function handleNavDropClick(e, navSection, navToggleButton, navSections) {
if (isDesktop.matches) {
// prevent double event on child button
e.stopPropagation();
const expanded = navSection.getAttribute('aria-expanded') === 'true';
navToggleButton.setAttribute('aria-expanded', expanded ? 'false' : 'true');
toggleAllNavSections(navSections);
navSection.setAttribute('aria-expanded', expanded ? 'false' : 'true');
}
}

/**
* loads and decorates the header, mainly the nav
* @param {Element} block The header block element
Expand Down Expand Up @@ -199,15 +215,18 @@ export default async function decorate(block) {

const navSections = nav.querySelector('.nav-sections');
if (navSections) {
navSections.querySelectorAll(':scope .default-content-wrapper > ul > li').forEach((navSection) => {
if (navSection.querySelector('ul')) navSection.classList.add('nav-drop');
navSection.addEventListener('click', () => {
if (isDesktop.matches) {
const expanded = navSection.getAttribute('aria-expanded') === 'true';
toggleAllNavSections(navSections);
navSection.setAttribute('aria-expanded', expanded ? 'false' : 'true');
}
});
navSections.querySelectorAll(':scope .default-content-wrapper > ul > li').forEach((navSection, index) => {
if (navSection.querySelector('ul')) {
navSection.classList.add('nav-drop');
const navButton = document.createElement('button');
navButton.classList.add('nav-drop-toggle');
navButton.setAttribute('aria-label', `Nav section ${index + 1} toggle`);
navButton.setAttribute('aria-expanded', 'false');
// allow clicking on both nav section and interactive toggle button
navButton.addEventListener('click', (e) => handleNavDropClick(e, navSection, navButton, navSections));
navSection.addEventListener('click', (e) => handleNavDropClick(e, navSection, navButton, navSections));
navSection.appendChild(navButton);
}
});
}

Expand All @@ -227,7 +246,7 @@ export default async function decorate(block) {
</button>`;
hamburger.addEventListener('click', () => toggleMenu(nav, navSections));
nav.prepend(hamburger);
nav.setAttribute('aria-expanded', 'false');
if (!isDesktop.matches) nav.setAttribute('aria-expanded', 'false');
// prevent mobile nav behavior on window resize
toggleMenu(nav, navSections, isDesktop.matches);
isDesktop.addEventListener('change', () => toggleMenu(nav, navSections, isDesktop.matches));
Expand Down