# xpl-top-nav

Top navigation shell for brand, primary navigation, right-side actions, and an optional account menu.

## Usage

Use slots only for layout content. The component provides the internal wrappers for the right rail and user menu. The right side renders `right-aside` then the user menu (built-in trigger + panel). `left-aside` is intended for `xpl-top-nav-item` links (each item should use **`slot="left-aside"`** so they are direct children of the internal **`role="list"`** — avoid wrapping them in a single container with that slot). Set the **`child-account-label`** attribute (or **`childAccountLabel`** in React) for the built-in menu trigger label beside the chevron; use **`slot="user-menu-trigger"`** to replace that trigger entirely. If `brand-href` is set, the brand uses native link navigation. If not, the component emits `brandClick`.

### Web component (typical layout)

```html
<xpl-top-nav
  brand-name="My App"
  brand-href="/"
  label="Main navigation"
  child-account-label="Account"
  nav-control
>
  <xpl-top-nav-item slot="left-aside" label="Home" href="/" icon="house-4"></xpl-top-nav-item>
  <xpl-top-nav-item slot="left-aside" label="Reports" href="/reports"></xpl-top-nav-item>

  <xpl-button slot="right-aside" variant="secondary">Help</xpl-button>
  <xpl-nav-header-menu slot="user-menu" username="Alex User" email="alex@example.com">
    <xpl-top-nav-item slot="right-aside-mobile" label="Help" href="/help"></xpl-top-nav-item>
    <xpl-top-nav-item label="Profile" href="/profile" icon="person-1"></xpl-top-nav-item>
    <xpl-top-nav-item label="Sign out" href="/logout" icon="open-rect-arrow-out"></xpl-top-nav-item>
  </xpl-nav-header-menu>
</xpl-top-nav>
```

### Logo from URL and SPA brand (no `brand-href`)

```html
<xpl-top-nav brand-logo="https://example.com/logo.svg" brand-name="Apollo">
  <xpl-top-nav-item slot="left-aside" label="Dashboard" href="#"></xpl-top-nav-item>
</xpl-top-nav>
```

Listen for `brandClick` on the host when `brand-href` is omitted.

### Plain HTML (static documentation)

Use the same tags in any HTML page that loads Apollo styles and the component bundle. Prefer slots and `xpl-top-nav-item` rather than copying internal BEM classes.

## Responsive behavior

- `nav-control` enables the mobile drawer toggle. The control is only visible on **narrow viewports** (see **`top-nav.css`**: one pixel below Tailwind **`md`**; hidden from **`md`** / **768px** width upward at default Tailwind).
- Below **`md`**, **`slot="left-aside"`** (`.xpl-nav-left`) is **hidden** unless **`nav-control`** is set **and** the drawer is open (`xpl-top-nav--nav-control` + `xpl-top-nav--open`). **Without `nav-control` there is no mobile drawer** — primary links are not shown in the narrow bar (duplicate into **`xpl-nav-header-menu`** or widen the viewport if they must stay reachable).
- Pressing `Escape` closes nested dropdowns first, then closes the mobile drawer if it is still open (when `nav-control` is set). Focus then returns to the hamburger toggle. **Escape** is ignored for closing the drawer while **any** descendant **`xpl-popover`** is open (including the account menu or a consumer-slotted popover in **`left-aside`** / **`right-aside`**).
- Opening the built-in **account** popover while the mobile drawer is open **closes the drawer** and emits **`navOpen(false)`** so the two surfaces do not stack.
- On **narrow viewports**, **`slot="right-aside"`** is hidden in the bar. Duplicate those actions into **`xpl-nav-header-menu`** **`slot="right-aside-mobile"`** (narrow-only strip below profile, above the list) so they stay reachable from the account menu.
- On **`xpl-top-nav-item`**, use **`selected`** for the current page (**`aria-current="page"`**) and optional **`active`** for extra accent styling; see the item readme for modifier details.
- **`slot="dropdown"`** does not implement arrow-key roving focus; put focusable elements inside the panel or treat full “menu” keyboard behavior as a future enhancement.
- **Grouping nav items:** put an **`xpl-top-nav-menu`** in a parent **`xpl-top-nav-item`**'s **`slot="dropdown"`** to render a desktop flyout of **`xpl-top-nav-item`** rows (e.g. grouping by product). The same markup also drives the **mobile cascade** below — see **`xpl-top-nav-menu`**'s readme for the full interaction model (back row, focus management, Escape / drawer-close lifecycle).
- **Mobile cascade:** below **`md`**, tapping a **`left-aside`** parent that has a menu swaps the drawer's Level 1 list for that group's Level 2 rows **in place** (no second panel) — the group's **`xpl-top-nav-menu`** auto-renders a back row (leading `chevron-left` + its `label`) as the first row. Tapping the back row, or pressing **Escape** once, pops back to Level 1; a second **Escape**, or the hamburger toggle, closes the drawer (and resets any still-drilled-in group so the drawer reopens at Level 1). Cascade depth is capped at 2 levels and is mobile-only — the desktop flyout is always a single level of links.
- **Child-location context on small screens:** for a "Change Account" style row (a plain **`xpl-top-nav-item`** you author in **`slot="user-menu"`**), set **`description`** to the current child-location's name so it reads e.g. "Change Account" / "Acme Corp — Downtown Location" on narrow viewports where that context isn't otherwise visible in the bar. This is a content pattern (see the **`TopNavigation`** story), not a separate framework feature — **`description`** already exists on **`xpl-top-nav-item`**.

## Theming

- The host applies the Foundation `dark` scope so the bar and its slotted children resolve dark-mode purpose tokens automatically.
- Floating overlays rendered outside the host, such as teleported popover content, may need their own `dark` class if they should match the shell.

## Icon Mapping

| Location | Icon name | Notes |
| -------- | --------- | ----- |
| Mobile nav toggle (`nav-control`) | `menu` | Shown inside the bar control when `nav-control` is set. |
| Built-in account trigger | `chevron-down` | `xpl-button` `icon` prop (end). |
| Dropdown row (`xpl-top-nav-item` with `slot="dropdown"`) | `chevron-down` | Trailing indicator on the row control. |

Consumer-provided `icon` props on `xpl-top-nav-item` use names from `@xplortech/apollo-icons`.

## Token use

| Area | Token / variable |
| ---- | ---------------- |
| Bar surface | `var(--xpl-background-surface-default)` |
| Bar text (default) | `var(--xpl-text-subdued)` |
| Bar height | `var(--xpl-size-350)` |
| Bar padding | `var(--xpl-space-8)` / `var(--xpl-space-16)` |
| Bottom border | `var(--xpl-border-small)` `var(--xpl-border-default)` |
| Focus ring | `var(--xpl-border-focus)` with `outline-2` / `outline-offset-2` (see `top-nav.css`) |
| Panel / drawer width | `var(--xpl-top-nav-panel-width)` (component custom property in `top-nav.css`) |
| Account popover offset | `var(--xpl-space-16)` below bar (`margin-top` on menu content) |

## Accessibility

- **`nav` landmark:** The `label` prop maps to `aria-label` on the inner `<nav>`. If omitted, it defaults to **`top-navigation-bar`**. Use a unique label when multiple navigation regions exist (e.g. side nav + top nav).
- **Mobile control:** With `nav-control`, the toggle is a native `<button>` with `aria-expanded` reflecting the drawer and an accessible name for open/close.
- **Account menu:** The built-in `xpl-button` trigger sets `aria-haspopup="dialog"` to match the internal `xpl-popover` trigger. Chevron-only mode (no `child-account-label` text) adds `aria-label="Open account menu"`. The popover's internal wrapper `<span>` is hidden via CSS `visibility: hidden` so the `xpl-button` is the **single tab stop** — click and keyboard events still bubble to the popover. Custom triggers via `slot="user-menu-trigger"` should set their own accessible name and `aria-haspopup`.
- **Primary links:** Brand and items use `xpl-top-nav-item` (native `<a>` or `role="button"`) with keyboard activation (Enter / Space where applicable).
- **Dropdown items:** Items with `slot="dropdown"` expose `aria-expanded` on the inner control; **Escape** closes the dropdown (document capture). **Escape** on the shell closes the mobile drawer only after dropdowns and **any** open descendant **`xpl-popover`** are closed (document capture vs bubble ordering). Arrow-key navigation within the dropdown panel is not built in.
- **Icon-only items:** With **`icon`** and no **`label`** or default-slot text, the inner control gets **`aria-label`** (**`item-aria-label`** to override, else **`Navigation item`**).
- **Lists:** `left-aside` is wrapped in `role="list"`; each primary link `xpl-top-nav-item` uses `role="listitem"`. The built-in brand row (`xpl-top-nav__brand`) omits `listitem` because it sits outside that list; `slot="right-aside-mobile"` rows omit it as well. `xpl-nav-header-menu` default-slot rows still use `listitem` (menu list contract).

<!-- Auto Generated Below -->


## Overview

Horizontal top navigation with **`left-aside`** and a **right rail**: **`right-aside`** then **user menu**
(see **`.xpl-top-nav__right-rail`** in `render()`). The account menu uses an internal **`xpl-popover`** (**`bottom-end`**, **`display="menu"`**): built-in trigger uses **`child-account-label`** (prop / attribute) as the label beside a trailing **`chevron-down`** on **`xpl-button`** (**`icon`** / **`iconPosition="end"`**), or **`slot="user-menu-trigger"`** for a custom trigger; panel from **`slot="user-menu"`** (e.g. **`xpl-nav-header-menu`**).
Optional **`nav-control`** and **`navOpen`**. **`left-aside`** uses **`role="list"`** for primary **`xpl-top-nav-item`** rows (**`role="listitem"`** on each item host). The built-in **`xpl-top-nav__brand`** item omits **`listitem`** (it is not a child of that list). **`label`** maps to **`nav`** **`aria-label`**. Opening state is internal (not a prop) — use **`navOpen`** and host **`xpl-top-nav--open`** for styling.

**Escape:** With **`nav-control`**, **Escape** closes the mobile drawer and emits **`navOpen`** **`false`** (skips while **any** descendant **`xpl-popover`** is open — detected from each popover’s inner **`.xpl-popover--is-open`** marker — or when a **`xpl-top-nav-item`** dropdown is open — capture/bubble order). Focus returns to the nav toggle after the drawer closes (**Escape** or second toggle click).
**`xpl-top-nav-item`** **`slot="dropdown"`** uses document **capture** for **Escape** first; the shell uses **bubble**
on **`ownerDocument`** so nested dropdowns close before the drawer.

**Theming:** The host always includes Foundation’s **`dark`** class so **`--xpl-*`** purpose tokens under this subtree
resolve to dark-mode values even when the page root is light. Floating overlays (e.g. **`xpl-popover`** / **`xpl-dropdown`**
content teleported outside the host) do not inherit this scope — add **`dark`** on those panels if they should match.

**Layout:** **`right-aside`** and the user-menu region are wrapped internally (**`.xpl-top-nav__right-lead`**, etc.).
Consumers should not add those BEM classes — slot content only.

**Brand logo:** use **`brand-logo`** (URL) for a built-in **`<img>`**, or **`slot="brand-logo"`** for custom markup (e.g. **`xpl-icon`**, SVG). When the URL prop is set, it takes precedence and the slot is not used for the image. On **narrow viewports** (one pixel below Tailwind **`md`** — see **`top-nav.css`**), **`brand-name`** text is hidden in the bar (logo / slot mark only); keep a meaningful **`alt`** on **`brand-logo`** or accessible slotted mark for context.

**Narrow / mobile** (same breakpoint as above): **`slot="left-aside"`** is **hidden** in the bar until **`nav-control`**
is set **and** the drawer is open (floating panel per Figma). **Without **`nav-control`**, there is no mobile drawer** —
primary links remain hidden below **`md`** until the viewport is wide enough for the horizontal bar. The built-in **brand**
stays in the top bar (**hamburger → brand** when **`nav-control`** is set). **`right-aside`** is hidden in the bar on narrow
viewports (**`top-nav.css`**, one pixel below Tailwind **`md`**); duplicate those actions into **`xpl-nav-header-menu`**
**`slot="right-aside-mobile"`** (below profile, above the list; narrow-only CSS in **`top-nav.css`**) per Figma.

**Account menu vs drawer:** Opening the built-in account **`xpl-popover`** while the mobile drawer is open **closes the drawer**
and emits **`navOpen(false)`** so the two layers do not stack.

## Properties

| Property            | Attribute             | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 | Type      | Default     |
| ------------------- | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------- | ----------- |
| `brandHref`         | `brand-href`          | Destination URL for the brand control when using native navigation (**`<a href>`**). When unset, the brand is a **`role="button"`** control and **`brandClick`** fires on activation instead.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               | `string`  | `undefined` |
| `brandLogo`         | `brand-logo`          | Optional image URL for the brand mark (leading the brand row, before **`brand-name`** text). When set, renders **`<img class="xpl-top-nav__brand-logo">`** and ignores **`slot="brand-logo"`** for the mark. When unset, use **`slot="brand-logo"`** for custom logo content.                                                                                                                                                                                                                                                                                                                                                                                                               | `string`  | `undefined` |
| `brandName`         | `brand-name`          | Visible product / app name next to **`brand-logo`**. Omit for logo-only; provide **`brand-logo`** and/or this string to show the built-in brand row (**`left-aside`** is then primary nav links only).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      | `string`  | `undefined` |
| `childAccountLabel` | `child-account-label` | Label text for the built-in account menu trigger (shown **before** the **`chevron-down`** icon) when **`slot="user-menu"`** is present and **`slot="user-menu-trigger"`** is not used. When empty or whitespace-only, the trigger shows only the chevron and uses **`aria-label="Open account menu"`** on the button.                                                                                                                                                                                                                                                                                                                                                                       | `string`  | `undefined` |
| `label`             | `label`               | Accessible name for the `<nav>` landmark (maps to `aria-label`). Use a unique label when the page has multiple navigation regions (e.g. side nav + top nav). When unset, defaults to `top-navigation-bar`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  | `string`  | `undefined` |
| `navControl`        | `nav-control`         | When true, renders the navigation control (hamburger) for toggling the left slot on small viewports. The control is **hidden from the Tailwind `md` breakpoint up**; collapse/show for **`left-aside`** uses **`max-width: calc(theme('screens.md') - 1px)`** in **`top-nav.css`** (Figma’s 767px at a 16px root and default **`screens.md`**). Reflected to the **`nav-control`** attribute so host updates (framework props) re-run **`@Watch`**: drawer state and document **Escape** handling stay in sync, the host **`xpl-top-nav--nav-control`** class toggles for **`top-nav.css`**, and **`navOpen(false)`** is emitted if **`nav-control`** is cleared while the drawer was open. | `boolean` | `undefined` |


## Events

| Event        | Description                                                                                                                                                                                                                                                          | Type                      |
| ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------- |
| `brandClick` | Emitted when the brand control is activated and **`brandHref`** is **not** set (SPA / in-app navigation). **`detail`** is the **`MouseEvent`** from **`xpl-top-nav-item`** **`navItemClick`**. Not emitted when **`brandHref`** is set (use native link navigation). | `CustomEvent<MouseEvent>` |
| `navOpen`    | Emitted when the mobile nav open state changes: hamburger toggle, or **Escape** closing the drawer (when **`nav-control`** is set and the bar was open). Detail is the new open state (**`true`** = open, **`false`** = closed).                                     | `CustomEvent<boolean>`    |


## Slots

| Slot                  | Description                                                                                                                                                                                                                                                                                                                                                     |
| --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `"brand-logo"`        | Custom brand mark when **`brand-logo`** URL is not set; placed before **`brand-name`** in the brand row.                                                                                                                                                                                                                                                        |
| `"left-aside"`        | Primary navigation (**`xpl-top-nav-item`** links). After the brand on wide viewports; below **`md`**, visible only inside the **`nav-control`** drawer when open (otherwise hidden).                                                                                                                                                                            |
| `"right-aside"`       | Right-side actions before the user menu. Hidden on narrow viewports (**`top-nav.css`** below **`md`**).                                                                                                                                                                                                                                                         |
| `"user-menu"`         | Account popover panel only (e.g. **`xpl-nav-header-menu`** with menu **`xpl-top-nav-item`** children). Trigger is **`slot="user-menu-trigger"`** or the built-in button (**`child-account-label`** text + chevron). Use **`slot="right-aside-mobile"`** on **`xpl-nav-header-menu`** for narrow-only duplicates of **`right-aside`** (below the profile block). |
| `"user-menu-trigger"` | Optional override for the popover trigger. When set, the built-in **`child-account-label`** trigger is ignored.                                                                                                                                                                                                                                                 |


## Dependencies

### Depends on

- [xpl-icon](../xpl-icon)
- [xpl-top-nav-item](xpl-top-nav-item)
- [xpl-popover](../xpl-popover)
- [xpl-button](../xpl-button)

### Graph
```mermaid
graph TD;
  xpl-top-nav --> xpl-icon
  xpl-top-nav --> xpl-top-nav-item
  xpl-top-nav --> xpl-popover
  xpl-top-nav --> xpl-button
  xpl-top-nav-item --> xpl-icon
  xpl-button --> xpl-icon
  style xpl-top-nav fill:#f9f,stroke:#333,stroke-width:4px
```

----------------------------------------------

*Built with [StencilJS](https://stenciljs.com/)*
