# Pulse Heart

> Like button that contracts to a dot, flips colour and pulses back as the count ticks over.

A like button with a heartbeat. Click it and the glyph contracts to a dot, flips to the liked colour at its smallest point, then swells back past full size with a back-ease while the pill beats and the changed digit of the count rolls over. Click again to unlike. Keyboard presses switch instantly.

<demo-pulse-heart>



</demo-pulse-heart>

## Installation

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

## Usage

```vue
<script setup lang="ts">
  import { ref } from 'vue';
  import PulseHeart from '~/components/ui/pulse-heart/PulseHeart.vue';

  const liked = ref(false);
</script>

<template>
  <PulseHeart v-model:liked="liked" :count="1204" liked-color="#ff4d6d" />
</template>
```

## Props

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

<tbody>
  <tr>
    <td>
      <code>
        liked
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        undefined
      </code>
    </td>
    
    <td>
      Controlled state (<code>
        v-model:liked
      </code>
      
      ). Unset = uncontrolled
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        default-liked
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        false
      </code>
    </td>
    
    <td>
      Initial state when uncontrolled
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        count
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0
      </code>
    </td>
    
    <td>
      The number shown; a press adds or removes one
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        show-count
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      Show the count beside the glyph
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        icon
      </code>
    </td>
    
    <td>
      <code>
        'heart' | 'star' | 'thumb'
      </code>
    </td>
    
    <td>
      <code>
        'heart'
      </code>
    </td>
    
    <td>
      Built-in glyph (or use the <code>
        icon
      </code>
      
       slot)
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        idle-outline
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      Idle glyph is an outline; <code>
        false
      </code>
      
       makes it solid
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        size
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        40
      </code>
    </td>
    
    <td>
      Glyph size in px; the pill and text scale with it
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        corner
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        32
      </code>
    </td>
    
    <td>
      Pill corner radius in px
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        liked-color
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        '#ff4d6d'
      </code>
    </td>
    
    <td>
      Glyph colour when liked
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        idle-color
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        '#8b8b93'
      </code>
    </td>
    
    <td>
      Glyph colour when idle
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        pill-color
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        '#232326'
      </code>
    </td>
    
    <td>
      Pill background
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        text-color
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        '#f5f5f5'
      </code>
    </td>
    
    <td>
      Count colour
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        duration
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        560
      </code>
    </td>
    
    <td>
      The whole pulse, in ms
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        dot-size
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.3
      </code>
    </td>
    
    <td>
      How small the glyph contracts, as a fraction of its size
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        overshoot
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        1.7
      </code>
    </td>
    
    <td>
      Back-ease overshoot of the swell
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        beat
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        3
      </code>
    </td>
    
    <td>
      How much the pill contracts with the beat, in percent
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        roll-duration
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        350
      </code>
    </td>
    
    <td>
      Count roll duration, in ms
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        disabled
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        false
      </code>
    </td>
    
    <td>
      Disables the button
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        label
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        'Like'
      </code>
    </td>
    
    <td>
      Accessible name
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        class
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        ''
      </code>
    </td>
    
    <td>
      Extra classes for the button
    </td>
  </tr>
</tbody>
</table>

## Events

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

<tbody>
  <tr>
    <td>
      <code>
        change
      </code>
    </td>
    
    <td>
      <code>
        (liked: boolean, count: number)
      </code>
    </td>
    
    <td>
      Every press, with the next state
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        update:liked
      </code>
    </td>
    
    <td>
      <code>
        liked: boolean
      </code>
    </td>
    
    <td>
      For <code>
        v-model:liked
      </code>
    </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; it scales as a whole instead of the path
    </td>
  </tr>
</tbody>
</table>

## Accessibility

- Native `<button>` with `aria-pressed`; its accessible name is “Like, 1,204” (the visible count is `aria-hidden`).
- Keyboard activation and `prefers-reduced-motion` skip the pulse and switch state instantly.
- Focus ring uses the liked colour; on coarse pointers the hit area is at least 44×44px.
- Ported from [React Bits](https://reactbits.dev/micro/pulse-heart) by David Haz.
