# Fuzzy Text

> Canvas text that shivers in shifted strips, resolves out of static on first view, splits into color fringes, and reacts to hover, clicks or timed glitches.

Text drawn on a canvas and sliced into thin strips that jitter sideways, up and down, or both. Hover the text to raise the fuzz, or switch the trigger to run it all the time or fire a burst on each click. Turn on the intro and the text fades in as coarse static that settles into clean letters; color split tears it into offset red, green and blue copies. Try the presets in the panel.

<demo-fuzzy-text>



</demo-fuzzy-text>

## Installation

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

## Usage

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

<template>
  <FuzzyText
    text="404"
    :font-size="140"
    :base-intensity="0.2"
    :hover-intensity="0.5"
    intro
  />
  <FuzzyText
    text="GLITCH"
    trigger="click"
    :color-split="0.6"
    :slice-size="5"
    :fuzz-range="50"
    glitch-mode
  />
</template>
```

The canvas follows the surrounding text color by default (`currentColor`), so it switches with the theme. Presets from the demo are exported as `fuzzyTextPreset('static' | 'glitch' | 'shiver' | 'melt')`.

## 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 draw
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        font-size
      </code>
    </td>
    
    <td>
      <code>
        number | string
      </code>
    </td>
    
    <td>
      <code>
        'clamp(2rem, 8vw, 8rem)'
      </code>
    </td>
    
    <td>
      Any CSS size or a number in px. Responsive sizes redraw on window resize
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        font-weight
      </code>
    </td>
    
    <td>
      <code>
        number | string
      </code>
    </td>
    
    <td>
      <code>
        900
      </code>
    </td>
    
    <td>
      Font weight
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        font-family
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        'inherit'
      </code>
    </td>
    
    <td>
      Font family. <code>
        'inherit'
      </code>
      
       uses the font of the surrounding text
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        color
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        'currentColor'
      </code>
    </td>
    
    <td>
      Text color. Follows the surrounding text color by default, including theme switches
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        color-split
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0
      </code>
    </td>
    
    <td>
      0–1. Splits the text into offset RGB copies that tear apart with the fuzz. Dark text splits into cyan/magenta/yellow
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        trigger
      </code>
    </td>
    
    <td>
      <code>
        'hover' | 'always' | 'click'
      </code>
    </td>
    
    <td>
      <code>
        'hover'
      </code>
    </td>
    
    <td>
      What raises the fuzz: the pointer, always on, or a burst per click that eases back
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        enable-hover
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      Raise the fuzz to <code>
        hover-intensity
      </code>
      
       while the pointer is over the text (trigger <code>
        hover
      </code>
      
      )
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        base-intensity
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.18
      </code>
    </td>
    
    <td>
      Fuzz strength at rest
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        hover-intensity
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.5
      </code>
    </td>
    
    <td>
      Fuzz while hovered. The constant level for <code>
        always
      </code>
      
      , the burst strength for <code>
        click
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        fuzz-range
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        30
      </code>
    </td>
    
    <td>
      Furthest a strip can shift (px)
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        slice-size
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        1
      </code>
    </td>
    
    <td>
      Strip thickness (px). 1 is a fine fuzz, larger values tear the text into bands
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        fps
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        60
      </code>
    </td>
    
    <td>
      Frame rate cap
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        direction
      </code>
    </td>
    
    <td>
      <code>
        'horizontal' | 'vertical' | 'both'
      </code>
    </td>
    
    <td>
      <code>
        'horizontal'
      </code>
    </td>
    
    <td>
      Which way the strips shift
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        transition-duration
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0
      </code>
    </td>
    
    <td>
      Frames the fuzz takes to ease between intensities. 0 switches instantly
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        intro
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        false
      </code>
    </td>
    
    <td>
      Fade in as heavy, coarse static that resolves into clean letters the first time it is drawn
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        intro-duration
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        1400
      </code>
    </td>
    
    <td>
      Intro length (ms)
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        click-effect
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        false
      </code>
    </td>
    
    <td>
      Short full-intensity burst on click (trigger <code>
        hover
      </code>
      
       or <code>
        always
      </code>
      
      )
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        glitch-mode
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        false
      </code>
    </td>
    
    <td>
      Full-intensity bursts at a regular interval
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        glitch-interval
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        2000
      </code>
    </td>
    
    <td>
      Time between glitch bursts (ms)
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        glitch-duration
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        200
      </code>
    </td>
    
    <td>
      Length of each glitch burst (ms)
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        gradient
      </code>
    </td>
    
    <td>
      <code>
        string[] | null
      </code>
    </td>
    
    <td>
      <code>
        null
      </code>
    </td>
    
    <td>
      Two or more colors for a left-to-right gradient fill
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        letter-spacing
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0
      </code>
    </td>
    
    <td>
      Extra space between letters (px)
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        class
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        ''
      </code>
    </td>
    
    <td>
      Extra classes on the canvas
    </td>
  </tr>
</tbody>
</table>

## Accessibility

- The canvas has `role="img"` and an `aria-label` with the text, so screen readers read it as plain text.
- With `prefers-reduced-motion: reduce` the rest fuzz drops to 0, the intro and glitch bursts are skipped, and `always` holds the base level.
- The render loop pauses while the canvas is off screen and stops redrawing once the text is still.
- Ported from [React Bits](https://reactbits.dev/text-animations/fuzzy-text) by David Haz.
