Text✨ Premium

Split Hover

On hover, each letter scatters randomly across the space — a CSS elastic spring snaps it back the moment the mouse leaves.

JSHoverInteractive

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 portfolio — Full-width interactive headline — Split Hover example 1

① Creative portfolio — Full-width interactive headline

WhenA design studio or agency homepage displays the studio name or a bold keyword at very large size.
WhyLetters exploding on hover immediately signal the page is alive — with no visible framework. On a dark background, the effect is striking at display sizes.
SettingsFont-size 5–8 rem, randomX raised to 160 (±80 px), randomY to 120 (±60 px), randomRotate to 180 (±90°), duration 0.6 s for a wider sweep.
Game or dark app — Interactive welcome screen title — Split Hover example 2

② Game or dark app — Interactive welcome screen title

WhenA browser game or dark-themed app welcome screen invites the user to 'touch' the title before starting.
WhyThe chaotic scatter + elastic snap delivers a playful, mechanical texture that fits a gaming register without requiring an animation library.
SettingsFont-size 3.5 rem bold, randomX at 120 (±60 px), sharpened cubic-bezier (e.g. .68,-.8,.265,1.8) for a stronger spring, scattered opacity at 0.15 for a dissolution feel.
Event poster — Full-width festival headline — Split Hover example 3

③ Event poster — Full-width festival headline

WhenA cultural event or festival page displays the event name at very large scale on a dark background — with no clickable element in the area.
WhyAt large scale, the letter explosion is spectacular with zero usability constraint: no navigation link to preserve, no affordance to respect. The effect reads as an expressive gesture, not a disruption.
SettingsFont-size 5–7 rem, randomX raised to 160 (±80 px), randomY to 120 (±60 px), randomRotate to 180 (±90°), duration 0.6 s, scattered opacity at 0.08 for a near-full dissolution.

How it works

On load, initSplitHover() splits the string character by character, wrapping each letter in an inline-block <span data-index>. This makes every glyph independently transformable by the CSS engine without disturbing the surrounding text flow.

On mouseenter, the .scattered class is added to the container (dropping each span's opacity to 0.3 via CSS), and each letter receives a transform: translate(Xpx, Ypx) rotate(Rdeg) computed with Math.random(): X amplitude ±50 px, Y ±40 px, rotation ±45°. Values differ on every hover — no memorised trajectory.

On mouseleave, transforms are reset to translate(0,0) rotate(0deg) and .scattered is removed. The CSS transition cubic-bezier(.68, -.55, .265, 1.55) (a back-out curve) produces a brief overshoot past the target position before settling — the spring bounce that gives the animation its elastic character.

The whole effect runs on standard CSS transitions and DOM events: no requestAnimationFrame, no library. Removing the two mouseenter/mouseleave listeners cleanly disables it with no residual state.

Accessibility

  • prefers-reduced-motion NOT IMPLEMENTED: the code does not detect the system preference. Letters always animate, even when the OS requests reduced motion. Wrap the listeners in a matchMedia('(prefers-reduced-motion: reduce)') guard if the context requires it.
  • The letter <span> elements carry no ARIA attributes. A screen reader will announce each letter individually ("H", "O", "V"…). Add aria-label="full text" on the container and aria-hidden="true" on the child spans for a natural reading experience.
  • The effect is only triggered by mouseenter/mouseleave — it is inactive with keyboard navigation and on touch screens. No focus or touchstart equivalent is provided in the reference code.
  • Text contrast at rest depends entirely on the background provided by the implementer. Ensure a ≥ 4.5:1 ratio (WCAG AA) between text color and background.
  • The Reset button in the demo has no aria-label: the visible label "Reset" is sufficient in production, but the inline onclick attribute should be replaced with an event listener for proper separation of concerns.

Browser compatibility

Requires CSS transitions and DOM events — natively supported in all modern browsers. No Canvas, no experimental APIs.

Chrome 100+✓ Full
Firefox 100+✓ Full
Safari 15+✓ Full
Edge 100+✓ Full
Mobile iOS✓ Static — hover inactive
Android Chrome✓ Static — hover inactive

On mobile and touch screens, <code>mouseenter</code> never fires: letters remain in their original positions and the text stays readable. No JS errors, no broken layout.

The code

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

index.html — structure
<div class="demo-preview">
  <!-- JS rebuilds these spans via initSplitHover() -->
  <span class="demo-text text-split-hover" id="splitHoverText">
    <span data-index="0">H</span>
    <span data-index="1">O</span>
    <span data-index="2">V</span>
    <span data-index="3">E</span>
    <span data-index="4">R</span>
    <span data-index="6">M</span>
    <span data-index="7">E</span>
  </span>
  <!-- Reset button (demo) -->
  <button class="replay-btn" onclick="replaySplitHover()">Reset</button>
</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
JS constant randomX (line ~14) 100 Horizontal scatter amplitude. Each letter moves up to ±(value/2) px. Raise for an explosive burst, lower for a subtle shimmer.
JS constant randomY (line ~15) 80 Vertical scatter amplitude (±(value/2) px). Match randomX for isotropic scatter, or lower for a purely horizontal effect.
JS constant randomRotate (line ~16) 90 Rotation range (±(value/2) degrees). Set to 0 to remove rotation and keep translation only.
CSS transition-duration on .text-split-hover span .4s Duration of the elastic return. Reduce to .2s for a snappy feel, raise to .7s for a cinematic animation.
CSS cubic-bezier on .text-split-hover span (.68,-.55,.265,1.55) Elasticity curve of the return. Increase the negative values (e.g. -.8) for a stronger spring. Replace with ease-out to remove the overshoot.
CSS opacity on .text-split-hover.scattered span 0.3 Letter opacity in scattered state. 0 = full disappearance, 1 = no fade (translation only visible).
CSS font-size on .demo-text 2.5rem Text size — adapt to context (1.5 rem for a nav item, 6 rem for a full-page headline).

FAQ

Yes. initSplitHover() rebuilds the spans from an internal constant ('HOVER ME'). Edit that string in the JS to change what is displayed — the span splitting and event binding regenerate automatically on the next call.
The elastic curve is set in the CSS rule .text-split-hover span { transition: transform .4s cubic-bezier(.68, -.55, .265, 1.55) }. Replace cubic-bezier(.68, -.55, .265, 1.55) with ease-out or cubic-bezier(0,.8,.4,1) for a smooth return without overshoot.
Not in its current form: mouseenter and mouseleave do not fire on pointer-less devices. On mobile, letters remain static — the text stays readable and no error is thrown. For a touch version, add touchstart/touchend listeners that call the same scatter and reset logic.