Text✨ Premium

Magnetic Cursor Text

Each character acts as a magnet: it follows the cursor within a configurable radius and snaps back with an elastic CSS transition.

JSMouseInteractive

You'll get access to the interactive demo with a free account.

This effect is part of Effect.Labs — 811 vanilla effects, some free, some premium. Text has 56 effects, including 4 free. Explore the category →

3 usage examples

Creative agency hero — Full-screen magnetic heading — Magnetic Cursor Text example 1

① Creative agency hero — Full-screen magnetic heading

WhenThe user lands on the fold of a design site or portfolio: the cursor is naturally moving across the full surface.
WhyAt a large size (4–6 rem), each letter has room to move freely; the effect is immediately noticeable without instruction and communicates interactivity by itself.
Settingsfont-size: 5rem, letter-spacing: 0.06em, magnetic radius 150 px, strength 0.45 — letters follow the cursor widely for maximum impact.
Editorial hook — Magnetic section keyword — Magnetic Cursor Text example 2

② Editorial hook — Magnetic section keyword

WhenAn online magazine or blog features a single word (category, theme) as an interactive section heading, on a dark or contrasted background.
WhyThe effect on a single 5–8 letter word creates living typography that distinguishes the hook from surrounding content without overflowing the text column.
Settingsfont-size: 3.5rem, color: #f59e0b, radius 100 px, strength 0.30, transition-duration: 0.4s for a slower, more expressive return.
Full-screen menu — Magnetic navigation items — Magnetic Cursor Text example 3

③ Full-screen menu — Magnetic navigation items

WhenA fullscreen overlay opens on burger-menu click: items (WORK / ABOUT / CONTACT) span the full viewport height, and the cursor roams freely over a large intentional interaction surface.
WhyEach word displayed at a large size (4–6 rem) gives letters room to move freely without disrupting the click target; the deliberate interaction of a full-screen menu makes the magnetism expected and memorable, never disruptive.
Settingsfont-size: 4.6rem, font-weight: 900, letter-spacing: 0.04em, radius 130 px, strength 0.40 — the listening zone covers the entire overlay to activate the field before the cursor even reaches the letters.

How it works

The splitChars(el) function reads the data-text attribute of the target element and replaces its raw text content with a series of <span class="char"> — one per glyph. Spaces are converted to non-breaking spaces to prevent inline collapse. Each span is display: inline-block, making each letter independently translatable without triggering a reflow on the rest of the text.

The CSS rule .kt-magnetic .char { transition: transform .3s cubic-bezier(.25, .46, .45, .94) } delegates animation entirely to the browser. The chosen curve produces a slight overshoot on return — the characteristic "elastic snap-back" — without any requestAnimationFrame loop. JS only writes the final style; the browser handles all interpolation.

On each mousemove event, the Euclidean distance between the cursor and the bounding-box center of each .char is computed. If it falls below the magnetic radius (JS constant, ~120 px by default), the letter receives style.transform = translate(dx·f, dy·f) where f = (1 − dist/radius) × strength — a factor that decays linearly to zero at the field boundary, simulating magnetic flux falloff.

When the cursor exits the radius or the container, transforms are reset to an empty string. The CSS transition then springs each character back to its natural position — no cleanup state required on the JS side. The data-text attribute stores the original string and lets splitChars() rebuild spans if the content changes dynamically.

Accessibility

  • prefers-reduced-motion absent: the shipped code does not disable the transition. Add in production: @media (prefers-reduced-motion: reduce) { .kt-magnetic .char { transition: none } } to stop letter displacement for motion-sensitive users.
  • The <span class="char"> elements may be read letter by letter by some screen readers. Wrap the .kt-text in an element with aria-label="YOUR TEXT" and add aria-hidden="true" to each .char so the word is announced as a single block.
  • Contrast: the default #fff color on #0a0a0f background achieves ~20:1 ratio (WCAG AAA). Verify after any palette change.
  • The effect is triggered by mousemove only — no keyboard or touch alternative. For a purely decorative heading this is not an accessibility blocker; text stays readable and correctly positioned without interaction.
  • No role or aria-label is present on the container in the shipped code: as the text is semantic HTML, no additional role is required if the heading hierarchy is respected.

Browser compatibility

Uses only CSS transform + transition and the mousemove event — no canvas, WebGL, or requestAnimationFrame dependency.

Chrome 49+✓ Full
Firefox 51+✓ Full
Safari 10+✓ Full
Edge 17+✓ Full
Mobile iOS✓ Touch, no hover
Android Chrome✓ Full

Without mouse events (touch devices, legacy browsers), characters stay at their natural position. Text is fully readable and layout does not break.

The code

HTML structure to paste into your page (CSS + JS available with a premium account):

index.html — structure
<div class="demo-preview">
  <div class="kt-text kt-magnetic" id="magnetic1" data-text="YOUR TEXT">
    <!-- Generated by splitChars() or written manually -->
    <span class="char" data-char="Y">Y</span>
    <span class="char" data-char="O">O</span>
    <span class="char" data-char="U">U</span>
    <span class="char" data-char="R">R</span>
  </div>
</div>
🔒 Unlock the full code — from €2.99 the first month

Full HTML + CSS + JS, copy-paste ready — with hundreds of premium effects.

Customize

Options passed to the API or data-* attributes:

Option / propertyDefaultEffect
font-size on .kt-text 2.2rem Character size. 4–6 rem for a hero, 1.4–2 rem for navigation.
font-weight on .kt-text 800 Font weight. 900 for maximum impact, 400–600 for a softer editorial style.
color on .kt-text #fff Character color. Accepts any CSS value: hex, oklch, or a gradient via background-clip: text.
letter-spacing on .kt-text .02em Letter spacing. Widen to 0.06–0.12 em on large sizes for a monumental effect.
transition-duration on .kt-magnetic .char 0.3s Elastic return duration. 0.15 s = crisp, dry response; 0.5 s = slow, organic magnetism.
transition-timing-function on .kt-magnetic .char cubic-bezier(.25,.46,.45,.94) Return curve. Try cubic-bezier(.34,1.56,.64,1) for a more pronounced bounce.
data-text on .kt-text MAGNETIC Source text used by splitChars() to generate spans. Change the word without touching the HTML.
Magnetic radius (JS constant) 120 Pixel distance below which a character is attracted. Increase for a wider attraction field.
Attraction strength (JS constant) 0.38 Maximum displacement factor. 0 = no movement, 0.6 = very pronounced displacement.

FAQ

splitChars() handles every character including spaces. For multi-word phrases, one .kt-text.kt-magnetic per word is preferred — transforms can otherwise create visual overlaps between letters if displacement exceeds the word's natural footprint.
Yes. Attach the listener to any ancestor and pass e.clientX / e.clientY to the per-.char distance calculation. The magnetic field then activates as soon as the cursor enters the parent zone, well before it hovers over the letters.
transform: translate() is handled by the browser's compositor thread — no layout reflow, no repaint. CSS transitions run independently of the JS main thread, guaranteeing 60 fps even on a complex document.