jixoai-ui
the terminal design language
loading the style sheet…
Skip to content
docs · print medium: screen

registry:ui · Data Entry

range — the native slider, semantic surface

The native slider, semantic surface: a REAL input[type=range] as the base (2026-09-01 native rebase — jx-pure is the foundation; the registry version adds slots and semantics, never re-draws the control). The platform owns pointer, keyboard (arrows/Home/End/PageUp/Down), RTL, the label[for] binding and form submission (name= rides the input own FormData lane); the component adds the label row, the step-precision value readout, the optional tick ruler (one mark per step, inset to the thumb's travel; click a mark to snap, the wheel fine-tunes one input-step per detent behind a declarative touch-action axis grammar (wheel: true | 'x' | 'y' | 'xy' | 'none' | { x?, y? } with per-detent multipliers — owned gestures are swallowed and default-prevented, the page never scrolls under the slider), the ruler composes declaratively through RangeTick scales (marks at scale × step periods, lengths grading ascending by value), the vertical orientation face (orient attribute + writing-mode: min at the physical bottom, geometry on the width axis, the ruler maps bottom-up), the family error law (aria-invalid + aria-describedby + dashed-ring thumb), density tiers and external-write snapping. Paint is the jx-pure range recipe verbatim on the component hook — one visual law, two mounting surfaces.

real input[type=range] platform keyboard + pointer label[for] binding ticks: click-to-snap + wheel jx-pure paint, same law

range

A real input[type=range] underneath — drag, tap-to-jump, ←→/↑↓/Home/End/PageUp/Down and RTL are the engine's own, engine-tested; the component carries the paint, the label row, the readout and the error law. With ticks on, the ruler rides the thumb's travel box: click a mark to snap to it, wheel the slider to fine-tune one step per notch.

40
range-usage.svelte svelte
<Range
  label="volume"
  bind:value
  min={0}
  max={100}
  step={1}
/>

range

Native base — the platform contract, for free

A real input[type=range] is the control: pointer behavior, the full keyboard contract (←→/↑↓ step, Home/End jump, PageUp/PageDown stride), RTL mirroring, the label[for] binding and form submission are the ENGINE's own — nothing hand-held, nothing simulated. The registry surface adds what the bare element cannot: the label row with live readout, the tick ruler (one 4px mark per snap point as a repeating gradient, inset to the thumb's travel box — half a thumb per side — with the explicit end tick at 100%; click a mark to snap to it, wheel the slider for one-step fine-tuning), the family error law, and the jx-pure range paint (groove track, ringed disc thumb, primary fill) GENERATED onto the component's own hook from the same css-law — one visual law, machine-projected mounting surfaces.

40
drag · tap-to-jump · value: 40
6.0
step 0.5 · click a mark to snap · value: 6
0.35
decimals snap at the step's precision · value: 0.35

Tab into a slider and drive it: ←→/↑↓ step by step, Home/End jump to the bounds, PageUp/PageDown stride — all the platform's own range behavior, plus its native form semantics (name= submits the numeric string through the input's own FormData lane). The mirrored layout below costs the component nothing: the engine draws RTL itself.

RTL + error wiring

70
6.0
fill grows from the right (the law's :dir(rtl) fill mirror), ticks mirror, arrow keys flip — the platform's own RTL
0

volume is required

The error prop is the family law on the native control too: aria-invalid + aria-describedby ride the input, the readout takes the destructive mark, and the thumb border dashes — the monochrome invalid signal, no second hue.

Range usage
<!-- the base is the REAL input[type=range]; the component adds
     the label row, readout, ticks and the error law -->
<Range label="volume" bind:value={volume} min={0} max={100} />

<!-- the ruler composes declaratively: RangeTick scales render marks at
     scale × step periods, lengths grade ascending (ruler metaphor) —
     click a mark to snap, the wheel fine-tunes one detent at a time -->
<Range label="gain" bind:value={gain} min={0} max={10} step={0.5}>
  {#snippet ticks()}
    <RangeTick scale={1} />
    <RangeTick scale={5} />
    <RangeTick scale={10} />
  {/snippet}
</Range>

<!-- the wheel surface is declarative too (touch-action axis grammar):
     'y' = plain wheel only, 'x' = shift+wheel, wheel={false} disables,
     {'{ y: 0.2 }'} = five detents per input-step -->
<Range label="fine" bind:value={fine} wheel={'{ y: 0.2 }'} />

<!-- orientation="vertical" rides the platform's vertical face —
     min at the physical bottom (writing-mode + orient), the ruler
     maps bottom-up on the block axis -->
<Range label="fader" bind:value={fader} orientation="vertical" ticks />

<!-- keyboard is the platform's: ←→/↑↓ step · Home/End jump ·
     PageUp/PageDown stride · RTL mirrors (the platform owns the
     thumb; the law carries one :dir(rtl) fill-mirror rule) -->
<div dir="rtl"><Range label="volume (rtl)" bind:value={v} /></div>

types

Range variants

Use a plain slider for continuous input, add steps and ticks when values are discrete, or flip the axis — vertical rides the platform's own vertical face with min at the physical bottom.

range · variants

40
4
50
30
click a mark to snap · wheel = one detent
30
range-types-demo.svelte svelte
<script lang="ts">
  import Range, { RangeTick } from '@ui/range';
</script>

<div class="grid gap-4 sm:grid-cols-3">
  <div class="border border-border p-4"><Range label="continuous" value={40} /></div>
  <div class="border border-border p-4"><Range label="stepped" value={4} min={0} max={10} step={1} /></div>
  <div class="border border-border p-4"><Range label="with ticks" value={50} ticks /></div>
  <div class="border border-border p-4"><Range label="ruler (1/5/10)" bind:value={rulerValue} min={0} max={100} step={1}>{#snippet ticks()}<RangeTick scale={1} /><RangeTick scale={5} /><RangeTick scale={10} />{/snippet}</Range><span class="text-muted-foreground text-[12px]">click a mark to snap · wheel = one detent</span></div>
  <div class="border border-border flex items-center justify-center p-4"><Range label="vertical fader" orientation="vertical" value={30} ticks min={0} max={10} step={1} /></div>
</div>

usage

Usage

Bind the numeric value; min, max, step, ticks, and RTL all remain explicit props.

Range usage
<!-- the base is the REAL input[type=range]; the component adds
     the label row, readout, ticks and the error law -->
<Range label="volume" bind:value={volume} min={0} max={100} />

<!-- the ruler composes declaratively: RangeTick scales render marks at
     scale × step periods, lengths grade ascending (ruler metaphor) —
     click a mark to snap, the wheel fine-tunes one detent at a time -->
<Range label="gain" bind:value={gain} min={0} max={10} step={0.5}>
  {#snippet ticks()}
    <RangeTick scale={1} />
    <RangeTick scale={5} />
    <RangeTick scale={10} />
  {/snippet}
</Range>

<!-- the wheel surface is declarative too (touch-action axis grammar):
     'y' = plain wheel only, 'x' = shift+wheel, wheel={false} disables,
     {'{ y: 0.2 }'} = five detents per input-step -->
<Range label="fine" bind:value={fine} wheel={'{ y: 0.2 }'} />

<!-- orientation="vertical" rides the platform's vertical face —
     min at the physical bottom (writing-mode + orient), the ruler
     maps bottom-up on the block axis -->
<Range label="fader" bind:value={fader} orientation="vertical" ticks />

<!-- keyboard is the platform's: ←→/↑↓ step · Home/End jump ·
     PageUp/PageDown stride · RTL mirrors (the platform owns the
     thumb; the law carries one :dir(rtl) fill-mirror rule) -->
<div dir="rtl"><Range label="volume (rtl)" bind:value={v} /></div>

a11y

Accessibility

The native input IS the slider: the platform's implicit semantics and keyboard contract, named by a real label[for].

Keyboard

KeyAction
Arrow keysChange by step
Home / EndJump to min / max
Page Up / DownChange by a larger step when supported
Wheel (hover)Fine-tune by detent — every detent = one input-step × the axis multiplier; declarative in the touch-action grammar: wheel is true/‘xy’ (default) | ‘y’ | ‘x’ | false/‘none’ | { x, y }. Owned gestures are swallowed and never scroll the page; ctrlKey pinch-zoom is never captured; disabled sliders ignore it.
Click a tickSnap to that mark (pointer-only convenience; the ruler stays aria-hidden — the step semantics live on the input, whose arrows refine from the snapped value).
Form resetThe platform restores the input; the component re-syncs the bound value, the readout and aria-valuetext (a reset fires no input events by itself).

ARIA

AttributeValueDescription
input[type=range]implicit sliderThe platform semantics: value/min/max/step are native truth; no roles to maintain.
label[for]the field idA REAL label binds to the labelable input (a div never could).
aria-valuetextstep-precision readoutThe formatted value for assistive tech (decimal steps); follows form resets too.
.jx-slider-ticksaria-hiddenThe tick ruler rides the thumb’s travel box (half a thumb inset per side); a visual aid that also snaps on click — the step semantics stay on the input itself.

theming

Density and tokens

The slider's geometry chain derives from its own size container (container-type: size): the thumb is the input's full height, the track and the ring are fractions of it — every internal proportion scales as one unit when the height lane changes.

xs
50
sm
50
default
50
lg
50
TokenDefaultSource
--jx-range-thumb100cqhcomponent
--jx-range-trackcalc(100cqh / 2.5)component
--jx-range-ringcalc(100cqh / 8)component
--jx-slider-fill-colorvar(--primary)component
--jx-tick-stepruntime step percentage (step / (max − min) × 100)component
--jx-hit28 / 32 / 40 / 48pxdensity
--jx-icon16 / 18 / 20 / 24pxdensity
--jx-text11 / 12 / 13 / 15pxdensity
--jx-line16 / 18 / 20 / 24pxdensity

axes

Universal props

The eight-axis surface (explicit-props): size · shape · radius · density · color · theme · elevation · motion — each axis takes named steps, auto (inherit the ambient context; stamps nothing), an exact number (px · coefficient · dp · hue per axis), or query() for responsive/container-conditional values. The family CONSUMES size and color: the native element never receives them (the §1 native collision rule).

range · universal props

40
60
range-universal.svelte svelte
<Range label="px number" min={0} max={100} value={40} size={14} density="small" />
<Range label="named steps" min={0} max={100} value={60} size="large" radius="medium" />

api

API

Props define the numeric model, naming, visual options, and the error law; form lifecycle is native.

Properties

PropertyTypeDefaultDescription
value bindnumber0Bindable committed value; external writes snap into [min, max] on the step.
min number0Lower bound (native).
max number100Upper bound (native).
step number1Increment for pointer and keyboard (native); step <= 0 or non-finite falls back to the platform default (1).
name string—Form field name — the input submits its numeric string itself.
ticks boolean | snippetfalseThe tick ruler: true draws one default RangeTick per step (inset to the thumb’s travel); a ticks snippet composes RangeTick scales — marks at scale × step periods, lengths grading ascending by value. Clicking a mark snaps the value.
wheel boolean | ‘x’ | ‘y’ | ‘xy’ | ‘none’ | { x?, y? }trueThe wheel fine-tune surface, declarative in the touch-action axis grammar: each axis is false (off), true (one input-step per detent) or a multiplier (0.2 → five detents per input-step). Owned gestures are swallowed and default-prevented; ctrlKey pinch-zoom is never captured.
orientation 'horizontal' | 'vertical''horizontal'Vertical rides the platform’s vertical face (writing-mode + orient): min at the physical bottom, the ruler maps bottom-up on the block axis, height defaults to --jx-range-length (10rem).
showValue booleantrueShows the current value readout.
error string—Adds invalid state and message.
disabled booleanfalseThe platform disabled semantics (pointer, keyboard, form).

Universal props

PropertyTypeDefaultDescription
size'small' | 'medium' | 'large' | 'auto' | number'auto' · ambient scopethe base scale — root font-size; parts size in em
shape'round' | 'scoop' | 'bevel' | 'notch' | 'square' | 'squircle' | 'auto''auto' · ambient scopecorner geometry (CSS corner-shape; §14 degrade table)
radius'small' | 'medium' | 'large' | 'auto' | number'auto' · ambient scopecorner size; auto = the concentric broadcast (§3)
density'small' | 'medium' | 'large' | 'auto' | number'auto' · ambient scopespacing/leading scale over the kernel channels (§4)
color'primary' | 'secondary' | 'error' | 'warn' | 'success' | 'info' | 'auto' | number | string'auto' · ambient scopethe hue axis of the fixed oklch primary system (§5)
theme'light' | 'dark' | 'system' | 'auto''auto' · ambient scopelight/dark profile; system = the JS-mutable global (§6)
elevation'level-1' | 'level0' | 'level1' | 'level2' | 'level3' | 'level4' | 'level5' | 'auto' | number'auto' · ambient scopeofficial M3 levels over the surface ladder (§7)
motion'reduced' | 'subtle' | 'normal' | 'expressive' | 'auto' | number'auto' · ambient scopeintensity across the motion kernels (§8)
jixoai-ui ui.jixoai.com the jixoai design language

Theme

Brand hue

0°

Auto cycle · 24h