# Range A multi-thumb range input for selecting a span of numeric values between a minimum and maximum. It supports tick marks, value labels, and configurable stepping. ```tsx demo import { Range } from "@tenstorrent/vesper/range"; export default function RangeDemo() { return ( ); } ``` ## Options | Prop | Type | Description | Default | | ----------------------- | ---------------------------- | ------------------------------------------------------------------------------------------------------------ | ------------ | | `thumbAriaLabels` | `string[]` | Accessible `aria-label` attributes for each thumb, in the same order as the values. Required. | — | | `values` | `number[]` | The values of each thumb (controlled mode). The number of thumbs matches the length of this array. | — | | `defaultValues` | `number[]` | The initial thumb values (uncontrolled mode). | `[min, max]` | | `onValuesChange` | `(values: number[]) => void` | Callback fired as thumb values change during interaction. Receives the full array of current values. | — | | `onValuesCommit` | `(values: number[]) => void` | Callback fired when a thumb interaction is completed, eg. on pointer up. Receives the final array of values. | — | | `min` | `number` | The minimum allowed value. | `0` | | `max` | `number` | The maximum allowed value. | `100` | | `step` | `number` | The stepping interval between selectable values. | `1` | | `minStepsBetweenThumbs` | `number` | The minimum number of steps required between thumbs, preventing them from overlapping. | `1` | | `showTicks` | `boolean` | When `true`, renders tick marks along the track at each step interval. | `false` | | `showValueLabels` | `boolean` | When `true`, displays a value label above each thumb. | `false` | | `valueLabels` | `string[]` | Custom display labels for each thumb. When unset, each label falls back to that thumb's current value. | — | | `disabled` | `boolean` | When `true`, prevents interaction. | `false` | | `name` | `string` | The form field name submitted with form data. Every thumb submits its value under this name. | — | | `form` | `string` | Associates the range with a `
` element by its `id`. | — | All other props are forwarded to the wrapping `
` element. If you only need to select a single value, reach for [the `Slider` component](./slider.mdx) instead: it renders the same track, ticks, and labels, but exposes a single-value API. ## Examples ### Uncontrolled vs controlled Render a `Range` in an uncontrolled fashion to let it keep track of its own values. Pass `defaultValues` if the thumbs should start somewhere other than `min` and `max`: ```tsx demo import { Range } from "@tenstorrent/vesper/range"; export default function UncontrolledRange() { return ( ); } ``` If you need to control the values, use the `values` and `onValuesChange` props. `onValuesChange` fires continuously as a thumb is dragged, and always receives the full array of values: ```tsx demo import { useState } from "react"; import { Button } from "@tenstorrent/vesper/button"; import { Range } from "@tenstorrent/vesper/range"; import { Typography } from "@tenstorrent/vesper/typography"; export default function ControlledRange() { const [price, setPrice] = useState([25, 75]); return (
Selected values: {price.join(", ")}
); } ``` Use `onValuesCommit` when you only care about the final values of an interaction, eg. to avoid firing a request on every pointer move: ```tsx import { Range } from "@tenstorrent/vesper/range"; export default function PriceRange() { return ( fetchResults({ min, max })} thumbAriaLabels={["Price (min)", "Price (max)"]} /> ); } ``` ### Bounds and stepping `min`, `max`, and `step` describe the values a thumb can take, defaulting to `0`, `100`, and `1`. A `step` of `50` on a range of `0-1000` restricts thumbs to multiples of 50: ```tsx demo import { Range } from "@tenstorrent/vesper/range"; export default function LargeStepRange() { return ( ); } ``` The `step` also determines how far a thumb moves per arrow key press when it is focused. ### Keeping thumbs apart By default, thumbs must stay at least one step apart. Raise `minStepsBetweenThumbs` when a range needs a wider minimum span. The range below keeps its thumbs at least 20 apart: ```tsx demo import { Range } from "@tenstorrent/vesper/range"; export default function CustomStepBetweenThumbsRange() { return ( ); } ``` The value is measured in steps, not in units, so a `minStepsBetweenThumbs` of `2` on a range with a `step` of `5` keeps thumbs at least 10 apart. ### Showing ticks Pass `showTicks` to mark each step along the track. Ticks are rendered between `min` and `max`, so a range of `0-10` with a `step` of `1` renders nine of them: ```tsx demo import { Range } from "@tenstorrent/vesper/range"; export default function RangeWithTicks() { return ( ); } ``` > [!NOTE] > > Ticks are drawn per step, so a fine `step` on a wide range produces a dense, noisy track. Ticks work best when the range has a handful of stops, eg. a range of `0-100` with a `step` of `10`. ### Value labels Pass `showValueLabels` to render each thumb's current value above it as you drag: ```tsx demo import { Range } from "@tenstorrent/vesper/range"; export default function RangeShowingValueLabels() { return ( ); } ``` Use `valueLabels` to display something other than the raw value, such as a formatted or unit-suffixed one. Labels are matched to thumbs by index, and are only rendered when `showValueLabels` is also passed: ```tsx demo import { useState } from "react"; import { Range } from "@tenstorrent/vesper/range"; export default function CustomThumbLabelsRange() { const [price, setPrice] = useState([30, 70]); return ( `$${value}`)} thumbAriaLabels={["Price (min)", "Price (max)"]} /> ); } ``` > [!IMPORTANT] > > Value labels are presentational, and are not announced by assistive technology. > > Screen readers announce the underlying numeric value of a thumb instead. If a label carries meaning that the number alone doesn't, such as a currency or a unit, include it in the `thumbAriaLabels` too. ### More than two thumbs A `Range` renders one thumb per entry in `values` or `defaultValues`, so it isn't limited to two. Give every thumb an entry in `thumbAriaLabels`, and in `valueLabels` when you are customising labels: ```tsx demo import { Range } from "@tenstorrent/vesper/range"; export default function ThreeThumbsRange() { return ( ); } ``` ### Labels and accessible names `thumbAriaLabels` names the individual thumbs. It is required, and each thumb needs its own entry. A thumb without a label is announced with no indication of which end of the range it controls. ```tsx demo import { Range } from "@tenstorrent/vesper/range"; export default function RangeDemo() { return ( ); } ``` ### Keyboard interaction Each thumb is a native ``, so a `Range` is fully keyboard operable once a thumb is focused: | Key | Behaviour | | ------------------------- | -------------------------------------------------------------------- | | `ArrowRight` / `ArrowUp` | Increases the focused thumb by `step`. | | `ArrowLeft` / `ArrowDown` | Decreases the focused thumb by `step`. | | `Shift` + any arrow key | Moves the focused thumb by 10 units instead of `step`. | | `PageUp` / `PageDown` | Increases / decreases the focused thumb by 10 units. | | `Home` | Moves the focused thumb as far towards `min` as it is allowed to go. | | `End` | Moves the focused thumb as far towards `max` as it is allowed to go. | ### Disabling the range Pass `disabled` to prevent all interaction. A disabled `Range` still displays its current values, but its thumbs are skipped in the tab order and excluded from form data: ```tsx demo import { Range } from "@tenstorrent/vesper/range"; export default function DisabledRange() { return ( ); } ``` ### Usage in forms Each thumb renders a native ``, so passing a `name` includes every thumb's value in form submissions under that name. Since every thumb submits under the same `name`, the form below submits `price=20&price=80`. Read both values with `FormData.getAll`: ```tsx const data = new FormData(form); data.getAll("price"); // ["20", "80"] ``` ```tsx demo import { useState } from "react"; import { Button } from "@tenstorrent/vesper/button"; import { Range } from "@tenstorrent/vesper/range"; import { Typography } from "@tenstorrent/vesper/typography"; export default function FormRangeDemo() { const [submitted, setSubmitted] = useState(null); return ( { event.preventDefault(); const data = new FormData(event.currentTarget); setSubmitted(data.getAll("price").map(String)); }} > {submitted !== null && ( Submitted values: {submitted.join(", ")} )} ); } ```