Skip to content

fix(ui): make mobile nav toggle keyboard accessible#8801

Open
abhijeetnardele24-hash wants to merge 1 commit intonodejs:mainfrom
abhijeetnardele24-hash:fix/navbar-toggle-accessibility
Open

fix(ui): make mobile nav toggle keyboard accessible#8801
abhijeetnardele24-hash wants to merge 1 commit intonodejs:mainfrom
abhijeetnardele24-hash:fix/navbar-toggle-accessibility

Conversation

@abhijeetnardele24-hash
Copy link
Copy Markdown

Description

Replaces the mobile navigation toggle's hidden-checkbox + label pattern with a real button so the control is focusable and exposed with the correct button semantics to keyboard and assistive technology users.

Changes

  • replace the Label-driven mobile toggle with a native button
  • add aria-controls and aria-expanded to the toggle
  • remove the now-unused hidden checkbox styling
  • add a focused NavBar test covering the accessible toggle state

Validation

  • node --enable-source-maps --import=global-jsdom/register --import=tsx --import=../../tests/setup.mjs --test src/Containers/NavBar/__tests__/index.test.jsx
  • corepack pnpm exec prettier --check packages/ui-components/src/Containers/NavBar/index.tsx packages/ui-components/src/Containers/NavBar/index.module.css packages/ui-components/src/Containers/NavBar/__tests__/index.test.jsx

Fixes #7895

@abhijeetnardele24-hash abhijeetnardele24-hash requested a review from a team as a code owner April 5, 2026 07:34
Copilot AI review requested due to automatic review settings April 5, 2026 07:34
@vercel
Copy link
Copy Markdown

vercel bot commented Apr 5, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
nodejs-org Ready Ready Preview Apr 5, 2026 7:35am

Request Review

@cursor
Copy link
Copy Markdown

cursor bot commented Apr 5, 2026

PR Summary

Low Risk
Low risk: small, localized UI/accessibility change to the NavBar toggle with a new test; main risk is minor behavioral/styling regressions in the mobile menu open/close state.

Overview
Updates NavBar’s mobile menu toggle to use a native button (instead of the hidden checkbox + label pattern), adding aria-controls/aria-expanded and toggling menu visibility via React state.

Removes the now-unused hidden checkbox styling and adds a focused test to assert correct button semantics and open/close behavior of the mobile navigation.

Reviewed by Cursor Bugbot for commit f4e9cb3. Bugbot is set up for automated code reviews on this repo. Configure here.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 5, 2026

👋 Codeowner Review Request

The following codeowners have been identified for the changed files:

Team reviewers: @nodejs/nodejs-website

Please review the changes when you have a chance. Thank you! 🙏

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR updates the NavBar component in packages/ui-components to make the mobile navigation toggle properly keyboard accessible and semantically correct by replacing the hidden checkbox + label approach with a native <button> and adding ARIA state.

Changes:

  • Replace the label/checkbox toggle with a native button that controls menu open/close state via React state.
  • Add aria-controls and aria-expanded to reflect the controlled region and current open/closed state.
  • Remove the now-unused hidden checkbox styling and add a new NavBar test for toggle state.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

File Description
packages/ui-components/src/Containers/NavBar/index.tsx Swap hidden-checkbox toggle for a real button and toggle menu visibility via state + ARIA.
packages/ui-components/src/Containers/NavBar/index.module.css Remove checkbox styles and adjust toggle styling for a button element.
packages/ui-components/src/Containers/NavBar/tests/index.test.jsx Add a test asserting aria-expanded and visibility classes during toggling.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +57 to +72
<button
className={styles.sidebarItemTogglerLabel}
htmlFor="sidebarItemToggler"
role="button"
type="button"
aria-label={sidebarItemTogglerAriaLabel}
aria-controls="navbar-navigation"
aria-expanded={isMenuOpen}
onClick={() => setIsMenuOpen(previousState => !previousState)}
>
{navInteractionIcons[isMenuOpen ? 'close' : 'show']}
</Label.Root>
</button>
</div>

<input
className={classNames('peer', styles.sidebarItemToggler)}
id="sidebarItemToggler"
type="checkbox"
onChange={e => setIsMenuOpen(() => e.target.checked)}
aria-label={sidebarItemTogglerAriaLabel}
tabIndex={-1}
/>
<div className={classNames(styles.main, `hidden peer-checked:flex`)}>
<div
id="navbar-navigation"
className={classNames(styles.main, isMenuOpen ? 'flex' : 'hidden')}
>
Copy link

Copilot AI Apr 5, 2026

Choose a reason for hiding this comment

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

aria-controls points at a hard-coded id (navbar-navigation). If multiple NavBar instances render on the same page (e.g., in Storybook/docs), this creates duplicate IDs and breaks the aria-controls relationship. Consider generating a unique id per component instance (e.g., via React useId) and wiring both aria-controls and the controlled element's id to that value.

Copilot uses AI. Check for mistakes.
Comment on lines +57 to +66
<button
className={styles.sidebarItemTogglerLabel}
htmlFor="sidebarItemToggler"
role="button"
type="button"
aria-label={sidebarItemTogglerAriaLabel}
aria-controls="navbar-navigation"
aria-expanded={isMenuOpen}
onClick={() => setIsMenuOpen(previousState => !previousState)}
>
{navInteractionIcons[isMenuOpen ? 'close' : 'show']}
</Label.Root>
</button>
Copy link

Copilot AI Apr 5, 2026

Choose a reason for hiding this comment

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

The CSS module class name sidebarItemTogglerLabel is now applied to a <button>, which makes the naming misleading and harder to maintain (it’s no longer a label). Consider renaming the class (and corresponding usages) to reflect the new semantics (e.g., sidebarItemTogglerButton).

Copilot uses AI. Check for mistakes.
Comment on lines +44 to +49
await user.click(menuButton);

assert.equal(menuButton.getAttribute('aria-expanded'), 'true');
assert.match(navigation.className, /\bflex\b/);

await user.click(menuButton);
Copy link

Copilot AI Apr 5, 2026

Choose a reason for hiding this comment

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

This test claims to validate keyboard accessibility, but it only uses user.click(). To actually cover the reported issue, exercise keyboard interaction (e.g., user.tab() to focus the toggle, then user.keyboard('{Enter}') / '{Space}') and assert aria-expanded / visibility changes.

Suggested change
await user.click(menuButton);
assert.equal(menuButton.getAttribute('aria-expanded'), 'true');
assert.match(navigation.className, /\bflex\b/);
await user.click(menuButton);
await user.tab();
assert.equal(document.activeElement, menuButton);
await user.keyboard('{Enter}');
assert.equal(menuButton.getAttribute('aria-expanded'), 'true');
assert.match(navigation.className, /\bflex\b/);
await user.keyboard(' ');

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This test seems unnecessary

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

The toggle navigation menu button is not keyboard accessible

3 participants