> For the complete documentation index, see [llms.txt](https://analog.groupthought.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://analog.groupthought.com/reference/alpine/header-menu.md).

# header-menu (Analog)

The store and directives behind the desktop nav: which popup is open, and the nav items and popups that open and close it.

The store and directives behind the desktop nav: which popup is open, and the nav items and popups that open and close it.

| Directive / magic                 | What it does                                                                                                                             |
| --------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| `x-header-menu-nav-item="menuId"` | A top-level nav item. Opens its popup on hover, closes it when the pointer leaves                                                        |
| `x-header-menu-nav-item.sublinks` | Marks the item as having sub-links, so it counts as having a popup before any megamenu registers                                         |
| `x-header-menu-nav-item:link`     | A link that toggles the popup instead of navigating (`role="button"`)                                                                    |
| `x-header-menu-nav-item:button`   | The keyboard disclosure button inside the item                                                                                           |
| `x-header-menu-popup="menuId"`    | Registers a flyout popup for that menu id                                                                                                |
| `x-header-menu-popup.dropdown`    | Registers a dropdown popup instead                                                                                                       |
| `$headerMenuNavItem`              | The item's state: `hasMegaMenu`, `hasSubLinks`, `hasPopup`, `hasActivePopup`, `buttonEl`, `openPopup()`, `closePopup()`, `togglePopup()` |
| `$headerMenuPopup`                | The popup's state: `menuId`, `isVisible`, `referenceEl`, `floatTargetEl`, `close()`, `cancelClose()`, `scheduleClose()`                  |
| `$store.headerMenu`               | The global state: `activePopupId`, `activePopupType`, `activePopup`, `open()`, `close()`, `scheduleClose()`, `cancelClose()`             |

One store, `Alpine.store('headerMenu')`, holds the single active popup for the whole header. Hover opens an item's popup after 100ms and schedules a close 200ms after the pointer leaves; hovering the popup itself cancels that close. The link and button carry `aria-haspopup`, `aria-expanded`, and `aria-controls` pointing at `header-menu-popup-<menuId>`. On the keyboard, the down arrow opens the popup and moves focus to its first tabbable element, the up arrow and Escape close it, Escape inside the popup returns focus to the nav item, and tabbing out of the popup closes it and moves focus to the next item in the header. A mousedown outside closes the active popup, and so does scrolling the page while the pointer is off the popup.

## Examples

### A nav item

Use `x-header-menu-nav-item` on the list item, with the link and the keyboard button inside:

```html
<li
  x-header-menu-nav-item.sublinks="{{ menu_id }}"
  data-header-menu-nav-item
  data-menu-id="{{ menu_id }}"
>
  <a href="{{ link.url }}" data-float-target>Shop</a>
  <button x-header-menu-nav-item:button="{{ menu_id }}" aria-label="…">▾</button>
</li>
```

`data-menu-id` is how the store finds the item again: same-titled links share a menu id by design, so lookups match on this attribute instead of element ids. The `data-float-target` element is what a dropdown floats against.

### Showing controls only when a popup exists

Use `$headerMenuNavItem.hasPopup` to hide the disclosure button on plain links:

```html
<li x-show="$headerMenuNavItem.hasPopup">
  <button x-header-menu-nav-item:button="{{ menu_id }}">…</button>
</li>
```

`hasPopup` is true when the item has sub-links (the `.sublinks` modifier) or when a custom megamenu block claims the link after load. The item finds its megamenu by looking up `[data-megamenu="<menuId>"]` once the page is idle.

### A popup panel

Use `x-header-menu-popup` to register a panel with the store:

```html
<div x-header-menu-popup.dropdown="{{ menu_id }}" id="header-menu-popup-{{ menu_id }}">
  …
</div>
```

In Analog, [x-header-dropdown](/reference/alpine/header-dropdown.md) and [x-megamenu:content](/reference/alpine/megamenu.md) bind this directive. Popups register in the store's `popups` Map keyed by menu id, and a later registration for the same id replaces the default panel. A custom megamenu block uses this to claim a link that would otherwise get an auto-generated dropdown or megamenu.

## Customizing

Configure menus, dropdowns, and custom megamenu blocks in the header section. See [Header](/features/header.md). In the theme editor, selecting a megamenu block pins its popup open (`themeEditorSelectedPopupId`) so hover-out and scroll do not close the popup while the merchant edits it.
