# Dragging and Resizing

Move and resize objects, reveal swipe actions, press and hold, pick points, draw ranges, and preview choices.

`createMove` moves objects, `createResize` changes their bounds, `createSwipeActions` reveals actions behind a row, `bindHold` responds to a press that's held, and `bindRepeat` keeps stepping while a button is held. `createArea` picks two-dimensional values, `createSpans` edits ranges on tracks, and `createPreview` lets people try a value before choosing it.

Import these helpers from `@ui-sdk/core`. When a helper asks for a `set` callback, connect it to your [change](/docs/core/kernel/handle-changes#changes). For sorting items into an order, see [Drag and Drop](/docs/core/behaviors/drag-and-drop).

## Moving in Two Dimensions

### Place Anything

Keep positions in a `Record<string, MovePoint>` and start a `MoveDraft | null` field at `null`. In `api`, connect them to `createMove`:

```ts
const change = createChange(context)
const move = createMove(context, {
  key: "positions",
  draft: "moving",
  set: (value, details) => change("positions", value, details),
  step: () => 4,
  bounds: () => ({ minX: 0, minY: 0, maxX: 264, maxY: 144 }),
})
```

Return `move` from `api`. The positioned slot uses the committed coordinates for its base position. Add `moving` to the primitive's `live` fields and let a live slot binding write the drag offset:

```ts
attrs: (state, { key }) => ({
  style: css({
    position: "absolute",
    left: `${state.positions[key].x}px`,
    top: `${state.positions[key].y}px`,
    "touch-action": "none",
  }),
}),
live: live((state, { api, key }) => ({
  "data-dragging": api.move.dragging(state, key) ? "" : undefined,
  style: api.move.style(state, key),
})),
on: {
  keydown: (event, { api, key }) => api.move.keydown(event, key),
  pointerdown: (event, { el, api, key }) => api.move.pointerdown(event, key, el!),
},
```

Render that slot as a focusable element with an accessible name, inside a positioned container. The live binding updates `translate`, `--move-x`, and `--move-y` without a framework render for each pointer move.

Drag a sticker, or focus it and press Alt with an arrow to move it 4 pixels. Add Shift to move ten times as far. Escape during a drag puts it back.

`step` is the grid a dropped position lands on, and how far Alt with an arrow moves it; Shift moves `largeStep`, ten steps by default. `bounds` limits committed coordinates; the drag preview can go outside them before release. A drop reports `drag`, a keyboard nudge reports `move-key`, and screen readers hear the new position.

### Several at Once

Add `group` to move a selection together:

```ts
group: (key) => {
  const { selected } = context.store.get()
  return selected.includes(key) ? selected : [key]
},
step: () => 20,
disabled: (key) => key === "ship",
label: (key) => titles[key],
```

`step` sets the grid and the distance for Alt with an arrow, 1 by default. `disabled` blocks pointer and keyboard moves and removes that item from a moving group.

Click a note to select it, or Shift-click to add one. Drag a selected note to move the group. Alt with an arrow nudges it 20 pixels; the pinned note stays put.

Select on pointerdown before calling `move.pointerdown`. Keep the selection when the pressed note is already in it. The demo combines this with [spatial focus](/docs/core/behaviors/focus#spatial-focus), so plain arrows move between notes.

`label` supplies the name for announcements. `message` can customize the sentence using the moved `keys`, the first item's `label`, and its new `at` point. `moveMessage` supplies the default wording.

### Zoom and Moving from Code

On a zoomed canvas, tell the behavior how many screen pixels represent one unit:

```ts
scale: () => context.store.get().zoom,
```

The pointer distance is divided by that scale. Programmatic moves still use the canvas's own units:

```ts
move.moveTo("circle", { x: 100, y: 60 })
move.moveBy(["circle"], 10, 0)
```

Change the zoom, then drag a shape. Use the buttons to move the circle without dragging.

Both methods report `imperative` and apply `bounds`. They don't apply the drag grid or filter disabled keys, so check those application rules before calling them if needed.

## Resizing Boxes

### Handles on a Box

A box has `{ x, y, width, height }`. Keep boxes in state, start a `ResizeDraft | null` field at `null`, and add `createResize` in `api`:

```ts
const resize = createResize(context, {
  boxes: (state) => state.boxes,
  draft: "draft",
  set: (value, details) => change("boxes", value, details),
  min: () => ({ width: 60, height: 40 }),
  bounds: () => ({ left: 0, top: 0, right: 320, bottom: 200 }),
  step: () => 4,
})
```

The box slot reads its committed size with `boxStyle`. List `draft` in the primitive's `live` fields, then apply `resize.style` through a live binding:

```ts
box: slot({
  attrs: (state, { api }) => ({ style: api.resize.boxStyle(state, "frame") }),
  live: live((state, { api }) => ({ style: api.resize.style(state, "frame") })),
  on: {
    pointerdown: (event, { el, api }) => api.resize.pointerdown(event, "frame", "move", el!),
  },
}),
```

Position the box absolutely and give each handle its own ID. Handles use compass names: `n`, `e`, `s`, `w`, `ne`, `nw`, `se`, and `sw`. Use `move` for the box itself.

Render handles as buttons with names such as "Resize bottom right". Their slots call `resize.handleAttrs(handle)` for the cursor and touch behavior, `resize.pointerdown(event, key, handle, el)` for dragging, and `resize.keydown(event, key, handle)` for arrows.

Drag a handle to resize the frame, or drag the frame to move it. Focus a handle and use the arrows; add Shift for larger steps. Escape cancels a drag.

`handleAttrs` doesn't supply a label or make an element focusable. The button does that. Arrow keys move an edge by `step`, the grid a dropped box lands on, or by `largeStep` with Shift, ten steps by default; an edge handle responds along its own axis. Changes report `drag` or `resize-key`.

### Keeping a Shape

Set an aspect ratio and size limits in the resize options:

```ts
aspect: () => (context.store.get().locked ? 16 / 9 : undefined),
min: () => ({ width: 96, height: 54 }),
max: () => ({ width: 256, height: 144 }),
largeStep: () => 16,
```

Resize the video frame while 16:9 is checked. Uncheck it to resize freely, or hold Shift while dragging a corner to preserve the frame's starting ratio.

`aspect` is width divided by height. A corner follows the edge that moved further; an edge handle adjusts the other dimension around its center. `min` and `max` bound the size, while `bounds` describes the surrounding area. Here `largeStep` makes Shift with an arrow move 16 pixels, while plain arrows keep moving 1.

### Several Boxes, and from Code

The same behavior can manage several boxes. Add `scale` for a zoomed desktop and `disabled` for boxes the user shouldn't move or resize:

```ts
scale: () => 0.75,
disabled: (key) => key === "notes",
label: (key) => titles[key],
```

Move or resize Mail on the scaled desktop. Notes is locked. Maximize sets Mail's box, and Grow enlarges it from the bottom-right corner.

There are two ways to change a box from code:

```ts
resize.resizeTo("mail", { x: 0, y: 0, width: 400, height: 240 })
resize.resizeBy("mail", "se", 40, 40)
```

`resizeBy` applies the handle's size limits and disabled rule. `resizeTo` writes the supplied box directly, so validate its dimensions and permissions yourself. Both report `imperative`.

To render a live size label, leave the draft out of `live` and read `resize.box(state, key)`. It returns the draft box during the drag and the committed box otherwise. `dragging(state, key)` returns the active handle. `label` and `message` customize announcements, with `resizeMessage` as the default.

## Swiping Rows Aside

### Actions Behind a Row

`createSwipeActions` keeps one row open at a time. Start `open` as `SwipeOpen | null` and `swiping` as `SwipeDraft | null`, both `null`:

```ts
const swipe = createSwipeActions(context, {
  key: "open",
  draft: "swiping",
})
```

Each row has moving content above actions on its `start` or `end` side. Bind the row with `bindRow(el, key)` and each action group with `bindActions(el, rowKey, side)`. Return those cleanups from the slots.

Put `swiping` in `live`. The content slot combines a transform with live attributes:

```ts
attrs: () => ({ style: css({ transform: "translateX(var(--swipe-x, 0px))" }) }),
live: live((state, { api, key }) => api.swipe.contentAttrs(state, key)),
```

Call `swipe.pointerdown(event, key, el)` and `swipe.keydown(event, key)` from the row. Give it `position: relative`, `overflow: hidden`, and `touch-action: pan-y`. The actions slot uses `swipe.actionsAttrs(state, rowKey, side)` to make hidden actions inert.

Swipe a message left to reveal Delete. From the keyboard, focus a row and press Left to reach its action, then Escape to close it.

A swipe waits for horizontal movement so vertical scrolling still works. Releasing past half the actions' width keeps them open; a flick can open or close them sooner. Action widths come from your layout.

`start` actions appear when swiping toward the end of the row, left to right in English. The direction reverses in right-to-left layouts. `contentAttrs` supplies `--swipe-x`, `data-swipe`, and `data-swiping`, so you can disable transitions while the pointer moves.

### Full Swipes

Add `onFullSwipe` to perform an action when the user swipes far enough. The callback performs the work; the helper doesn't click an action button for you:

```ts
onFullSwipe: (key, side) => {
  context.options().onAction?.(key, side)
  return true
},
fullSwipe: () => 0.5,
disabled: (key) => key === context.options().pinned,
```

Return whether the action was accepted. `fullSwipe` is a fraction of the row's width, 0.6 by default. The content gets `data-full` while release would trigger it.

Swipe right to mark a message read or left to archive it. Past half the row, release to perform the action. The pinned message stays closed; Peek and Close control the actions from code.

`swipe.show(key, "end", { focus: true })` opens a row and focuses its first action. `swipe.close({ focus: true })` closes it and returns focus to the row. Omit `focus` to leave focus where it is. `swipe.open()` reads the open row.

### Drag to Dismiss

A toast or card that's dragged far enough is dismissed on release. Give `scrubTo` a single snap point at 0: within the threshold its result has that `point`, so the toast snaps back, and past it, there's no point to return to. Ask on every move, so the toast can show what letting go will do:

```ts
import { scrubTo } from "@ui-sdk/core/number"

const keeps = { min: -width, max: width, step: 1, snap: { points: [0], threshold: width * 0.3 } }

onMove: ({ x }) => {
  moved = x - startX
  setOffset(moved)
  setDismissing(scrubTo(moved, keeps).point === undefined)
},
onEnd: () => (scrubTo(moved, keeps).point === undefined ? dismiss(Math.sign(moved)) : setOffset(0)),
```

Style `data-dismissing` so the change is clear before release, like iOS: here the layer behind the toast turns red and says "Release to dismiss". Dragging back under the threshold takes it back. Keep a Dismiss button in each toast for the keyboard.

Drag a toast sideways: past a third of its width, the layer behind turns red, and letting go dismisses it. Drag back, or press Escape, to keep it.

## Pressing and Holding

### Hold to Confirm

`bindHold` runs `onHold` once a press lasts past `delay`, 500 milliseconds by default. Give it a button and keep its cleanup:

```ts
import { bindHold } from "@ui-sdk/core"

const unbind = bindHold(button, {
  delay: 1500,
  onHold: () => deleteProject(),
})
```

It works with a mouse, a finger, a pen, and Space or Enter while the button has focus. While pressed, the button has `data-holding`, and `data-held` once it's held. `--hold-delay` holds the delay, so CSS can fill the button over exactly that time:

```css
[data-holding] .fill {
  transform: scaleX(1);
  transition: transform var(--hold-delay) linear;
}
```

Press and hold Delete Project until it fills. Let go early and nothing is deleted. From the keyboard, focus it and hold Space.

`onEnd` runs once the press ends, with `held` and `canceled`. A press is canceled when the pointer moves more than `tolerance` pixels before it's held, 10 by default, when a finger starts to scroll, when Escape is pressed, or when focus leaves the button or the window. A canceled press doesn't click, and Escape doesn't reach a dialog or popup around the button. Say that the button needs holding in its label or description, as the hint does here.

### Repeat While Held

`bindRepeat` steps once when a button is pressed, then keeps stepping while it's held, like a stepper's buttons, a scroll arrow or a remote's volume button. Give it what one step does:

```ts
import { bindRepeat } from "@ui-sdk/core"

const unbind = bindRepeat(button, {
  repeat: 80,
  onStep: () => zoomBy(5),
})
```

After `delay`, 400 milliseconds by default, `onStep` runs again every `repeat` milliseconds, 60 by default, until the press ends. Return `false` from `onStep` to stop repeating, like at a bound. `count` says which step of the press it is, starting at 1, and `onEnd` gets the total once the press ends.

You don't need a click handler. A mouse or a key steps as it goes down, and the click that follows doesn't step again. A finger or a pen steps when it lifts, or once it's held, so a scroll that starts on the button never steps. A click with no press behind it, like a screen reader's, steps once.

Press Zoom In once to add 5%. Hold it and the zoom keeps climbing until you let go or it reaches 400%. Slide off the button while holding and it waits until you come back.

While held, the repeats wait whenever the pointer is off the button, and go on when it comes back, like a scroll bar's arrows. A short press of Space or Enter steps once; held, they repeat at the same pace as a pointer, however fast the keyboard repeats the key. Pass `keys: []` to leave keys to the browser, and each click it sends steps once. Releasing, a cancel from the browser, Escape, or focus leaving the button or the window ends the press.

`bindRepeat` is built on `bindHold`, so the button has `data-holding` while pressed and `data-held` once it repeats. `bindHold` takes `repeat` too, for an action that waits until the press is held before its first run.

`onPress` runs as a press starts, with `input` set to `mouse`, `touch`, `pen` or `keyboard`. Return `false` to ignore it, like a context menu area that opens on a long press only for touch, since a mouse has right-click. Pass `disabled` to refuse presses and stop a hold from repeating. While a finger or pen presses, the browser's own long-press menu and iOS's callout stay closed. The cleanup stops a press under way without calling `onEnd`.

## Two-Dimensional Values

### A Point on a Pad

`createArea` maps a surface to two values. Read the point from state and write each change back:

```ts
const area = createArea(context, {
  value: (state) => ({ x: state.mix.warmth, y: state.mix.energy }),
  set: (value, details) =>
    change("mix", { warmth: value.x, energy: value.y }, details),
  x: () => ({ min: 0, max: 100, step: 1, label: "Warmth" }),
  y: () => ({ min: 0, max: 100, step: 1, label: "Energy" }),
})
```

The surface starts pointer tracking; the thumb supplies keyboard controls and slider attributes:

```ts
pad: slot({
  on: { pointerdown: (event, { el, api }) => api.area.pointerdown(event, el!) },
}),
thumb: slot({
  attrs: (state, { api }) => ({ ...api.area.thumbAttrs(state), style: api.area.thumbStyle(state) }),
  on: { keydown: (event, { api }) => api.area.keydown(event) },
}),
```

Make the surface positioned with `touch-action: none`. The thumb's position is relative to it. Unlike a move draft, this picker calls `set` throughout the gesture, with the reasons a slider reports: `track-press` for the press, `drag` as it moves and `step-key` for keys.

Click or drag on the pad to change warmth and energy. Focus the thumb and use the arrows, with Shift for larger steps.

Each axis defaults to 0–1 in steps of 0.01. The y axis runs bottom to top, and x follows the text direction. Arrows move one step, Shift moves ten, Page Up and Page Down move y by a tenth of its range, and Home and End go to x's ends.

`thumbAttrs` exposes one slider with value text describing both axes. `thumbStyle` sets its percentage position and the `--area-x` and `--area-y` fractions.

### What's Read Out, and When It Settles

Use `valueText` for a readable description and `onCommit` for work that should wait until a gesture ends:

```ts
valueText: ({ x, y }) => `${x}% saturation, ${y}% brightness`,
onCommit: (value) => context.options().onColorCommit?.(value),
disabled: () => context.store.get().locked,
```

Drag to adjust saturation and brightness. The swatch follows immediately; the saved value is logged on release or after a keyboard change. Lock disables the picker, and Reset sets its value from code.

`onCommit` runs at the end of pointer tracking or after a successful keyboard change. `disabled` prevents user input and removes the thumb from the tab order.

`area.set({ x: 60, y: 80 })` snaps and clamps a programmatic value with reason `imperative`. It still works when disabled and doesn't call `onCommit`. `area.fractions(state)` gives each value's position within its axis, useful for a gradient or swatch.

## Ranges on Tracks

### Bookings on a Timeline

A `Span` has an `id`, a `track`, and `start` and `end` values in your units. For room bookings, those units can be minutes from midnight. Keep bookings in an array and a `SpansDraft<Booking> | null` field at `null`:

```ts
type Booking = Span & { title: string }
type RoomsState = { bookings: Booking[]; draft: SpansDraft<Booking> | null }

let booked = 0
const spans = createSpans<RoomsState, Booking>(context, {
  key: "bookings",
  draft: "draft",
  set: (value, details) => change("bookings", value, details),
  axis: "x",
  min: () => 9 * 60,
  max: () => 17 * 60,
  step: () => 30,
  overlap: false,
  create: (span) => ({ ...span, id: `booking-${++booked}`, title: "New" }),
})
```

Render `spans.spans(state)` so the draft appears during a drag. A drop commits the array once, with the affected `id` and `action` in its details.

Bind tracks with `bindTrack(el, key)` and bookings with `bindSpan(el, id)`. Track presses call `trackPointerdown(event, key)`; booking presses call `spanPointerdown(event, id)`. Add `data-span-edge="start"` or `"end"` to resize handles inside a booking.

Drag on an empty stretch to book a room. Drag a booking to move it, or its end to resize it. Values snap to half hours, and bookings can't overlap.

Use `spanStyle(booking)` to position a booking, and `spanAttrs(state, id)` for `data-span-id` and drawing, dragging, or resizing states. Supply its focusability and accessible name yourself; `label(booking)` supplies the name used in announcements. Give tracks and draggable spans `touch-action: none`.

Call `keydown(event, id)` on the booking. Arrows along the track move it; Shift with those arrows moves its end. Arrows across tracks change rooms, and Delete or Backspace removes it. Each key commits immediately.

`create` supplies a new booking's ID and data. Without it, empty tracks don't create bookings. `overlap: false` rejects proposed ranges that overlap another on the same track.

### Days That Run Down

Tracks run vertically by default. Add duration limits, a click duration, and an explicit track order for a calendar:

```ts
minSize: () => 60,
maxSize: () => 180,
size: () => 60,
tracks: () => ["Mon", "Tue", "Wed"],
createAutoScroll: { edge: 32 },
```

Drag down a day to draw an event, or click for an hour. Move or resize events between one and three hours. Hold a drag near the top or bottom to scroll.

`minSize` and `maxSize` bound duration; `size` determines what a click creates. `tracks` controls keyboard travel across days. Auto-scroll accepts the [same options as other drags](/docs/core/behaviors/drag-and-drop#scrolling-while-dragging).

When overlap is allowed, give each event a lane so they can share the day's width:

```ts
const lanes = stackSpans(plans.filter((other) => other.track === plan.track))
const style = spans.spanStyle(plan, lanes.get(plan.id))
```

`spansOn(state, track)` reads one track's ranges. `valueAt(track, x, y)` converts a pointer position to a value within that track's range, snapped to `step`. `action(state, id)` reports the active pointer action.

### Changing Ranges from Code

Use `update` and `remove` for form fields or action buttons:

```ts
spans.update("standup", { start: booking.start + 30, end: booking.end + 30 })
spans.update("standup", { end: booking.end + 30 })
spans.remove("standup")
```

`update` applies range and overlap rules. These methods report `imperative`; they don't check `disabled`, which applies to pointer and keyboard input. `isSpanFree(bookings, proposed)` checks whether a proposed booking overlaps another on its track.

Move, lengthen, or delete Standup with the buttons. Book noon checks whether the room is free. The board meeting is locked, and leaving the window during a drag cancels it.

`spans.cancel()` ends a drag without committing it. If you cancel when the window loses focus, remove that listener when the primitive is destroyed:

```ts
window.addEventListener("blur", spans.cancel)
context.onDestroy(() => window.removeEventListener("blur", spans.cancel))
```

`message` customizes announcements with `spansMessage` as the default. Set `createAutoScroll: false` when your tracks don't need edge scrolling.

## Previews

### Press and Slide

A preview lets someone try a value before choosing it. Keep a `string | null` preview beside the chosen value, and render `preview ?? value`:

```ts
const preview = createPreview(context, {
  key: "preview",
  slot: "font",
  value: () => context.store.get().value,
  choose: (key, details) => change("value", key, details),
})
```

The preview writes directly to its state field. Add `set` if it needs to go through a change callback too.

Keep the existing roving focus binding for the font slot, and add these handlers:

```ts
on: {
  pointerdown: (event, { el, api, key }) => api.preview.pointerdown(event, key, el!.parentElement!),
  keydown: (event, { el, api, key }) => {
    if (api.preview.keydown(event, key)) {
      return
    }
    const moved = api.roving.keydown(event, el!)
    if (moved) {
      api.preview.show(moved.key, { reason: "list-navigation", event })
    }
  },
},
```

Press a font and slide over the others to preview them. Release over one to choose it, or outside to cancel. With the keyboard, arrows preview and Enter or Space chooses.

Pass the common parent surface to `pointerdown` to track the gesture across the options. `show(key)` previews one, `choose(key)` commits it, and `clear()` returns to the chosen value. Enter and Space choose; Escape clears.

Clear the preview when focus leaves the group, unless a pointer is still scrubbing:

```ts
focusout: (event, { el, api }) => {
  if (api.preview.dragging() || el?.contains(event.relatedTarget as Node)) {
    return
  }
  api.preview.clear({ reason: "focus-out", event })
},
```

Pressing and sliding can also try out an order. Measure each tab when the press starts, and make the left edge of every slot the dragged tab could take a snap point. `scrubTo` pulls the tab into a slot as it comes close, the nearest slot shows the order it would make, and letting go keeps it:

```ts
const slots = lefts([...others, key], widths)

onMove: ({ x, event }) => {
  const { value: left } = scrubTo(start + x, { min: 0, max: slots.at(-1), step: 1, snap: { points: slots, threshold: 12 } }, {
    snap: !event.metaKey,
  })
  preview(insert(others, key, nearest(slots, left)), left)
},
```

For the keyboard, `stepFromKey` and `stepBy` move the focused tab one place, or to either end with Home and End, the same way they step a slider.

Drag a tab along the bar: it snaps into each place as it nears it, and the others make room. Hold Command or Ctrl to place it freely, or press Escape to cancel. With the keyboard, Option or Alt with an arrow moves the focused tab.

### Hovering, and What's Read Out

For a hover preview, use `show` and `clear` on enter and leave, then `choose` on click:

```ts
pointerenter: (event, { api, key }) => api.preview.show(key, { reason: "item-hover", event }),
pointerleave: (event, { api }) => api.preview.clear({ reason: "item-hover", event }),
click: (event, { api, key }) => api.preview.choose(key, { event }),
```

Hover over a theme to try it and click to keep it. Arrows preview too; Enter chooses and Escape clears. Contrast is disabled.

`disabled(key)` prevents previewing or choosing an option. `label(key)` and `message(label)` customize announcements for previews with reason `list-navigation`; pointer previews don't announce every pass over an option.

## API Reference

The option tables link back to the examples that introduce them.

- `key` (`keyof State & string`): The state key that holds every item's position, a `Record<string, MovePoint>`. Example: [Place Anything](#place-anything).
- `draft` (`keyof State & string`): The state key that holds the `MoveDraft` while a pointer drags. Start it at `null`, and list it in `live`. Example: [Place Anything](#place-anything).
- `set` (`(value: Record<string, MovePoint>, details: { reason: MoveReason; event?: Event; }) => boolean`): Asks for new positions and returns whether they went through. Pass the primitive's change: `set: (value, details) => change("positions", value, details)`. Example: [Place Anything](#place-anything).
- `group?` (`((key: string) => string[])`): The keys that move together with the one pressed, like the selection when it holds it. Defaults to that one key. Example: [Several at Once](#several-at-once).
- `step?` (`(() => number)`): The grid a dropped position lands on, and how far Alt with an arrow nudges. Defaults to 1. Example: [Several at Once](#several-at-once).
- `largeStep?` (`(() => number)`): How far Alt with Shift and an arrow nudges. Defaults to ten steps. Example: [Place Anything](#place-anything).
- `scale?` (`(() => number)`): How many screen pixels one unit of position takes, like a canvas's zoom, so a drag follows the pointer when the items are scaled. Defaults to 1. Example: [Zoom and Moving from Code](#zoom-and-moving-from-code).
- `bounds?` (`((key: string) => { minX: number; minY: number; maxX: number; maxY: number; } | undefined)`): Keeps positions inside a box, like the board's size minus a card's. Example: [Place Anything](#place-anything).
- `disabled?` (`((key: string) => boolean)`): Whether an item can't be moved. Example: [Several at Once](#several-at-once).
- `label?` (`((key: string) => string | undefined)`): The name screen readers hear for an item. Example: [Several at Once](#several-at-once).
- `message?` (`((message: MoveMessage) => string)`): The text screen readers hear after a move. Defaults to English. Example: [Several at Once](#several-at-once).

- `boxes` (`(state: State) => Record<string, ResizeBox>`): Every box in state, by key, like `(state) => state.boxes` or `(state) => ({ crop: state.crop })`. Example: [Handles on a Box](#handles-on-a-box).
- `draft` (`keyof State & string`): The state key that holds the `ResizeDraft` while a pointer drags. Start it at `null`, and list it in `live`. Example: [Handles on a Box](#handles-on-a-box).
- `set` (`(value: Record<string, ResizeBox>, details: { reason: ResizeReason; event?: Event; }) => boolean`): Asks for new boxes and returns whether they went through. Pass the primitive's change: `set: (value, details) => change("boxes", value, details)`, or `change("crop", value.crop, details)` for a primitive with one box. Example: [Handles on a Box](#handles-on-a-box).
- `min?` (`((key: string) => { width: number; height: number; })`): The smallest a box gets. Defaults to 1 by 1. Example: [Handles on a Box](#handles-on-a-box).
- `max?` (`((key: string) => { width: number; height: number; } | undefined)`): The largest a box gets. Example: [Keeping a Shape](#keeping-a-shape).
- `bounds?` (`((key: string) => { left: number; top: number; right: number; bottom: number; } | undefined)`): Keeps a box inside an area, like the image a crop sits on. Example: [Handles on a Box](#handles-on-a-box).
- `aspect?` (`((key: string) => number | undefined)`): Keeps width over height at this ratio, like `16 / 9`. Without one, Shift on a corner keeps the ratio the box had when the drag started. Example: [Keeping a Shape](#keeping-a-shape).
- `step?` (`(() => number)`): The grid a dropped box lands on, and how far an arrow moves an edge. Defaults to 1. Example: [Handles on a Box](#handles-on-a-box).
- `largeStep?` (`(() => number)`): How far Shift with an arrow moves an edge. Defaults to ten steps. Example: [Keeping a Shape](#keeping-a-shape).
- `scale?` (`(() => number)`): How many screen pixels one unit takes, like a canvas's zoom. Defaults to 1. Example: [Several Boxes, and from Code](#several-boxes-and-from-code).
- `disabled?` (`((key: string) => boolean)`): Whether a box can't be changed. Example: [Several Boxes, and from Code](#several-boxes-and-from-code).
- `label?` (`((key: string) => string | undefined)`): The name screen readers hear for a box. Example: [Several Boxes, and from Code](#several-boxes-and-from-code).
- `message?` (`((message: ResizeMessage) => string)`): The text screen readers hear after a change. Defaults to English. Example: [Several Boxes, and from Code](#several-boxes-and-from-code).

- `key` (`keyof State & string`): The state key that holds the `SwipeOpen` row, or `null`. Example: [Actions Behind a Row](#actions-behind-a-row).
- `draft` (`keyof State & string`): The state key that holds the `SwipeDraft` while a pointer swipes. Start it at `null`, and list it in `live`. Example: [Actions Behind a Row](#actions-behind-a-row).
- `onFullSwipe?` (`((key: string, side: SwipeSide, details: { event: Event; }) => boolean)`): Runs a side's first action when a swipe goes past `fullSwipe` of the row's width, and returns whether it did. Without it, a long swipe only opens the row. Example: [Full Swipes](#full-swipes).
- `fullSwipe?` (`(() => number)`): How far across the row a swipe runs the first action, as a fraction of its width. Example: [Full Swipes](#full-swipes).
- `disabled?` (`((key: string) => boolean)`): Whether a row can't be swiped. Example: [Full Swipes](#full-swipes).

- `delay?` (`number`): How long a press lasts before it's held, in milliseconds. Example: [Hold to Confirm](#hold-to-confirm).
- `repeat?` (`number`): Runs `onHold` again this often while held, in milliseconds, like a stepper's button. Without it, `onHold` runs once. Example: [Repeat While Held](#repeat-while-held).
- `tolerance?` (`number`): How far a pointer can move before it's held, in pixels, before the press is canceled, like when a finger starts to scroll. Example: [Hold to Confirm](#hold-to-confirm).
- `keys?` (`string[]`): The keys that press it while it has focus. A short press clicks it, as a pointer's does. An empty list leaves keys alone. Example: [Repeat While Held](#repeat-while-held).
- `disabled?` (`(() => boolean)`): Whether a press can't start, and a held one stops repeating. Example: [Repeat While Held](#repeat-while-held).
- `onPress?` (`((details: HoldDetails) => boolean | void)`): Runs as a press starts. Return `false` to ignore it, like a mouse press where only touch holds. Example: [Repeat While Held](#repeat-while-held).
- `onHold?` (`((details: HoldDetails & { count: number; }) => boolean | void)`): Runs once the press is held, then every `repeat` milliseconds. Return `false` to stop repeating, like at a bound; the press goes on until it ends. Example: [Repeat While Held](#repeat-while-held).
- `onEnd?` (`((details: HoldEndDetails) => void)`): Runs once when the press ends, released or canceled. Example: [Hold to Confirm](#hold-to-confirm).

- `delay?` (`number`): How long a press lasts before it starts repeating, in milliseconds. Example: [Repeat While Held](#repeat-while-held).
- `repeat?` (`number`): How often it steps again while held, in milliseconds. Example: [Repeat While Held](#repeat-while-held).
- `keys?` (`string[]`): The keys that press it while it has focus. An empty list leaves keys to the browser, which clicks a button once per key press. Example: [Repeat While Held](#repeat-while-held).
- `disabled?` (`(() => boolean)`): Whether a press can't start, and a held one stops repeating. Example: [Repeat While Held](#repeat-while-held).
- `onPress?` (`((details: HoldDetails) => boolean | void)`): Runs as a press starts. Return `false` to ignore it. Example: [Repeat While Held](#repeat-while-held).
- `onStep?` (`((details: RepeatDetails) => boolean | void)`): Runs once for each press, then every `repeat` milliseconds while it's held. Return `false` to stop repeating, like at a bound. Example: [Repeat While Held](#repeat-while-held).
- `onEnd?` (`((details: RepeatEndDetails) => void)`): Runs once when a press ends, released or canceled, and after a click's step. Example: [Repeat While Held](#repeat-while-held).

- `value` (`(state: State) => AreaValue`): Reads the point from state, like `(state) => state.value`, or one made from other state, like a color's saturation and brightness. Example: [A Point on a Pad](#a-point-on-a-pad).
- `set` (`(value: AreaValue, details: { reason: AreaReason; event?: Event; }) => boolean`): Asks for a new value and returns whether it went through. Pass the primitive's change: `set: (value, details) => change("value", value, details)`. Example: [A Point on a Pad](#a-point-on-a-pad).
- `x?` (`(() => AreaAxis)`): The x axis, left to right. Example: [A Point on a Pad](#a-point-on-a-pad).
- `y?` (`(() => AreaAxis)`): The y axis, bottom to top, like a chart: a higher value sits higher. Example: [A Point on a Pad](#a-point-on-a-pad).
- `valueText?` (`((value: AreaValue) => string)`): The text screen readers hear for the value. Defaults to each axis's label and percentage. Example: [What's Read Out, and When It Settles](#whats-read-out-and-when-it-settles).
- `onCommit?` (`((value: AreaValue, details: { reason: AreaReason; event?: Event; }) => void)`): Runs when a press or a drag ends, or a key changed the value, with the value it ended on, like Slider's `onValueCommit`. Example: [What's Read Out, and When It Settles](#whats-read-out-and-when-it-settles).
- `disabled?` (`(() => boolean)`): Whether it can't change. Example: [What's Read Out, and When It Settles](#whats-read-out-and-when-it-settles).

- `key` (`keyof State & string`): The state key that holds the spans, an array of `Span`. Example: [Bookings on a Timeline](#bookings-on-a-timeline).
- `draft` (`keyof State & string`): The state key that holds the `SpansDraft` while a pointer drags. Start it at `null`. Example: [Bookings on a Timeline](#bookings-on-a-timeline).
- `set` (`(value: S[], details: { reason: SpansReason; event?: Event; id: string; action: SpanAction; }) => boolean`): Asks for the new spans and returns whether they went through. Pass the primitive's change: `set: (value, details) => change("spans", value, details)`. Example: [Bookings on a Timeline](#bookings-on-a-timeline).
- `min?` (`(() => number)`): Where tracks start, in their units. Example: [Bookings on a Timeline](#bookings-on-a-timeline).
- `max?` (`(() => number)`): Where tracks end. Example: [Bookings on a Timeline](#bookings-on-a-timeline).
- `step?` (`(() => number)`): What every start and end snaps to, and what arrows move by. Example: [Bookings on a Timeline](#bookings-on-a-timeline).
- `minSize?` (`(() => number)`): The shortest a span can be. Example: [Days That Run Down](#days-that-run-down).
- `maxSize?` (`(() => number)`): The longest a span can be. Example: [Days That Run Down](#days-that-run-down).
- `axis?` (`"x" | "y"`): The direction tracks run: `y` down, like a calendar's days, or `x` across, like a Gantt chart's rows. Example: [Bookings on a Timeline](#bookings-on-a-timeline).
- `tracks?` (`(() => string[])`): The tracks in the order the arrows across them go through. Defaults to the bound tracks in document order. Example: [Days That Run Down](#days-that-run-down).
- `overlap?` (`boolean`): Whether spans on one track can overlap. When they can't, a drag stops where it would. Example: [Bookings on a Timeline](#bookings-on-a-timeline).
- `create?` (`((span: Span) => S | null)`): Makes the span a press on a track starts to draw, with a new `id` and anything else it needs, like a title, or returns `null` to not draw. Runs on every press on an empty stretch of track. Without it, nothing is drawn. Example: [Bookings on a Timeline](#bookings-on-a-timeline).
- `size?` (`(() => number | undefined)`): How long a span a click on an empty track makes. Without it, a click makes none. Example: [Days That Run Down](#days-that-run-down).
- `disabled?` (`((span: S) => boolean)`): Whether a span can't change. Example: [Changing Ranges from Code](#changing-ranges-from-code).
- `label?` (`((span: S) => string)`): What screen readers call a span, like `"Standup, Tuesday 9:00 to 9:30"`. Defaults to its track and range. Example: [Bookings on a Timeline](#bookings-on-a-timeline).
- `message?` (`((message: SpansMessage<S>) => string)`): The text screen readers hear for each change. Defaults to English. Example: [Changing Ranges from Code](#changing-ranges-from-code).
- `createAutoScroll?` (`false | Omit<AutoScrollOptions, "ignore" | "onScroll">`): How a drag near a scroller's edge scrolls it, or `false` not to. Example: [Days That Run Down](#days-that-run-down).

- `key` (`keyof State & string`): The state key that holds the previewed option's key, or `null`. Example: [Press and Slide](#press-and-slide).
- `slot` (`string`): The slot whose parts are the options. Example: [Press and Slide](#press-and-slide).
- `set?` (`((value: string | null, details: { reason: PreviewReason; event?: Event; }) => boolean)`): Asks for a new preview. Defaults to setting the state key; pass the primitive's change to fire `onPreviewChange`. Example: [Press and Slide](#press-and-slide).
- `choose` (`(key: string, details: { reason: "item-press"; event?: Event; }) => boolean`): Chooses an option. Pass the primitive's change for its value. Example: [Press and Slide](#press-and-slide).
- `value?` (`(() => string | null)`): The chosen option now, which previews as nothing. Example: [Press and Slide](#press-and-slide).
- `disabled?` (`((key: string) => boolean)`): Whether an option can't be previewed or chosen. Example: [Hovering, and What's Read Out](#hovering-and-whats-read-out).
- `label?` (`((key: string) => string)`): An option's name for screen readers. Defaults to its key. Example: [Hovering, and What's Read Out](#hovering-and-whats-read-out).
- `message?` (`((label: string) => string)`): What screen readers hear when the keyboard previews one. Defaults to English. Example: [Hovering, and What's Read Out](#hovering-and-whats-read-out).
