# Target Cursor

> A reticle cursor whose corner brackets snap around targets, matching their shape.

A spinning reticle replaces the mouse cursor. Move it over any element with the `cursor-target` class and the four corner brackets snap around it, follow its corner radius and show its `data-cursor-label`. Hold the mouse button to squeeze the brackets. Pass `container` to keep the cursor inside one area.

<demo-target-cursor>



</demo-target-cursor>

## Installation

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

## Usage

```vue
<script setup lang="ts">
  import { ref } from 'vue';
  import TargetCursor from '~/components/ui/target-cursor/TargetCursor.vue';

  const stage = ref<HTMLElement>();
</script>

<template>
  <div ref="stage" class="grid h-125 place-items-center gap-4">
    <img
      class="cursor-target rounded-[20px]"
      data-cursor-label="View"
      src="/sky.webp"
      alt=""
    />
    <button class="cursor-target rounded-xl" data-cursor-label="Play">
      Play
    </button>
  </div>
  <TargetCursor
    :container="stage"
    cursor-color="#ffffff"
    blend-mode="difference"
  />
</template>
```

The cursor is teleported to `<body>` and only mounts on devices with a fine pointer that can hover, so touch screens keep their normal behaviour. All motion runs on springs in one `requestAnimationFrame` loop that sleeps when nothing moves.

## Props

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

<tbody>
  <tr>
    <td>
      <code>
        target-selector
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        '.cursor-target'
      </code>
    </td>
    
    <td>
      CSS selector for the elements the brackets lock onto.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        cursor-color
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        '#ffffff'
      </code>
    </td>
    
    <td>
      Color of the brackets and dot.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        cursor-color-on-target
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      —
    </td>
    
    <td>
      Color the cursor blends to while locked onto a target.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        spin-duration
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        2
      </code>
    </td>
    
    <td>
      Seconds per turn while the cursor roams. <code>
        0
      </code>
      
       stops the spin.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        hover-duration
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.2
      </code>
    </td>
    
    <td>
      How long the brackets take to snap onto a target, in seconds.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        parallax-on
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      Lets the locked brackets lean slightly toward the cursor.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        hide-default-cursor
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      Hides the system cursor while this cursor is shown.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        size
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        36
      </code>
    </td>
    
    <td>
      Size of the reticle while roaming, in px.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        corner-size
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        12
      </code>
    </td>
    
    <td>
      Length of each bracket arm, in px.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        thickness
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        3
      </code>
    </td>
    
    <td>
      Stroke width of the brackets, in px.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        padding
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        6
      </code>
    </td>
    
    <td>
      Gap between a locked target and the brackets, in px.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        match-radius
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      Rounds the brackets to follow the target's corner radius.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        dot-size
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        4
      </code>
    </td>
    
    <td>
      Size of the center dot, in px. <code>
        0
      </code>
      
       hides it.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        blend-mode
      </code>
    </td>
    
    <td>
      <code>
        'normal' | 'difference' | 'exclusion' | 'multiply' | 'screen' | 'overlay'
      </code>
    </td>
    
    <td>
      <code>
        'difference'
      </code>
    </td>
    
    <td>
      CSS blend mode of the cursor. Difference keeps it visible on any background.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        show-label
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      Shows the <code>
        data-cursor-label
      </code>
      
       text of a locked target above the brackets.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        click-effect
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      Squeezes the brackets and dot while the mouse button is held.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        container
      </code>
    </td>
    
    <td>
      <code>
        HTMLElement | string | null
      </code>
    </td>
    
    <td>
      <code>
        null
      </code>
    </td>
    
    <td>
      Limits the cursor to one element or selector. By default it works across the page.
    </td>
  </tr>
</tbody>
</table>

## Accessibility

- The cursor layer is `aria-hidden` and `pointer-events: none`; it never takes focus or blocks clicks.
- It is skipped entirely on touch and coarse-pointer devices.
- With `prefers-reduced-motion: reduce` the roaming spin stops; the brackets still snap to targets.
- Labels come from `data-cursor-label` and are decorative; keep real accessible names (`aria-label`, text) on the targets themselves.
- Ported from [React Bits](https://reactbits.dev/animations/target-cursor) by David Haz.
