# Curved Loop

> Looping marquee text that travels along an arc, a wave, a ring or a straight line, with drag-to-fling, outlined letters, accent separators and a scroll boost.

Text that loops forever along an SVG path. It can follow an arc, a wave, a closed ring or a straight line, and repeats can be joined by an accent mark in its own color. Drag the text to throw it — a ring follows the pointer around — and it glides back to its normal speed in whichever direction it was thrown. With a scroll boost, scrolling the page gives the loop a burst of speed that eases off. Pick a preset or tune it in the panel.

<demo-curved-loop>



</demo-curved-loop>

## Installation

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

## Usage

```vue
<script setup lang="ts">
  import CurvedLoop from '~/components/ui/curved-loop/CurvedLoop.vue';
</script>

<template>
  <CurvedLoop
    marquee-text="Scroll faster"
    shape="line"
    separator="/"
    separator-color="#ff5a1f"
    :speed="1"
    :scroll-boost="2"
    :font-size="136"
    :font-weight="800"
    :letter-spacing="-2"
    :edge-fade="0.4"
    outline
  />
</template>
```

The loop is drawn in a 1440px wide SVG view box and scales with its container, so `font-size` and `letter-spacing` are measured against that width. A ring always fits a whole number of repeats, so it never shows a seam.

## Props

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

<tbody>
  <tr>
    <td>
      <code>
        marquee-text
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        ''
      </code>
    </td>
    
    <td>
      The text that loops along the path. Repeats are joined with a space
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        shape
      </code>
    </td>
    
    <td>
      <code>
        'arc' | 'wave' | 'circle' | 'line'
      </code>
    </td>
    
    <td>
      <code>
        'arc'
      </code>
    </td>
    
    <td>
      The path the text travels on. Circle closes the loop into a ring that always fits a whole number of repeats
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        color
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        'currentColor'
      </code>
    </td>
    
    <td>
      Color of the text, or of its outline when outline is on. Follows the surrounding text color by default
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        separator
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        ''
      </code>
    </td>
    
    <td>
      A mark placed between repeats, such as ✦ or /. Leave empty to join repeats with a space
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        separator-color
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      —
    </td>
    
    <td>
      Paints the separator in its own solid color. Matches the text when unset
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        direction
      </code>
    </td>
    
    <td>
      <code>
        'left' | 'right'
      </code>
    </td>
    
    <td>
      <code>
        'left'
      </code>
    </td>
    
    <td>
      Which way the text travels. On a circle, right turns clockwise. A drag flips it to the way it was thrown
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        speed
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        2
      </code>
    </td>
    
    <td>
      How fast the text travels along the path. <code>
        0
      </code>
      
       keeps it still until dragged or scrolled
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        scroll-boost
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0
      </code>
    </td>
    
    <td>
      How much page scrolling speeds the loop up for a moment before it eases back. <code>
        0
      </code>
      
       turns it off
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        curve-amount
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        400
      </code>
    </td>
    
    <td>
      How far the path bends. On the arc it is the dip at the middle (negative arches up), on the wave it sets the height
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        font-size
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        96
      </code>
    </td>
    
    <td>
      Letter size in px for a 1440px wide loop. The whole loop scales with its container
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        font-weight
      </code>
    </td>
    
    <td>
      <code>
        number | string
      </code>
    </td>
    
    <td>
      <code>
        700
      </code>
    </td>
    
    <td>
      Weight of the letters
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        letter-spacing
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0
      </code>
    </td>
    
    <td>
      Extra space between letters in px, measured like <code>
        font-size
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        edge-fade
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0
      </code>
    </td>
    
    <td>
      How much of each side fades out, from 0 to 1. At 1 only the middle stays fully visible
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        outline
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        false
      </code>
    </td>
    
    <td>
      Draws the letters as outlines instead of solid text
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        interactive
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      Lets people drag the text and fling it. Rings follow the pointer around
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        pause-on-hover
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        false
      </code>
    </td>
    
    <td>
      Eases the text to a stop while the pointer is over it
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        class
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        ''
      </code>
    </td>
    
    <td>
      Classes merged onto the wrapper, for example to change the font
    </td>
  </tr>
</tbody>
</table>

## Accessibility

- The SVG has `role="img"` and an `aria-label` with the marquee text, so screen readers read it once instead of every repeat.
- When the user prefers reduced motion, the loop stays still; it can still be dragged.
- The loop pauses while it is off screen.
- Ported from [React Bits](https://reactbits.dev/text-animations/curved-loop) by David Haz.
