# Ribbons

> Springy WebGL2 ribbons that trail the cursor, in lens, comet or even shapes, with an optional rolling wave, soft edges and tail fade.

Ribbons of color that chase the cursor on springs and trail behind it. Move the pointer (or tap on touch screens) anywhere over the area and the ribbons follow, overshoot and settle. The motion runs on a fixed time step, so it looks the same at any frame rate.

<demo-ribbons>



</demo-ribbons>

## Installation

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

## Usage

```vue
<script setup lang="ts">
  import { Ribbons } from '~/components/ui/ribbons';
</script>

<template>
  <div class="relative h-125 w-full overflow-hidden">
    <Ribbons
      class="absolute inset-0"
      :colors="['#3847ff', '#7c84ff', '#c7cbff']"
      shape="comet"
      :thickness="26"
      :length="2.2"
      :wave="0.3"
      fade
    />
  </div>
</template>
```

The canvas ignores pointer events, so content underneath stays clickable. It listens to pointer movement on the window and only reacts while the pointer is inside its own box.

## Props

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

<tbody>
  <tr>
    <td>
      <code>
        colors
      </code>
    </td>
    
    <td>
      <code>
        string[]
      </code>
    </td>
    
    <td>
      <code>
        ['#3847ff', '#7c84ff', '#c7cbff']
      </code>
    </td>
    
    <td>
      One ribbon per color, drawn back to front (up to 12). Any CSS color works.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        shape
      </code>
    </td>
    
    <td>
      <code>
        'lens' | 'comet' | 'even'
      </code>
    </td>
    
    <td>
      <code>
        'lens'
      </code>
    </td>
    
    <td>
      Width along each ribbon. Lens swells in the middle, comet is widest at the cursor, even stays level.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        thickness
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        30
      </code>
    </td>
    
    <td>
      Width of each ribbon at its widest, in pixels.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        length
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        1.4
      </code>
    </td>
    
    <td>
      How far the ribbons trail behind the cursor. Higher values leave longer, lazier trails.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        spread
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        18
      </code>
    </td>
    
    <td>
      Space between the ribbons, in pixels.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        stiffness
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.5
      </code>
    </td>
    
    <td>
      How tightly the ribbons chase the cursor, from 0 to 1.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        bounce
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.5
      </code>
    </td>
    
    <td>
      How far the ribbons overshoot and swing back when the cursor stops or turns, from 0 to 1.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        wave
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0
      </code>
    </td>
    
    <td>
      A wave that rolls along each ribbon, from 0 to 1.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        softness
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0
      </code>
    </td>
    
    <td>
      Blurs the ribbon edges into soft light trails, from 0 to 1.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        fade
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        false
      </code>
    </td>
    
    <td>
      Fades each ribbon out toward its tail.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        opacity
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        1
      </code>
    </td>
    
    <td>
      Opacity of the ribbons.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        background-color
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        'transparent'
      </code>
    </td>
    
    <td>
      Color behind the ribbons. The canvas is transparent by default.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        dpr
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      auto
    </td>
    
    <td>
      Device pixel ratio override. Defaults to the screen ratio capped at 2.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        class
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        ''
      </code>
    </td>
    
    <td>
      Extra classes for the container.
    </td>
  </tr>
</tbody>
</table>

## Accessibility

- The effect is decorative: the canvas has no role and ignores pointer events.
- With `prefers-reduced-motion: reduce` the rolling wave is turned off; the ribbons still follow the pointer, since that motion is user-driven.
- Rendering pauses while the component is off screen, and the WebGL program and buffers are released on unmount.
- Ported from [React Bits](https://reactbits.dev/animations/ribbons) by David Haz.
