Hide the native cursor. Replace with a smooth dot that grows when it hovers anything interactive — and magnetically pulls the CTA toward it. Awwwards staple.
A custom cursor is a JavaScript-controlled element that follows the mouse and replaces the native arrow. It can change size, color, and label depending on what it's hovering. The "magnetic" variant goes further: when the cursor enters certain interactive elements (CTAs, links), it snaps to them and pulls them slightly toward the actual cursor position, creating a tactile gravity-like feel.
Set cursor: none on the container. Create an absolutely-positioned element (the custom cursor). On mousemove, update its transform: translate(x, y). For smooth following, lerp toward the target position each animation frame. On mouseenter of any interactive element (with a data attribute like data-cursor), add a class that grows the cursor and changes its color. For magnetic CTAs, calculate the cursor's offset from the button's center and apply a translate to the button itself (10–20% of the offset).
Move your mouse inside the demo. Hover the cards and the big CTA — feel the magnet pull.
The dot grows on links, snaps onto the big CTA, and inverts colors over light surfaces using mix-blend-mode.
Start the journeyBuild a custom cursor system. Apply cursor:none to the page container and all its descendants. Create a position:absolute cursor div (16px circle, brand accent color, transform-origin center). On mousemove, set CSS variables --x and --y on the container and lerp the cursor's transform toward them via requestAnimationFrame (with a 0.15 lerp factor for smooth easing). Add mix-blend-mode:difference so the cursor inverts over light and dark backgrounds equally well. For interactive elements (links, buttons), add data-cursor attribute — on mouseenter, add a .hovering class to the cursor that grows it to 60px and changes the background to white. For magnetic CTAs (data-cursor="cta" data-magnet), additionally apply a translate to the button equal to 18% of the cursor's offset from the button's center on mousemove, returning to 0,0 on mouseleave. Show a small text label inside the cursor when over a CTA ("View →"). Disable the entire system on touch devices (window.matchMedia('(hover: none)').matches) and inside form inputs.