custom scrollbar implemented
Some checks failed
Rebuild signaller for deprived.dev to rebuild site / test_service (push) Failing after 0s

This commit is contained in:
BOTAlex 2025-08-20 02:26:40 +02:00
parent c099b9ce9f
commit 7a8d61d598
3 changed files with 116 additions and 83 deletions

View file

@ -7,7 +7,7 @@
<div class="flex flex-col items-center justify-center w-full h-full">
<div class="flex flex-col w-72">
<div class="w-full h-72 bg-lime-200"></div>
<CustomScrollBar overflowX="scroll" overflowY="hidden" Class="h-26">
<CustomScrollBar overflowX="scroll" overflowY="hidden" Class="">
<div class="flex w-full gap-4">
{#each { length: 4 } as i}
<img

View file

@ -11,8 +11,8 @@
import DeprivedLogo from "$lib/images/DeprivedLogo.svelte";
import HamburgerMenuIcon from "$lib/images/HamburgerMenuIcon.svelte";
const footerCollapseThreshold: string = "1000px";
const headerCollapseThreshold: string = "1000px";
const footerCollapseThreshold: string = "40rem";
const headerCollapseThreshold: string = "40rem";
let footerCollapse: boolean;
let isMobile: boolean = $state(false);
@ -31,6 +31,7 @@
import { onMount } from "svelte";
import Zooter from "./comps/Zooter.svelte";
import CustomScrollBar from "./comps/CustomScrollBar.svelte";
onMount(async () => {
const lock = document.createElement("meta");
@ -82,86 +83,94 @@
bind:matches={isMobile}
/>
<div class="flex flex-col justify-between min-h-screen bg-base-200 p-0">
<header class="{hideOnPrint ? 'hide-on-print' : ''} bg-base-300">
<div class="nav-bar pr-4">
{#if !isMobile}
<div class="desktop items-center">
<a href="/" class="nav-head">
<DeprivedLogo
Class="fill-base-content p-2"
Style="width: 3.5rem; height: auto;"
/>
<!-- <h3 id="logo-text">The Deprived Devs</h3> -->
</a>
<div class="nav-spacer" />
<CustomScrollBar
overflowX="hidden"
overflowY="auto"
Class="h-screen"
requireAbsolute={true}
hideOnMobile={true}
>
<div class="flex flex-col justify-between min-h-screen bg-base-200 p-0">
<header class="{hideOnPrint ? 'hide-on-print' : ''} bg-base-300">
<div class="nav-bar pr-4">
{#if !isMobile}
<div class="desktop items-center">
<a href="/" class="nav-head">
<DeprivedLogo
Class="fill-base-content p-2"
Style="width: 3.5rem; height: auto;"
/>
<!-- <h3 id="logo-text">The Deprived Devs</h3> -->
</a>
<div class="nav-spacer" />
<!-- <a href="/">Home</a> -->
<!-- <a href="/zhen/notes/physics/1?hideOnPrint=1" target="_blank" style="width: 7.5rem;">Notes</a> -->
<!-- <a href="/">Home</a> -->
<!-- <a href="/zhen/notes/physics/1?hideOnPrint=1" target="_blank" style="width: 7.5rem;">Notes</a> -->
{@render SwitchThemeButton()}
{@render SwitchThemeButton()}
<a
href="/zhen/cv/rev3?hideOnPrint=1"
target="_blank"
style="width: 7.5rem;"
class="text-center justify-center">Zhen CV</a
>
<!-- <a href="/tools" style="width: 7.5rem;" class="text-center">Tools</a> -->
<a href="https://botalex.itch.io/" target="_blank">Games</a>
<!-- <a href="/posts">Blog</a>
<a href="/about">About</a> -->
</div>
{:else}
<div class="collapsed shadow-xl">
<a onclick={resetNavBar} href="/" class="nav-head">
<DeprivedLogo
Class="fill-base-content p-2"
Style="width: 3.5rem; height: auto;"
/>
<!-- <h3 id="logo-text">The Deprived Devs</h3> -->
</a>
<div class="nav-spacer" />
{@render SwitchThemeButton()}
<div class="px-1"></div>
<button
id="toggle-nav"
onclick={() => {
navbarHidden = !navbarHidden;
console.log(navbarHidden);
}}
>
<HamburgerMenuIcon Class="fill-base-content" />
</button>
</div>
{#if !navbarHidden}
<div class="nav-list" transition:fly={{ y: -25, duration: 350 }}>
<!-- <a onclick={resetNavBar} href="/">Home</a> -->
<a
onclick={resetNavBar}
href="https://botalex.itch.io/"
target="_blank">Games</a
>
<a
href="/zhen/cv/rev3?hideOnPrint=1"
target="_blank"
class="justify-center">Zhen's CV</a
style="width: 7.5rem;"
class="text-center justify-center">Zhen CV</a
>
<!-- <a onclick={resetNavBar} href="/posts">Blog</a>
<a onclick={resetNavBar} href="/about">About</a> -->
<!-- <a href="/tools" style="width: 7.5rem;" class="text-center">Tools</a> -->
<a href="https://botalex.itch.io/" target="_blank">Games</a>
<!-- <a href="/posts">Blog</a>
<a href="/about">About</a> -->
</div>
{:else}
<div class="collapsed shadow-xl">
<a onclick={resetNavBar} href="/" class="nav-head">
<DeprivedLogo
Class="fill-base-content p-2"
Style="width: 3.5rem; height: auto;"
/>
<!-- <h3 id="logo-text">The Deprived Devs</h3> -->
</a>
<div class="nav-spacer" />
{@render SwitchThemeButton()}
<div class="px-1"></div>
<button
id="toggle-nav"
onclick={() => {
navbarHidden = !navbarHidden;
console.log(navbarHidden);
}}
>
<HamburgerMenuIcon Class="fill-base-content" />
</button>
</div>
{#if !navbarHidden}
<div class="nav-list" transition:fly={{ y: -25, duration: 350 }}>
<!-- <a onclick={resetNavBar} href="/">Home</a> -->
<a
onclick={resetNavBar}
href="https://botalex.itch.io/"
target="_blank">Games</a
>
<a
href="/zhen/cv/rev3?hideOnPrint=1"
target="_blank"
class="justify-center">Zhen's CV</a
>
<!-- <a onclick={resetNavBar} href="/posts">Blog</a>
<a onclick={resetNavBar} href="/about">About</a> -->
</div>
{/if}
{/if}
{/if}
</div>
</header>
<div class="flex-1">
<!-- Page content -->
{@render children?.()}
</div>
</header>
<div class="flex-1">
<!-- Page content -->
{@render children?.()}
<Zooter bind:hideOnPrint />
</div>
<Zooter bind:hideOnPrint />
</div>
</CustomScrollBar>
{#if footerCollapse}
<style>

View file

@ -1,15 +1,18 @@
<script lang="ts">
import { onMount, onDestroy } from "svelte";
import MediaQuery from "svelte-media-queries";
// Public props
export let overflowX: "auto" | "scroll" | "hidden" = "hidden";
export let overflowY: "auto" | "scroll" | "hidden" = "auto";
export let hideOnMobile = false; // True if hide scrollbar when mobile detected
// Visual tuning
export let thickness = 12; // px
export let padding = 0; // px, space around track inside the overlay
export let minThumb = 24; // px
export let contentPadding = 16; // px, inner padding for your content
export let minThumb = 10; // px
export let contentPadding = 0; // px, inner padding for your content
export let Class = ""; // Extra classes for the scrollbar
// Styling (customize freely)
@ -18,9 +21,11 @@
export let trackOpacity = 0.55;
export let thumbClass =
"bg-black border-x-1 opacity-50 border-white text-xs text-center text-opacity-50 overflow-hidden flex items-center text-nowrap justify-center";
"bg-black opacity-50 border-white text-xs text-center text-opacity-50 overflow-hidden flex items-center text-nowrap justify-center";
export let thumbLength = 20; // px. doesn't work for some reason, idk
export let requireAbsolute = false; // Some needs absolute for some reason. idk
let viewport: HTMLDivElement;
let vBar: HTMLDivElement; // vertical bar container
let hBar: HTMLDivElement; // horizontal bar container
@ -29,6 +34,7 @@
let showBarY = false;
let showBarX = false;
let isMobile = false;
// ——— utils
const sMaxY = () =>
@ -41,9 +47,13 @@
function updateVisibility() {
showBarY =
overflowY !== "hidden" && viewport.scrollHeight > viewport.clientHeight;
overflowY !== "hidden" &&
viewport.scrollHeight > viewport.clientHeight &&
!(hideOnMobile && isMobile);
showBarX =
overflowX !== "hidden" && viewport.scrollWidth > viewport.clientWidth;
overflowX !== "hidden" &&
viewport.scrollWidth > viewport.clientWidth &&
!(hideOnMobile && isMobile);
}
function updateVerticalThumb() {
@ -268,15 +278,25 @@
$: pb = contentPadding + (showBarX ? thickness + padding * 2 : 0);
</script>
<MediaQuery query="(max-width: 40rem)" bind:matches={isMobile} />
<!-- svelte-ignore element_invalid_self_closing_tag -->
<!-- svelte-ignore a11y_role_has_required_aria_props -->
<!-- Wrapper -->
<div class="relative overflow-hidden {Class}">
<div
class="relative {overflowY == 'hidden'
? ''
: 'overflow-y-hidden'} {overflowX == 'hidden'
? ''
: 'overflow-x-hidden'} {Class}"
>
<!-- The real, native scrolling area (we hide its native scrollbar) -->
<div
bind:this={viewport}
class="csb-viewport absolute inset-0 overflow-auto focus:outline-none"
class="csb-viewport {requireAbsolute
? 'absolute'
: ''} inset-0 overflow-auto focus:outline-none"
style="
padding: {contentPadding}px {pr}px {pb}px {contentPadding}px;
overscroll-behavior: contain;
@ -292,7 +312,7 @@
{#if showBarY}
<div
bind:this={vBar}
class="absolute"
class="absolute bg-base-200"
style="
top: {padding}px;
bottom: {padding}px;
@ -304,7 +324,7 @@
>
<div class="absolute inset-0 corner-border-container">
<div
class="transition-opacity {trackClass} w-full"
class="transition-opacity {trackClass} w-full h-full"
style="
pointer-events: auto;
{trackStyle}
@ -318,14 +338,18 @@
role="scrollbar"
aria-orientation="vertical"
tabindex="0"
class={`absolute left-[2px] right-[2px] rounded-full cursor-grab active:cursor-grabbing focus-visible:outline focus-visible:outline-2 focus-visible:outline-sky-500 ${thumbClass}`}
class={`absolute border-y-2 left-0 right-0 rounded-none cursor-grab active:cursor-grabbing focus-visible:outline focus-visible:outline-2 focus-visible:outline-sky-500 ${thumbClass}`}
style="height: {thumbLength}px; pointer-events: auto; touch-action: none;"
on:pointerdown={onVPointerDown}
on:pointermove={onVPointerMove}
on:pointerup={endVDrag}
on:pointercancel={endVDrag}
on:keydown={onVKeyDown}
></div>
>
<span class="rotate-90">
-------------------------------------------------Scroll-------------------------------------------------
</span>
</div>
</div>
{/if}
@ -358,7 +382,7 @@
role="scrollbar"
aria-orientation="horizontal"
tabindex="0"
class={`absolute top-0 bottom-0 rounded-none cursor-grab active:cursor-grabbing focus-visible:outline focus-visible:outline-2 focus-visible:outline-sky-500 ${thumbClass}`}
class={`absolute border-x-2 top-0 bottom-0 rounded-none cursor-grab active:cursor-grabbing focus-visible:outline focus-visible:outline-2 focus-visible:outline-sky-500 ${thumbClass}`}
style="width: {thumbLength}px; pointer-events: auto; touch-action: none;"
on:pointerdown={onHPointerDown}
on:pointermove={onHPointerMove}