# Split Text

> Splits text into letters, words or lines that rise, flip, pop, blur, skew or wave into place, loop, replay on hover or click, and lift under the cursor.

Text broken into letters, words or lines that animate in one after another when the text scrolls into view. Pick a reveal style (rise, fade, blur, flip, pop, skew or wave), let pieces pass through an accent color on the way in, loop the animation, or replay it on every hover or click. After the reveal, letters lift under the cursor or ripple from where it enters. Try the presets in the panel.

<demo-split-text>



</demo-split-text>

## Installation

```bash
npx shadcn-vue@latest add "https://nxui.geoql.in/r/split-text.json"
```

## Usage

```vue
<script setup lang="ts">
  import SplitText from '~/components/ui/split-text/SplitText.vue';

  function onDone() {
    console.log('All letters have animated!');
  }
</script>

<template>
  <SplitText
    text="Hello, you!"
    class="text-6xl font-extrabold"
    effect="rise"
    split-type="chars"
    :delay="50"
    :duration="1.25"
    hover-effect="lift"
    @letter-animation-complete="onDone"
  />

  <SplitText
    text="Flip on hover"
    effect="flip"
    trigger="hover"
    accent-color="#ff5a36"
  />
</template>
```

Custom `from` / `to` objects replace the effect, for example `:from="{ opacity: 0, y: 40, rotate: -8 }"`. Supported keys: `opacity`, `x`, `y` (px), `scale`, `rotate`, `rotateX`, `rotateY`, `skewX`, `skewY` (deg) and `blur` (px).

## Props

<table>
<thead>
  <tr>
    <th>
      Prop
    </th>
    
    <th>
      Type
    </th>
    
    <th>
      Default
    </th>
    
    <th>
      Description
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        text
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      —
    </td>
    
    <td>
      The text to animate
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        tag
      </code>
    </td>
    
    <td>
      <code>
        'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'p' | 'span'
      </code>
    </td>
    
    <td>
      <code>
        'p'
      </code>
    </td>
    
    <td>
      Element to render
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        effect
      </code>
    </td>
    
    <td>
      <code>
        'rise' | 'fade' | 'blur' | 'flip' | 'pop' | 'skew' | 'wave'
      </code>
    </td>
    
    <td>
      <code>
        'rise'
      </code>
    </td>
    
    <td>
      Reveal style. Ignored when <code>
        from
      </code>
      
       or <code>
        to
      </code>
      
       is set
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        trigger
      </code>
    </td>
    
    <td>
      <code>
        'view' | 'load' | 'hover' | 'click'
      </code>
    </td>
    
    <td>
      <code>
        'view'
      </code>
    </td>
    
    <td>
      When the reveal plays. <code>
        view
      </code>
      
       waits until the text scrolls in, <code>
        load
      </code>
      
       plays right away, <code>
        hover
      </code>
      
      /<code>
        click
      </code>
      
       also replay it each time
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        loop
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        false
      </code>
    </td>
    
    <td>
      Animate the pieces out and back in, over and over
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        loop-delay
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        1.5
      </code>
    </td>
    
    <td>
      Pause between loops (s)
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        hover-effect
      </code>
    </td>
    
    <td>
      <code>
        'none' | 'lift' | 'wave'
      </code>
    </td>
    
    <td>
      <code>
        'none'
      </code>
    </td>
    
    <td>
      After the reveal: lift pieces near the cursor, or send a ripple from where the cursor enters
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        color
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      —
    </td>
    
    <td>
      Text color. Follows the surrounding color when unset
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        accent-color
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      —
    </td>
    
    <td>
      Color each piece passes through while it reveals
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        delay
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        50
      </code>
    </td>
    
    <td>
      Delay between pieces (ms)
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        duration
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        1.25
      </code>
    </td>
    
    <td>
      Duration of each piece (s)
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        ease
      </code>
    </td>
    
    <td>
      <code>
        string | (t: number) => number
      </code>
    </td>
    
    <td>
      —
    </td>
    
    <td>
      GSAP-style ease name (<code>
        'power3.out'
      </code>
      
      , <code>
        'expo.out'
      </code>
      
      , <code>
        'back.out(2)'
      </code>
      
      , <code>
        'bounce.out'
      </code>
      
      , <code>
        'elastic.out(1, 0.3)'
      </code>
      
      ) or a function
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        split-type
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        'chars'
      </code>
    </td>
    
    <td>
      <code>
        'chars'
      </code>
      
      , <code>
        'words'
      </code>
      
      , <code>
        'lines'
      </code>
      
       or <code>
        'words, chars'
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        stagger-from
      </code>
    </td>
    
    <td>
      <code>
        'start' | 'center' | 'end' | 'edges' | 'random'
      </code>
    </td>
    
    <td>
      <code>
        'start'
      </code>
    </td>
    
    <td>
      Where the stagger starts
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        mask
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        false
      </code>
    </td>
    
    <td>
      Clip each piece to its own box so it slides out from behind an edge
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        from
      </code>
    </td>
    
    <td>
      <code>
        SplitTextVars
      </code>
    </td>
    
    <td>
      —
    </td>
    
    <td>
      Custom start values, replacing the effect
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        to
      </code>
    </td>
    
    <td>
      <code>
        SplitTextVars
      </code>
    </td>
    
    <td>
      —
    </td>
    
    <td>
      Custom end values, replacing the effect
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        threshold
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.1
      </code>
    </td>
    
    <td>
      How far into the viewport the text must be before it plays (0–1)
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        root-margin
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        '-100px'
      </code>
    </td>
    
    <td>
      Extra offset for the start line (<code>
        px
      </code>
      
      , <code>
        em
      </code>
      
      , <code>
        rem
      </code>
      
       or <code>
        %
      </code>
      
      )
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        text-align
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        'center'
      </code>
    </td>
    
    <td>
      Text alignment
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        class
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        ''
      </code>
    </td>
    
    <td>
      Extra classes on the root
    </td>
  </tr>
</tbody>
</table>

## Events

<table>
<thead>
  <tr>
    <th>
      Event
    </th>
    
    <th>
      Payload
    </th>
    
    <th>
      Description
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        letter-animation-complete
      </code>
    </td>
    
    <td>
      —
    </td>
    
    <td>
      Fires once, when the first reveal ends
    </td>
  </tr>
</tbody>
</table>

## Accessibility

- The text stays real text in the DOM, so it can be selected and read by screen readers.
- With `prefers-reduced-motion: reduce` the text shows at once and the completion event still fires.
- With `trigger="click"` the root shows a pointer cursor; it is not focusable, so do not use it as the only way to reach an action.
- Ported from [React Bits](https://reactbits.dev/text-animations/split-text) by David Haz.
