Modules
StringCursor
Global cursor runtime that tracks pointer position, writes cursor CSS variables, and manages portal classes.
StringCursor
StringCursor is one of the more complex built-in modules. It is global, but it also manages per-object state, floating portal nodes, hover lifecycle cleanup, and multiple output channels.
At a high level it does two separate jobs:
- writes target-local cursor variables to elements marked with
string="cursor" - moves optional floating cursor portals marked with
string-cursor="..."
That split matters enough that the module now has dedicated deep-dive pages.
Public API
Target Attributes
These attributes are read from elements marked with string="cursor".
| Attribute | Type | Default | Real runtime effect |
| -------------------------- | ------------------------------ | ---------------------------- | --------------------------------------------------------------------------------- | ------------------------------------------- |
| string-cursor-target | string | default | Selects which cursor portal IDs this target controls. Supports comma or |separated lists.\* targets every portal. |
| string-cursor-class | string | "" | Adds this class to matching portals while the target is hovered. |
| string-alignment | enum: start, center, end | center | Changes how local --x and --y are normalized. |
| string-cursor-enter | enum: snap, smooth | snap | Controls how local cursor values enter when the pointer first touches the target. |
| string-cursor-leave | enum: snap, smooth | smooth | Controls how local cursor values reset after leaving the target. |
| string-cursor-leave-hold | boolean | false | Freezes the last local values after leaving instead of easing back to zero. |
| string-lerp | number | inherited from global lerp | Smoothing factor for target-local cursor motion. |
| string-cursor-vars | string list | "" | Enables extra variables: xpx, ypx, dx, dy, angle, angle-deg. |
Portal Attributes
These attributes are read from floating cursor portal elements.
| Attribute | Type | Default | Real runtime effect |
|---|---|---|---|
string-cursor | string | default | Registers the portal and gives it an addressable ID. |
string-cursor-lerp | number | global cursor-lerp | Changes how quickly the portal itself follows the pointer. |
CSS Variables and DOM Output
The detailed output contract now has its own page:
Target CSS variables
On string="cursor" elements the module writes:
--x--y
These are normalized local coordinates, not viewport pixels.
alignment="center"gives roughly-1to1alignment="start"gives0at the left/top edge and1at the opposite sidealignment="end"gives-1to0
If string-cursor-vars includes extra flags, the module also writes:
--x-px--y-px--dx--dy--angle--angle-deg
Portal CSS variables
On string-cursor="..." portals the module writes:
--x--y--x-lerp--y-lerp
Portal --x and --y are viewport pixel positions, so consume them with * 1px.
Classes
The runtime toggles:
-showon portals while at least one linked target is hovered- the class from
string-cursor-classon matching portals while that target is hovered
Events
StringCursor emits these target-scoped events:
| Channel | Payload | Fired when |
|---|---|---|
cursor:start:<id> | null | Target-local tracking becomes active |
cursor:move:<id> | { x, y } | Normalized target-local coordinates change |
cursor:pixel:<id> | { x, y } | Pixel coordinates inside the target change |
cursor:end:<id> | null | Target-local tracking settles or resets |
If at least one portal exists, the module also emits a global portal event:
| Channel | Payload |
|---|---|
cursor | { x, y, stepX, stepY } |
Mirror Behavior
If a mirror element is linked with string-copy-from, the target-local CSS variables are written to both the source element and its mirrors. Portal behavior is still owned by the source target ID.
Quick Example
<div string-cursor="default" class="cursor-portal">
<span class="cursor-text">View</span>
</div>
<article
class="project-card"
string="cursor"
string-id="project-1"
string-cursor-target="default"
string-cursor-class="-view-project"
string-cursor-enter="smooth"
string-cursor-leave="smooth"
>
<img src="/assets/project1.jpg" alt="Project Thumbnail" class="project-image" />
<div class="project-info">
<h3>Design System</h3>
<p>2026</p>
</div>
</article>
.cursor-portal {
position: fixed;
left: 0;
top: 0;
width: 16px;
height: 16px;
background: black;
border-radius: 50%;
pointer-events: none;
transform: translate3d(calc(var(--x, 0) * 1px), calc(var(--y, 0) * 1px), 0) translate(-50%, -50%);
display: flex;
align-items: center;
justify-content: center;
}
.cursor-portal:not(.-show) {
opacity: 0;
}
.cursor-text {
color: white;
font-size: 12px;
opacity: 0;
}
.cursor-portal.-view-project {
width: 64px;
height: 64px;
}
.cursor-portal.-view-project .cursor-text {
opacity: 1;
}
.project-card {
border: 1px solid black;
padding: 1rem;
background: white;
transform: translate3d(calc(var(--x, 0) * 8px), calc(var(--y, 0) * 8px), 0);
}
Registration
import StringTune, { StringCursor } from '@fiddle-digital/string-tune';
const stringTune = StringTune.getInstance();
stringTune.use(StringCursor, { lerp: 0.8 });
stringTune.start(60);
Detailed Behavior
StringCursordoes not create a portal for you. If your UI needs a floating cursor, you must add a portal element yourself.string-target-disableandstring-target-style-disableare intentionally not documented as supported API. The current runtime parses older setting names, but the current module implementation does not apply those flags.target-classis also intentionally omitted from the supported contract. It is parsed, but not used by the current runtime.- On coarse pointer devices the module disables itself.