Showcase Pointer

Named, colored cursors that move like teammates: hover, visually press, drag, select and type. Bring an interface to life without activating real clicks, links or forms.

Starter kit

12 components ready to use

Settings

Notifications

Updates from your team

Homepage hero

Controls

Demo

Demo
Loop

Cursors

Color 1
Name 1
Color 2
Name 2
Color 3
Name 3

Motion

Label animation
Click effect
Speed
Idle drift (px)
Cursor size

Movement

Easing
Curvature
Overshoot
Reach time (ms)
Distance slowdown (ms)
Longest move (ms)
Landing spread
Drag pace

Trail

Cursor trail (ms)
Trail width (px)
Label trail

Properties

19 properties
PropertyTypeDefaultDescription
cursorsCursorConfig[]—Each cursor: id, name, color, optional textColor, start position and script. Scripts of different cursors run at the same time.
script stepsCursorStep—enter, leave, move, hover, click, drag (from, to), select (target, start, end), marquee (from, to, items), type (target, text), say (text, duration), wait, clear and call. Targets are selectors, elements, { target, x, y } fractions or { x, y } in px or %.
refShowcasePointerHandle—cursor(id) returns moveTo, hover, click, drag, select, marquee, type, say, wait, enter, leave and clear, each a Promise. run(id, steps) and restart() are also available.
labelAnimation'roll' | 'masked-lift' | 'fade' | 'none'fadeHow the name label enters and changes when a cursor speaks. Wheel roll turns each letter on a drum; Masked Lift raises the text through a mask. Both use power4.out.
speednumber1Time scale for movement, pauses and typing. 2 plays the scripts twice as fast.
clickEffectbooleantruePress the arrow and pulse a ring in the cursor color on every simulated press. Preview state can respond through onCursorPress; no native click is dispatched.
idleDriftnumber1.5Gentle sway while a cursor rests, like a hand on a mouse. It fades out during moves, drags and selections.
labelLagnumber0.35How far the name label trails behind the arrow on a critically damped spring. 0 keeps it rigid; 1 lets it swing behind fast moves.
cursorSizenumber1Scale of the arrow and label.
loopbooleantrueReplay the scripts after every cursor finishes. Selections clear and onCycle resets the demo.
trailnumber0Length of a fading trail behind each arrow, in its color, measured in time. 0 draws no trail. The trail shrinks away while the cursor rests.
trailWidthnumber3Stroke width at the newest end of the trail; older parts taper and fade.
motionCursorMotion{"reachTime":240,"reachScale":150,"maxReach":1500,"curvature":1,"overshoot":1,"easing":"human","landingSpread":1,"dragPace":1.45}Movement rules: curvature, overshoot, reachTime, reachScale, maxReach, easing, landingSpread and dragPace. Omitted rules keep the defaults, which reproduce the built-in feel.
playingbooleantrueFalse pauses every cursor in place. Scripts also pause off-screen.
loopDelaynumber1400Milliseconds between loops.
showLabelsbooleantrueShow the name pills.
onCursorPress(press: CursorPress) => void—Receives cursorId and the target element after a simulated press. Use it to update local preview state. Scripted presses never dispatch native clicks or submit forms; this callback should not invoke production actions.
onCycle() => void—Runs before each loop, after selections and hover states clear. Reset the state your scripts changed.
height / className / stylenumber | string / string / CSSProperties—Style the container. The overlay never receives pointer events, so the real user can keep using the content.

Usage

Usage
import { useState } from 'react';
import { ShowcasePointer } from './components/showcase-pointer';

export function Example() {
  const [published, setPublished] = useState(false);
  return (
    <ShowcasePointer
      height={420}
      onCursorPress={({ target }) => {
        if (target.closest('#publish')) setPublished(true);
      }}
      onCycle={() => setPublished(false)}
      cursors={[
        {
          id: 'one',
          name: "Kubai",
          color: "#0d99ff",
          script: [
            { action: 'hover', target: '#publish' },
            { action: 'click', target: '#publish' },
            { action: 'say', text: 'Shipped!', duration: 1600 },
            { action: 'wait', duration: 1600 },
          ],
        },
        {
          id: 'two',
          name: "Mara",
          color: "#f24e9c",
          script: [
            { action: 'select', target: '#copy', start: 0, end: 18 },
            { action: 'wait', duration: 2000 },
          ],
        },
      ]}
    >
      <p id="copy">Design together, ship faster.</p>
      <button id="publish" type="button" onClick={() => setPublished(true)}>
        {published ? 'Published' : 'Publish'}
      </button>
    </ShowcasePointer>
  );
}

Free for personal and commercial projects. Redistributing components, kits or templates requires permission. Read the license.