# xpl-side-nav-item

Row for [`xpl-side-nav`](../xpl-side-nav): place items in `slot="section"` or `slot="nav-footer"`. Supports **leaf** (button), **expandable** (`has-children`), and **link** (slot-only) modes, plus `selected`, `disabled`, and `hidden`.

## Changes in v2 (breaking)

`xpl-side-nav` is in **beta**. v2 makes the following breaking changes — no compat shims are provided.

### Migration table

| API | v1 (before) | v2 (after) | Action required |
|---|---|---|---|
| Leading icon slot | `slot="icon"` | `slot="leading"` | Rename the `slot` attribute on slotted icon elements |
| Parent leading icon | Not rendered (chevron only) | Rendered alongside the trailing chevron | Pass `icon` prop or `slot="leading"` content to parent rows |
| Trailing slot | Not available | `slot="trailing"` | Add to leaf items for custom trailing content |
| Chevron position | Leading (left side) | Trailing (right side), auto-rendered for parents | No action needed for auto chevron; remove manual chevron markup if any |
| Chevron override | Not possible | `slot="trailing"` content replaces auto chevron on parents | Pass `slot="trailing"` to customize the trailing column on a parent |

### Before / after examples

**Leading icon slot rename:**
```html
<!-- v1 -->
<xpl-side-nav-item label="Dashboard">
  <xpl-icon slot="icon" icon="house-4" size="16"></xpl-icon>
</xpl-side-nav-item>

<!-- v2 -->
<xpl-side-nav-item label="Dashboard">
  <xpl-icon slot="leading" icon="house-4" size="16"></xpl-icon>
</xpl-side-nav-item>
<!-- or use the icon prop shorthand -->
<xpl-side-nav-item label="Dashboard" icon="house-4"></xpl-side-nav-item>
```

**Parent rows now show leading icon + chevron:**
```html
<!-- v1: icon prop was ignored on parents -->
<xpl-side-nav-item has-children label="Settings" icon="gear">...</xpl-side-nav-item>

<!-- v2: icon prop renders in the leading slot; chevron auto-renders in trailing -->
<xpl-side-nav-item has-children label="Settings" icon="gear">...</xpl-side-nav-item>
```

**Adding a trailing icon to a leaf item:**
```html
<!-- v2: slot="trailing" for custom trailing content -->
<xpl-side-nav-item label="Xplor Growth" icon="chart-line-up">
  <xpl-icon slot="trailing" icon="external-link" size="16" aria-hidden="true"></xpl-icon>
</xpl-side-nav-item>
```

---

## Modes

- **Leaf (default)** — Button with optional `label`, `icon` prop / `slot="leading"`, and `slot="trailing"`. Click emits **`navClick`**.
- **Expandable (`has-children`)** — Put nested `xpl-side-nav-item` elements in the **default slot**. Row renders **leading icon** (from `icon` prop or `slot="leading"`) **+ label + auto chevron** in the trailing position (**`chevron-down`** when collapsed, **`chevron-up`** when expanded). The chevron is replaced if `slot="trailing"` content is provided. Click toggles **`expanded`** and emits **`expandedChange`**.
- **Link (`link`)** — **Default slot only**: renders a bare `<slot>` with no inner button. Use when the row must be a real `<a>` element — for framework router components, browser right-click / open-in-new-tab behaviour, or when semantic link vs. button distinction matters for screen readers. For typical in-app navigation the default button mode is simpler. The `label` and `icon` props are ignored in link mode; supply all content in your slotted anchor. **Cannot** be combined with `has-children`.

## Rules

| Topic | Behavior |
|---|---|
| **`link` vs `has-children`** | Mutually exclusive. When **`link`** is true, **`hasChildren`** is always false; if both attributes appear in markup, `has-children` is cleared. |
| **Leading icon** | `icon` prop / `slot="leading"` apply to all item types including parents. Nested sub-items (inside a parent) have the leading column hidden in CSS — use label only. |
| **Trailing chevron** | Auto-rendered for `has-children` items when `slot="trailing"` is empty. Providing `slot="trailing"` content replaces the auto chevron. |
| **Events** | **`navClick`** — leaf button rows. **`expandedChange`** — expandable rows only (detail = new `expanded` boolean). |

## Usage

### Leaf (icon prop)
```html
<xpl-side-nav-item label="Dashboard" icon="house-4" selected></xpl-side-nav-item>
```

### Leaf (slot="leading")
```html
<xpl-side-nav-item label="Dashboard">
  <xpl-icon slot="leading" icon="house-4" size="16"></xpl-icon>
</xpl-side-nav-item>
```

### Leaf with trailing icon
```html
<xpl-side-nav-item label="Xplor Growth" icon="chart-line-up">
  <xpl-icon slot="trailing" icon="external-link" size="16" aria-hidden="true"></xpl-icon>
</xpl-side-nav-item>
```

### Expandable parent (leading icon + auto chevron)
```html
<xpl-side-nav-item has-children label="Settings" icon="gear" expanded>
  <xpl-side-nav-item label="Profile"></xpl-side-nav-item>
  <xpl-side-nav-item label="Billing"></xpl-side-nav-item>
</xpl-side-nav-item>
```

### Link (slot-only)

Wrap the anchor's children in the component CSS classes to match the icon color, label color, and spacing of button-mode items. Avoid whitespace text nodes between flex children — they render as a space character and add an unwanted gap.

```html
<xpl-side-nav-item link>
    <!-- No whitespace between __leading / __label spans and the closing </a> -->
    <a href="/reports" class="xpl-side-nav-item__link"><span class="xpl-side-nav-item__leading"><xpl-icon icon="chart-line" size="16"></xpl-icon></span><span class="xpl-side-nav-item__label">Reports</span></a>
</xpl-side-nav-item>
```

### Disabled and hidden
```html
<xpl-side-nav-item label="Disabled" disabled></xpl-side-nav-item>
<xpl-side-nav-item label="Hidden" hidden></xpl-side-nav-item>
```

## Slots

| Slot | Description |
|---|---|
| `leading` | Leading icon column. Default content is an `xpl-icon` from the `icon` prop. Override with slotted content. Applies to all item types including parents. Renamed from `icon` in v2. |
| `trailing` | Trailing content (e.g. `xpl-icon icon="external-link"`). On `has-children` items, the auto chevron is rendered here when this slot is empty (`chevron-down` collapsed, `chevron-up` expanded); slotted content replaces the chevron. |
| *(default)* | **`has-children`:** nested `xpl-side-nav-item` children. **`link`:** one `<a>` (or router link component) containing all row content. Unused in leaf button mode. |

## Link mode checklist

- Do **not** use `label` or `icon` props — they are ignored in link mode. Put all text and icons inside your slotted anchor.
- Use **`class="xpl-side-nav-item__link"`** on the anchor, **`class="xpl-side-nav-item__leading"`** on the icon wrapper, and **`class="xpl-side-nav-item__label"`** on the text span for consistent icon color, label typography, hover, and focus styling.
- **No whitespace between flex children** — ensure no newlines or spaces sit between `__leading`, `__label`, and the `</a>` tag. Whitespace between flex items renders as a space character and shifts the label to the right.
- Set **`aria-current="page"`** on the anchor (not the host) when the route is active.
- For **disabled** links: set `disabled` on the host and `aria-disabled="true"` + `tabindex="-1"` on the anchor (see **Accessibility**).

## Accessibility

- **Selected (non-link):** `aria-current="page"` on the `button` (leaf or expandable row).
- **Selected (link):** set `aria-current="page"` on your slotted anchor.
- **Expandable:** `aria-expanded` on the parent `button` matches `expanded`.
- **Trailing decorative icons:** add `aria-hidden="true"` to trailing `xpl-icon` elements. If the trailing icon conveys meaning (e.g. external link), provide accessible labeling in the surrounding context.
- **Disabled:** use the `disabled` prop on non-link rows; for link mode, coordinate host `disabled` with the anchor as above.
- **Hidden:** `hidden` hides the row; it does not disable interaction by itself — use `disabled` when appropriate.

## Mobile behavior (below `md` / 768px)

Inside the `xpl-side-nav` mobile drawer, items are automatically restyled by CSS — no props or markup changes needed.

| Property | Desktop | Mobile |
|---|---|---|
| Outer wrapper tap target | — | `min-height: 48px; padding: 6px 8px` |
| Inner button `min-height` | 32px | 0 (tap target comes from wrapper) |
| Leading / trailing icon size | 16px (`--xpl-size-100`) | 24px (`--xpl-size-150`) |
| Top-level label | `title-4` (16px, medium, lh 1.5) | `title-3` (20px, medium, lh 1.4) |
| Child item label | `title-5` | `title-4` (16px, medium, lh 1.5) |
| Footer item label | `title-5` | `title-4` (16px, medium, lh 1.5) |
| Footer row min-height | `28px` (one step smaller than section rows) | `--xpl-size-200` (32px) |
| Footer leading / trailing icon | 16px (`--xpl-size-100`) | 20px (`--xpl-size-125`) |
| Child label alignment offset | `padding-left: var(--xpl-side-nav-child-indent)` (32px) | Same — child labels stay aligned with the parent label column on every viewport |

**Link mode and mobile icons:** in button mode the component automatically adjusts the `xpl-icon` `size` prop to 24 below `md`. In link mode, where the consumer controls the anchor content, the CSS scales the `svg` element inside `__leading` / `__trailing` via a presentational-attribute override — no `size` prop change needed.

## Token reference

| Token | Usage |
|---|---|
| `--xpl-icon-default` | Leading and trailing icon color (default, hover, selected) |
| `--xpl-icon-disabled` | Leading, trailing, and label color when disabled |
| `--xpl-text-strong` | Label color |
| `--xpl-text-disabled` | Label color when disabled |
| `--xpl-background-brand-secondary-surface` | Selected row background |
| `--xpl-background-surface-transparent-10` | Hover row background |
| `--xpl-border-focus` | Focus outline color |
| `--xpl-space-8` | `margin-right` on the leading icon box (spacing between icon and label); `margin-left` on the trailing box |
| `--xpl-size-100` | Leading / trailing icon box: 16px on desktop |
| `--xpl-size-150` | Leading / trailing icon box on mobile (overrides `--xpl-size-100` below `md`) |
| `--xpl-size-200` | `min-height` of the inner link / button (32px desktop) |
| `--xpl-size-300` | `min-height` of the outer row wrapper on mobile (48px tap target) |

## Related

- [xpl-side-nav](../xpl-side-nav) — Sidebar container (`slot="section"`, `slot="nav-footer"`).

<!-- Auto Generated Below -->


## Overview

Navigation row for `xpl-side-nav`. Renders a button (default) or a slot-only
anchor container (`link` mode). Three slot positions are available:

- `slot="leading"` — leading icon column (renamed from `slot="icon"` in v2).
  The `icon` prop is a convenience shorthand that renders an `xpl-icon` here
  when no slotted `leading` content is present.
- `slot="trailing"` — trailing content (e.g. `xpl-icon icon="external-link"`).
  When `has-children` is true and no `trailing` slot content is provided, an
  auto chevron is rendered automatically in this position: `chevron-down` when
  collapsed, `chevron-up` when expanded.
- default slot — nested `xpl-side-nav-item` children (expandable parents only).

**v2 breaking changes (beta):**
- `slot="icon"` renamed to `slot="leading"` — no alias kept.
- Parent rows (`has-children`) now render the leading icon alongside the chevron.
  Previously parents showed chevron + label only (no leading icon).
- The chevron has moved from the leading position to the trailing position.
- A slotted `slot="trailing"` replaces the auto chevron on parent rows.

**Migration:**
| Before (v1) | After (v2) | Action |
|---|---|---|
| `slot="icon"` | `slot="leading"` | Rename the slot attribute |
| Parent row: chevron only (no leading) | Parent row: leading icon + chevron | Pass `icon` prop or `slot="leading"` |
| (no trailing slot) | `slot="trailing"` | Add for custom trailing content |

## Properties

| Property      | Attribute      | Description                                                                                                                                                                                                                                                                                                                                                                    | Type      | Default     |
| ------------- | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------- | ----------- |
| `disabled`    | `disabled`     | Whether the item is currently disabled.                                                                                                                                                                                                                                                                                                                                        | `boolean` | `false`     |
| `expanded`    | `expanded`     | Whether the item is currently expanded (only applies when `has-children` is true).                                                                                                                                                                                                                                                                                             | `boolean` | `false`     |
| `hasChildren` | `has-children` | Whether the item has child items; when true, shows expand/collapse and a default slot for nested items. Mutually exclusive with `link`. If `link` is already true, setting `has-children` is reverted.                                                                                                                                                                         | `boolean` | `false`     |
| `hidden`      | `hidden`       | Whether the component is hidden.                                                                                                                                                                                                                                                                                                                                               | `boolean` | `false`     |
| `icon`        | `icon`         | Optional icon name; renders as the default content for `slot="leading"`. When `slot="leading"` content is present, this prop is ignored. In v2, this prop applies to all item types including parent rows (`has-children`).                                                                                                                                                    | `string`  | `undefined` |
| `label`       | `label`        | The display label for the navigation item (button / expandable rows; omit in `link` mode).                                                                                                                                                                                                                                                                                     | `string`  | `undefined` |
| `link`        | `link`         | When true, the row becomes a slot-only container for consumer-provided links. The consumer provides the full anchor element (or router link) via the default slot. In link mode, `label` and `icon` props should not be used. Link mode does not support `has-children`; use the default button mode for expandable items. When `link` is true, `hasChildren` is always false. | `boolean` | `false`     |
| `selected`    | `selected`     | Whether the item is currently in a selected state.                                                                                                                                                                                                                                                                                                                             | `boolean` | `false`     |


## Events

| Event            | Description                                                                                            | Type                   |
| ---------------- | ------------------------------------------------------------------------------------------------------ | ---------------------- |
| `expandedChange` | Emitted when the expanded state changes (`has-children` items only). Detail is the new expanded value. | `CustomEvent<boolean>` |
| `navClick`       | Emitted when a leaf item is clicked. Expandable parents use `expandedChange` instead.                  | `CustomEvent<void>`    |


## Dependencies

### Depends on

- [xpl-icon](../../xpl-icon)

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

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

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