Power-User Systems

Rotunda

Real HTML panels on a drag-spun 3D cylinder. A hand-rolled perspective projection written onto DOM elements rather than a canvas — so the text stays selectable, the links stay clickable, and a screen reader still reads every panel while the ring turns.

Install

# Rotunda needs no npm packages

Then copy 2 files into your project:

  • components/registry/rotunda/rotunda.tsx
  • hooks/use-animation-loop.ts

Props

PropTypeDefaultDescription
radiusnumber380Distance from the axis to each panel face. Together with the number of panels this decides whether the ring closes: too small for the count and the faces intersect, which is the one way this arrangement can look broken rather than tight.
perspectivenumber1200Depth of the projection, and the control that decides whether this reads as expensive or as a cheap paper fan. Near enough and the front panel distorts violently; far enough and the ring flattens into a row of cards.
autoSpinnumber0.06Idle rotation while nothing is being dragged. It stops the instant the pointer arrives and does not restart until it leaves, because a carousel that keeps moving under your cursor is a carousel you cannot read.
frictionnumber0.94How much of a fling survives, and the whole feel of the throw. Applied per second rather than per frame, so the same gesture coasts the same distance on a 60Hz and a 120Hz display — the naive per-frame form silently halves the throw on better hardware.
snapbooleantrueSettle to the nearest panel once a fling drops below walking pace. Off, the ring stops wherever it stops — honest for a texture, wrong for a menu.
snapStiffnessnumber140How hard the ring is pulled to the nearest face. Damping is derived from it at the critical ratio, so raising this arrives faster without ever adding the overshoot that would read as a wobble on a menu.
depthDimnumber0.65How far the back of the ring dims toward the background. Depth cueing sells the illusion harder than the rotation does, because a CSS transform gives you no occlusion to work with.
depthBlurnumber3Blur on panels past the halfway point. Applied to an inner wrapper rather than the panel itself: a filter on a child of a preserve-3d parent establishes a containing block and flattens the entire ring into a stack.
faceCamerabooleanfalseKeep every panel square to the viewer rather than tangent to the ring. This turns the carousel into a card rack, and it is the right choice the moment the panels carry real copy you expect anyone to read off-centre.
dragSensitivitynumber0.35Degrees of rotation per pixel of drag. The honest test is whether the panel under your finger stays under your finger — too low and the ring feels geared down, too high and a small gesture spins it past the panel you were reaching for.

What It Demonstrates

Drag
Moving an element by grabbing it, often with momentum when released.
Momentum
Motion that carries velocity, especially after a drag or interruption.
Damping
How quickly a spring settles. Lower damping means more bounce and oscillation.
Perspective
How strong the 3D effect looks — a lower value exaggerates depth, like the viewer is closer.
3D tilt / Flip
Rotate in 3D space (rotateX / rotateY) to add depth.
Spatial consistency
Animating so an element keeps its identity and position across states, so users never lose track of where things went.

Related Components

Appears In