# Spring Check

A to-do style checkbox. Click a row, or focus it and press Space or Enter, to check it. One spring value drives everything: the fill grows out of the box center and swells slightly past full, the tick draws in, the words fade, and a rule wipes across the label. A pointer press dips the box, keyboard toggles snap without animation.

<demo-spring-check>



</demo-spring-check>

## Installation

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

## Usage

```vue
<script setup lang="ts">
  import { ref } from 'vue';
  import SpringCheck from '~/components/ui/spring-check/SpringCheck.vue';

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

<template>
  <div class="flex flex-col items-start gap-1">
    <SpringCheck v-model="shipped" label="Ship the build" />
    <SpringCheck label="Update the changelog" />
    <SpringCheck label="Book the launch call" default-checked strike="center" />
  </div>
</template>
```

Leave out `v-model` to let the row keep its own state, starting from `default-checked`. A change to `v-model` from outside also animates on the spring.

## Props

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

<tbody>
  <tr>
    <td>
      <code>
        label
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        'Ship the build'
      </code>
    </td>
    
    <td>
      Words beside the box. The rule matches their width.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        model-value
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        undefined
      </code>
    </td>
    
    <td>
      Checked state (<code>
        v-model
      </code>
      
      ). Undefined means uncontrolled.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        default-checked
      </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 row and ignores input.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        color
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        '#ffffff'
      </code>
    </td>
    
    <td>
      Ink: the label, the ring, the rule and the focus outline.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        fill-color
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        '#ffffff'
      </code>
    </td>
    
    <td>
      Fill that swells out of the box center.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        check-color
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        '#0b0b0f'
      </code>
    </td>
    
    <td>
      Stroke of the tick drawn over the fill.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        box-size
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        28
      </code>
    </td>
    
    <td>
      Box side in px. Ring, gap and row height derive from it.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        box-radius
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        9
      </code>
    </td>
    
    <td>
      Box corner radius in px. Half the size makes a circle.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        font-size
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        18
      </code>
    </td>
    
    <td>
      Label size in px. The rule thickness derives from it.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        bounce
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.2
      </code>
    </td>
    
    <td>
      How far the fill swells past full. 0 arrives dead, 0.5 rebounds twice.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        strike-lag
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.12
      </code>
    </td>
    
    <td>
      Where on the spring the rule starts. 0 wipes with the fill.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        done-opacity
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.42
      </code>
    </td>
    
    <td>
      How much ink the words keep once checked.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        strike
      </code>
    </td>
    
    <td>
      <code>
        'left' | 'center' | 'right' | 'none'
      </code>
    </td>
    
    <td>
      <code>
        'left'
      </code>
    </td>
    
    <td>
      Where the rule wipes from, or no rule.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        aria-label
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        undefined
      </code>
    </td>
    
    <td>
      Accessible name when the label is not text.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        class
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        ''
      </code>
    </td>
    
    <td>
      Extra classes for the row.
    </td>
  </tr>
</tbody>
</table>

## Events

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

<tbody>
  <tr>
    <td>
      <code>
        update:modelValue
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      Fires on every toggle.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        change
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      Fires on every toggle.
    </td>
  </tr>
</tbody>
</table>

## Slots

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

<tbody>
  <tr>
    <td>
      <code>
        default
      </code>
    </td>
    
    <td>
      Replaces the label text. Set <code>
        aria-label
      </code>
      
       if it is not text.
    </td>
  </tr>
</tbody>
</table>

## Accessibility

- The row is a `<button role="checkbox">` with `aria-checked`, so Space and Enter toggle it.
- Keyboard focus draws an outline around the box in the ink color.
- With `prefers-reduced-motion`, the state changes without the spring or the press dip.
- Ported from [React Bits](https://reactbits.dev/micro/spring-check) by David Haz.
