# Magnet Lines

> A field of magnetic lines that swing toward the cursor, ripple outward and spin with every click.

A WebGL2 field of lines that fills its container. Move the cursor and every line swings on a spring to follow it, with the motion rippling outward from the pointer. Click to send a ring that spins the lines as it travels. Pick a field mode (orbit, attract, spiral, dipole), draw lines as bars, arrows or compass needles, and tilt the plane in 3D.

<demo-magnet-lines>



</demo-magnet-lines>

## Installation

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

## Usage

```vue
<script setup lang="ts">
  import MagnetLines from '~/components/ui/magnet-lines/MagnetLines.vue';
</script>

<template>
  <!-- Fills its parent -->
  <div class="relative h-150 w-full">
    <MagnetLines mode="orbit" line-color="#efefef" :spacing="30" />
  </div>

  <!-- Fixed square field -->
  <MagnetLines
    container-size="min(440px, 86%)"
    mode="attract"
    shape="needle"
    accent-color="#f43f5e"
    :spacing="56"
    :line-height="34"
  />
</template>
```

All lines are drawn in one instanced draw call. The render loop sleeps when
nothing moves, when the field is off screen, and when the tab is hidden.

## Props

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

<tbody>
  <tr>
    <td>
      <code>
        spacing
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        30
      </code>
    </td>
    
    <td>
      Distance between lines in px. The grid fills the area with as many lines as fit.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        rows
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      —
    </td>
    
    <td>
      Exact number of rows. Overrides <code>
        spacing
      </code>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        columns
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      —
    </td>
    
    <td>
      Exact number of columns. Overrides <code>
        spacing
      </code>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        container-size
      </code>
    </td>
    
    <td>
      <code>
        number | string
      </code>
    </td>
    
    <td>
      —
    </td>
    
    <td>
      Draws a square field of this width instead of filling the parent. Numbers are px; strings take any CSS length.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        line-color
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        '#efefef'
      </code>
    </td>
    
    <td>
      Color of the lines.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        accent-color
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      —
    </td>
    
    <td>
      Color lines take on near the cursor. Compass needles use it for their north half.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        line-width
      </code>
    </td>
    
    <td>
      <code>
        number | string
      </code>
    </td>
    
    <td>
      <code>
        2
      </code>
    </td>
    
    <td>
      Thickness of each line. Numbers are px; strings take any CSS length.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        line-height
      </code>
    </td>
    
    <td>
      <code>
        number | string
      </code>
    </td>
    
    <td>
      <code>
        18
      </code>
    </td>
    
    <td>
      Length of each line. Numbers are px; strings take any CSS length.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        base-angle
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        -10
      </code>
    </td>
    
    <td>
      Resting angle in degrees, used before the intro and outside the reach.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        mode
      </code>
    </td>
    
    <td>
      <code>
        'orbit' | 'attract' | 'spiral' | 'dipole'
      </code>
    </td>
    
    <td>
      <code>
        'orbit'
      </code>
    </td>
    
    <td>
      Rings around the cursor, lines aiming at it, a vortex, or a bar magnet that turns with your movement.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        shape
      </code>
    </td>
    
    <td>
      <code>
        'line' | 'arrow' | 'needle'
      </code>
    </td>
    
    <td>
      <code>
        'line'
      </code>
    </td>
    
    <td>
      Rounded bar, arrow showing the field direction, or two-tone compass needle.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        rainbow
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        false
      </code>
    </td>
    
    <td>
      Colors every line by the direction it points, so the field becomes a color wheel.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        radius
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        200
      </code>
    </td>
    
    <td>
      Size of the area around the cursor where lines are brightest and longest, in px.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        fade
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.65
      </code>
    </td>
    
    <td>
      How much lines dim away from the cursor, 0 to 1. 0 keeps every line at full strength.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        edge-fade
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0
      </code>
    </td>
    
    <td>
      Fades the edges into the page, 0 to 1.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        stretch
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.4
      </code>
    </td>
    
    <td>
      How much lines grow near the cursor, 0 to 2.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        reach
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0
      </code>
    </td>
    
    <td>
      Distance in px within which lines follow the cursor. Farther lines settle back. 0 means every line follows.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        scatter
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0
      </code>
    </td>
    
    <td>
      Randomizes each line's resting angle, 0 to 1, like loose iron filings. Pairs well with <code>
        reach
      </code>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        inertia
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.35
      </code>
    </td>
    
    <td>
      How slowly the lines swing into place, 0 to 1. 0 snaps them instantly.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        bounce
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.3
      </code>
    </td>
    
    <td>
      How much the lines overshoot and wobble as they settle, 0 to 1.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        ripple
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.3
      </code>
    </td>
    
    <td>
      Delays lines by their distance from the cursor so movement ripples outward, 0 to 1.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        click-pulse
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      Clicking sends a ring that spins the lines as it travels outward.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        intro
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      Lines fade in from the center and swing into place on mount.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        idle
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        false
      </code>
    </td>
    
    <td>
      Moves the field on its own when the cursor is away.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        tilt-x
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0
      </code>
    </td>
    
    <td>
      Tilts the field back in 3D around the horizontal axis, in degrees. A full-size field stretches to the horizon.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        tilt-y
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0
      </code>
    </td>
    
    <td>
      Tilts the field in 3D around the vertical axis, in degrees.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        max-device-pixel-ratio
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        2
      </code>
    </td>
    
    <td>
      Highest pixel ratio the canvas renders at.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        class
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        ''
      </code>
    </td>
    
    <td>
      Extra classes merged onto the container.
    </td>
  </tr>
</tbody>
</table>

## Accessibility

- The canvas is decorative (`aria-hidden="true"`) and never captures pointer events, so content layered on top stays clickable.
- With `prefers-reduced-motion: reduce`, the intro, idle drift, click pulses, springs and ripple lag are all turned off; lines snap straight to the field.
- Ported from [React Bits](https://reactbits.dev/backgrounds/magnet-lines) by David Haz.
