# Swipe Toast

> Toast that swipes down to dismiss and burns a thin fuse for its remaining time.

A toast that rises in, burns a thin fuse along its edge for the time it has left, and closes itself when the fuse runs out. Drag it down (or flick it) to dismiss, hover or focus it to freeze the fuse, and press Escape to close it from the keyboard.

<demo-swipe-toast>



</demo-swipe-toast>

## Installation

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

## Usage

```vue
<script setup lang="ts">
  import { ref } from 'vue';
  import SwipeToast from '~/components/ui/swipe-toast/SwipeToast.vue';
  import type { SwipeToastCloseReason } from '~/components/ui/swipe-toast/types';

  const open = ref(true);

  function undo() {
    // restore the archived file
  }

  function onClose(reason: SwipeToastCloseReason) {
    console.log('closed by', reason);
    open.value = false;
  }
</script>

<template>
  <SwipeToast
    :open="open"
    icon="lucide:circle-check"
    title="File archived"
    description="Moved to Archive"
    action-label="Undo"
    :duration="4000"
    inline
    @action="undo"
    @close="onClose"
  />
</template>
```

Without `inline`, the toast is fixed to the bottom-right corner of the viewport. With `inline`, it sits in normal flow and its slot collapses smoothly after it leaves, so a column of toasts closes up without jumping.

## Props

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

<tbody>
  <tr>
    <td>
      <code>
        title
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        'File archived'
      </code>
    </td>
    
    <td>
      The first line (or use the <code>
        title
      </code>
      
       slot).
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        description
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        ''
      </code>
    </td>
    
    <td>
      The dimmer second line. Empty hides it.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        icon
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        ''
      </code>
    </td>
    
    <td>
      Iconify name of an 18px icon before the text (or use the <code>
        icon
      </code>
      
       slot).
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        action-label
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        ''
      </code>
    </td>
    
    <td>
      The inverted button. Empty hides it.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        open
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      Flip to <code>
        false
      </code>
      
       to close from outside. <code>
        true
      </code>
      
       again re-enters.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        background
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        '#27272a'
      </code>
    </td>
    
    <td>
      The card surface, and the action text.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        color
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        '#f5f5f5'
      </code>
    </td>
    
    <td>
      Ink: title, description at 66% and the action fill.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        fuse-color
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        '#f5a524'
      </code>
    </td>
    
    <td>
      The burning line.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        width
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        356
      </code>
    </td>
    
    <td>
      Card width in pixels, capped at the parent when inline.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        radius
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        12
      </code>
    </td>
    
    <td>
      Corner radius in pixels.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        slide-ms
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        400
      </code>
    </td>
    
    <td>
      How long the rise and the drop take.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        settle-bounce
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.2
      </code>
    </td>
    
    <td>
      Overshoot of the return after an abandoned swipe. <code>
        0
      </code>
      
       stops dead.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        swipe-distance
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        40
      </code>
    </td>
    
    <td>
      How far a slow drag must go before release dismisses. A flick always does.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        duration
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        4000
      </code>
    </td>
    
    <td>
      Milliseconds until it closes itself. Changing it re-arms the fuse. <code>
        0
      </code>
      
       keeps it until dismissed.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        fuse
      </code>
    </td>
    
    <td>
      <code>
        'bottom' | 'top' | 'none'
      </code>
    </td>
    
    <td>
      <code>
        'bottom'
      </code>
    </td>
    
    <td>
      Which edge the line burns along. <code>
        none
      </code>
      
       keeps the timer but shows nothing.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        pause-on-hover
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      Hovering freezes the line and the timer.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        close-button
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        false
      </code>
    </td>
    
    <td>
      Adds a cross at the end of the row.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        inline
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        false
      </code>
    </td>
    
    <td>
      In flow inside the parent instead of fixed to the viewport corner.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        dismissible
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      Off removes the swipe and Escape.
    </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>
        action
      </code>
    </td>
    
    <td>
      —
    </td>
    
    <td>
      The action button was pressed, before the close.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        close
      </code>
    </td>
    
    <td>
      <code>
        'timeout' | 'swipe' | 'action' | 'close' | 'escape' | 'programmatic'
      </code>
    </td>
    
    <td>
      Fired after the exit, and after the inline slot has collapsed.
    </td>
  </tr>
</tbody>
</table>

## Slots

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

<tbody>
  <tr>
    <td>
      <code>
        icon
      </code>
    </td>
    
    <td>
      Replaces the <code>
        icon
      </code>
      
       prop.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        title
      </code>
    </td>
    
    <td>
      Replaces the <code>
        title
      </code>
      
       text.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        description
      </code>
    </td>
    
    <td>
      Replaces the <code>
        description
      </code>
      
       text.
    </td>
  </tr>
</tbody>
</table>

## Accessibility

- The card is a polite live region (`role="status"`, `aria-live="polite"`), so screen readers announce it without interrupting.
- The card is focusable; focus pauses the fuse just like hover, and Escape closes it.
- When the action or close button is pressed from the keyboard, the toast closes instantly instead of animating.
- With `prefers-reduced-motion`, the rise and drop become a plain fade and the swipe-out skips the throw.
- Ported from [React Bits](https://reactbits.dev/micro/swipe-toast) by David Haz.
