# Bounce Cards

> A fan of photo cards that bounce in, then spread apart and straighten the one you hover.

A loose fan of bordered photo cards that pop in one after another on springs. Hover a card and it straightens, lifts and grows while its neighbours slide away; move off and the fan settles back. Cards become buttons when you listen to `card-click`, and a click or Enter gives the card a little squash. Try the presets in the panel.

<demo-bounce-cards>



</demo-bounce-cards>

## Installation

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

## Usage

```vue
<script setup lang="ts">
  import BounceCards from '~/components/ui/bounce-cards/BounceCards.vue';

  const images = [
    '/photos/one.webp',
    { src: '/photos/two.webp', alt: 'Mountains at dusk' },
    '/photos/three.webp',
  ];

  function onCardClick(index: number) {
    console.log('clicked', index);
  }
</script>

<template>
  <BounceCards
    :images="images"
    :container-width="500"
    :container-height="320"
    :spread="85"
    :rotation="10"
    @card-click="onCardClick"
  />
</template>
```

Every card is one spring body (position, rotation, scale) stepped at 240 Hz on a single rAF loop that stops once everything rests, so hovering never flickers.

## Props

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

<tbody>
  <tr>
    <td>
      <code>
        images
      </code>
    </td>
    
    <td>
      <code>
        Array<string | { src: string; alt?: string }>
      </code>
    </td>
    
    <td>
      <code>
        []
      </code>
    </td>
    
    <td>
      Images for the cards, from left to right
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        container-width
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        400
      </code>
    </td>
    
    <td>
      Width of the container in px
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        container-height
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        400
      </code>
    </td>
    
    <td>
      Height of the container in px
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        card-size
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        200
      </code>
    </td>
    
    <td>
      Width and height of each card in px
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        spread
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        85
      </code>
    </td>
    
    <td>
      Horizontal distance between cards in px
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        rotation
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        10
      </code>
    </td>
    
    <td>
      Largest tilt in degrees. Each card leans by a different share of it, like a loose pile
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        arc
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0
      </code>
    </td>
    
    <td>
      Drops the outer cards by this many px so the fan curves. Negative values curve it up
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        push-distance
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        160
      </code>
    </td>
    
    <td>
      How far the other cards slide away from the hovered one, in px
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        hover-scale
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        1.06
      </code>
    </td>
    
    <td>
      Scale of the hovered card as it straightens and comes to the front
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        bounciness
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.6
      </code>
    </td>
    
    <td>
      How much the cards overshoot as they pop in, 0–1
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        animation-delay
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.5
      </code>
    </td>
    
    <td>
      Seconds before the first card pops in
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        animation-stagger
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.06
      </code>
    </td>
    
    <td>
      Seconds between cards popping in
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        enable-hover
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      Spreads the cards apart on hover
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        border-width
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        5
      </code>
    </td>
    
    <td>
      Width of the card border in px
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        border-color
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        '#ffffff'
      </code>
    </td>
    
    <td>
      Color of the card border
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        radius
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        25
      </code>
    </td>
    
    <td>
      Corner radius of the cards in px
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        shadow
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      Soft drop shadow that deepens on hover
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        transform-styles
      </code>
    </td>
    
    <td>
      <code>
        string[]
      </code>
    </td>
    
    <td>
      —
    </td>
    
    <td>
      Custom positions such as <code>
        'rotate(5deg) translate(-150px)'
      </code>
      
      . Overrides spread, rotation and arc
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        class
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        ''
      </code>
    </td>
    
    <td>
      Extra classes on the container
    </td>
  </tr>
</tbody>
</table>

## Events

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

<tbody>
  <tr>
    <td>
      <code>
        card-click
      </code>
    </td>
    
    <td>
      <code>
        index: number
      </code>
    </td>
    
    <td>
      Card clicked or activated with Enter / Space. Binding it makes the cards focusable buttons
    </td>
  </tr>
</tbody>
</table>

## Accessibility

- With a `card-click` listener every card is a `role="button"` with `tabindex="0"`, a label from its `alt` text (or "Card N") and a visible focus ring. Focusing a card spreads the fan like a hover.
- Without a listener the cards are plain images and stay out of the tab order.
- With `prefers-reduced-motion: reduce` the cards appear at full size without popping and the springs are critically damped.
- `touch-action: pan-y` keeps vertical page scrolling working on touch screens.
- Ported from [React Bits](https://reactbits.dev/components/bounce-cards) by David Haz.
