# Line Sidebar

> List navigation with a rail of markers; items near the cursor shift, tint and grow their markers, and the clicked item stays lit.

A vertical menu beside a rail of marker lines. Move the cursor along the list and the nearest items slide right, blend toward the accent color and stretch their markers, fading off with distance. Click an item to pin it. Tune the reach, shift and falloff curve in the panel.

<demo-line-sidebar>



</demo-line-sidebar>

## Installation

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

## Usage

```vue
<script setup lang="ts">
  import { ref } from 'vue';
  import LineSidebar from '~/components/ui/line-sidebar/LineSidebar.vue';

  const active = ref<number | null>(null);

  function onItemClick(index: number, label: string) {
    console.log(index, label);
  }
</script>

<template>
  <LineSidebar
    v-model="active"
    :items="['Overview', 'Components', 'Animations', 'Backgrounds']"
    accent-color="#A855F7"
    :proximity-radius="100"
    :max-shift="30"
    falloff="smooth"
    @item-click="onItemClick"
  />
</template>
```

Every item exposes a single `--effect` custom property (0–1) eased by one
frame-rate-independent rAF loop — label color, horizontal shift, index
opacity and marker scale all read it, so nothing staggers. The clicked
item pins to full effect until another is chosen.

## Props

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

<tbody>
  <tr>
    <td>
      <code>
        items
      </code>
    </td>
    
    <td>
      <code>
        string[]
      </code>
    </td>
    
    <td>
      12 samples
    </td>
    
    <td>
      Menu labels
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        accent-color
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        '#A855F7'
      </code>
    </td>
    
    <td>
      Proximity/active tint
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        text-color
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        '#c4c4c4'
      </code>
    </td>
    
    <td>
      Resting label color
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        marker-color
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        '#6c6c6c'
      </code>
    </td>
    
    <td>
      Resting marker color
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        show-index
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      Show 01/02/… prefixes
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        show-marker
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      Show the marker rail
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        proximity-radius
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        100
      </code>
    </td>
    
    <td>
      Cursor influence radius (px)
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        max-shift
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        30
      </code>
    </td>
    
    <td>
      Max label shift (px)
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        falloff
      </code>
    </td>
    
    <td>
      <code>
        'linear' | 'smooth' | 'sharp'
      </code>
    </td>
    
    <td>
      <code>
        'smooth'
      </code>
    </td>
    
    <td>
      Proximity falloff curve
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        marker-length
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        60
      </code>
    </td>
    
    <td>
      Marker line length (px)
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        marker-gap
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0
      </code>
    </td>
    
    <td>
      Gap between marker and label (px)
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        tick-scale
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.5
      </code>
    </td>
    
    <td>
      In-between tick length ratio
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        scale-tick
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      Ticks also grow with proximity
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        item-gap
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        20
      </code>
    </td>
    
    <td>
      Vertical gap between items (px)
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        font-size
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        1.1
      </code>
    </td>
    
    <td>
      Label size (rem)
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        smoothing
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        100
      </code>
    </td>
    
    <td>
      Easing time constant (ms)
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        default-active
      </code>
    </td>
    
    <td>
      <code>
        number | null
      </code>
    </td>
    
    <td>
      <code>
        null
      </code>
    </td>
    
    <td>
      Item active on first render when <code>
        v-model
      </code>
      
       is not bound
    </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>

## v-model / Events

<table>
<thead>
  <tr>
    <th>
      API
    </th>
    
    <th>
      Type
    </th>
    
    <th>
      Description
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        modelValue
      </code>
    </td>
    
    <td>
      <code>
        number | null
      </code>
    </td>
    
    <td>
      Active item index
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        item-click
      </code>
    </td>
    
    <td>
      <code>
        (index: number, label: string)
      </code>
    </td>
    
    <td>
      Fired on item click
    </td>
  </tr>
</tbody>
</table>

## Accessibility

- The root is a `<nav>` landmark; the active item carries `aria-current="true"`.
- Items are list entries, not links. Wrap the labels in links or buttons in your own fork if they must be reachable by keyboard.
- The effect runs on one rAF loop that stops as soon as every item settles.
- Ported from [React Bits](https://reactbits.dev/components/line-sidebar) by David Haz.
