# Root

> [llms.txt](https://zriz.dev/llms.txt)

`Root` loads the photo, keeps the crop and holds every other part.

The page at https\://zriz.dev/docs/root runs this cropper: a 3:2 photo on a bare cropper, and open, reset and download in the corners. Download locks the cropper while it saves.

## Anatomy

Everything goes inside `Root`. `Image`, `Mask` and `Area` go in `Viewport`. The controls can sit anywhere, in any wrapper:

```tsx
<Cropper.Root src={photo}>
  <Cropper.Viewport style={{ height: 400 }}>
    <Cropper.Image />
    <Cropper.Mask />
    <Cropper.Area aria-label="Crop area">
      <Cropper.Grid />
    </Cropper.Area>
  </Cropper.Viewport>
  <Cropper.Dial aria-label="Straighten">
    <Cropper.DialTicks />
  </Cropper.Dial>
  <div className="toolbar">
    <Cropper.Rotate>Rotate</Cropper.Rotate>
    <Cropper.Flip>Flip</Cropper.Flip>
    <Cropper.Reset>Reset</Cropper.Reset>
  </div>
</Cropper.Root>
```

## Lock the cropper while you save

`disabled` ignores every gesture, key and button press, and every cropper method that moves the crop, such as `rotate90()` or `setZoom()`, except `setValue()`, `setImage()` and `setOptions()`. `Root` gets `data-disabled`, so CSS can grey it out. `value` and a new `src` still apply, and `aspect`, `rotation` and `zoom` set while disabled apply once it ends:

```tsx
<Cropper.Root src={photo} disabled={saving}>
  <Cropper.Viewport style={{ height: 400 }}>
    <Cropper.Image />
    <Cropper.Mask />
    <Cropper.Area aria-label="Crop area" />
  </Cropper.Viewport>
</Cropper.Root>
```

Grey out the buttons while it is set:

```css
[data-zriz-item="root"][data-disabled] button {
  opacity: 0.4;
  pointer-events: none;
}
```

The demo above sets `disabled` while its download runs. It adds handles, a grid and the styles from the zriz site.

## Dim the controls during a gesture

`Root` carries data attributes for what the cropper is doing. This rule fades the toolbar during any gesture, a key press included, and for a short hold after it, see [accessibility](https://zriz.dev/docs/accessibility.md):

```css
[data-zriz-item="root"][data-interacting] .toolbar {
  opacity: 0.4;
}
```

## Put it in a drawer

`Root` carries `data-vaul-no-drag` and `data-base-ui-swipe-ignore`, so a swipe on the photo or a control moves the crop and leaves a Vaul or Base UI drawer where it is. Give the drawer a handle or free space outside `Root`. To let the drawer drag from inside, set the attribute to `null` on `Root`, such as `data-vaul-no-drag={null}`.

## API

### `Root`

Renders a `<div>`. Every other part goes inside it. Every part also takes `className`, `render` and the props of its element, see [composition](https://zriz.dev/docs/composition.md). `className` and `render` get `{ snapshot, chrome }`. `snapshot` holds the fields `useCropperState` lists on [hooks](https://zriz.dev/docs/hooks.md). `chrome` holds `grid`, `active`, `transition` and `overscroll`, the values behind `data-grid`, `data-interacting`, `data-transition` and `data-overscroll`.

- `src` `string | Blob | null`

  The photo: a URL or a `Blob` such as a `File`. `null` clears it, `undefined` leaves it alone
- `crossOrigin` `"anonymous" | "use-credentials"`

  The CORS mode when the photo is on another domain
- `previewEdge` `number`

  The longest side of the preview in pixels. The default follows the screen, see [loading images](https://zriz.dev/docs/loading-images.md). A grid of small `Root`s saves memory with a low one. A new `previewEdge`, `crossOrigin` or `svg` loads the photo again and fires `onLoad` again. The crop stays, so keep `previewEdge` steady
- `svg` `boolean` = `true`

  `false` refuses SVG files, which then fail with `decode`. With `true`, an SVG that would freeze the page fails with `too-large`, see [security](https://zriz.dev/docs/security.md)
- `image` `Size | null`

  The photo's pixel size up front, read once
- `value` `CropValue`

  Controls the whole value. `onValueChange` does not fire when you set it
- `defaultValue` `Partial<CropValue>`

  The starting value. Fields you leave out use the defaults
- `aspect` `"free" | "original" | number`

  Controls the aspect alone, a ratio like `4 / 3`
- `rotation` `number`

  Controls the straighten angle alone, up to 45° each way
- `zoom` `number`

  Controls the zoom alone. 1 is the largest crop
- `shape` `"rect" | "circle"` = `"rect"`

  The crop shape. A circle locks the aspect to 1, draws a round mask and exports a round PNG, and the stylesheet hides the corner handles, see [avatar](https://zriz.dev/docs/avatar.md). Back to `"rect"`, the crop takes the aspect it held before. A `Root` that started as a circle takes `defaultValue` or `"free"`, and reset follows
- `minSize` `Size` = `{ width: 1, height: 1 }`

  The smallest crop in source pixels, along the photo's own sides. A large one narrows the straighten range. [Exporting](https://zriz.dev/docs/exporting.md) says when a crop can come out smaller
- `maxZoom` `number` = `15`

  The deepest zoom. Every gesture, key and control stops there
- `onValueChange` `(value, details) => void`

  At most once per frame while the crop moves, with a reason and `cancel()`, see [events](https://zriz.dev/docs/events.md)
- `onValueCommit` `(value, details) => void`

  Called once when a gesture or a write is done, with `pixelCrop`, the export plan, see [events](https://zriz.dev/docs/events.md)
- `onLoad` `(image) => void`

  Called when the photo is decoded and ready to crop
- `onError` `(error) => void`

  Called with a typed error when the photo cannot load, see [errors](https://zriz.dev/docs/errors.md)
- `cropper` `Cropper`

  Uses an instance from `useCropper`. `image`, `defaultValue`, `plugins` and `focalLength` then come from `useCropper`
- `actionsRef` `Ref<Cropper>`

  Gives you the cropper, for calls like `rotate90()` from a button
- `plugins` `readonly CropperPlugin[]`

  Runs each plugin's `init` once, when `Root` makes the cropper, see [cropper](https://zriz.dev/docs/cropper.md)
- `disabled` `boolean` = `false`

  Ignores gestures, keys, buttons and the cropper methods that move the crop. `value` and `setValue()` still apply. Sets `data-disabled`
- `motion` `"auto" | "always" | "reduce"` = `"auto"`

  Follows the system motion setting, or forces full or short motion
- `recenterDelay` `number` = `300`

  Milliseconds after a gesture before the crop recentres and the scrim comes back
- `dir` `"ltr" | "rtl"`

  The reading direction for `Dial`, `Zoom` and `AspectGroup`. In RTL their left and right swap. `Area`'s arrow keys stay physical. With no value, each of these parts reads its computed `direction` at the press. With a value, `Root` also sets the HTML `dir` attribute
- `locale` `string`

  Formats the values screen readers hear
- `focalLength` `number` = `26`

  The lens as a 35 mm equivalent, used for perspective correction. At a strong tilt, zriz lengthens a lens too short to keep the photo in front of it. Read once, when `Root` makes the cropper. `cropper.setOptions()` changes it, from the next `setValue()`, `reset()` or `setImage()`

| Attribute | When |
| - | - |
| `data-interacting` | During a gesture and for a short hold after it, see [accessibility](https://zriz.dev/docs/accessibility.md) |
| `data-panning` | From a press on the photo until the pointer lets go, even if it never moves |
| `data-pinching` | While two fingers pinch or twist on a touch screen, or a trackpad pinches in Safari |
| `data-resizing` | From a press on a handle until the pointer lets go, even if it never moves |
| `data-rotating` | From a press on the dial until the pointer lets go, even if it never moves, and while the dial glides after |
| `data-zooming` | While the wheel pans or zooms, from a press on `Zoom` until the pointer lets go, or while a `useZoom` slider moves |
| `data-settling` | While any animation runs: a glide, a spring back, a recentre or a button's move |
| `data-changed` | While `Reset` has a change to undo |
| `data-mirrored` | While the photo is flipped |
| `data-overscroll` | While you pull past an edge or a zoom limit |
| `data-disabled` | While `disabled` is set |
| `data-reduced-motion` | While short motion is on |
| `data-orientation` | `"landscape"` above 1, `"portrait"` below 1, `"square"` at 1 and while the aspect is `"free"` or `"original"` |
| `data-grid` | `"coarse"`, `"fine"` or `"none"`, see [crop area](https://zriz.dev/docs/crop-area.md) |
| `data-transition` | `"rotate"`, `"flip"`, `"morph"` or `"recenter"` while that transition runs. `"morph"` covers reset, an aspect pick, an orientation swap and undo. A reset or undo across a quarter turn runs as `"rotate"`, with the crop frame and the photo outside it still in view |
| `data-shape` | `"rect"` or `"circle"` |
