# Section nav

The page's sections as a list of links ("On this page"), the one being read marked `aria-current="location"` as the page scrolls (nothing is announced: scrolling isn't news). Each is a real link to `#id`, so it can be opened in a new tab or copied. Choosing one scrolls to the section, `offset` pixels clear of a sticky header, and moves focus to its heading (or to the section, made focusable with `tabIndex={-1}`), so a keyboard or screen-reader user lands where they asked rather than at the top of the page. The address's `#id` changes to the section's, replacing the history entry, so Back leaves the page rather than stepping through its sections.

Give `root` when the sections scroll inside a container rather than the page. A section whose element isn't on the page is left out of the list, with a warning in development, until it arrives: the page is looked at again a moment after it changes, so sections drawn after a fetch or behind Suspense get their links when they appear.

- Registry name: `section-nav` (`@candor/section-nav`)
- Tier: Pro. Installing it needs a Pro, Premium or Team licence key: the person runs `npx candor-ui key <key>` themselves; never ask for the key in chat or write it to a file.
- Docs: https://candor.themethodic.com/docs/section-nav

## Install

```bash
npx candor-ui add section-nav
```

Run `npx candor-ui@latest init` once first (https://candor.themethodic.com/get-started.md). Without the CLI: `npx shadcn@latest add @candor/section-nav`.
It brings in: locale, motion, tokens, utils.

## Example

```tsx
"use client"

import * as React from "react"
import { SectionNav } from "@/components/candor/section-nav"

const sections = [
  { id: "section-nav-overview", label: "Overview" },
  { id: "section-nav-access", label: "Who can see it" },
  { id: "section-nav-roles", label: "Roles", level: 3 as const },
  { id: "section-nav-retention", label: "How long it's kept" },
]

export default function Example() {
  const scroller = React.useRef<HTMLDivElement>(null)
  return (
    <div className="flex max-w-2xl gap-4 rounded-xl border bg-card p-4">
      <div ref={scroller} className="h-64 flex-1 overflow-y-auto pe-2 text-sm">
        {sections.map((s) => (
          <section key={s.id} id={s.id} className="flex min-h-40 flex-col gap-2 py-2">
            {s.level === 3 ? <h4 className="font-medium">{s.label}</h4> : <h3 className="font-semibold">{s.label}</h3>}
            <p className="text-muted-foreground">
              What the policy says about {s.label.toLowerCase()}, written out in full so the section is tall enough to scroll past.
            </p>
          </section>
        ))}
      </div>
      <SectionNav sections={sections} root={scroller} className="w-44 shrink-0" />
    </div>
  )
}
```

## <SectionNav>

Props:

- `sections: SectionNavItem[]`: The sections, in page order.
- `label?: string | undefined`: The navigation's accessible name. "On this page" by default.
- `offset?: number | undefined` (default `0`): Pixels of a sticky header over the top of the scroll area: a section counts as reached below it, and is scrolled to just under it.
- `root?: React.RefObject<HTMLElement | null> | undefined`: The element the sections scroll in, when it isn't the page.
- `onNavigate?: ((id: string) => void) | undefined`: Called with a section's id when the person chooses it, after focus has moved.
- `className?: string | undefined`: Classes for the navigation, merged with Candor's.

## Strings (`sectionNav`)

The fixed words, English by default. Override any of them under `sectionNav` in `<LocaleProvider strings={{ sectionNav: { … } }}>`; the table is exported as `sectionNavStrings`.

- `label`: On this page

## Types

### SectionNavItem

One section of the page `SectionNav` lists: the id of its element, the words for it, and its heading level.

```ts
{
  /** The `id` of the section's element (or its heading) on the page; the link's `#id`. */
  id: string;
  /** The words for it in the list, usually its heading's. */
  label: string;
  /** 2 (the default) for a section, 3 for one inside it, drawn indented. */
  level?: 2 | 3 | undefined;
}
```

## Motion

- **scroll** (move): Choosing a section scrolls the page (or `root`) to it smoothly. Under reduced motion, the calm level or `data-motion="none"` it jumps there at once. Either way focus lands on the section's heading.

Set with `--cd-motion-section-nav-<moment>-*` in your brand file; the families are in the design reference.

## Rules for using Candor UI

- Components take plain typed data. Never pass model output as HTML or render it as a link; messages are typed blocks.
- A control that can't be used says why: wrap it in `<Locked reason="…">`, don't just disable it.
- Don't show a value you don't have: leave it out (it renders as —) rather than guess. An estimate is labelled as one.
- Nothing is pre-selected in a decision a person makes, and the agent is named from your app's data, never from model output.
- Your app's words go in `LocaleProvider`: its fixed strings in `strings`, its nouns in `nouns`; entity kinds and agent roles in `EntityKindsProvider` and `AgentRolesProvider`.
