Slider field
A labelled slider with a value readout, min and max hints and reset to default.
Interruptions
How much a supplier has to say before the remittance agent stops talking.
At 2 words, while the agent reads a remittance
- “uh-huh”Agent keeps going
- “wait, which invoice”Agent stops
- “can you repeat the amount please”Agent stops
import { InfoTip } from "@oration/canon/components/info-tip";import { SliderField } from "@oration/canon/components/slider-field";import { Well } from "@oration/canon/components/well";import * as React from "react";export function Hero() { const [threshold, setThreshold] = React.useState(2); const words = (n: number) => (n === 1 ? "1 word" : `${n} words`); const examples = [ { said: "uh-huh", count: 1 }, { said: "wait, which invoice", count: 3 }, { said: "can you repeat the amount please", count: 6 }, ]; return ( <div className="flex w-full max-w-lg flex-col gap-4 rounded-xl bg-card p-4 text-left shadow-border"> <div className="flex flex-col gap-1"> <p className="text-sm font-semibold text-foreground"> Interruptions </p> <p className="text-13 text-muted-foreground"> How much a supplier has to say before the remittance agent stops talking. </p> </div> <SliderField label="Interruption word threshold" info={ <InfoTip title="Interruption word threshold" description="The number of words the caller has to say before the agent stops mid-sentence. Low values feel responsive; high values ride over background noise." /> } value={threshold} onChange={setThreshold} min={1} max={5} defaultValue={2} format={words} minLabel="Stops at any sound" maxLabel="Only full sentences" /> <Well> <p className="mb-2 text-13 font-medium text-foreground"> At {words(threshold)}, while the agent reads a remittance </p> <ul className="flex flex-col gap-1.5 text-13"> {examples.map((example) => { const stops = example.count >= threshold; return ( <li key={example.said} className="flex items-center justify-between gap-3" > <span className="text-muted-foreground"> “{example.said}” </span> <span className={ stops ? "font-medium text-foreground" : "text-muted-foreground" } > {stops ? "Agent stops" : "Agent keeps going"} </span> </li> ); })} </ul> </Well> </div> );}Usage#
Slider field is a labelled slider for tuning one number in settings: a 14px label, a live readout, captions for both ends, an optional hint, and a Reset to … button that appears once the value leaves its default. Agent settings use it for the interruption word threshold, speaking speed, voice stability and ambient volume. The mistake people make is leaving format as the raw number. The readout, the end captions, the reset label and the value screen readers hear all come from format, so put the unit there: 2 words, 30%, 1.00×.
When to use
- For a setting on a bounded scale where the relative position matters more than the exact number: speed, stability, volume.
- For a small integer range with meaningful ends, such as an interruption threshold from Stops at any sound to Only full sentences.
- When the setting has a recommended default people should be able to return to.
- Beside a preview that updates as the value moves, so people see what the number means.
When not to use
- For an exact value people type, such as a timeout in seconds. Use Duration picker
- For a range with two thumbs, or a bare slider without the label row. Use Slider
- To show a measured amount that people can't change. Use Meter
- For two to five named modes rather than a scale. Use Segmented control
The Ink Fill Rule
The Tabular Figures Rule
Anatomy#
Plays under the agent's voice.
- Label. Base UI
Slider.Labelat 14px weight 500, with an optionalinfotip beside it. - Reset button. An extra-small ghost button, Reset to plus the formatted default. It appears only while the value differs from
defaultValue. - Readout. The formatted value at 13px weight 500 in tabular figures, right-aligned.
- Track. A 4px Well Gray track with an indigo fill, and a 14px white thumb with an indigo edge and a 20px extra hit area around it.
- Captions. 12px Slate Meta labels for each end:
minLabelandmaxLabel, or the formatted min and max. - Hint. An optional 13px sentence under the slider, linked to the thumb.
Examples#
Units and end captions
format writes the unit into the readout and the announced value. minLabel and maxLabel say what each end does, and hint says when to change it.
Slow down slightly for agents that read long invoice numbers.
import { SliderField } from "@oration/canon/components/slider-field";import * as React from "react";export function Formats() { const [speed, setSpeed] = React.useState(1); const [volume, setVolume] = React.useState(0.3); return ( <div className="flex w-full max-w-md flex-col divide-y divide-border rounded-xl bg-card shadow-border"> <div className="p-4"> <SliderField label="Speed" value={speed} onChange={(next) => setSpeed(Math.round(next * 100) / 100)} min={0.7} max={1.2} step={0.05} defaultValue={1} format={(n) => `${n.toFixed(2)}×`} minLabel="Slower" maxLabel="Faster" hint="Slow down slightly for agents that read long invoice numbers." /> </div> <div className="p-4"> <SliderField label="Ambient volume" value={volume} onChange={setVolume} min={0} max={1} step={0.05} defaultValue={0.3} format={(n) => `${Math.round(n * 100)}%`} minLabel="Silent" maxLabel="Loud" /> </div> </div> );}Reset to default
With defaultValue, a Reset to 75% button appears once the value moves away, and disappears when it is back.
import { SliderField } from "@oration/canon/components/slider-field";import * as React from "react";export function Reset() { const [similarity, setSimilarity] = React.useState(0.9); return ( <div className="w-full max-w-md"> <SliderField label="Similarity" value={similarity} onChange={setSimilarity} min={0} max={1} step={0.05} defaultValue={0.75} format={(n) => `${Math.round(n * 100)}%`} minLabel="Looser" maxLabel="Closest to sample" /> </div> );}Disabled
When a setting doesn't apply, dim it and say why in the hint, rather than hiding it.
Stability applies to ElevenLabs voices. This agent uses Cartesia Sonic.
import { SliderField } from "@oration/canon/components/slider-field";import * as React from "react";export function Disabled() { const [stability, setStability] = React.useState(0.5); return ( <div className="w-full max-w-md"> <SliderField label="Stability" value={stability} onChange={setStability} min={0} max={1} step={0.05} defaultValue={0.5} format={(n) => `${Math.round(n * 100)}%`} minLabel="Expressive" maxLabel="Consistent" hint="Stability applies to ElevenLabs voices. This agent uses Cartesia Sonic." disabled /> </div> );}States#
| State | Treatment |
|---|---|
| Rest | The fill runs from the start to the thumb. |
| Hover | A 3px Focus Indigo ring at 50% around the thumb, over 150ms. |
| Focus visible | The same 3px ring while the thumb has keyboard focus. |
| Dragging | The ring stays while the thumb is pressed. |
| Changed | The reset button scales in from 0.96 with a fade on the fast spring, and leaves on the faster exit. |
| Disabled | The track and thumb dim to 50% and ignore input. The label and readout stay at full strength. |
Behavior#
- Controlled only:
valueandonChange(value). It holds a single number. stepdefaults to 1. Round inonChangewhen the step is a fraction, as speed does withMath.round(next * 100) / 100, to avoid floating-point tails.- The thumb aligns to the track's edges at min and max, so it never overhangs the row.
- Clicking anywhere on the track moves the thumb there; dragging follows the pointer.
- The reset button sets
defaultValuethroughonChangeand then disappears. - Pass an
InfoTiptoinfofor jargon such as stability or interruption threshold.
Do and don't#
defaultValue to the recommended setting so people can get back to it in one click.Content#
- The label names the setting, not the control: Speed, Interruption word threshold.
- End captions describe the effect, in two or three words: Stops at any sound, Only full sentences.
formatincludes the unit and pluralizes: 1 word, 3 words.- The hint says when to change it: Slow down slightly for agents that read long reference numbers.
Accessibility#
- The label is a Base UI
Slider.Label, so the thumb is named by it. formatalso setsaria-valuetext, so screen readers hear 30% instead of 0.3.- The visible readout and end captions are
aria-hidden. If an end's meaning matters, repeat it inhint, which is linked witharia-describedby. - The thumb is 14px with an extra 10px on every side, for a 34px target.
- When the value is off, explain why beside it or in the hint rather than only dimming it.
| Keys | Action |
|---|---|
| ←→ | Moves by one step. Up and down arrows do the same. |
| Shift→ | Moves by 10 units, Base UI's large step. |
| Page UpPage Down | Moves by 10 units. |
| HomeEnd | Jumps to min or max. |
Design tokens#
| Token | Used for |
|---|---|
--muted | Track |
--primary | Fill |
--ring | Thumb edge and its 3px ring at 50% |
--muted-foreground | Captions, hint and the reset button |
shadow-xs | Thumb lift |
spring.fast | Reset button entrance |
exit.fast | Reset button exit |
API reference#
SliderField
A labelled single-value slider with readout, captions and reset.
| Prop | Type | Default | Description |
|---|---|---|---|
labelRequired | ReactNode | No default | The setting's name. |
valueRequired | number | No default | The current value. |
onChangeRequired | (value: number) => void | No default | Called as the thumb moves and when reset is pressed. |
minRequired | number | No default | The low end. |
maxRequired | number | No default | The high end. |
step | number | 1 | The increment. |
defaultValue | number | No default | The recommended value. Shows the reset button whenever value differs. |
format | (value: number) => string | (v) => String(v) | Formats the readout, captions, reset label and aria-valuetext. |
minLabel | ReactNode | No default | Caption for the low end. Defaults to format(min). |
maxLabel | ReactNode | No default | Caption for the high end. Defaults to format(max). |
hint | ReactNode | No default | A sentence under the slider, linked to the thumb. |
info | ReactNode | No default | An InfoTip beside the label. |
disabled | boolean | No default | Dims the track and blocks input. |
id | string | No default | Base for the hint's id. |
className | string | No default | On the root. |
Known gaps#
Where the implementation and the system disagree today. Follow the system, not the gap.
largeStep isn't exposed, so Shift+arrow and Page Up or Down move by Base UI's default of 10 units. On a 0 to 1 or 1 to 5 range, one press jumps to the end.
The reset button unmounts as soon as it is pressed, so keyboard focus falls to the page.
The end captions are aria-hidden; their meaning reaches screen readers only if it is repeated in hint.
The thumb is hard-coded bg-white, and disabled dims only the track, not the label and readout.
The fill is Quiet Indigo. DESIGN.md lists checked controls, selection and focus as indigo's uses but doesn't name sliders.