Cursor Design
Custom cursor styling via CSS cursor: url() or JavaScript-driven cursor effects — magnetic cursors, trailing blobs, and context-sensitive shapes — used to reinforce brand personality.
Plain English
The cursor is the one element that follows the user everywhere, yet most products ignore it entirely. Custom cursor design replaces the default OS arrow with a branded shape, dot, or animated blob that responds to what is under it. On a portfolio site, hovering a project might enlarge the cursor and show a "View" label; hovering a draggable element switches it to a grab hand. The most advanced variant is the magnetic cursor — a softly animated circle that follows the mouse with a physics-like lag, making the interface feel luxurious and hand-crafted. Done well, cursor design makes premium sites feel alive. Done poorly (too large, too slow, or covering content), it becomes the first thing users want to turn off.
Technical
CSS-only: `cursor: url("cursor.svg") 16 16, auto` — the two numbers are the hotspot offset (where clicks register). SVG cursors must be < 128×128px. For JS-driven effects: hide the default cursor with `cursor: none` on `body`, render a `<div class="cursor">` absolutely positioned, and update its position on `mousemove` using `requestAnimationFrame`. Smooth lag: instead of snapping to mouse position, lerp toward it each frame — `cursorX += (targetX - cursorX) * 0.1`. Magnetic hover effect: on `mouseenter` of a button, translate the button slightly toward the cursor using the offset delta and a strength multiplier (typically 0.3–0.5). Context-switching: add a class to the cursor element based on what is hovered — `.cursor--hover`, `.cursor--drag`, `.cursor--text` — and style each with a CSS transition. Always add `@media (pointer: coarse)` detection to skip all cursor effects on touch devices.
Live Demo
Cursor Design
Hover over each box to see that cursor. The last box tracks your mouse with a custom cursor element.
Custom cursor — follows mouse
Move your mouse here
cursor: none + onMouseMove tracking
The right cursor is an invisible affordance — it tells users what will happen before they click.
Usage
✓ Good usage
A creative agency site with a 14px dark dot that expands to a 50px outlined circle with "Open" text when hovering project cards, and reverts to the dot elsewhere — contextual, purposeful, and visually polished.
✗ Bad usage
A cursor that is 80px wide, lags 300ms behind the mouse, and covers form labels during text input — the interaction cost outweighs the aesthetic benefit and breaks usability.
Recommended values
- Default custom cursor dot: 12–16px diameter
- Hover enlarged state: 40–60px with mix-blend-mode: difference
- Lerp speed (smoothness): 0.08–0.15 per frame (lower = more lag)
- Magnetic attraction strength: 0.3–0.5 multiplier
- Transition between states: 0.2–0.3s ease
- Always: cursor: none on body, only when pointer: fine (mouse)
Common mistakes
- Not hiding the system cursor with `cursor: none` — both the custom and OS cursor appear simultaneously.
- Applying cursor effects on touch/mobile devices — `pointer: coarse` means there is no cursor, causing a blank or broken element on screen.
- Cursor lag so extreme it obscures clicks — users click where the cursor appears, not where the actual hotspot is; lag must be visual only, not functional.
- No cursor visibility in text inputs — custom cursors should revert to `cursor: text` inside `<input>` and `<textarea>`.
AI Prompt Example
Copy this into Claude, Cursor, Bolt, or v0.
Add a custom cursor. Hide the default cursor on desktop (pointer: fine) with `body { cursor: none }`. Render a fixed 14px circle `.cursor` that tracks `mousemove` via JS with `requestAnimationFrame` and a lerp factor of 0.1. On hover over links and buttons, add class `.cursor--hover` that scales the cursor to 48px with a 0.25s transition. On touch devices (`pointer: coarse`), skip all cursor logic entirely.