Skip to content

@vibe-labs/design-vue-sliders

Vue 3 slider component for the Vibe Design System. Single value, range, vertical, marks, tooltips, and multiple thumb shapes — all backed by @vibe-labs/design-components-sliders CSS tokens.

Installation

ts
import { VibeSlider } from "@vibe-labs/design-vue-sliders";

Requires the CSS layer from @vibe-labs/design-components-sliders.

Components

VibeSlider

Flexible slider supporting single value, dual-thumb range, vertical orientation, step marks, value tooltips, and multiple thumb shapes.

Usage

vue
<!-- Basic -->
<VibeSlider v-model="volume" :min="0" :max="100" />

<!-- With tooltip on hover -->
<VibeSlider v-model="brightness" tooltip="hover" />

<!-- Always-visible tooltip with formatter -->
<VibeSlider v-model="price" :min="0" :max="1000" :step="10" tooltip="always" :format-value="(v) => `£${v}`" />

<!-- Range (dual-thumb) -->
<VibeSlider v-model="priceRange" :range="true" :min="0" :max="500" :step="5" />

<!-- With marks -->
<VibeSlider v-model="rating" :min="1" :max="5" :step="1" :marks="true" />

<!-- Custom marks with labels -->
<VibeSlider
  v-model="temp"
  :min="0"
  :max="100"
  :marks="[
    { value: 0, label: 'Cold' },
    { value: 50, label: 'Warm' },
    { value: 100, label: 'Hot' },
  ]"
/>

<!-- Sizes -->
<VibeSlider v-model="val" size="sm" />
<VibeSlider v-model="val" size="lg" />

<!-- Thumb shapes -->
<VibeSlider v-model="position" thumb-shape="bar" />
<VibeSlider v-model="position" thumb-shape="pill" />

<!-- Track colors -->
<VibeSlider v-model="health" color="success" />
<VibeSlider v-model="danger" color="danger" />

<!-- Vertical -->
<VibeSlider v-model="level" orientation="vertical" style="height: 200px" />

<!-- Disabled -->
<VibeSlider v-model="locked" :disabled="true" />

<!-- Decimal step -->
<VibeSlider v-model="opacity" :min="0" :max="1" :step="0.01" :format-value="(v) => `${Math.round(v * 100)}%`" />

Props

PropTypeDefaultDescription
modelValuenumber | [number, number]Value (number for single, tuple for range)
minnumber0Minimum value
maxnumber100Maximum value
stepnumber1Step increment
rangebooleanfalseEnable dual-thumb range mode
sizeSliderSize"md"sm · md · lg
orientationSliderOrientation"horizontal"horizontal · vertical
thumbShapeSliderThumbShape"circle"circle · bar · pill · none
colorSliderTrackColor"accent"accent · success · warning · danger
tooltipSliderTooltipMode"none"none · hover · always
marksboolean | number[] | SliderMark[]Show marks (true = auto from step, capped at 50)
formatValue(value: number) => stringStringFormat value for tooltip and aria-valuetext
disabledbooleanfalseDisabled state
ariaLabelstringAccessible label for single / first thumb
ariaLabelEndstringAccessible label for second thumb (range)
idstringautoHTML id
namestringForm name (range adds -end suffix for second input)

Events

EventPayloadDescription
update:modelValuenumber | [number, number]Value changed
dragStartSliderDragEventThumb drag started
dragEndSliderDragEventThumb drag finished

Exposed Methods

MethodDescription
focus()Focus the first (or only) thumb
blur()Blur the active thumb
valueComputed current value (reactive)

Keyboard

KeyAction
ArrowRight / ArrowUpIncrease by step
ArrowLeft / ArrowDownDecrease by step
PageUpIncrease by 10× step or 10% of range
PageDownDecrease by 10× step or 10% of range
HomeJump to minimum
EndJump to maximum

Accessibility

  • Each thumb has role="slider" with full ARIA attributes
  • aria-valuemin, aria-valuemax, aria-valuenow, aria-valuetext
  • aria-orientation for vertical mode
  • Hidden <input type="range"> elements for form submission
  • Range mode constrains aria-valuemin/aria-valuemax per thumb to prevent overlap
  • Focus ring via :focus-visible on thumb elements

Composables

useSliderDrag(options)

Core composable for pointer, touch, and keyboard interaction. Handles step snapping, range thumb clamping, floating-point precision, and vertical orientation.

ts
import { useSliderDrag } from "@vibe-labs/design-vue-sliders";

const { isDragging, activeThumb, thumbPercents, onThumbDown, onTrackDown, onKeyDown, valueToPercent, percentToValue } = useSliderDrag({
  trackEl,
  min: toRef(props, "min"),
  max: toRef(props, "max"),
  step: toRef(props, "step"),
  orientation: toRef(props, "orientation"),
  disabled: toRef(props, "disabled"),
  values: internalValues,
  range: toRef(props, "range"),
  onUpdate: (index, value) => {
    /* emit */
  },
});

Key features:

  • Pointer capture — reliable drag even outside the slider bounds
  • Click-to-jump — clicking the track moves the nearest thumb
  • Range clamping — thumbs can't cross each other
  • Step snapping — values always land on valid steps
  • Float precision — no 0.1 + 0.2 = 0.300000004 artifacts

Types

ts
interface SliderMark {
  value: number;
  label?: string;
}

type SliderMarkProp = boolean | number[] | SliderMark[];
type SliderFormatFn = (value: number) => string;

interface SliderDragEvent {
  thumb: 0 | 1;
  value: number;
}

Dependencies

PackagePurpose
@vibe-labs/design-components-slidersCSS tokens + generated styles

Build

bash
npm run build

Built with Vite + vite-plugin-dts. Outputs ES module with TypeScript declarations.