# Pill Nav

> A pill navigation bar where a fill rises through each link as its label rolls over, with a sliding active dot, a gliding pill option and a menu that folds in on small screens.

A pill navigation bar. Hover a link and a circle rises through it while the label rolls over. A small dot slides to the current page. Switch to the glide effect to slide one pill between links instead. When the links no longer fit, they fold into a menu button that opens a panel.

<demo-pill-nav>



</demo-pill-nav>

## Installation

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

## Usage

```vue
<script setup lang="ts">
  import PillNav from '~/components/ui/pill-nav/PillNav.vue';
  import type { PillNavItem } from '~/components/ui/pill-nav/types';

  const items: PillNavItem[] = [
    { label: 'Home', href: '/', icon: 'lucide:house' },
    { label: 'Journal', href: '/journal' },
    { label: 'Photos', href: '/photos' },
    { label: 'About', href: '/about' },
  ];
</script>

<template>
  <PillNav
    logo="/logo.svg"
    logo-alt="Acme"
    :items="items"
    active-href="/"
    theme="dark"
    hover-effect="fill"
    indicator="dot"
  />
</template>
```

Pass a router link with `link-component` (for example `:link-component="NuxtLink"`). Use the `logo` slot instead of `logo` to render your own mark.

## 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>
        PillNavItem[]
      </code>
    </td>
    
    <td>
      4 samples
    </td>
    
    <td>
      The navigation links. Items without an <code>
        href
      </code>
      
       render as buttons.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        logo
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      —
    </td>
    
    <td>
      Image URL shown in the round logo button. It spins on hover.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        logo-alt
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        'Logo'
      </code>
    </td>
    
    <td>
      Accessible label for the logo link.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        logo-href
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        '#'
      </code>
    </td>
    
    <td>
      Where the logo links to.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        active-href
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      —
    </td>
    
    <td>
      Marks the item with this href as the current page. Leave it out to let the nav track clicks itself.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        default-active-index
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0
      </code>
    </td>
    
    <td>
      Which item starts active when <code>
        active-href
      </code>
      
       is not set.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        link-component
      </code>
    </td>
    
    <td>
      <code>
        string | Component
      </code>
    </td>
    
    <td>
      <code>
        'a'
      </code>
    </td>
    
    <td>
      Renders the links, so you can pass your router's link. It receives <code>
        href
      </code>
      
      , <code>
        class
      </code>
      
       and children.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        theme
      </code>
    </td>
    
    <td>
      <code>
        'dark' | 'light'
      </code>
    </td>
    
    <td>
      <code>
        'dark'
      </code>
    </td>
    
    <td>
      Color scheme of the surfaces, labels and highlight.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        layout
      </code>
    </td>
    
    <td>
      <code>
        'split' | 'joined'
      </code>
    </td>
    
    <td>
      <code>
        'split'
      </code>
    </td>
    
    <td>
      Split puts the logo in its own bubble beside the bar. Joined holds everything in one capsule.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        item-style
      </code>
    </td>
    
    <td>
      <code>
        'pill' | 'plain'
      </code>
    </td>
    
    <td>
      <code>
        'pill'
      </code>
    </td>
    
    <td>
      Pill gives every item its own raised pill. Plain shows the labels straight on the bar.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        hover-effect
      </code>
    </td>
    
    <td>
      <code>
        'fill' | 'glide' | 'none'
      </code>
    </td>
    
    <td>
      <code>
        'fill'
      </code>
    </td>
    
    <td>
      Fill rises a circle through the hovered pill as its label rolls over. Glide slides one pill to the cursor.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        indicator
      </code>
    </td>
    
    <td>
      <code>
        'dot' | 'pill' | 'none'
      </code>
    </td>
    
    <td>
      <code>
        'dot'
      </code>
    </td>
    
    <td>
      How the current page is marked: a dot that slides between items, or a filled pill.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        surface
      </code>
    </td>
    
    <td>
      <code>
        'glass' | 'solid' | 'none'
      </code>
    </td>
    
    <td>
      <code>
        'glass'
      </code>
    </td>
    
    <td>
      Background of the bar and logo: frosted glass, an opaque fill, or nothing.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        size
      </code>
    </td>
    
    <td>
      <code>
        'sm' | 'md' | 'lg'
      </code>
    </td>
    
    <td>
      <code>
        'md'
      </code>
    </td>
    
    <td>
      Height, padding and text size.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        highlight-color
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      —
    </td>
    
    <td>
      Color of the fill, the gliding pill and the dot. Labels on it pick black or white for contrast.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        highlight-text-color
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      —
    </td>
    
    <td>
      Overrides the label color on the highlight.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        bar-color
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      —
    </td>
    
    <td>
      Overrides the background of the bar and logo.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        text-color
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      —
    </td>
    
    <td>
      Overrides the label color.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        bounce
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.3
      </code>
    </td>
    
    <td>
      How much the gliding pill and the dot overshoot as they settle, from 0 to 1.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        stretch
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.5
      </code>
    </td>
    
    <td>
      How far the gliding pill stretches toward its target before the back edge catches up, from 0 to 1.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        collapse
      </code>
    </td>
    
    <td>
      <code>
        'auto' | 'always' | 'never'
      </code>
    </td>
    
    <td>
      <code>
        'auto'
      </code>
    </td>
    
    <td>
      When the links fold into a menu button. Auto folds them only when they no longer fit.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        intro
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      Pops the logo in and unrolls the bar on mount.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        aria-label
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        'Main'
      </code>
    </td>
    
    <td>
      Accessible name of the nav landmark.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        class
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        ''
      </code>
    </td>
    
    <td>
      Extra classes for the nav.
    </td>
  </tr>
</tbody>
</table>

### PillNavItem

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

<tbody>
  <tr>
    <td>
      <code>
        label
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      Display text
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        href
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      Link URL. Leave it out to render a button
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        icon
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      Iconify name, e.g. <code>
        lucide:house
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        ariaLabel
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      Accessible name
    </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: PillNavItem, index: number, event: MouseEvent)
      </code>
    </td>
    
    <td>
      Fired when an item is clicked.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        menu-toggle
      </code>
    </td>
    
    <td>
      <code>
        (open: boolean)
      </code>
    </td>
    
    <td>
      Fired when the folded menu opens or closes.
    </td>
  </tr>
</tbody>
</table>

## Slots

<table>
<thead>
  <tr>
    <th>
      Slot
    </th>
    
    <th>
      Description
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        logo
      </code>
    </td>
    
    <td>
      Custom content for the round logo button (replaces <code>
        logo
      </code>
      
      ).
    </td>
  </tr>
</tbody>
</table>

## Accessibility

- Renders a `<nav>` landmark named by `aria-label`. The current item gets `aria-current="page"`.
- Every link is a real `<a>` (or `<button>` without `href`) with a visible focus ring. Keyboard focus also moves the gliding pill.
- The menu button exposes `aria-expanded` and `aria-controls`. Escape and clicks outside close the menu; Escape returns focus to the button.
- With `prefers-reduced-motion: reduce`, the intro, springs and roll transitions are switched off.
- Ported from [React Bits](https://reactbits.dev/components/pill-nav) by David Haz.
