# Flowing Menu

> A full-height menu with one liquid band that glides and stretches between rows and reveals a scrolling marquee of each item.

A full-height menu where one band follows your cursor from row to row. Its two edges are springs of different stiffness, so the band stretches like liquid on the way and leans toward the cursor inside a row. Under the band each row shows a marquee of its text and image; moving the cursor sideways speeds the marquee up or reverses it. On mount the rows rise in, the lines draw and the band sweeps once down the menu. Tab through the rows to move the band with the keyboard.

<demo-flowing-menu>



</demo-flowing-menu>

## Installation

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

## Usage

```vue
<script setup lang="ts">
  import FlowingMenu from '~/components/ui/flowing-menu/FlowingMenu.vue';
  import type { FlowingMenuItem } from '~/components/ui/flowing-menu/types';

  const items: FlowingMenuItem[] = [
    {
      link: '#',
      text: 'Mojave',
      meta: '2018',
      image: 'https://picsum.photos/600/400?random=1',
    },
    {
      link: '#',
      text: 'Sonoma',
      meta: '2023',
      image: 'https://picsum.photos/600/400?random=2',
    },
    {
      link: '#',
      text: 'Monterey',
      meta: '2021',
      image: 'https://picsum.photos/600/400?random=3',
    },
    {
      link: '#',
      text: 'Sequoia',
      meta: '2024',
      image: 'https://picsum.photos/600/400?random=4',
    },
  ];

  function onItemClick(item: FlowingMenuItem, index: number) {
    console.log(index, item.text);
  }
</script>

<template>
  <div class="relative h-150 w-full">
    <FlowingMenu
      :items="items"
      :band-inset="12"
      :band-radius="999"
      carry-content
      image-shape="circle"
      dim-inactive
      :dividers="false"
      marquee-bg-color="#ff6a3d"
      @item-click="onItemClick"
    />
  </div>
</template>
```

The menu fills its parent's height and splits it evenly between rows, so give the parent a height.

## 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>
        FlowingMenuItem[]
      </code>
    </td>
    
    <td>
      <code>
        []
      </code>
    </td>
    
    <td>
      Menu rows.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        speed
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        60
      </code>
    </td>
    
    <td>
      Marquee speed in px per second.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        direction
      </code>
    </td>
    
    <td>
      <code>
        'left' | 'right' | 'alternate'
      </code>
    </td>
    
    <td>
      <code>
        'left'
      </code>
    </td>
    
    <td>
      Which way the marquee runs. Alternate flips every other row.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        follow-cursor
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.4
      </code>
    </td>
    
    <td>
      How much moving the cursor sideways speeds up or reverses the marquee (0–1).
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        duration
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.5
      </code>
    </td>
    
    <td>
      Time in seconds the band takes to glide to a row.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        stretch
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.6
      </code>
    </td>
    
    <td>
      How much the band stretches while it moves, from 0 (rigid) to 1.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        magnet
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.25
      </code>
    </td>
    
    <td>
      How much the band leans toward the cursor inside a row (0–1).
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        band-inset
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0
      </code>
    </td>
    
    <td>
      Gap in px between the band and the row edges.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        band-radius
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0
      </code>
    </td>
    
    <td>
      Corner radius of the band in px. Use a large value for a capsule.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        carry-content
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        false
      </code>
    </td>
    
    <td>
      Moves the marquee with the band instead of keeping it fixed in each row.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        text-color
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        '#ffffff'
      </code>
    </td>
    
    <td>
      Color of the menu text.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        bg-color
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        '#120F17'
      </code>
    </td>
    
    <td>
      Background color of the menu.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        marquee-bg-color
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        '#ffffff'
      </code>
    </td>
    
    <td>
      Background color of the band.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        marquee-text-color
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        '#120F17'
      </code>
    </td>
    
    <td>
      Text color inside the band.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        border-color
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        'rgba(255, 255, 255, 0.14)'
      </code>
    </td>
    
    <td>
      Color of the lines between rows.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        dividers
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      Draws lines between rows.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        dim-inactive
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        false
      </code>
    </td>
    
    <td>
      Fades the other rows while the band is on one.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        align
      </code>
    </td>
    
    <td>
      <code>
        'center' | 'left'
      </code>
    </td>
    
    <td>
      <code>
        'center'
      </code>
    </td>
    
    <td>
      Alignment of the row labels.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        show-index
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        false
      </code>
    </td>
    
    <td>
      Numbers each row.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        show-arrow
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        false
      </code>
    </td>
    
    <td>
      Adds an arrow to the end of each row.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        image-shape
      </code>
    </td>
    
    <td>
      <code>
        'pill' | 'rounded' | 'circle'
      </code>
    </td>
    
    <td>
      <code>
        'pill'
      </code>
    </td>
    
    <td>
      Shape of the images in the band.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        intro
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      Rows rise in, the lines draw and the band sweeps once down the menu.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        active-index
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      —
    </td>
    
    <td>
      Marks the current page row with a dot and <code>
        aria-current="page"
      </code>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        aria-label
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        'Menu'
      </code>
    </td>
    
    <td>
      Accessible name of the navigation.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        class
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        ''
      </code>
    </td>
    
    <td>
      Extra classes for the menu.
    </td>
  </tr>
</tbody>
</table>

### FlowingMenuItem

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

<tbody>
  <tr>
    <td>
      <code>
        text
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      Row label.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        link
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      Renders the row as a link. Without it, a button.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        image
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      Image shown between the marquee copies.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        marquee
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      Text that scrolls in the band. Defaults to <code>
        text
      </code>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        meta
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      Short note at the end of the row.
    </td>
  </tr>
</tbody>
</table>

## Events

<table>
<thead>
  <tr>
    <th>
      Event
    </th>
    
    <th>
      Payload
    </th>
    
    <th>
      Description
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        item-click
      </code>
    </td>
    
    <td>
      <code>
        (item: FlowingMenuItem, index: number, event: MouseEvent)
      </code>
    </td>
    
    <td>
      A row was clicked.
    </td>
  </tr>
</tbody>
</table>

## Accessibility

- The menu is a `<nav>` with a list; each row is a real link (or a button when it has no `link`).
- Keyboard focus moves the band to the focused row and shows a focus ring; leaving the menu sends the band out.
- The band, marquee copies, index, meta and arrow are `aria-hidden`, so screen readers read each row label once.
- With `prefers-reduced-motion: reduce` the band jumps instead of springing, the marquee stays still and the intro does not play.
- Ported from [React Bits](https://reactbits.dev/components/flowing-menu) by David Haz.
