v0.3 · TypeScript · Zero deps

Radial menus that predict
where you're going

Open a context menu and select items by moving your pointer in a direction — not by hovering over them. Includes safe triangle dropdown submenu navigation with velocity prediction.

Try the demo → Quick start Docs
$ npm install prediction-cone
$ yarn add prediction-cone
$ pnpm add prediction-cone
$ bun add prediction-cone
Interactive

Radial menu — prediction cone

Click inside the area below, then move your pointer in any direction to select.

Move your pointer after opening to select

—
Getting started

Quick start

import { createPredictionConeMenu } from 'prediction-cone';

const menu = createPredictionConeMenu({
  items: [
    { id: 'cut',     label: '✂️ Cut'     },
    { id: 'copy',    label: '📋 Copy'    },
    { id: 'paste',   label: '📌 Paste'   },
    { id: 'settings', label: '⚙️ Settings' },
  ],
  ringRadius:       120,
  itemSize:         60,
  coneHalfAngleDeg: 22.5,
  deadzone:         30,
});

// Attach to the whole page — right-click anywhere
menu.attach(document, { trigger: 'contextmenu' });

menu.on('select', ({ item }) => {
  console.log('Selected:', item?.label);
});
Reference

API

.attach(element, options?)

Attach to a DOM element. Opens on the configured trigger event (click, contextmenu, pointerdown).

.detach(target?)

Remove all event listeners previously added by .attach().

.openAt(x, y)

Open the menu programmatically at given viewport coordinates.

.close()

Dismiss the menu without firing a select event.

.setItems(items)

Replace the item list at runtime without re-creating the menu.

.on(event, handler)

Subscribe to open, close, change, or select events. Returns unsubscribe function.

.destroy()

Full cleanup — removes DOM, listeners and debug canvas.

Dropdown Menu

createDropdownMenu(options)

Create a list-based dropdown with velocity-predictive safe triangle submenu navigation.

item.children

Add nested ConeItem[] to show a submenu. Triangle zone adapts to cursor speed automatically.

item.separator

Set separator: true to render a horizontal divider row.

item.danger / item.disabled

Visual modifiers — danger renders the item in red, disabled disables interaction.

Customization

CSS variables

Set any of these on an ancestor element to theme the menu.

--pcm-bg
Item background
--pcm-text
Item text color
--pcm-activeBg
Active item background
--pcm-activeText
Active item text color
--pcm-border
Border color
--pcm-shadow
Box shadow