XYPad
Two-dimensional slider component.
Source Style- React
- Svelte
- Vue
function App() { const [valueX, setValueX] = useState(32) const [valueY, setValueY] = useState(56) return ( <div> <XYPad.Root className={xyPadTheme.root} value={[valueX, valueY]} min={0} max={100} onChange={([x, y]) => { setValueX(x) setValueY(y) }} > <XYPad.Area className={xyPadTheme.area}> <XYPad.Thumb className={xyPadTheme.thumb} aria-label={['X position', 'Y position']} /> </XYPad.Area> </XYPad.Root> <p> x: {valueX}, y: {valueY} </p> </div> ) }
/** The demo theme for `XYPad`, as a CSS Module: one class per part, applied by* the caller. Copy this file into your project and edit it.*//** The size the thumb is drawn at, the room the pad leaves for it, and the* smallest the area may become. Three parts need the value and nothing* inherits it, so it is composed into each of them.*/.thumbSize {--thumb-size: 22px;}.root {composes: thumbSize;display: inline-block;margin: calc(var(--thumb-size) / 2);cursor: pointer;-webkit-tap-highlight-color: transparent;&[data-readonly] {cursor: not-allowed;}}.area {composes: thumbSize;/* Room for the thumb to travel. A percentage size, or a flex row thatoverrides the automatic minimum size, may shrink the area to nothingwith the thumb left sitting on a line. */--min-width: calc(var(--thumb-size) * 2);--min-height: calc(var(--thumb-size) * 2);width: 120px;height: 120px;min-width: var(--min-width);min-height: var(--min-height);background-color: oklch(95% 0 0);}.thumb {composes: thumbSize;--color: oklch(59.5% 0.17548 266.373);width: var(--thumb-size);height: var(--thumb-size);border-radius: 50%;outline: none;background-color: var(--color);&[data-disabled] {background-color: oklch(50.462% 0.03307 270.021);}&:focus-within {box-shadow: 0px 0px 0px 3pxcolor-mix(in srgb, var(--color) 20%, transparent);}}/** Dark mode follows whatever marks the page — `.dark` and `[data-theme=dark]`* are what this site and Storybook set.*/:global(.dark) .area,:global([data-theme='dark']) .area {background-color: oklch(41.7% 0 0);}:global(.dark) .thumb,:global([data-theme='dark']) .thumb {--color: oklch(64.5% 0.17548 266.373);&:hover {--color: oklch(67.5% 0.17548 266.373);}}
<script lang="ts">
import { XYPad } from '@tremolo-ui/svelte'
// Copy this file from the Styling page into your own project.
import xyPadTheme from './XYPad.module.css'
let value: [number, number] = $state([32, 56])
</script>
<XYPad.Root class={xyPadTheme.root} bind:value min={0} max={100}>
<XYPad.Area class={xyPadTheme.area}>
<XYPad.Thumb
class={xyPadTheme.thumb}
aria-label={['X position', 'Y position']}
/>
</XYPad.Area>
</XYPad.Root>
<p>x: {value[0]}, y: {value[1]}</p>
<script setup lang="ts">
import { ref } from 'vue'
import type { XY } from '@tremolo-ui/dom'
import { XYPad, XYPadArea, XYPadThumb } from '@tremolo-ui/vue'
// Copy this file from the Styling page into your own project.
import xyPadTheme from './XYPad.module.css'
const value = ref<XY<number>>([32, 56])
</script>
<template>
<XYPad v-model="value" :class="xyPadTheme.root" :min="0" :max="100">
<XYPadArea :class="xyPadTheme.area">
<XYPadThumb
:class="xyPadTheme.thumb"
:aria-label="['X position', 'Y position']"
/>
</XYPadArea>
</XYPad>
<p>x: {{ value[0] }}, y: {{ value[1] }}</p>
</template>
Import
- React
- Svelte
- Vue
import { XYPad } from '@tremolo-ui/react'
import { XYPad } from '@tremolo-ui/svelte'
import { XYPad, XYPadArea, XYPadThumb } from '@tremolo-ui/vue'
API Reference
Each part lists the props of its own. It also takes the attributes of the
element it renders, such as className and style.
In Svelte and Vue the props are the same, written their way — class rather
than className, bind:value or v-model, snippets or slots for content. See
Svelte and Vue.
State is on the element as a data attribute, so it is a selector away — see Styling. An attribute is only there while its state is on.
Root
Holds the value of both axes and the settings, and passes them to the parts inside.
| Prop | Type | Default |
|---|---|---|
value* | XY<number> | — |
min* | number | readonly [number, number] | — |
max* | number | readonly [number, number] | — |
step | number | readonly [number, number] | 1 |
scale | Scale | readonly [Scale, Scale] | linearScale |
reverse | boolean | readonly [boolean, boolean] | false |
wheel | ModifierValue<InputEventOption> | null | ['raw', 1] |
dragSensitivity | ModifierValue<number> | { default: 1, shift: 0.1 } |
keyboard | ModifierValue<InputEventOption> | null | { default: ['raw', 1], shift: ['raw', 0.1] } |
dragCursor | Cursor | 'pointer' |
disabled | boolean | — |
readOnly | boolean | — |
onChange | (value: XY<number>) => void | — |
onChangeStart | (value: XY<number>, source: ChangeSource) => void | — |
onChangeEnd | (value: XY<number>, source: ChangeSource) => void | — |
changeEndDelay | number | 500 |
children* | ReactNode | — |
actionsRef | Ref<XYPadMethods> | — |
Full reference: XYPadProps
| Data attribute | Values |
|---|---|
[data-disabled] | Present when disabled is set |
[data-readonly] | Present when readonly is set |
Area
The surface the thumb moves across. XYPad.Thumb goes inside it.
Set its size and background with CSS or style. It takes the attributes of a
<div>.
Thumb
The handle. Two visually hidden range inputs inside it, one per axis, take the focus and carry the ARIA.
Its children are drawn inside it. The thumb is one element either way, so what
is passed there is decoration on top of it rather than a replacement for it —
className and style are how its own appearance is changed.
| Prop | Type | Default |
|---|---|---|
color | string | — |
aria-label | string | readonly [string | undefined, string | undefined] | — |
aria-labelledby | string | readonly [string | undefined, string | undefined] | — |
aria-describedby | string | readonly [string | undefined, string | undefined] | — |
aria-valuetext | string | readonly [string | undefined, string | undefined] | — |
actionsRef | Ref<XYPadThumbMethods> | — |
Full reference: XYPadThumbProps
| Custom property | Written by | What it sets |
|---|---|---|
--color | color | Colour of the thumb. |
| Data attribute | Values |
|---|---|
[data-disabled] | Present when disabled is set |
[data-readonly] | Present when readonly is set |