Skip to content
🌏 Translated with the assistance of DeepSeek and ChatGPT

ScrollBar

A pixel-style scrollbar component, based on OverlayScrollbars.

Scroll Container

Use the ScrollBar component to wrap your content inside a scrollbar. You may need to set height styles.

Pass scrollOffset as the scrollbar offset. Providing scrollOffset puts it in controlled mode. Not providing it or setting it to undefined puts it in uncontrolled mode, where you can pass the defaultScrollOffset property as the default value.

Updates to scrollOffset are debounced and only trigger when scrolling stops. If you need to listen for scrolling, listen to the scroll event.

For the ScrollBar component, after obtaining its DOM element, you can access properties like scrollBy, scrollTo, scrollTop, scrollLeft, etc., just like a native element.

scrollOffset: { "left": 0, "top": 0 }
To be, or not to be, that is the question:
Whether 'tis nobler in the mind to suffer
The slings and arrows of outrageous fortune,
Or to take arms against a sea of troubles
And by opposing end them. To die—to sleep,
No more; and by a sleep to say we end
The heart-ache and the thousand natural shocks
That flesh is heir to: 'tis a consummation
Devoutly to be wish'd. To die, to sleep;
To sleep, perchance to dream—ay, there's the rub:
For in that sleep of death what dreams may come,
When we have shuffled off this mortal coil,
Must give us pause—there's the respect
That makes calamity of so long life.
<template>
	<px-space direction="vertical">
		<div>scrollOffset: {{ scrollOffset }}</div>
		<px-scroll-bar
			v-model:scroll-offset="scrollOffset"
			style="height: 200px"
			@scroll="scrollHandler"
		>
			To be, or not to be, that is the question:<br />
			Whether 'tis nobler in the mind to suffer<br />
			The slings and arrows of outrageous fortune,<br />
			Or to take arms against a sea of troubles<br />
			And by opposing end them. To die—to sleep,<br />
			No more; and by a sleep to say we end<br />
			The heart-ache and the thousand natural shocks<br />
			That flesh is heir to: 'tis a consummation<br />
			Devoutly to be wish'd. To die, to sleep;<br />
			To sleep, perchance to dream—ay, there's the rub:<br />
			For in that sleep of death what dreams may come,<br />
			When we have shuffled off this mortal coil,<br />
			Must give us pause—there's the respect<br />
			That makes calamity of so long life.
		</px-scroll-bar>
	</px-space>
</template>

<script lang="ts" setup>
import { ref } from 'vue'

const scrollOffset = ref({ left: 0, top: 0 })

const scrollHandler = (e: Event) => {
	console.log((e.target as Element).scrollLeft, (e.target as Element).scrollTop)
}
</script>

Scrollbar Styles

The scrollbar currently has two style variants: pixel-style ('pixel', default) and simple mode ('simple'), set via variant.

Loooooooooooooooong text
Loooooooooooooooong text
Loooooooooooooooong text
Loooooooooooooooong text
Loooooooooooooooong text
Loooooooooooooooong text
Loooooooooooooooong text
Loooooooooooooooong text
Loooooooooooooooong text
Loooooooooooooooong text
Loooooooooooooooong text
Loooooooooooooooong text
Loooooooooooooooong text
Loooooooooooooooong text
Loooooooooooooooong text
Loooooooooooooooong text
Loooooooooooooooong text
Loooooooooooooooong text
Loooooooooooooooong text
Loooooooooooooooong text
<template>
	<px-space direction="vertical">
		<px-radio-group v-model="variant" :options="options"></px-radio-group>
		<px-scroll-bar style="height: 200px" :variant="variant">
			Loooooooooooooooong text<br />
			Loooooooooooooooong text<br />
			Loooooooooooooooong text<br />
			Loooooooooooooooong text<br />
			Loooooooooooooooong text<br />
			Loooooooooooooooong text<br />
			Loooooooooooooooong text<br />
			Loooooooooooooooong text<br />
			Loooooooooooooooong text<br />
			Loooooooooooooooong text<br />
			Loooooooooooooooong text<br />
			Loooooooooooooooong text<br />
			Loooooooooooooooong text<br />
			Loooooooooooooooong text<br />
			Loooooooooooooooong text<br />
			Loooooooooooooooong text<br />
			Loooooooooooooooong text<br />
			Loooooooooooooooong text<br />
			Loooooooooooooooong text<br />
			Loooooooooooooooong text<br />
		</px-scroll-bar>
	</px-space>
</template>

<script lang="ts" setup>
import { ref } from 'vue'

const variant = ref('pixel')

const options = [
	{
		label: 'Pixel',
		value: 'pixel'
	},
	{
		label: 'Simple',
		value: 'simple'
	}
]
</script>

Functional Usage

Use the useScrollBar hook to manually add a pixel-style scrollbar to a specific area. Add the attribute data-overlayscrollbars-initialize to the element that needs the scrollbar to prevent flickering issues.

Scroll bar ready ? false
Now might I do it pat, now he is praying;
And now I'll do't. And so he goes to heaven;
And so am I revenged. That would be scann'd:
A villain kills my father; and for that,
I, his sole son, do this same villain send
To heaven.
O, this is hire and salary, not revenge.
He took my father grossly, full of bread;
With all his crimes broad blown, as flush as May;
And how his audit stands who knows save heaven?
But in our circumstance and course of thought,
'Tis heavy with him: and am I then revenged,
To take him in the purging of his soul,
When he is fit and season'd for his passage?
No!
Up, sword; and know thou a more horrid bent:
When he is drunk asleep, or in his rage,
Or in the incestuous pleasure of his bed;
At gaming, swearing, or about some act
That has no relish of salvation in't;
Then trip him, that his heels may kick at heaven,
And that his soul may be as damn'd and black
As hell, whereto it goes. My mother stays:
This physic but prolongs thy sickly days.
<template>
	<px-space direction="vertical">
		<span> Scroll bar ready ? {{ initialized }} </span>
		<div style="height: 200px" ref="targetRef" data-overlayscrollbars-initialize>
			Now might I do it pat, now he is praying;<br />
			And now I'll do't. And so he goes to heaven;<br />
			And so am I revenged. That would be scann'd:<br />
			A villain kills my father; and for that,<br />
			I, his sole son, do this same villain send<br />
			To heaven.<br />
			O, this is hire and salary, not revenge.<br />
			He took my father grossly, full of bread;<br />
			With all his crimes broad blown, as flush as May;<br />
			And how his audit stands who knows save heaven?<br />
			But in our circumstance and course of thought,<br />
			'Tis heavy with him: and am I then revenged,<br />
			To take him in the purging of his soul,<br />
			When he is fit and season'd for his passage?<br />
			No!<br />
			Up, sword; and know thou a more horrid bent:<br />
			When he is drunk asleep, or in his rage,<br />
			Or in the incestuous pleasure of his bed;<br />
			At gaming, swearing, or about some act<br />
			That has no relish of salvation in't;<br />
			Then trip him, that his heels may kick at heaven,<br />
			And that his soul may be as damn'd and black<br />
			As hell, whereto it goes. My mother stays:<br />
			This physic but prolongs thy sickly days.
		</div>
	</px-space>
</template>

<script lang="ts" setup>
import { useScrollBar } from '@pixelium/web-vue'

// If on-demand import
// import { useScrollBar } from '@pixelium/web-vue/es'

import { onMounted, shallowRef } from 'vue'
const [init, getInstance, initialized] = useScrollBar()

const targetRef = shallowRef<HTMLDivElement | null>(null)

onMounted(() => {
	if (!targetRef.value) {
		return
	}
	init({
		target: targetRef.value
	})
})
</script>

This way, you can add a scrollbar to the <body> element. You can then normally access native properties like window.scrollX, window.scrollY, window.scroll, window.scrollTo, window.scrollBy, etc.

To prevent flickering, add the data-overlayscrollbars-initialize attribute to both <html> and <body>.

ts
import { useScrollBar } from '@pixelium/web-vue'

// If on-demand import
// import { useScrollBar } from '@pixelium/web-vue/es'

const [init] = useScrollBar()

init({
  target: document.body
})

API

If it doesn't meet your needs, you can refer to the OverlayScrollbars documentation for usage details.

The pixel-style scrollbar themes are named 'pixel-scrollbar-theme' (corresponding to theme="pixel") and 'simple-scrollbar-theme' (corresponding to theme="simple"). After importing the full styles or manually importing @pixelium/web-vue/es/scroll-bar/index.css, you can use it with OverlayScrollbars.

ScrollBarProps

AttributeTypeOptionalDefaultDescriptionVersion
scrollOffset{ left?: number; top?: number } | nullTrueScroll offset, controlled mode, supports v-model.0.1.0
defaultScrollOffset{ left?: number; top?: number } | nullTrueDefault value for scroll offset, uncontrolled mode.0.1.0
variant'pixel' | 'simple'True'pixel'The style variant of the scrollbar.0.1.0
showScrollPaddingbooleanTruetrueWhether to add padding on the side where the scrollbar appears.0.1.0

ScrollBarEvents

EventParameterDescriptionVersion
update:scrollOffsetvalue: { left: number; top: number }Callback when scrollOffset updates.0.1.0
initializeinstance: OverlayScrollbarsCallback when the scrollbar initializes. Receives an OverlayScrollbars instance as the parameter. For specific details, refer to the OverlayScrollbars documentation.0.1.0
updateCallback when the scrollbar updates.0.1.0
scrollevent: EventCallback when scrolling occurs.0.1.0

ScrollBarSlots

SlotParameterDescriptionVersion
defaultContent of the scroll container.0.1.0

ScrollBarExpose

AttributeTypeOptionalDefaultDescriptionVersion
scrollTo{ (options?: ScrollToOptions): void; (x: number, y: number): void }FalseSame behavior as native scrollTo.0.1.0
scrollBy{ (options?: ScrollToOptions): void; (x: number, y: number): void }FalseSame behavior as native scrollBy.0.1.0

useScrollBar

ts
import { type UseOverlayScrollbarsInitialization, type UseOverlayScrollbarsInstance } from 'overlayscrollbars-vue'

const useScrollBar: (variant?: "pixel" | "simple") => readonly [UseOverlayScrollbarsInitialization, UseOverlayScrollbarsInstance, Ref<boolean, boolean>]