Media & effects
Cursor Follower
Spring-damped pointer follower with custom icon content
detent-cursor-follower
Port status
- Catalog status
- implemented
- Origin
- upstream
- Phase
- 4
- Wave
- phase-4-pointer
- Group
- Media & effects
4.5 KB gzip for the class module · inlines the shared spring
Variants
Move the pointer anywhere on this page: each follower tracks it with the pinned spring (stiffness 450,
damping 32, mass 0.5) and scales to 1.15 while the pointer is moving. The first follower
on this page paints the built-in ring; the ones below render slotted content. The last two opt out of the
default-true flags through the property, because a present attribute would mean true.
With prefers-reduced-motion: reduce every follower maps 1:1 onto the pointer, drops the velocity scale
and schedules no frame. Touch input never moves them: a follower is a hover-only affordance.
Recorded states
reduced motionrecorded state; no setter
Usage
Import
import '/components/cursor-follower/define.js';
// or register it yourself
import { DetentCursorFollower } from '/components/cursor-follower/cursor-follower.js';
Attributes
dampinghide-on-leavemassoffset-xoffset-yscale-on-movesizestiffness
Properties
No properties are recorded for this component.
Slots
default
Events
No events are recorded for this component.
Example
Runnable example: site/examples/cursor-follower.ts. The snippet below is generated from the catalogued attributes.
<detent-cursor-follower
damping="32"
hide-on-leave
mass="0.5"
offset-x="0"
offset-y="0"
scale-on-move
size="40"
stiffness="450"
></detent-cursor-follower>
Source
The pinned upstream source for this row:
src/components/ui/CursorFollower.tsxsha256 eaa887d42fdc…
Native files
The native module and its artifacts:
src/components/cursor-follower/cursor-follower.tssrc/components/cursor-follower/styles.csssrc/components/cursor-follower/define.tsdocs/components/cursor-follower.mdsite/examples/cursor-follower.tstests/browser/cursor-follower.spec.tsevidence/components/cursor-follower.md
API
Boolean attributes follow HTML presence semantics: present means true, absent means false, and disabled="false" still means disabled.
Attributes
| Attribute | Property | Type | Default | Notes |
|---|---|---|---|---|
damping | damping | number | 32 | Kebab-case attribute; camelCase property. |
hide-on-leave | hideOnLeave | boolean | true | Presence semantics: present means true, absent means false. |
mass | mass | number | 0.5 | Kebab-case attribute; camelCase property. |
offset-x | offsetX | number | 0 | Kebab-case attribute; camelCase property. |
offset-y | offsetY | number | 0 | Kebab-case attribute; camelCase property. |
scale-on-move | scaleOnMove | boolean | true | Presence semantics: present means true, absent means false. |
size | size | number | 40 | Kebab-case attribute; camelCase property. |
stiffness | stiffness | number | 450 | Kebab-case attribute; camelCase property. |
Properties
No property-only values are recorded for this component.
Events
No custom events are recorded for this component.
Slots
| Slot | Notes |
|---|---|
default | Consumer content; rendered through a slot, never re-created. |
CSS customisation
- React `children`/`ReactNode` props become DOM slots; caller content stays light DOM.
- `className` prop is replaced by documented CSS parts/custom properties; no Tailwind in consumers.
- React state/hooks replaced by element-owned state with pre-upgrade property preservation.
- The catalog documents a size default of 40 while the pinned source destructures 32; the catalog row is the API authority, so the native default is 40. Owner confirmed the catalog value on 2026-10-29.
- Touch and other non-hover pointers never drive the follower (upstream attached window pointermove for every pointer type).
- The built-in ring uses --detent-cursor-follower-ring (#6b7280) instead of the upstream 30% text colour, which measured 1.9:1 on white (accessibility correction).
- aria-hidden moved from the moving div to the host.
What it does
A fixed-position follower that springs after the pointer and renders the consumer's slotted content at its centre.
Upstream behaviour
- Configurable spring dynamics (stiffness, damping, and mass)
- Dynamic velocity scaling when pointer is in motion
- Automatic viewport departure detection and graceful exit
- Reduced motion support with instant 1:1 hardware coordinate mapping
- Supports any custom React icon, text pill, or visual indicator
Differences from upstream
- React `children`/`ReactNode` props become DOM slots; caller content stays light DOM.
- `className` prop is replaced by documented CSS parts/custom properties; no Tailwind in consumers.
- React state/hooks replaced by element-owned state with pre-upgrade property preservation.
- The catalog documents a size default of 40 while the pinned source destructures 32; the catalog row is the API authority, so the native default is 40. Owner confirmed the catalog value on 2026-10-29.
- Touch and other non-hover pointers never drive the follower (upstream attached window pointermove for every pointer type).
- The built-in ring uses --detent-cursor-follower-ring (#6b7280) instead of the upstream 30% text colour, which measured 1.9:1 on white (accessibility correction).
- aria-hidden moved from the moving div to the host.