Menu
A lightweight, accessible navigation and content system built using the native HTML <details>
and <summary>
elements.
Enhances the default behavior with JavaScript for better usability, keyboard support, and ARIA state management — without relying on external libraries.
- Native
<details>
used for dropdown menus in navigation and content. - Automatic closing of
<details>
when clicking outside a navigation area. - Keyboard support: pressing
Escape
closes the currently focused<details>
. - ARIA attributes (
aria-expanded
) synchronized with open/close state. <details>
elements can be grouped with a shareddata-group
attribute for mutually exclusive behavior (opening one closes the others), supported in all major browsers.- Responsive hamburger menu with accessible ARIA controls.
- Modern CSS Anchor Positioning used for aligning dropdown menus.
Built with vanilla ES6 JavaScript, focusing on modern syntax and browser APIs.
The JavaScript has been split into separate modules, improving code modularity:
details.js
: Handles<details>
accessibility and behavior.- Syncs
aria-expanded
between<summary>
and<details>
. - Closes dropdowns when clicking outside navigation.
- Supports closing via the
Escape
key and restores focus. - Implements grouped accordion behavior using the
data-group
attribute: when one<details>
in a group opens, any others in the same group automatically close.
- Syncs
hamburger-button.js
: Toggles the mobile navigation menu.- Updates
aria-expanded
on the button. - Switches between hidden/visible menu states.
- Updates the visually hidden text for screen readers.
- Updates
loader.js
: See Loader Git repositorytheme.js
: Handles theme toggling (light/dark mode) and local storage management.
- Anchor Positioning (
position-anchor
) is used to align dropdown menus relative to their<summary>
.- In browsers without Anchor Positioning support (e.g., Firefox), the layout gracefully falls back to a standard flow layout, preserving an unbroken user experience.
- Uses media query range syntax for responsive breakpoints.
- Mobile-first design, with progressive enhancements for larger viewports.
- Dropdown menus adapt to available space without JavaScript intervention.
- ARIA attributes (
aria-expanded
) dynamically updated on all<summary>
elements. - Escape key support: closes the open
<details>
and returns focus to its<summary>
. - Clicking outside a
<nav>
closes only its dropdown menus. - Mobile hamburger menu includes
aria-controls
andaria-expanded
for screen reader support. - Semantic HTML structure (
<header>
,<nav>
,<main>
,<details>
,<summary>
) aids navigation for assistive technologies.
If JavaScript is disabled, the loader animation, theme-toggler and mobile menu cease to function. However, all content remains accessible.
As of September 2025, Caniuse reports that the following browsers/platforms support anchor positioning:
- Chrome v.140
- Edge v.140
- Opera v.122
- Chrome for Android v.139
- Safari on IOS v.26
- Android Browser v.139
Caniuse reports that "All major browser engines are working on implementing this spec".
Note: unsupported browsers/platforms will display the default details
behaviour.
The anchor positioning code for the main navigation comes from a Codepen by Ryan Trimble. My only changes were:
- Changing IDs to classes
- Adding
position-try-fallbacks: flip-block, flip-inline
to the target's::details-content
- Making both targets
position-area: block-end center
The application includes a dark mode and light mode toggle:
- The current theme state is stored in local storage and applied automatically on page reload.
- Accessible buttons with appropriate ARIA attributes are used to improve usability.
Important
Remember to change const LOCAL_STORAGE_PREFIX
in js-modules/theme.js
to a unique identifier.
The application has been tested on the following platforms and browsers:
- Operating System: Windows 10
- Browsers:
- Google Chrome (full support, including Anchor Positioning)
- Mozilla Firefox (no Anchor Positioning support yet, but layout gracefully falls back to default
<details>
behavior) - Microsoft Edge (full support, including Anchor Positioning)
The layout and functionality have been verified in both browser and device simulation views to ensure responsiveness and usability.
- Clone or download the repository to your local machine.
- Open the project folder and start a simple HTTP server (e.g., using
Live Server
in VS Code or Python'shttp.server
module). - Open the project in a modern browser (e.g., Chrome, Firefox, or Edge).
If you want to deploy a minified version of this project to GitHub Pages, read on.
Run this once in your project root to install dev dependencies:
npm install
Important
Any assets not described in package.json
must be added. In the current project we don't have an img
folder. If you create one and add images to it, you have to add this to copy:assets
, e.g.
"copy:assets": "shx cp -r site.webmanifest favicon.ico favicon-16x16.png favicon-32x32.png apple-touch-icon.png android-chrome-192x192.png android-chrome-512x512.png docs/",
"copy:assets": "shx cp -r img site.webmanifest favicon.ico favicon-16x16.png favicon-32x32.png apple-touch-icon.png android-chrome-192x192.png android-chrome-512x512.png docs/",
etc, etc.
Then in the terminal, run:
npm run build
Once you've created a repository and pushed the files,
- go to
https://github.com/[your-name]/[your-project-name]/settings/pages
. - Under "Build and deployment > Branch" make sure you set the branch to
main
and folder to/docs
. - Click "Save".
Note
For a detailed description of the build process, configuration files and npm packages see my GitHub Pages Optimised Build.