# Bell Toggle

> Pill toggle where a press rings the bell, crossfades the label and unfurls the pill.

A subscribe toggle. Click it and the pill inverts, the label crossfades and the width springs to the new label while the bell rings with a decaying swing and sound waves. When the `count` grows while it is on, a badge pops in and the bell gives a short wobble. Keyboard toggles switch state without the ring.

<demo-bell-toggle>



</demo-bell-toggle>

## Installation

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

## Usage

```vue
<script setup lang="ts">
  import { ref } from 'vue';
  import BellToggle from '~/components/ui/bell-toggle/BellToggle.vue';

  const subscribed = ref(false);
  const unread = ref(0);
</script>

<template>
  <BellToggle
    v-model:pressed="subscribed"
    off-label="Notify me"
    on-label="You'll be notified"
    :count="unread"
    :ring-amplitude="17"
    :ring-passes="5"
  />
</template>
```

The ring is a Web Animations API keyframe track. Each pass swings the other way with a smaller angle (`ring-decay` shapes the falloff), and the passes bunch up toward the end like a real bell settling. A new ring starts from the bell's current angle, so rapid toggles never snap. The width change is a `clip-path` inset driven by one spring, so the text never reflows.

## Props

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

<tbody>
  <tr>
    <td>
      <code>
        off-label
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        'Notify me'
      </code>
    </td>
    
    <td>
      Label while off.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        on-label
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        "You'll be notified"
      </code>
    </td>
    
    <td>
      Label while on.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        icon
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        'lucide:bell'
      </code>
    </td>
    
    <td>
      Iconify name of the bell glyph. The <code>
        icon
      </code>
      
       slot overrides it.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        label
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        off-label
      </code>
    </td>
    
    <td>
      Accessible name of the button.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        color
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        '#f5f5f5'
      </code>
    </td>
    
    <td>
      Text and icon color while off.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        background
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        '#27272a'
      </code>
    </td>
    
    <td>
      Pill color while off.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        on-color
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        '#18181b'
      </code>
    </td>
    
    <td>
      Text and icon color while on.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        on-background
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        '#f5f5f5'
      </code>
    </td>
    
    <td>
      Pill color while on.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        size
      </code>
    </td>
    
    <td>
      <code>
        'sm' | 'md' | 'lg'
      </code>
    </td>
    
    <td>
      <code>
        'md'
      </code>
    </td>
    
    <td>
      Pill height 36, 44 or 52 px; font, icon and padding follow.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        radius
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        22
      </code>
    </td>
    
    <td>
      Corner radius in px.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        ring-amplitude
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        17
      </code>
    </td>
    
    <td>
      First swing angle in degrees.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        ring-passes
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        5
      </code>
    </td>
    
    <td>
      Number of swings.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        ring-decay
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        1
      </code>
    </td>
    
    <td>
      How fast the swings shrink. Higher dies out faster.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        ring-duration
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        820
      </code>
    </td>
    
    <td>
      Ring length in ms.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        ring-pivot
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        16
      </code>
    </td>
    
    <td>
      Hinge height as a percent of the icon, from the top.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        crossfade-ms
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        200
      </code>
    </td>
    
    <td>
      Label crossfade and color transition in ms.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        reveal-bounce
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0
      </code>
    </td>
    
    <td>
      Bounce of the width spring.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        count
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0
      </code>
    </td>
    
    <td>
      Unread count. Shown in the badge while on; an increase wobbles the bell.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        badge
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      Show the count badge.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        badge-color
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        '#ef4444'
      </code>
    </td>
    
    <td>
      Badge background.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        badge-text-color
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        '#ffffff'
      </code>
    </td>
    
    <td>
      Badge digit color.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        waves
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      Sound waves that pulse on each swing.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        clapper
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        false
      </code>
    </td>
    
    <td>
      Draw a bell with a separate clapper that swings a little behind.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        pressed
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        undefined
      </code>
    </td>
    
    <td>
      Controlled state (<code>
        v-model:pressed
      </code>
      
      ).
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        default-pressed
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        false
      </code>
    </td>
    
    <td>
      Initial state when uncontrolled.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        disabled
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        false
      </code>
    </td>
    
    <td>
      Dims the pill and ignores input. The state is kept.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        class
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        ''
      </code>
    </td>
    
    <td>
      Extra classes for the root.
    </td>
  </tr>
</tbody>
</table>

## Events

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

<tbody>
  <tr>
    <td>
      <code>
        update:pressed
      </code>
    </td>
    
    <td>
      <code>
        (pressed: boolean)
      </code>
    </td>
    
    <td>
      The pill was toggled.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        change
      </code>
    </td>
    
    <td>
      <code>
        (pressed: boolean)
      </code>
    </td>
    
    <td>
      Same moment.
    </td>
  </tr>
</tbody>
</table>

## Slots

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

<tbody>
  <tr>
    <td>
      <code>
        icon
      </code>
    </td>
    
    <td>
      Custom glyph that rings in place of the bell (not with <code>
        clapper
      </code>
      
      ).
    </td>
  </tr>
</tbody>
</table>

## Accessibility

- A native `button` with `aria-pressed`. Its accessible name is `label`, or `off-label` if not set.
- Space and Enter toggle it. Keyboard toggles skip the ring and the width spring.
- Focus shows an inset ring.
- With `prefers-reduced-motion: reduce`, the bell does not ring, the width jumps and the label does not blur.
- Ported from [React Bits](https://reactbits.dev/micro/bell-toggle) by David Haz.
