Skip to content

Slider ​

An input where the user selects a value from within a given range.
vue
vue
<script setup lang="ts">
import { ref } from 'vue'
import { SliderRange, SliderRoot, SliderThumb, SliderTrack } from '@oku-ui/primitives'

const sliderValue = ref([50])
</script>

<template>
  <SliderRoot
    v-model="sliderValue"
    class="relative flex items-center select-none touch-none w-[200px] h-5"
    :max="100"
    :step="1"
  >
    <SliderTrack class="bg-blackA10 relative grow rounded-full h-[3px]">
      <SliderRange class="absolute bg-white rounded-full h-full" />
    </SliderTrack>
    <SliderThumb
      class="block w-5 h-5 bg-white shadow-[0_2px_10px] shadow-blackA7 rounded-[10px] hover:bg-violet3 focus:outline-none focus:shadow-[0_0_0_5px] focus:shadow-blackA8"
      aria-label="Volume"
    />
  </SliderRoot>
</template>

Features ​

  • Can be controlled or uncontrolled.
  • Supports multiple thumbs.
  • Supports a minimum value between thumbs.
  • Supports touch or click on track to update value.
  • Supports Right to Left direction.
  • Full keyboard navigation.

Installation ​

Install the component from your command line.

sh
sh
$ npm add @oku-ui/primitives

Anatomy ​

Import all parts and piece them together.

vue
vue
<script setup>
import { SliderRange, SliderRoot, SliderThumb, SliderTrack } from '@oku-ui/primitives'
</script>

<template>
  <SliderRoot>
    <SliderTrack>
      <SliderRange />
    </SliderTrack>
    <SliderThumb />
  </SliderRoot>
</template>

API Reference ​

Root ​

Contains all the parts of a slider. It will render an input for each thumb when used within a form to ensure events propagate correctly.

PropDefaultType
as
'div'
object | AsTag
defaultValue
number[]
dir
'ltr' | 'rtl'
disabled
boolean
inverted
boolean
max
number
min
number
minStepsBetweenThumbs
number
name
string
orientation
'horizontal' | 'vertical'
step
number
value
number[]
EmitPayload
update:value
[value: number[]]
valueCommit
[value: number[]]
Data AttributeValue
[data-disabled]Present when disabled
[data-orientation]"vertical" | "horizontal"

Track ​

The track that contains the SliderRange.

PropDefaultType
as
'div'
object | AsTag
Data AttributeValue
[data-disabled]Present when disabled
[data-orientation]"vertical" | "horizontal"

Range ​

The range part. Must live inside SliderTrack.

PropDefaultType
as
'div'
object | AsTag
Data AttributeValue
[data-disabled]Present when disabled
[data-orientation]"vertical" | "horizontal"

Thumb ​

A draggable thumb. You can render multiple thumbs.

PropDefaultType
as
'div'
object | AsTag
name
string
Data AttributeValue
[data-disabled]Present when disabled
[data-orientation]"vertical" | "horizontal"

Examples ​

Vertical orientation ​

Use the orientation prop to create a vertical slider.

vue
vue
// index.vue
<script setup>
import { SliderRange, SliderRoot, SliderThumb, SliderTrack } from '@oku-ui/primitives'
</script>

<template>
  <SliderRoot
    class="SliderRoot"
    :default-value="[50]"
    orientation="vertical"
  >
    <SliderTrack class="SliderTrack">
      <SliderRange class="SliderRange" />
    </SliderTrack>
    <SliderThumb class="SliderThumb" />
  </SliderRoot>
</template>
css
css
/* styles.css */
.SliderRoot {
  position: relative;
  display: flex;
  align-items: center;
}
.SliderRoot[data-orientation="vertical"] {
  flex-direction: column;
  width: 20px;
  height: 100px;
}

.SliderTrack {
  position: relative;
  flex-grow: 1;
  background-color: grey;
}
.SliderTrack[data-orientation="vertical"] {
  width: 3px;
}

.SliderRange {
  position: absolute;
  background-color: black;
}
.SliderRange[data-orientation="vertical"] {
  width: 100%;
}

.SliderThumb {
  display: block;
  width: 20px;
  height: 20px;
  background-color: black;
}

Create a range ​

Add multiple thumbs and values to create a range slider.

vue
vue
// index.vue
<script setup>
import { SliderRange, SliderRoot, SliderThumb, SliderTrack } from '@oku-ui/primitives'
</script>

<template>
  <SliderRoot :default-value="[25, 75]">
    <SliderTrack>
      <SliderRange />
    </SliderTrack>
    <SliderThumb />
    <SliderThumb />
  </SliderRoot>
</template>

Define step size ​

Use the step prop to increase the stepping interval.

vue
vue
// index.vue
<script setup>
import { SliderRange, SliderRoot, SliderThumb, SliderTrack } from '@oku-ui/primitives'
</script>

<template>
  <SliderRoot
    :default-value="[50]"
    :step="10"
  >
    <SliderTrack>
      <SliderRange />
    </SliderTrack>
    <SliderThumb />
  </SliderRoot>
</template>

Prevent thumb overlap ​

Use minStepsBetweenThumbs to avoid thumbs with equal values.

vue
vue
// index.vue
<script setup>
import { SliderRange, SliderRoot, SliderThumb, SliderTrack } from '@oku-ui/primitives'
</script>

<template>
  <SliderRoot
    :default-value="[25, 75]"
    :step="10"
    :min-steps-between-thumbs="1"
  >
    <SliderTrack>
      <SliderRange />
    </SliderTrack>
    <SliderThumb />
    <SliderThumb />
  </SliderRoot>
</template>

Accessibility ​

Adheres to the Slider WAI-ARIA design pattern.

Keyboard Interactions ​

KeyDescription
ArrowRight
Increments/decrements by the step value depending on orientation.
ArrowLeft
Increments/decrements by the step value depending on orientation.
ArrowUp
Increases the value by the step amount.
ArrowDown
Decreases the value by the step amount.
PageUp
Increases the value by a larger step.
PageDown
Decreases the value by a larger step.
Shift + ArrowUp
Increases the value by a larger step.
Shift + ArrowDown
Decreases the value by a larger step.
Home
Sets the value to its minimum.
End
Sets the value to its maximum.

Custom APIs ​

Create your own API by abstracting the primitive parts into your own component.

Abstract all parts ​

This example abstracts all of the Slider parts so it can be used as a self closing element.

Usage ​

vue
vue
<script setup lang="ts">
import { Slider } from './your-slider'
</script>

<template>
  <Slider :default-value="[25]" />
</template>

Implementation ​

ts
ts
// your-slider.ts
export { default as Slider } from 'Slider.vue'
vue
vue
 <!-- Slider.vue -->
<script setup lang="ts">
import { SlideRoot, SliderRange, type SliderRootEmits, type SliderRootProps, SliderThumb, SliderTrack, useForwardPropsEmits } from '@oku-ui/primitives'

const props = defineProps<SliderRootProps>()
const emits = defineEmits<SliderRootEmits>()

const forward = useForwardPropsEmits(props, emits)
</script>

<template>
  <SliderRoot v-bind="forward">
    <SliderTrack>
      <SliderRange />
    </SliderTrack>

    <SliderThumb
      v-for="(_, i) in value"
      :key="i"
    />
  </SliderRoot>
</template>

Caveats ​

Mouse events are not fired ​

Because of a limitation we faced during implementation, the following example won't work as expected and the @mousedown and @mousedown event handlers won't be fired:

vue
vue
<SliderRoot
  @mousedown="() => { console.log('onMouseDown')  }"
  @mouseup="() => { console.log('onMouseUp')  }"
>
  …
</SliderRoot>

We recommend using pointer events instead (eg. @pointerdown, @pointerup). Regardless of the above limitation, these events are better suited for cross-platform/device handling as they are fired for all pointer input types (mouse, touch, pen, etc.).