ComponentsData display

Presence Cursor

Another person's pointer on a shared surface, like a canvas or a document. It shows a colored arrow with a name tag, or with an avatar.

Name tags

Each cursor shows the person's email in their color.

Initials

Pass an Avatar with initials to show it next to the arrow instead of the tag.

Face photos

An Avatar with a photo. The initials show until the photo loads.

Usage

Presence Cursor usageTSX
import { PresenceCursor } from "@tulipfarm/sprout/presence-cursor";
import { Avatar, AvatarFallback, AvatarImage } from "@tulipfarm/sprout/avatar";

<div className="relative">
  {peers.map(peer => (
    <PresenceCursor
      key={peer.id}
      name={peer.email}
      color={peer.color}
      x={peer.x}
      y={peer.y}
    />
  ))}
</div>

<PresenceCursor
  name="[email protected]"
  color="#b55309"
  x={120}
  y={80}
  avatar={
    <Avatar size="sm">
      <AvatarImage src={jenny.image} alt="" />
      <AvatarFallback>JE</AvatarFallback>
    </Avatar>
  }
/>

x and y place the arrow tip, in px, from the top left of the nearest positioned parent. Give that parent position: relative. Your app sends the positions, for example from a realtime channel. The cursor does not track anything by itself.

color fills the arrow, the tag, and the avatar ring. The tag text is white, so pick colors with at least 4.5:1 contrast against white. Passavatar to show it in place of the tag. A small Avatar fits best. Long names cut off with an ellipsis at 16rem.

Accessibility

Each cursor is an image named after the person, so screen readers read the name in both forms. It ignores pointer events, so clicks go through it. It glides to a new spot over 180ms, and jumps when reduced motion is requested. In forced colors mode the arrow uses the system text color.

Theme tokens

The tag uses --sprout-radius-control and --sprout-shadow-control. Motion uses --sprout-motion-normal and --sprout-motion-ease. The color comes from the color prop, stored in --sprout-presence-color.