# Theme Menu

A round button that opens a menu with the light, dark and system themes.

```tsx
import { ThemeMenu } from 'ferry-ui'

export default function ThemeMenuHero() {
  // With no `value`, the menu reads and changes the nearest ThemeProvider.
  return <ThemeMenu />
}
```

This menu changes the theme of this site.

## Usage guidelines

- **For the top bar.** Put the menu in the `actions` of a [Top Bar](/docs/components/top-bar), or on a sign-in page.
- **No state in your code.** With no `value`, the menu reads and changes the nearest [ThemeProvider](/docs/utilities/theme-provider).
- **Not for a settings form.** In a form, bind a [Toggle Group](/docs/components/toggle-group) or a [Radio Group](/docs/components/radio-group) to `useTheme()`.

## Anatomy

The menu has one part. The icon of the button shows the current preference: a sun, a moon or a monitor.

```tsx title="Anatomy"

<ThemeMenu />
```

## Examples

### Controlled value

To control the menu, pass `value` and `onValueChange`. The menu then does not change the provider. Your code holds the preference.

```tsx
import * as React from 'react'
import { ThemeMenu, type ThemePreference } from 'ferry-ui'

export default function ThemeMenuControlled() {
  // Your code holds the preference. The menu does not change the ThemeProvider.
  const [preference, setPreference] = React.useState<ThemePreference>('system')

  return (
    <>
      <ThemeMenu value={preference} onValueChange={setPreference} />
      <span className="text-[13px] text-foreground-light">Preference: {preference}</span>
    </>
  )
}
```

### Translated texts

`label` sets the heading of the menu and the text of the tooltip. `labels` sets the names of the three options.

```tsx
import { ThemeMenu } from 'ferry-ui'

export default function ThemeMenuLabels() {
  return <ThemeMenu label="Thème" labels={{ light: 'Clair', dark: 'Sombre', system: 'Système' }} />
}
```

### Position and tooltip

`align` sets the side of the button that the menu aligns to. The default is `end`. `tooltip={false}` hides the tooltip.

```tsx
import { ThemeMenu } from 'ferry-ui'

export default function ThemeMenuAlign() {
  return (
    <>
      <ThemeMenu align="start" tooltip={false} />
      <ThemeMenu align="center" tooltip={false} />
      <ThemeMenu align="end" tooltip={false} />
    </>
  )
}
```

### In a top bar

`className` goes to the button. Use it to hide the menu on a phone.

```tsx
<TopBar
  actions={
    <>
      <TopBarSearch />
      <ThemeMenu className="max-sm:hidden" />
      <TopBarUserMenu name="Maya Chen" />
    </>
  }
/>
```

## Accessibility

- The accessible name of the button gives the label and the current option, for example "Theme: Dark".
- The tooltip needs a `TooltipProvider` above the menu. See [Tooltip](/docs/components/tooltip).
- <Kbd>Enter</Kbd> opens the menu. <Kbd>↑</Kbd> and <Kbd>↓</Kbd> move in the options. <Kbd>Enter</Kbd> selects an option.

## API reference

`ThemeMenu` accepts only the props of this table. The button has `data-slot="theme-menu-trigger"`, and the panel has `data-slot="theme-menu"`.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `value` | `"light" \| "dark" \| "system"` |  | Controlled theme preference. When omitted the menu reads and writes the nearest `ThemeProvider` through `useTheme()`. |
| `onValueChange` | `((value: ThemePreference) => void)` |  | Called with the chosen preference. In uncontrolled mode the `ThemeProvider` is updated too, so use this to observe (analytics, persisting to a profile). |
| `label` | `string` | `Theme` | Heading of the menu, tooltip and accessible name prefix (default "Theme"). |
| `labels` | `Partial<Record<ThemePreference, string>>` |  | Translated option names, e.g. `{ light: 'Clair', dark: 'Sombre', system: 'Système' }`. |
| `tooltip` | `boolean` | `true` | Show the label as a tooltip on the trigger (default `true`; needs a `TooltipProvider`). |
| `open` | `boolean` |  | Controlled open state of the menu (pair with `onOpenChange`). |
| `defaultOpen` | `boolean` |  | Initial open state when uncontrolled. |
| `onOpenChange` | `((open: boolean) => void)` |  | Called with the next open state. |
| `modal` | `boolean` |  | Radix modal mode (default `true`): set `false` to keep the page interactive while open. |
| `align` | `"center" \| "start" \| "end"` | `end` | Alignment of the menu against the trigger (default "end"). |
| `className` | `string` |  | Classes for the round trigger button (e.g. `max-sm:hidden`). |
| `contentClassName` | `string` |  | Classes for the menu panel (default width 160px). |
