# Pixel Transition

> Retro pixel dissolve that covers a card in solid pixels, swaps the content underneath and clears again.

A card that dissolves into pixels, swaps to its second slot at the midpoint and clears again. Hover it (or tab to it) to reveal the second content; with `trigger="click"` it toggles on click, tap, Enter or Space. Quick hovers reverse smoothly instead of jumping.

<demo-pixel-transition>



</demo-pixel-transition>

## Installation

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

## Usage

```vue
<script setup lang="ts">
  import PixelTransition from '~/components/ui/pixel-transition/PixelTransition.vue';
</script>

<template>
  <PixelTransition
    pattern="ripple"
    pixel-animation="grow"
    pixel-shape="circle"
    :grid-size="16"
    :gap="2"
    aspect-ratio="125%"
  >
    <template #first>
      <img src="/night.webp" alt="" class="size-full object-cover" />
    </template>
    <template #second>
      <img src="/day.webp" alt="" class="size-full object-cover" />
    </template>
  </PixelTransition>
</template>
```

The pixels are drawn on one canvas. Progress runs from 0 to 1: the first half covers the card, the content swaps at the middle, the second half clears it. Ripple spreads from the pointer and wipe sweeps in from the edge the pointer crossed.

## Props

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

<tbody>
  <tr>
    <td>
      <code>
        grid-size
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        10
      </code>
    </td>
    
    <td>
      Pixels across the card (2–80). Rows follow so every pixel stays square.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        pixel-color
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        '#ffffff'
      </code>
    </td>
    
    <td>
      Color of the pixels. Any CSS color works.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        mosaic
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0
      </code>
    </td>
    
    <td>
      Gives each pixel a slightly different tone of the pixel color, 0–1.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        animation-step-duration
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.4
      </code>
    </td>
    
    <td>
      Seconds the pixels take to cover the card, and again to clear it.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        pattern
      </code>
    </td>
    
    <td>
      <code>
        'random' | 'dither' | 'ripple' | 'wipe'
      </code>
    </td>
    
    <td>
      <code>
        'random'
      </code>
    </td>
    
    <td>
      Order the pixels appear in.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        randomness
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.3
      </code>
    </td>
    
    <td>
      Breaks up the ripple and wipe fronts, 0–1.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        pixel-animation
      </code>
    </td>
    
    <td>
      <code>
        'pop' | 'grow' | 'fade'
      </code>
    </td>
    
    <td>
      <code>
        'pop'
      </code>
    </td>
    
    <td>
      How each pixel appears and disappears.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        pixel-shape
      </code>
    </td>
    
    <td>
      <code>
        'square' | 'rounded' | 'circle'
      </code>
    </td>
    
    <td>
      <code>
        'square'
      </code>
    </td>
    
    <td>
      Shape of the pixels while the transition runs. They merge into a solid cover at the midpoint.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        gap
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0
      </code>
    </td>
    
    <td>
      Space between pixels in px.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        fps
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0
      </code>
    </td>
    
    <td>
      Caps how often the pixels redraw for a choppy retro feel. <code>
        0
      </code>
      
       redraws every frame.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        trigger
      </code>
    </td>
    
    <td>
      <code>
        'hover' | 'click'
      </code>
    </td>
    
    <td>
      <code>
        'hover'
      </code>
    </td>
    
    <td>
      What starts the transition.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        active
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      —
    </td>
    
    <td>
      Controls the state from outside. Use with <code>
        v-model:active
      </code>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        once
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        false
      </code>
    </td>
    
    <td>
      Keeps the second content once it has been revealed.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        aspect-ratio
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        '100%'
      </code>
    </td>
    
    <td>
      Percentages work like padding-top (<code>
        '125%'
      </code>
      
      ), anything else is a CSS ratio (<code>
        '4 / 5'
      </code>
      
      ).
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        class
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        ''
      </code>
    </td>
    
    <td>
      Extra classes for the card (it is <code>
        w-75
      </code>
      
       by default).
    </td>
  </tr>
</tbody>
</table>

## Events

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

<tbody>
  <tr>
    <td>
      <code>
        update:active
      </code>
    </td>
    
    <td>
      <code>
        active: boolean
      </code>
    </td>
    
    <td>
      Fires when hover, focus or a click asks for the other content.
    </td>
  </tr>
</tbody>
</table>

## Slots

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

<tbody>
  <tr>
    <td>
      <code>
        first
      </code>
    </td>
    
    <td>
      Content shown by default.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        second
      </code>
    </td>
    
    <td>
      Content revealed by the transition
    </td>
  </tr>
</tbody>
</table>

## Accessibility

- The card is focusable. In hover mode, keyboard focus (`:focus-visible`) reveals the second content; in click mode it is a `role="button"` with `aria-pressed` and toggles on Enter or Space.
- The hidden layer uses `visibility: hidden`, so screen readers only read the visible content.
- With `prefers-reduced-motion: reduce` the swap happens in one frame without the pixel animation.
- Ported from [React Bits](https://reactbits.dev/animations/pixel-transition) by David Haz.
