# MobileNav

> The phone tab bar — up to 5 destinations at the bottom of a top-level page, the selected one's icon in a yellow sticker.

Source: https://raft-design.botiverse.build/components/mobile-nav. Generated from the current release; where it differs from what you remember, this page is right.

MobileNav is the phone web's tab bar: a row of a product's destinations at the bottom of every top-level page. The selected destination's icon sits in a yellow sticker. Pushed pages cover it. The iOS and Android apps draw the same destinations with their own system tab bar instead ([Navigation](https://raft-design.botiverse.build/patterns/navigation.md#what-each-platform-draws)).

```tsx
import { CheckSquare, Home, Settings, Users } from "lucide-react";
import { useCallback, useState } from "react";
import { MobileNavItem, MobileNavLabel, MobileNavRoot } from "@botiverse/raft-ui";

export function MobileNavComposition() {
  const [selected, setSelected] = useState("home");

  const handleHomeClick = useCallback(() => setSelected("home"), []);
  const handleTasksClick = useCallback(() => setSelected("tasks"), []);
  const handleMembersClick = useCallback(() => setSelected("members"), []);
  const handleSettingsClick = useCallback(() => setSelected("settings"), []);

  return (
    <MobileNavRoot aria-label="Primary navigation">
      <MobileNavItem selected={selected === "home"} onClick={handleHomeClick} aria-label="Home">
        <Home />
        <MobileNavLabel>Home</MobileNavLabel>
      </MobileNavItem>
      <MobileNavItem selected={selected === "tasks"} onClick={handleTasksClick} aria-label="Tasks">
        <CheckSquare />
        <MobileNavLabel>Tasks</MobileNavLabel>
      </MobileNavItem>
      <MobileNavItem
        selected={selected === "members"}
        onClick={handleMembersClick}
        aria-label="Members"
      >
        <Users />
        <MobileNavLabel>Members</MobileNavLabel>
      </MobileNavItem>
      <MobileNavItem
        selected={selected === "settings"}
        onClick={handleSettingsClick}
        aria-label="Settings"
      >
        <Settings />
        <MobileNavLabel>Settings</MobileNavLabel>
      </MobileNavItem>
    </MobileNavRoot>
  );
}
```

## When to use

| Situation                           | Use                                                           |
| ----------------------------------- | ------------------------------------------------------------- |
| A product's destinations on a phone | MobileNav                                                     |
| The same destinations on desktop    | [AppRail](https://raft-design.botiverse.build/components/app-rail.md)                            |
| Sections of one page                | [Tabs](https://raft-design.botiverse.build/components/tabs.md)                                       |
| Another view of the same content    | [SegmentedControl](https://raft-design.botiverse.build/components/segmented-control.md) |

How the tab bar works with Back and pushed pages, and how many destinations it holds, is in [Navigation](https://raft-design.botiverse.build/patterns/navigation.md). Nothing is attached above the tab bar; where working agents appear instead is in [Working agents](https://raft-design.botiverse.build/people-and-agents/working-agents.md#where-working-shows). Which destinations a product has is on its own page ([Raft Chat](https://raft-design.botiverse.build/products/raft-chat.md)).

## Anatomy

- `MobileNavRoot`: the edge bar, on `layer-card` (in dark mode on `layer-frame`, the same dark layer as the tab header), with a `line-seam` along its top edge and padding for the bottom safe area. It runs the full width at the bottom edge; it is never a floating pill.
- `MobileNavItem`: one destination: a 22px icon, then its label.
- `MobileNavLabel`: the label, in `text-label`. It truncates.

## Search and More

A product can give one of the tab bar's slots to Search and make its last tab More. Raft Chat does both, in its mobile app and on the phone web.

- Make Search a tab like the others: its page is a top-level page, with no Back, and the bar stays with Search `selected`. Search never shows an attention dot. How its page and its results behave is in [Navigation](https://raft-design.botiverse.build/patterns/navigation.md#search-on-a-phone).
- Make the last tab More when a product has more destinations than fit. More's top-level page is a page of its own that lists the remaining destinations at its top; each row opens a pushed page under More, with Back. A link to one of those pages opens it under More, with More selected.

raft-ui draws both with `MobileNavItem`. Separate parts for a floating pill and a Search button (`MobileNavPill`, `MobileNavSearch`) are no longer planned.

## Behavior

- Hide the bar while the on-screen keyboard is open. Pinned above the keyboard, it would cover the field being typed in and the composer. When the keyboard closes, bring the bar back with the platform's default transition, not an instant jump, so people can see the navigation return rather than wonder where it went.
- Show the bar only on top-level pages: a page with Back never shows it ([Navigation](https://raft-design.botiverse.build/patterns/navigation.md#going-back)). Pushed pages cover it; menus, dialogs and bottom sheets leave it as it is.
- Keep the bar in place while a page scrolls. Don't hide it on scroll: it is how people know where they are.
- Tapping the selected item scrolls its page to the top, and on Search focuses the field. Each tab keeps its page's scroll position and filters while another tab is open ([Navigation](https://raft-design.botiverse.build/patterns/navigation.md#going-back)).

## States

| State                 | Look                                                                                                                                                               |
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Not selected          | Icon and label in `foreground-hint`                                                                                                                                |
| Selected (`selected`) | The icon in a 56×30px yellow sticker; the label in `foreground`, bold                                                                                              |
| Attention             | One pink pixel at the top right of the icon ([States](https://raft-design.botiverse.build/foundations/states.md)), never a number. Never on the selected item, with no exception |

The sticker wraps only the icon, not the whole item: a yellow item adds another strip of frame at the bottom of the screen, and wrapping only the icon halves the color while the selection still reads at a glance ([Navigation](https://raft-design.botiverse.build/patterns/navigation.md#where-you-are)).

## Content

- Label every item, in Title Case ([Typography](https://raft-design.botiverse.build/foundations/typography.md#casing)). Keep a label to at most 8 Latin letters or 4 Chinese characters, so 5 items fit at 320px; a longer one truncates.
- Which items can show an attention dot is the product's choice. Say other news inside the page instead.
- Show a dot, never a count, on the tab bar: the count belongs inside the page, next to what it counts.
- Dot More only for something inside More that shows nowhere else, and show the same dot on the row that caused it, so people can find what is new.

## Accessibility

- `MobileNavRoot` renders a `nav`. Name it with `aria-label="Primary"`.
- `MobileNavItem` renders the Base UI Button; `selected` sets `aria-pressed`. For an item that navigates, render a link (`render={<a href="…" />}` with `nativeButton={false}`) and set `aria-current="page"` on the selected one: raft-ui sets neither on a link.
- The visible label names the item. When an item shows an attention dot, give it an `aria-label` that says both: "More, new reply to your feedback". The dot itself is `aria-hidden`.

## API

### MobileNavItem

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `nativeButton` | `boolean` | `true` |  |
| `selected` | `boolean` |  |  |
| `render` | `ReactElement \| ((props, state) => ReactElement)` |  | Allows you to replace the component's HTML element with a different tag, or compose it with another component. |

Also takes the attributes of its element.

### MobileNavLabel

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `render` | `ReactElement \| ((props, state) => ReactElement)` |  | Allows you to replace the component's HTML element with a different tag, or compose it with another component. |

Also takes the attributes of its element.

### MobileNavRoot

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `render` | `ReactElement \| ((props, state) => ReactElement)` |  | Allows you to replace the component's HTML element with a different tag, or compose it with another component. |

Also takes the attributes of its element.
