Install
Drop the tag anywhere in your HTML — head or the end of body, it makes no difference. The script waits for the document before it draws anything.
<script src="https://cursor-pts9j3ezj-meetchauhanteam.vercel.app/embed.js?style=magnetic-blob&click=shockwave&color=ff00ff"></script>The response is assembled per configuration: you get the engine core plus only the style and effect you named, which is typically under 10kb gzipped. Nothing else is downloaded and there is no build step to add.
URL parameters
Every option is a query parameter. Values are clamped and validated server-side and again in the browser, so a bad value degrades to the default rather than breaking your page.
| Parameter | Type | Default | Meaning |
|---|---|---|---|
style | string | dot-ring | Cursor shape. One of 127 ids. |
click | string | ripple-clean | Click effect. One of 59 ids. |
hover | string | scale-expand | How the cursor reacts to interactive elements. |
color | hex | ffffff | Primary colour, without the leading #. |
color2 | hex | = color | Accent colour, used by trails and particles. |
size | 0.4 – 3 | 1 | Size multiplier. |
speed | 0.25 – 3 | 1 | Animation speed multiplier. |
opacity | 0.05 – 1 | 1 | Overall cursor opacity. |
blend | string | normal | CSS mix-blend-mode for the overlay. |
dark | 0 | 1 | 1 | Hints that the host page is dark. |
adapt | 0 | 1 | 1 | Keep the cursor legible against whatever it is over. Set 0 to pin your exact colour. |
motion | full | — | Set to `full` to ignore the reduced-motion preference. |
ignore | selector | — | Elements that keep the native cursor. |
z | integer | 2147483000 | z-index of the overlay canvas. |
You can also set any parameter as a data-attribute on the script tag. Attributes win over query parameters, which makes server-side templating easier.
Per-element control
The cursor classifies what it is over automatically — links, buttons, inputs, and media all get sensible treatment with no markup from you. When you want something specific, annotate the element.
data-cursor="native" | Restores the operating system cursor over this element. |
data-cursor="text" | Treats the element as text, whatever its tag. |
data-cursor="button" | Treats the element as interactive. |
data-cursor="image" | Treats the element as media. |
data-cursor="anything-else" | Marks a custom target, and doubles as its label. |
data-cursor-label="View" | Overrides the automatically detected label. |
data-cursor-color="#ff0055" | Overrides the cursor colour while hovering this element. |
Labels write themselves
Any transform that shows a label — label, label-chip, caption, typewriter — reads the text off the element itself. A button that says “Add to cart” shows “Add to cart”. You do not annotate anything.
The order below mirrors how a screen reader computes an accessible name, for the same reason: it is what the element genuinely calls itself. Markup that is good for assistive technology is markup that labels well here, and vice versa.
| 1 | data-cursor-label | Your explicit override. Always wins. |
| 2 | aria-label | What the element already announces itself as. |
| 3 | aria-labelledby | Resolved and joined, same as a screen reader would. |
| 4 | title | The native tooltip text. |
| 5 | alt | For images, or an image inside the element. |
| 6 | placeholder | For inputs with no other name. |
| 7 | visible text | Trimmed and truncated on a word boundary at 28 characters. |
| 8 | the element type | “Link”, “Button”, “View”, “Type” — so a label is never empty. |
Elements marked data-cursor="native" are skipped entirely — they get no label and no cursor.
It reads the page and adapts
The most common way a custom cursor fails is not ugliness — it is a white cursor crossing onto a white hero and disappearing. No style can solve that on its own, because a style cannot know what it is over.
So the engine reads the page underneath the pointer and shifts the cursor colour until it has at least 3:1 contrast against it. Only lightness moves; hue and saturation are kept. Your magenta stays magenta — it just gets lighter or darker so it can still be seen.
- How it readselementFromPoint, then a walk up the ancestors compositing every translucent background until it hits an opaque one. Computed styles, not pixels — no screen capture, no permission prompt, no 200kb dependency.
- Over images and gradientsIt says so rather than guessing. Styles that care fall back; the rest keep your configured colour.
- CostThrottled to roughly ten reads a second, and only when the pointer has actually travelled. The contrast solve is cached on the colour pair.
- Turning it offadapt=0 pins your exact colour. Worth it when the cursor is part of a fixed brand treatment and you control the backgrounds.
The neumorphic styles take this further and use the sampled colour as their own fill, because soft UI only works when the shape matches the surface it sits on. That is what lets them work on any page rather than one exact background.
Hover transforms
A hover transform composes with any style rather than replacing it, which is why the combination count is what it is.
- none
- The cursor ignores what it is over. Styles may still react on their own.
- scale-expand
- The whole cursor scales up over anything interactive.
- scale-contract
- The cursor tightens down instead of growing. Reads as focus.
- magnet
- The cursor is drawn toward the centre of whatever it is over.
- invert
- A difference-blended disc takes over on hover, inverting what is beneath.
- label
- Shows the element’s `data-cursor-label` in a pill beside the cursor.
- outline
- Traces a rounded box around the hovered element as you arrive.
- hide
- The cursor withdraws entirely, leaving the element to speak for itself.
- scale-grow
- Doubles the cursor. The most obvious possible signal that something is live.
- squash
- Flattens the cursor horizontally, like it is being pressed against glass.
- spin
- Rotates the whole cursor a half turn as it arrives on a target.
- tilt
- Skews the cursor as though it were lying on a surface seen at an angle.
- jitter
- Vibrates the cursor while it sits over something clickable.
- lift
- Raises the cursor and drops a soft shadow beneath it.
- ring-pulse
- Emits a continuous ring while you rest on a target.
- crosshair
- Extends full-height and full-width guides across the viewport.
- corners
- Puts registration corners on the element, ignoring the cursor entirely.
- underline
- Sweeps a rule along the bottom of whatever you are pointing at.
- highlight
- Washes a translucent marker stripe over the element, like a highlighter.
- label-chip
- A square-cornered chip naming the element, read from its own content.
- label-below
- Sets the element’s name underneath it, centred, like a photo caption.
- typewriter
- Types the element’s name out one character at a time beside the cursor.
- readout
- Prints the hovered element’s pixel dimensions, like a design tool.
- spotlight
- Darkens the whole page except a circle around the pointer.
- tether
- Draws a line from the cursor to the centre of the element it is over.
- orbit
- Sends three satellites into orbit around the cursor while it is on target.
- sparkle
- Scatters small stars around the pointer over anything interactive.
- hard-shadow
- Casts a flat offset shadow behind the cursor. Pairs with brutalist styles.
- magnet-strong
- Snaps hard to the centre of the target. Almost impossible to miss a click.
Runtime API
The script publishes a small global. Use it to change the cursor after load — on a theme switch, or per route.
CursorKit.update({ style: 'comet', color: '00ffcc' });
CursorKit.destroy(); // restore the native cursor
CursorKit.boot({ style: 'dot-ring' }); // start again
CursorKit.engine // the live Engine instance, or null
CursorKit.allStyles() // every style loaded in this bundleA per-config embed only contains the style and effect you asked for. To switch between many at runtime, load /embed.full.js instead — the whole library in one file.
Frameworks
Next.js — App Router
// app/layout.tsx
import Script from 'next/script';
<Script
src="https://cursor-pts9j3ezj-meetchauhanteam.vercel.app/embed.js?style=dot-ring&color=ffffff"
strategy="afterInteractive"
/>React — Vite or CRA
// index.html, before </body>
<script src="https://cursor-pts9j3ezj-meetchauhanteam.vercel.app/embed.js?style=goo&click=ink-splat"></script>Webflow / Squarespace / WordPress
Paste into the custom-code or header-injection field:
<script src="https://cursor-pts9j3ezj-meetchauhanteam.vercel.app/embed.js?style=ribbon&color=ff8a3d"></script>What it does on its own
- Touch devicesThe script exits before drawing and leaves the native cursor alone. Nothing is hidden and no canvas is created.
- Reduced motionTrails, particles and magnetism are dropped; the cursor shape stays. Override with motion=full if your use case genuinely needs it.
- Background tabsThe render loop stops on visibilitychange and restarts with a fresh clock, so returning to a tab never plays a backlog of frames.
- ErrorsIf a style throws, the engine tears itself down and restores the native cursor. A visitor is never left without a pointer.