Directive for adding a custom overlay scrollbar to scrollable regions.
Allows to add overlay scrollbars that will look the same for every browser. The directive sets up the scrollbars from the library OverlayScrollbars.
<div class="d-hmx164 d-w30p d-bar8 d-ba" v-dt-scrollbar> <dt-stack> <div v-for="item in items" class="item"> {{ item}} </div> </dt-stack> </div>
Import the directive and styling from dialtone
import { DtScrollbarDirective } from "@dialpad/dialtone/vue3";
// Import styling
import "overlayscrollbars/overlayscrollbars.css";
Install the directive into vue instance
app.use(DtScrollbarDirective);
To add a custom overlay scrollbar to a scrollable region, apply the v-dt-scrollbar directive to the parent element of the desired region.
This parent element should have one and only one child. In the case where there are siblings, the scrollable element should be wrapped inside a new <div> tag with the directive attached by adding <div v-dt-scrollbar></div> around the element.
There is no need to explicitly add an overflow property. If the section overflows the available vertical space, a vertical scrollbar will be present. Similarly, if it exceeds the horizontal space, a horizontal scrollbar will appear.
To customize the behavior of the scrollbar, you can use different show modes with the directive. The allowed values are 'enter' (default), 'always', 'scroll', and 'move'.
Show the scrollbar when the mouse enters the scrollable area. This is the default option, so no configuration is needed.
<div class="d-hmx164 d-w30p d-bar8 d-ba" v-dt-scrollbar> <dt-stack> <div v-for="item in items" class="item"> {{ item}} </div> </dt-stack> </div>
Always show the scrollbar if the region is overflowing the available space.
<div class="d-hmx164 d-w30p d-bar8 d-ba" v-dt-scrollbar:always> <dt-stack> <div v-for="item in items" class="item"> {{ item}} </div> </dt-stack> </div>
The object syntax is also supported:
<div v-dt-scrollbar="{ showScrollbar: 'always' }"> <div>content</div> </div>
Show the scrollbar on scroll.
<div class="d-hmx164 d-w30p d-bar8 d-ba" v-dt-scrollbar:scroll> <dt-stack> <div v-for="item in items" class="item"> {{ item}} </div> </dt-stack> </div>
Show the scrollbar when the mouse moves inside the scrollable area.
<div class="d-hmx164 d-w30p d-bar8 d-ba" v-dt-scrollbar:move> <dt-stack> <div v-for="item in items" class="item"> {{ item}} </div> </dt-stack> </div>
In addition to using directive arguments for scrollbar visibility (:always, :scroll, :move), you can pass a configuration object to the directive that supports additional options like offsets, CSS classes, and explicit show behavior.
<div v-dt-scrollbar="{ showScrollbar: 'always', offset: { blockStart: 64 } }"> <div>Scrollable content</div> </div>
| Property | Type | Default | Description |
|---|---|---|---|
showScrollbar | 'enter' | 'always' | 'scroll' | 'move' | 'enter' | Scrollbar visibility mode |
offset | Object | null | Offset configuration for scrollbar positioning |
offset.blockStart | number | string | undefined | Insets vertical scrollbar from the block-start (aka top) edge |
offset.blockEnd | number | string | undefined | Insets horizontal scrollbar from the block-end (aka bottom) edge |
offset.inlineStart | number | string | undefined | Insets horizontal scrollbar from the inline-start (aka left) edge |
offset.inlineEnd | number | string | undefined | Insets vertical scrollbar from the inline-end (aka right) edge |
blockClasses | string | undefined | CSS classes to apply to the vertical scrollbar |
inlineClasses | string | undefined | CSS classes to apply to the horizontal scrollbar |
The offset option allows you to adjust the positioning of scrollbars to accommodate fixed headers, footers, or other UI elements that overlap with the scrollable region. This eliminates the need for manual CSS overrides.
<div class="d-hmx164 d-w30p d-bar8 d-ba" v-dt-scrollbar="{ offset: { blockStart: 20, blockEnd: 20 }, showScrollbar: 'always' }"> <dt-stack> <div v-for="item in items" class="item"> {{ item}} </div> </dt-stack> </div>
| Property | Type | Description |
|---|---|---|
blockStart | number | string | Insets vertical scrollbar from the block-start (aka top) edge |
blockEnd | number | string | Insets horizontal scrollbar from the block-end (aka bottom) edge |
inlineStart | number | string | Insets horizontal scrollbar from the inline-start (aka left) edge |
inlineEnd | number | string | Insets vertical scrollbar from the inline-end (aka right) edge |
<div v-dt-scrollbar="{ offset: { blockStart: 64, blockEnd: 32 } }"> <div>Content with 64px block-start offset and 32px block-end offset</div> </div>
<div v-dt-scrollbar="{ offset: { blockStart: '4rem', blockEnd: '2em' } }"> <div>Content with rem/em offsets</div> </div>
<div v-dt-scrollbar="{ offset: { blockStart: 'var(--header-height)' } }"> <div>Content with CSS variable offset</div> </div>
<div v-dt-scrollbar="{ offset: { blockStart: 'calc(100% - 64px)' } }"> <div>Content with calculated offset</div> </div>
The directive supports applying custom CSS classes to scrollbar elements, allowing you to use utility classes or custom styles for scrollbar appearance.
<div class="d-hmx164 d-w30p d-bar8 d-ba" v-dt-scrollbar="{ blockClasses: 'd-w12 d-bgc-purple-300', showScrollbar: 'always' }"> <dt-stack> <div v-for="item in items" class="item"> {{ item}} </div> </dt-stack> </div>
<div v-dt-scrollbar="{ inlineClasses: 'd-h8 d-bgc-blue-300' }"> <div>Scrollable content with styled horizontal scrollbar</div> </div>
<div v-dt-scrollbar="{ blockClasses: 'd-w12', inlineClasses: 'd-h8' }"> <div>Different classes for each scrollbar</div> </div>
<div class="d-hmx164 d-w30p d-bar8 d-ba" v-dt-scrollbar="{ offset: { blockStart: 20, blockEnd: 20 }, blockClasses: 'd-w12 d-bgc-magenta-300', showScrollbar: 'always' }"> <dt-stack> <div v-for="item in items" class="item"> {{ item}} </div> </dt-stack> </div>
The showScrollbar property is validated to ensure only valid values are used. If an invalid value is provided, the directive will log an informational message and fall back to the default 'enter' mode.
Valid values: 'enter', 'always', 'scroll', 'move'
<!-- Valid --> <div v-dt-scrollbar="{ showScrollbar: 'always' }"></div> <!-- Invalid - falls back to 'enter' with console message --> <div v-dt-scrollbar="{ showScrollbar: 'invalid' }"></div>
When you have a fixed header that overlaps the scrollable region:
<div class="page"> <header class="fixed-header">Fixed Header (64px tall)</header> <div v-dt-scrollbar="{ offset: { blockStart: 64 } }" class="content"> <div>Scrollable content</div> </div> </div>
When you have both fixed header and footer:
<div class="page"> <header class="fixed-header">Header</header> <div v-dt-scrollbar="{ offset: { blockStart: 64, blockEnd: 48 } }" class="content"> <div>Scrollable content</div> </div> <footer class="fixed-footer">Footer</footer> </div>
Offsets can be reactive and will update automatically:
<template> <div v-dt-scrollbar="scrollbarConfig"> <div>Scrollable content</div> </div> </template> <script> export default { data() { return { headerVisible: true } }, computed: { scrollbarConfig() { return { offset: { blockStart: this.headerVisible ? 64 : 0 }, showScrollbar: 'always' } } } } </script>
Adding this directive to a DOM element or a Vue component will alter the DOM structure, by adding four elements inside the one that the directive was attached to. If the scrollable region is a Vue component, it's recommended to wrap it in a <div v-dt-scrollbar></div>, to avoid altering the structure that the component needs.
The added elements are:
os-size-observerThis can make it challenging to use with components that rely on event listeners or may even render it unusable.
Scrollbar documentation last updated Friday, September 4, 2026
fix/popover-modal-zindex-scope