# MagneticField

> Items are pulled toward a magnet and lean toward it, light ones more than heavy ones, then spring home. The magnet is the pointer or a draggable puck.

- Package: `fancy-ui-vue` (Vue 3.5+ + Tailwind CSS v4)
- Category: Effects
- Tags: magnet, pointer, spring, physics, attract, repel, drag, compound
- Types: `MagneticFieldProps`, `MagneticItemProps`, `MagnetPuckProps`, `MagneticPoint`, `MagneticSpring`, `MagneticSpringPreset`, `MagneticFieldSource`, `MagneticFieldContext`
- Docs: https://fancy-ui.rama.app/docs/components/magnetic-field
- Source: https://github.com/RamaHerbin/fancy-ui/tree/main/vue/src/components/magnetic-field

## Import

```vue
<script setup lang="ts">
import { MagneticField } from 'fancy-ui-vue';
</script>
```

Setup (once per project): `npm install fancy-ui-vue`, then put these lines in your main CSS file (the stylesheet imports go right after `@import "tailwindcss";`):

```css
@import "tailwindcss";
@import "fancy-ui-vue/tailwind.css";
@import "fancy-ui-vue/styles.css";
```

Full guide: https://fancy-ui.rama.app/llms.txt

The names below are the ones to write in a Vue template; when one differs from the Svelte name, the Svelte name follows in parentheses. Snippets become named slots (`#default` is the default snippet), a prop the Vue port declares as a model is a `v-model:<name>` (except `ref`, exposed via `defineExpose({ ref })`: put a template ref on the component and read `.ref`) and callbacks are listeners (`onclick` is `@click`). Every component sets `inheritAttrs: false`: `class` is a declared prop, merged where the Svelte component applies it (not always the root element), and only when the props table below lists it; other attributes do not fall through unless the package README lists the component as forwarding native attributes. Divergences: https://github.com/RamaHerbin/fancy-ui/tree/main/vue#divergences-from-the-svelte-api

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `source` | `"pointer" \| "manual"` | `"pointer"` | Where the magnet comes from. `"pointer"` follows a mouse or pen anywhere within `radius` of an item (touch is ignored); `"manual"` reads `position` instead. A `MagnetPuck` placed inside the field drives it in either mode while it is held. |
| `position` | `MagneticPoint \| null` | `null` | The magnet's position in px, relative to the field's padding box, used when `source` is `"manual"`. `null` means no magnet, and the items spring back to rest. |
| `radius` | `number` | `220` | Falloff distance from the item's nearest edge, in px: an item pulls hardest when the magnet touches or overlaps it and stops moving once the magnet is `radius` px away. It can still lean up to twice as far. |
| `strength` | `number` | `1` | Global pull multiplier, applied on top of each item's own `strength`. `0` turns the effect off without detaching anything. |
| `maxTranslate` | `number` | `24` | Largest distance, in px, that any item travels toward the magnet. |
| `maxRotate` | `number` | `6` | Largest lean, in degrees. Items lean toward the magnet's side like a crowd turning its head: clockwise when it is to their left, counter-clockwise when it is to their right. |
| `spring` | `MagneticSpringPreset \| Partial<MagneticSpring>` | `"default"` | How items move and settle: `"soft"`, `"default"` or `"snappy"`, or your own `{ stiffness, damping, mass }` (missing keys come from `"default"`). |
| `polarity` | `1 \| -1` | `1` | `1` attracts and `-1` repels. Only translation flips; the lean stays the same. |
| `disabled` | `boolean` | `false` | Detaches every listener and springs each item back to rest. |
| `@active-change` (Svelte `onActiveChange`) | `(active: boolean) => void` | — | Called when the field wakes up (a magnet is engaged or an item is still settling) and when it goes back to sleep. |
| `class` | `string` | — | Additional CSS classes. |
| `ref` | `HTMLDivElement \| null` | `null` | Element reference (bindable). |
| `MagneticItem.mass` | `number` | `1` | How heavy the item is. Above 1 it moves and leans less and settles more slowly; below 1 it is livelier. |
| `MagneticItem.strength` | `number` | `1` | Per-item pull multiplier, applied on top of the field's `strength`. |
| `MagneticItem.pull` | `boolean` | `true` | Whether the item moves toward the magnet. |
| `MagneticItem.rotate` | `boolean` | `true` | Whether the item leans toward the magnet. |
| `MagneticItem.inline` | `boolean` | `false` | Renders `<span>` wrappers with `display: inline-block` instead of block `<div>`s, for items that sit inside a line of text or an inline row. |
| `MagneticItem.class` | `string` | — | Additional CSS classes, on the static outer wrapper. |
| `MagneticItem.ref` | `HTMLElement \| null` | `null` | Reference to the static outer wrapper (bindable). |
| `MagnetPuck.v-model:position` (Svelte `MagnetPuck.position`) | `MagneticPoint` | `{ x: 48, y: 48 }` | The puck's centre in px, relative to its positioned parent (the field's padding box when it sits inside a `MagneticField`, where the field must be that parent: no positioned element in between). Bindable; dragging and the arrow keys update it. |
| `MagnetPuck.@position-change` (Svelte `MagnetPuck.onPositionChange`) | `(position: MagneticPoint) => void` | — | Called with each new position while the puck is dragged or moved with the keyboard. |
| `MagnetPuck.size` | `number` | `40` | Diameter in px. |
| `MagnetPuck.sticky` | `boolean` | `false` | Whether the puck keeps attracting once released. By default it attracts only while held, and the items spring back when it is let go. |
| `MagnetPuck.label` | `string` | `"Magnet"` | Accessible name. A built-in hint naming the keys describes the puck; pass your own `aria-describedby` to replace it. |
| `MagnetPuck.@held-change` (Svelte `MagnetPuck.onHeldChange`) | `(held: boolean) => void` | — | Called when the puck is picked up (pointer down, or Space/Enter) and put down. |
| `MagnetPuck.class` | `string` | — | Additional CSS classes. |
| `MagnetPuck.ref` | `HTMLDivElement \| null` | `null` | Element reference (bindable). |

## Slots

| Slot | Description |
| --- | --- |
| `#default` (Svelte `children`) | The content. Wrap each element that should react in a `MagneticItem`. |
| `MagneticItem.#default` (Svelte `MagneticItem.children`) | The element that reacts. |
| `MagnetPuck.#default` (Svelte `MagnetPuck.children`) | A custom face. Leave it out for the default black disc. |

## Example

No Vue example is published for this component yet: start from the import above and the props table. The full examples are Svelte-only, on https://fancy-ui.rama.app/docs/components/magnetic-field.md.
