# Radial menu (goodtabs UI)

A menu whose items fan out on a ring, placed with CSS cos() and sin().

## Install

```bash
npx shadcn@latest add https://ui.goodtabs.co/r/radial-menu.json
```

Or, with the @goodtabs namespace configured in components.json: `npx shadcn@latest add @goodtabs/radial-menu`

## Usage

```tsx
import { RadialMenu } from "@/components/ui/radial-menu"

<RadialMenu label="Actions" items={items} onSelect={run}>+</RadialMenu>
```

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| label | `string` |  | Accessible name for the menu and the trigger. |
| items | `RadialMenuItem[]` |  | Items with id, label and icon, first at the top. |
| onSelect | `(id: string) => void` |  | Called with the chosen item id. |
| radius | `number` | `76` | Ring radius in px. |
| children | `ReactNode` |  | Content of the trigger button. |
| defaultOpen | `boolean` | `false` | Start with the ring open. Focus does not move. |

## When to use

- A short set of four to eight actions on one object
- Touch-friendly actions that should feel close to the trigger

## When not to use

- Long or labelled lists, use a dropdown menu
- More than eight items, the chord gets too short

## Notes for agents

- Client component. A demo that passes onSelect must be a client component too
- Items are icon only with aria-label and title, so every icon needs a clear meaning
- Arrows move focus around the ring, Home and End jump, Escape closes and returns focus
- Placement uses CSS cos() and sin(), supported in current evergreen browsers

## The maths

- Item i of n sits at θ = -90deg + i × 360deg / n, so the first is at the top.
- Offset x = cos θ × r and y = sin θ × r from the trigger centre.
- With n = 6 and r = 64 the neighbour gap is 2r × sin(180deg / 6) = 64px.

Docs: https://ui.goodtabs.co/components/radial-menu