Skip to content

XYPad

A two-dimensional slider for setting a pair of values with one gesture — a focal point, a shadow offset, a filter pad.

Focal point
50, 50
HorizontalVertical
import { XYPad } from '@ontoui/react';
<XYPad.Root defaultValue={{ x: 50, y: 50 }}>
<XYPad.Label>Focal point</XYPad.Label>
<XYPad.Value />
<XYPad.Control>
<XYPad.Grid />
<XYPad.Thumb />
</XYPad.Control>
</XYPad.Root>;

Anatomy

PartElementDescription
XYPad.Root<div>Groups all parts and holds the pad state.
XYPad.Label<div>An accessible label associated with both axes.
XYPad.Value<output>Displays the current point as text.
XYPad.Control<div>The square, interactive surface. Sizes itself from its width.
XYPad.Grid<div>Optional decorative grid lines drawn across the surface.
XYPad.Thumb<div>The draggable point, and the pad’s two keyboard and form controls.

The value is a point — { x, y } — and each axis has its own range: minX, maxX, stepX and largeStepX, with minY, maxY, stepY and largeStepY alongside them. Both axes default to 0100 with a step of 1.

XYPad.Control is square (aspect-ratio: 1) and fills the width it is given, so size the pad by setting a width on XYPad.Root.

Examples

Vertical direction

y grows upwards by default, putting maxY at the top edge — the convention for a filter or EQ pad. Set yDirection="down" for values that map straight to a screen offset, such as a shadow, where maxY belongs at the bottom.

XYPad.Value takes a render prop to format the point, and XYPad.Thumb takes xLabel and yLabel to name the axes for screen readers.

Shadow offset
8px, 12px
Horizontal offsetVertical offset
const [offset, setOffset] = useState({ x: 8, y: 12 });
<XYPad.Root
value={offset}
onValueChange={setOffset}
minX={-24}
maxX={24}
minY={-24}
maxY={24}
yDirection="down"
>
<XYPad.Label>Shadow offset</XYPad.Label>
<XYPad.Value>{(value) => `${value.x}px, ${value.y}px`}</XYPad.Value>
<XYPad.Control>
<XYPad.Grid columns={6} rows={6} />
<XYPad.Thumb xLabel="Horizontal offset" yLabel="Vertical offset" />
</XYPad.Control>
</XYPad.Root>;

Snapping to a grid

stepX and stepY snap each axis as the pointer moves. Give XYPad.Grid a matching number of columns and rows to draw the positions the thumb will land on. Pass 0 as a step to leave an axis continuous.

Position
30, 70
HorizontalVertical
<XYPad.Root defaultValue={{ x: 30, y: 70 }} stepX={10} stepY={10}>
<XYPad.Label>Position</XYPad.Label>
<XYPad.Value />
<XYPad.Control>
<XYPad.Grid columns={10} rows={10} />
<XYPad.Thumb />
</XYPad.Control>
</XYPad.Root>

Disabled

Set disabled on XYPad.Root to prevent interaction.

Focal point
40, 60
HorizontalVertical
<XYPad.Root defaultValue={{ x: 40, y: 60 }} disabled>
<XYPad.Label>Focal point</XYPad.Label>
<XYPad.Value />
<XYPad.Control>
<XYPad.Grid />
<XYPad.Thumb />
</XYPad.Control>
</XYPad.Root>

In a form

nameX and nameY name the two values in the submitted form data. Without them the pad holds no form value.

<XYPad.Root nameX="focalX" nameY="focalY" defaultValue={{ x: 50, y: 50 }}>
<XYPad.Label>Focal point</XYPad.Label>
<XYPad.Control>
<XYPad.Grid />
<XYPad.Thumb />
</XYPad.Control>
</XYPad.Root>

Keyboard

XYPad.Thumb renders one range input per axis, so the pad has two tab stops and each value is announced and adjustable on its own. Both inputs answer to all four arrow keys — otherwise half the plane would be out of reach from whichever axis held focus.

KeyAction
Move by stepX.
Move the thumb up or down the pad by stepY.
Shift + arrowMove by the large step, ten steps by default.
Page Up / Page DownMove the focused axis by its large step.
Home / EndJump the focused axis to its minimum or maximum.

Arrow keys move the thumb in the direction pressed, so decreases y when yDirection is down. Page Up and Page Down step the value itself and are unaffected by the direction.

Accessibility

  • Always render XYPad.Label — it names both axes. Each input’s accessible name is the pad label followed by its axis name, so a labelled pad announces “Shadow offset, Horizontal”.
  • Rename the axes with xLabel and yLabel on XYPad.Thumb when “Horizontal” and “Vertical” don’t describe what the pad controls, or to translate them.
  • XYPad.Grid is decorative and hidden from assistive tech. Nothing about the value depends on it, so a pad without one behaves identically.

API Reference

XYPad.Root

Prop Type Default
children? ReactNode
className? string
style? CSSProperties
value? XYPadValue
defaultValue? XYPadValue { x: minX, y: minY }
onValueChange? (value: XYPadValue) => void
onValueCommitted? (value: XYPadValue) => void
minX? number 0
maxX? number 100
stepX? number 1
largeStepX? number stepX * 10
minY? number 0
maxY? number 100
stepY? number 1
largeStepY? number stepY * 10
yDirection? "up" | "down" 'up'
disabled? boolean false
nameX? string
nameY? string
ref? Ref<HTMLDivElement>

XYPad.Label

Prop Type Default
children? ReactNode
className? string
style? CSSProperties
ref? Ref<HTMLDivElement>

XYPad.Value

Prop Type Default
className? string
style? CSSProperties
children? (value: XYPadValue) => ReactNode (value) => `${value.x}, ${value.y}`
ref? Ref<HTMLDivElement>

XYPad.Control

Prop Type Default
children? ReactNode
className? string
style? CSSProperties
ref? Ref<HTMLDivElement>

XYPad.Grid

Prop Type Default
className? string
style? CSSProperties
columns? number 4
rows? number 4
ref? Ref<HTMLDivElement>

XYPad.Thumb

Prop Type Default
className? string
style? CSSProperties
xLabel? string 'Horizontal'
yLabel? string 'Vertical'
ref? Ref<HTMLDivElement>