# SubNav

> Navigation inside a tab: a list down the side of a tab's body that moves between that tab's own places — a record's invoices one at a time, a settings tab's sections — so a record can have tabs and still go deeper inside one without a second tab strip. Each SubNavItem is one place; below Tablet the list becomes a row of chips that scrolls sideways.

<!-- id: ui-subnav · area: ui · stability: stable · html: https://osysharp.com/reference/ui/subnav/ -->

## Summary        {#summary}

A record's tabs say WHICH part of it you are looking at; `SubNav` says which ONE of that part's things. It is a
vertical list of `SubNavItem`s down the side of a tab's body — each with its label, an optional hint under it (a date,
an amount) and an optional count — and the chosen one is `selected`. Put the content beside it in a grid or a row.

```osy title="a record's Invoices tab, one invoice at a time" test app=ui-subnav
class Bill { public string Number; public string Due; public string Total; }

List<Bill> Bills() {
  return [
    new Bill { Number = "F-2041", Due = "31 Oct", Total = "1,840.00" },
    new Bill { Number = "F-2007", Due = "30 Sep", Total = "1,840.00" },
  ];
}

[Route("/client")] [Render(CSR)] [AllowAnonymous]
component ClientPage() {
  string open = "F-2041";
  action Show(string number) { open = number; }
  render {
    Stack(gap: 4, p: 6) {
      Tabs { Tab("Overview"); Tab("Invoices", selected: true); }
      Row(gap: 5, align: Align.Start) {
        SubNav("Client invoices") {
          foreach (var b in Bills()) {
            SubNavItem(b.Number, selected: open == b.Number, onPress: () => Show(b.Number), hint: "Due " + b.Due);
          }
        }
        Card("Invoice " + open) { Text(Bills().First(b => b.Number == open).Total); }
      }
    }
  }
}
```

## Signature      {#signature}

```osy syntax
SubNav(string label) { SubNavItem(…); … }
SubNavItem(string label, bool selected = false, Action onPress = null, string hint = "", string count = "")
```

## Description    {#description}

### When is this better than another row of tabs?   {#why}

When the places are DATA rather than a fixed set — a record's invoices, its contacts, a list of saved views — or when
there are more than a handful. A second tab strip under the first reads as two levels of the same thing and runs out
of width; a list down the side scales with the data and says its places in full.

### What does it become on a phone?   {#narrow}

Below `Tablet` the list lays out as a row of chips that scrolls sideways, above the content — the same items, the same
selection, in the width a phone has.

### How is it announced?   {#semantics}

As a tab list named by `label`, each item a tab that is `selected` or not — which is what it is. Give the `SubNav` a
label that differs from the tab that holds it ("Client invoices" inside the "Invoices" tab), so a scope naming one
does not match both.

### How do I drive it in a test?   {#test}

`Ui.Click("F-2007", within: "Client invoices")`, then `Assert.Selected("F-2007")`.

## See also       {#see-also}

- [Arrangements — five ways to build a whole app, and which look wears each](https://osysharp.com/reference/ui/arrangements/) — the document desk, whose record goes deeper this way
- [Paper](https://osysharp.com/reference/ui/paper/) — showing the chosen item as the document it is
- [Dock](https://osysharp.com/reference/ui/dock/) — the other way to show one item beside a list
