Slider
<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.
$ npm add @oku-ui/primitives
Anatomy
Import all parts and piece them together.
<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.
Prop | Default | Type |
---|---|---|
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[] |
Emit | Payload |
---|---|
update:value | [value: number[]] |
valueCommit | [value: number[]] |
Data Attribute | Value |
---|---|
[data-disabled] | Present when disabled |
[data-orientation] | "vertical" | "horizontal" |
Track
The track that contains the SliderRange
.
Prop | Default | Type |
---|---|---|
as | 'div' | object | AsTag |
Data Attribute | Value |
---|---|
[data-disabled] | Present when disabled |
[data-orientation] | "vertical" | "horizontal" |
Range
The range part. Must live inside SliderTrack
.
Prop | Default | Type |
---|---|---|
as | 'div' | object | AsTag |
Data Attribute | Value |
---|---|
[data-disabled] | Present when disabled |
[data-orientation] | "vertical" | "horizontal" |
Thumb
A draggable thumb. You can render multiple thumbs.
Prop | Default | Type |
---|---|---|
as | 'div' | object | AsTag |
name | string |
Data Attribute | Value |
---|---|
[data-disabled] | Present when disabled |
[data-orientation] | "vertical" | "horizontal" |
Examples
Vertical orientation
Use the orientation
prop to create a vertical slider.
// 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>
/* 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.
// 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.
// 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.
// 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
Key | Description |
---|---|
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
<script setup lang="ts">
import { Slider } from './your-slider'
</script>
<template>
<Slider :default-value="[25]" />
</template>
Implementation
// your-slider.ts
export { default as Slider } from 'Slider.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:
<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.).