useAdjustable
Make a custom value control (a slider, knob or meter) adjustable. It gives the control
role="slider" with its value range, arrow/Page/Home/End keys on web, and increment/decrement actions for VoiceOver and TalkBack.The bar meter is the slider. Tab to it: the arrow keys move by
step (Shift+arrow by ten steps, and the horizontal arrows swap under RTL), PageUp/PageDown move by largeStep (default 10% of the range), and Home/End jump to min / max. VoiceOver and TalkBack adjust it with their increment/decrement gestures and read valueText ("60 percent"). useAdjustable({ value, min, max, step, onChange, label, valueText }) returns adjustableProps for the focusable element, increment / decrement (the − and + buttons here) and handleKeyDown; each change is clamped to the range and calls onChange, then onChangeEnd. It also takes disabled, readOnly, orientation: 'vertical', endless (no clamping and no Home/End) and getNextValue for detents.Loading demo…
import { useState } from 'react';
import { Block, IconButton, useAdjustable } from '@plocks/ui';
const LEVELS = 10;
export function Demo() {
const [level, setLevel] = useState(6);
const { adjustableProps, increment, decrement } = useAdjustable({
value: level,
min: 0,
max: LEVELS,
step: 1,
onChange: setLevel,
label: 'Volume',
valueText: (value) => `${value * 10} percent`,
});
return (
<Block direction="row" align="center" gap="sm">
<IconButton icon="minus" variant="ghost" accessibilityLabel="Quieter" onPress={() => decrement()} />
<Block {...adjustableProps} direction="row" align="flex-end" gap={4} p="xs" radius="sm">
{Array.from({ length: LEVELS }, (_, index) => (
<Block
key={index}
w={8}
h={8 + index * 3}
radius={2}
bg={index < level ? 'primary.5' : 'borderStrong'}
/>
))}
</Block>
<IconButton icon="plus" variant="ghost" accessibilityLabel="Louder" onPress={() => increment()} />
</Block>
);
}React Native design for iOS, Android, and Web.
Quick Links
Resources