Cube Scroll Menu
A full-screen cube menu that rotates between sections as you scroll, with framer-motion overlays, configurable CTA and hint copy, and callbacks for both selection and open state. Seventeen props cover colour, labelling and behaviour.
Included with membership
Members install Cube Scroll Menu with one command and get the full source, the usage code and the Framer remix, licensed for client work. One membership covers the whole library and the CLI.
When to use it
Primary navigation for a site with a handful of destinations, where the menu is meant to be an event. It suits eight to twelve entries; below that the drum barely turns, and well above it the wheel becomes a worse list than a list. It does not route on its own — you handle the navigation in onItemSelect.
- Wire onItemSelect — without it the menu spins and settles but nothing happens when an entry is chosen.
- Use onOpenChange to lock body scroll while the overlay is open, so the page behind does not move under it.
- Keep titles short. Each one has a single face, and a long title wraps badly on a curved surface.
- Raise zIndex above any fixed header, or the header floats over the overlay.
Avoid:
- Do not use it as a link list. Only the settled entry is comfortably readable, so a visitor cannot scan the options.
- Do not give two entries the same id — it keys the faces, and duplicates make the drum snap to the wrong one.
- Do not lower snapDelay much below the default; the drum starts snapping out from under a slow scroll.
Props
A full-screen navigation overlay whose entries sit on the faces of a turning drum. Scroll or drag to spin it, and it settles on the nearest entry when you stop. Opens from a button you control.
<CubeScrollMenu />
The overlay and its trigger.
| Prop | Type | Default | Description |
|---|---|---|---|
items* | CubeMenuItem[] | — | The entries, in order. Required — nothing renders without it. |
onItemSelect | (item: CubeMenuItem, index: number) => void | — | Fires when an entry is chosen. This is where you navigate; the component does not route on its own. |
onOpenChange | (open: boolean) => void | — | Fires whenever the overlay opens or closes, so surrounding UI can respond — locking scroll, dimming a header. |
ctaLabel | string | "Explore" | Text on the button that opens the menu. |
hintText | string | "Scroll to navigate" | Line shown inside the overlay telling people how to move the drum. |
overlayColor | string | "#050507" | Backdrop behind the drum. |
showDots | boolean | true | Shows a position indicator alongside the drum. |
zIndex | number | 50 | Stacking order of the overlay. Raise it if a fixed header sits on top. |
className | string | "" | Appended to the overlay's root. |
faceHeight | number | 80 | Height of one face in pixels, which sets how large each entry is and how far the drum turns between them. |
sliceCount | number | 4 | How many faces are visible around the drum at once. More shows further ahead and behind; fewer reads as a tighter wheel. |
wheelSensitivity | number | 0.35 | How far one wheel tick turns the drum. |
dragSensitivity | number | 0.55 | The same, for pointer drags. Higher than the wheel default by design. |
rotationDamping | number | 9 | How quickly the drum stops coasting. Higher settles sooner; lower keeps spinning after you let go. |
snapDelay | number | 160 | Milliseconds of stillness before it snaps to the nearest entry. Raise it if snapping interrupts a slow scroll. |
openDuration | number | 0.85 | Seconds the overlay takes to open. |
closeDuration | number | 0.6 | Seconds it takes to close. |
<CubeMenuItem />
typeOne entry on the drum.
| Prop | Type | Default | Description |
|---|---|---|---|
id* | string | number | — | Stable key for the entry. |
title* | string | — | The entry itself. Kept short — it has one face to sit on. |
image* | string | — | Picture shown alongside the entry. |
category | string | — | Optional small label above the title. |
description | string | — | Optional supporting line. |