Skip to content

Navigation menu

hc-navmenu is a top-level site navigation whose items can open content panels (a mega-menu). It is a disclosure set: each trigger is a button[aria-expanded] controlling a popover panel anchored under it with CSS Anchor Positioning. Panels open on hover / focus, one at a time, and the links inside stay real <a> — so it works for plain MPA navigation and htmx alike.

import { installNavmenu } from '@hypermedia-components/core';
installNavmenu();

A trigger gets aria-haspopup / aria-expanded / aria-controls wired automatically; its panel becomes a popover anchored beneath it.

  • Hover / focus opens a panel after a short intent delay and closes it after a brief grace period (so moving the pointer from the trigger into the panel doesn’t dismiss it). Only one panel is open at a time.
  • Keyboard: / open the panel and move focus to the first / last item inside; Esc closes it and returns focus to the trigger; Tab moves through triggers and panel links normally.
  • Links stay links. The behavior never intercepts clicks on <a> inside a panel — navigation is the browser’s (or htmx’s, if the link carries data-hx-*).

On narrow screens a horizontal mega-menu doesn’t fit. Because the panels are plain markup, the same content degrades well: collapse the bar behind a toggle (e.g. inside an hc-drawer) and let each section render as a stacked hc-collapsible. Switch layouts with a media query — the links and structure are identical, so no duplicate markup is required.

  • Wrap the menu in a <nav> with an aria-label; each panel trigger is a real <button> exposing aria-expanded and aria-controls.
  • Panels are popovers, so Esc and outside-click dismissal come from the platform; focus returns to the trigger on Esc.
  • Keep panel contents as real links / buttons in source order so they are reachable by Tab and announced normally.
  • Anchor Positioning is Baseline 2026; where it is missing, installNavmenu() falls back to JS positioning (the panel still opens and dismisses correctly everywhere popover is supported).
Token pathPurpose
navmenu.gapGap between top items.
navmenu.trigger-padding-x / -yTrigger / top-link padding.
navmenu.trigger-radiusTrigger / top-link radius.
navmenu.trigger-fgTrigger text color.
navmenu.trigger-hover-bgTrigger hover / open background.
navmenu.panel-bg / -fg / -borderPanel surface.
navmenu.panel-radius / -padding / -min-widthPanel box.
navmenu.panel-offsetGap between trigger and panel.
navmenu.link-padding-x / -y / -radiusPanel link box.
navmenu.link-fg / -hover-bgPanel link colors.
Show the generated CSS variables
--hc-navmenu-gap | -trigger-padding-x | -trigger-padding-y | -trigger-radius
--hc-navmenu-trigger-fg | -trigger-hover-bg
--hc-navmenu-panel-bg | -panel-fg | -panel-border | -panel-radius | -panel-padding | -panel-min-width | -panel-offset
--hc-navmenu-link-padding-x | -link-padding-y | -link-radius | -link-fg | -link-hover-bg
  • Menubar — application menu bar (actions), not site navigation.
  • Menu — a single dropdown action menu.
  • Drawer — a common host for the mobile version of a site nav.