Skip to content

Slider field

A labelled slider with a value readout, min and max hints and reset to default.

Status
Beta
Category
Inputs
Adoption
Not used yet
import { SliderField } from "@oration/canon/components/slider-field";
packages/canon/src/components/slider-field.tsx

Interruptions

How much a supplier has to say before the remittance agent stops talking.

Interruption word threshold

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

A slider's indigo fill marks a value someone is setting. When the amount is measured rather than chosen (credits used, talk ratio), draw it in ink steps with a meter instead.

The Tabular Figures Rule

The readout and the end captions are tabular, so the readout doesn't jitter as the value changes.

Anatomy#

Ambient volume

Plays under the agent's voice.

  1. Label. Base UI Slider.Label at 14px weight 500, with an optional info tip beside it.
  2. Reset button. An extra-small ghost button, Reset to plus the formatted default. It appears only while the value differs from defaultValue.
  3. Readout. The formatted value at 13px weight 500 in tabular figures, right-aligned.
  4. 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.
  5. Captions. 12px Slate Meta labels for each end: minLabel and maxLabel, or the formatted min and max.
  6. 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.

Speed

Slow down slightly for agents that read long invoice numbers.

Ambient volume
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.

Similarity
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

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#

States
StateTreatment
RestThe fill runs from the start to the thumb.
HoverA 3px Focus Indigo ring at 50% around the thumb, over 150ms.
Focus visibleThe same 3px ring while the thumb has keyboard focus.
DraggingThe ring stays while the thumb is pressed.
ChangedThe reset button scales in from 0.96 with a fade on the fast spring, and leaves on the faster exit.
DisabledThe track and thumb dim to 50% and ignore input. The label and readout stay at full strength.

Behavior#

  • Controlled only: value and onChange(value). It holds a single number.
  • step defaults to 1. Round in onChange when the step is a fraction, as speed does with Math.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 defaultValue through onChange and then disappears.
  • Pass an InfoTip to info for jargon such as stability or interruption threshold.

Do and don't#

Ambient volume
Do. Format the value with its unit and name what each end means: Slower and Faster, Silent and Loud.
Ambient volume
Don't. Show a raw 0.3 with 0 and 1 at the ends, so people have to guess the scale.
Speed
Do. Set defaultValue to the recommended setting so people can get back to it in one click.
Speed
Don't. Leave people to remember where the default was after a few experiments.

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.
  • format includes 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.
  • format also sets aria-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 in hint, which is linked with aria-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.
Keyboard interactions
KeysAction
←→Moves by one step. Up and down arrows do the same.
Shift→Moves by 10 units, Base UI's large step.
Page UpPage DownMoves by 10 units.
HomeEndJumps to min or max.

Design tokens#

Design tokens
TokenUsed for
--mutedTrack
--primaryFill
--ringThumb edge and its 3px ring at 50%
--muted-foregroundCaptions, hint and the reset button
shadow-xsThumb lift
spring.fastReset button entrance
exit.fastReset button exit

API reference#

SliderField

A labelled single-value slider with readout, captions and reset.

Props of SliderField
PropTypeDefaultDescription
labelRequiredReactNodeNo defaultThe setting's name.
valueRequirednumberNo defaultThe current value.
onChangeRequired(value: number) => voidNo defaultCalled as the thumb moves and when reset is pressed.
minRequirednumberNo defaultThe low end.
maxRequirednumberNo defaultThe high end.
stepnumber1The increment.
defaultValuenumberNo defaultThe 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.
minLabelReactNodeNo defaultCaption for the low end. Defaults to format(min).
maxLabelReactNodeNo defaultCaption for the high end. Defaults to format(max).
hintReactNodeNo defaultA sentence under the slider, linked to the thumb.
infoReactNodeNo defaultAn InfoTip beside the label.
disabledbooleanNo defaultDims the track and blocks input.
idstringNo defaultBase for the hint's id.
classNamestringNo defaultOn 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.