Skip to content

Instantly share code, notes, and snippets.

@Be1zebub
Created October 9, 2025 03:48
Show Gist options
  • Select an option

  • Save Be1zebub/b8c99d3dd6afe26f3ed404e0a01ca931 to your computer and use it in GitHub Desktop.

Select an option

Save Be1zebub/b8c99d3dd6afe26f3ed404e0a01ca931 to your computer and use it in GitHub Desktop.
the idea was make a custom customizable scrollbar track/thumb since default is bad at styling. so we disable defaults rendering & make custom. havent finished it, even idk if this works XD
<script lang="ts">
// WIP! Not finished yet!
import { onDestroy, onMount } from "svelte"
let wrapperNode: HTMLElement
let contentNode: HTMLElement
let showBar = false
let thumbHeight = 0
let thumbTop = 0
// dragging
let thumbDragging = false
let thumbDraggingStartY = 0
let thumbDraggingStartTop = 0
function startDragging(e: MouseEvent) {
e.preventDefault()
thumbDragging = true
thumbDraggingStartY = e.clientY
thumbDraggingStartTop = thumbTop
window.addEventListener("mousemove", handleDragging)
window.addEventListener("mouseup", stopDragging)
}
function handleDragging(e: MouseEvent) {
if (!thumbDragging) return
e.preventDefault()
const deltaY = e.clientY - thumbDraggingStartY
const trackHeight = wrapperNode.clientHeight
const scrollDelta = (deltaY / trackHeight) * contentNode.scrollHeight
contentNode.scrollTop = scrollDelta
}
function stopDragging() {
thumbDragging = false
window.removeEventListener("mousemove", handleDragging)
window.removeEventListener("mouseup", stopDragging)
}
// main
let observer: ResizeObserver
function updateScrollbar() {
if (!contentNode || !wrapperNode) return
const { clientHeight, scrollHeight, scrollTop } = contentNode
showBar = scrollHeight > clientHeight
if (!showBar) return
const trackHeight = wrapperNode.clientHeight
thumbHeight = Math.max(20, (clientHeight / scrollHeight) * trackHeight)
const maxScrollTop = scrollHeight - clientHeight
const maxThumbTop = trackHeight - thumbHeight
thumbTop = Math.min(maxThumbTop, (scrollTop / maxScrollTop) * maxThumbTop)
}
onMount(() => {
updateScrollbar()
observer = new ResizeObserver(updateScrollbar)
observer.observe(wrapperNode)
if (contentNode.children[0]) {
observer.observe(contentNode.children[0])
}
})
onDestroy(() => {
stopDragging()
if (observer) {
observer.disconnect()
}
})
</script>
<div class="scroll-wrapper" bind:this={wrapperNode}>
<div
class="scroll-content"
bind:this={contentNode}
on:scroll={updateScrollbar}
>
<slot />
</div>
{#if showBar}
<div class="scroll-track">
<button
aria-label="Scroll-thumb"
class="scroll-thumb"
style:bind:clientHeight={thumbHeight}
style:top={thumbTop}
class:dragging={thumbDragging}
on:mousedown={startDragging}
></button>
</div>
{/if}
</div>
<style>
.scroll-wrapper {
position: relative;
height: 100%;
width: 100%;
overflow: hidden;
}
.scroll-content {
height: 100%;
overflow-y: scroll;
width: calc(100% + 17px);
-ms-overflow-style: none;
scrollbar-width: none;
}
.scroll-content::-webkit-scrollbar {
display: none;
}
.scroll-track {
position: absolute;
top: 2px;
right: 2px;
bottom: 2px;
width: 8px;
background-color: rgba(255, 255, 255, 0.32);
border-radius: 4px;
}
.scroll-thumb {
position: absolute;
left: 0;
width: 100%;
background-color: rgb(255, 0, 0);
border-radius: 4px;
cursor: pointer;
transition: background-color 0.2s;
}
.scroll-thumb:hover,
.scroll-thumb.dragging {
background-color: #a8a8a8;
}
</style>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment