# Scroll Spin

Rotates its children with the page scroll.

## Home


Rotates whatever you put inside it as the page scrolls. Drag the viewport
buttons to see it at three widths - the amount of rotation per screen scrolled
is the same at every one.

<!-- ::start:showcase demo="scroll-spin" height="460" -->
<!-- ::end:showcase -->

The rotation is written straight onto the node in a `requestAnimationFrame`
callback, never through React state. At 60fps a state-driven version re-renders
the subtree on every frame of every scroll, which is the one thing guaranteed
to make a light page feel heavy.

> [!NOTE] Reduced motion
> Anyone with reduced motion set gets a still image. The effect never starts,
> rather than starting and being cancelled.


## Install

<!-- ::start:tabs -->

### TanStack

```shell
tanstack add https://adamjurek.com/r/tanstack/scroll-spin.json
```

### shadcn

```shell
pnpm dlx shadcn@latest add https://adamjurek.com/r/shadcn/scroll-spin.json
```

### pnpm

```shell
pnpm add @sushindustries/ui @sushindustries/atoms
```

<!-- ::end:tabs -->

## What you get

| | |
| --- | --- |
| Version | 0.1.0 |
| Category | motion · Scroll effects |
| Files | `scroll-spin.tsx`, `use-scroll-turn.ts` |
| Dependencies | None |
| Also installs | `use-scroll-turn` |
| Tags | scroll, transform, no-deps |

> [!NOTE] No runtime dependencies
> It brings nothing with it beyond the stylesheet.

## Get Started


Install commands are on Home, attached from the registry. This tab starts after
the install worked.

## Use it

```tsx
import { ScrollSpin } from "@sushindustries/ui";

export function Hero() {
	return (
		<ScrollSpin revolutions={2} tilt={8}>
			<img src="/mark.svg" alt="" />
		</ScrollSpin>
	);
}
```

The component emits class names from `@sushindustries/atoms`, so import that
stylesheet once at your root:

```ts
import "@sushindustries/atoms/atoms.css";
```


## API


<!-- generated:api -->

## Props

| Prop | Type | Default | Does |
| --- | --- | --- | --- |
| `children` | `ReactNode` | - | Whatever should turn. A logo, a mark, an image, a diagram. |
| `revolutions?` | `number` | `2` | Viewport heights per full revolution. Higher is slower. Tied to viewport height rather than pixels so the rotation per "screen scrolled" is the same on a phone and on a monitor. |
| `tilt?` | `number` | `8` | Degrees of wobble on the X axis. Set to 0 for a flat turntable. |

<!-- /generated:api -->

### `revolutions`

Measured in viewport heights, not pixels. A pixel constant would mean a phone
user scrolling one screen sees three times the rotation a desktop user sees for
the same gesture.

### `tilt`

The wobble is a sine of the same scroll value, so it returns to zero at every
half turn rather than drifting. Set it to `0` for a turntable; anything above
about `12` starts to read as a wobble rather than a tilt.

> [!CAUTION] It writes to `style.transform`
> Do not also animate `transform` on the same element from CSS. The component
> overwrites the property on every frame, and your keyframes will silently lose.

