diff --git a/blocks/header/header.css b/blocks/header/header.css index 56baf1a..fba2b02 100644 --- a/blocks/header/header.css +++ b/blocks/header/header.css @@ -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; @@ -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); } diff --git a/blocks/header/header.js b/blocks/header/header.js index f6771de..ad73bf7 100644 --- a/blocks/header/header.js +++ b/blocks/header/header.js @@ -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); }); } @@ -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 @@ -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); + } }); } @@ -227,7 +246,7 @@ export default async function decorate(block) { `; 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));