/* =============================== Overlay Scrollbar — Cross-browser (Edge/Firefox/Chrome/Safari) Fichier : _overlay-scrollbar.css =============================== */ /* ---------- Thèmes & variables ---------- */ :root { /* Couleurs (clair) — alignées sur les tokens de thème */ /* Track très discret */ --ovsb-track-bg: color-mix(in oklab, var(--border, rgba(15,23,42,0.25)) 16%, transparent); /* Pouce (thumb) subtil basé sur --scrollbar-thumb, avec légère teinte brand */ --ovsb-thumb-bg: color-mix( in oklab, var(--scrollbar-thumb, rgba(100,116,139,0.55)) 80%, color-mix(in oklab, var(--brand, #64748b) 18%, transparent) 20% ); --ovsb-thumb-bg-active: color-mix( in oklab, var(--scrollbar-thumb, rgba(100,116,139,0.55)) 65%, color-mix(in oklab, var(--brand-700, var(--brand, #64748b)) 40%, transparent) 35% ); /* Géométrie & transitions */ --ovsb-trans: 240ms ease; --ovsb-right: 2px; /* Décalage à droite de l’overlay */ --ovsb-thumb-height: 24px; /* Hauteur mini du thumb */ /* Épaisseurs / états Astuce : on anime l’épaisseur via scaleX pour éviter les changements de hit-area. Le track garde une largeur de hit-test stable. */ --ovsb-active-width: 10px; /* largeur visuelle en état "active" */ --ovsb-mini-width: 2px; /* largeur visuelle en état "mini" */ /* Ratio (mini vs active) → sert à scaleX. Si vous modifiez les largeurs, adaptez ce ratio. */ --ovsb-mini-scale: 0.2; /* 2 / 10 = 0.2 */ } .dark, [data-theme="dark"] { /* Légèrement plus visible sur fond sombre mais toujours discret */ --ovsb-track-bg: color-mix(in oklab, var(--border, rgba(148,163,184,0.35)) 22%, transparent); --ovsb-thumb-bg: color-mix( in oklab, var(--scrollbar-thumb, rgba(226,232,240,0.62)) 85%, color-mix(in oklab, var(--brand-700, var(--brand, #94a3b8)) 24%, transparent) 15% ); --ovsb-thumb-bg-active: color-mix( in oklab, var(--scrollbar-thumb, rgba(226,232,240,0.72)) 70%, color-mix(in oklab, var(--brand-700, var(--brand, #94a3b8)) 45%, transparent) 30% ); } /* Reduced motion */ @media (prefers-reduced-motion: reduce) { .ovsb, .ovsb__root, .ovsb__track, .ovsb__thumb { transition: none !important; } } /* ======================================== 1) Conteneur scrollable (hôte de l’overlay) ======================================== */ /* IMPORTANT : appliquer .ovsb-host sur l’élément QUI SCROLLE réellement */ .ovsb-host { position: relative; /* nécessaire pour positionner l’overlay en absolute */ overflow: auto; /* c’est bien LUI qui scrolle */ /* Masquer la scrollbar native (Firefox + Edge/Chrome/Safari) */ scrollbar-width: none; /* Firefox */ -ms-overflow-style: none; /* Edge/IE legacy */ } .ovsb-host::-webkit-scrollbar { width: 0 !important; /* Chrome/Edge/Safari */ height: 0 !important; background: transparent !important; } .ovsb-host::-webkit-scrollbar-thumb, .ovsb-host::-webkit-scrollbar-track, .ovsb-host::-webkit-scrollbar-corner { background: transparent !important; border: none !important; } /* Cas où le DOCUMENT scrolle (évitez si possible). Décommentez si nécessaire. */ /* html, body { scrollbar-width: none; -ms-overflow-style: none; } html::-webkit-scrollbar, body::-webkit-scrollbar { width: 0 !important; height: 0 !important; } */ /* Si vous décidez de garder la native (non recommandé ici), stabilisez le gutter : */ /* @supports (scrollbar-gutter: stable) { .ovsb-host-native { scrollbar-gutter: stable both-edges; } } */ /* ======================================== 2) Overlay (track + thumb) ======================================== */ /* Racine overlay : ne capture pas les events hors track/thumb */ .ovsb { position: absolute; top: 0; right: var(--ovsb-right); bottom: 0; width: 12px; /* largeur de HIT-TEST stable (garde la zone de survol constante) */ pointer-events: none; /* la racine ne capte pas, on laisse le contenu défiler naturellement */ z-index: 10; contain: layout paint; /* isolation pour perf */ } /* Nœud interne (utile si vous voulez des wrappers) */ .ovsb__root { position: absolute; inset: 0; } /* Piste (track) — zone cliquable/drag */ .ovsb__track { position: absolute; top: 0; right: 0; bottom: 0; width: 12px; /* hit-area stable */ border-radius: 9999px; background: var(--ovsb-track-bg); opacity: 0; /* géré par états */ pointer-events: auto; /* le track doit capter pour hover/drag */ transition: opacity var(--ovsb-trans); will-change: opacity; } /* Thumb (manette) — épaisseur animée via scaleX (pas de shift) */ .ovsb__thumb { position: absolute; top: 0; /* la position Y est appliquée via transform translateY */ right: 1px; /* léger décalage pour équilibre visuel */ width: var(--ovsb-active-width); height: var(--ovsb-thumb-height); border-radius: 9999px; background: var(--ovsb-thumb-bg); /* Position & épaisseur animées : translateY: mis à jour dynamiquement (via style inline ou CSS var) scaleX: mini/active */ transform: translateY(var(--ovsb-thumb-y, 0px)) scaleX(var(--ovsb-mini-scale)); transform-origin: right center; transition: transform var(--ovsb-trans), background-color var(--ovsb-trans), opacity var(--ovsb-trans); opacity: 0; /* masqué par défaut */ pointer-events: auto; /* doit capter pour le drag */ will-change: transform, opacity, background-color; cursor: grab; } .ovsb__thumb:active { cursor: grabbing; } /* ======================================== 3) États (Hidden / Mini / Active) ======================================== */ /* HIDDEN : invisible, aucune interactivité */ .ovsb--hidden .ovsb__track, .ovsb--hidden .ovsb__thumb { opacity: 0; pointer-events: none; /* rien ne capte, laisse le contenu réagir librement */ } /* MINI : visible, thumb mince (scaleX mini), track discret */ .ovsb--mini .ovsb__track { opacity: 1; } .ovsb--mini .ovsb__thumb { opacity: 1; background: var(--ovsb-thumb-bg); transform: translateY(var(--ovsb-thumb-y, 0px)) scaleX(var(--ovsb-mini-scale)); } /* ACTIVE : au survol de la zone scrollbar → thumb plus épais + couleur active */ .ovsb--active .ovsb__track { opacity: 1; } .ovsb--active .ovsb__thumb { opacity: 1; background: var(--ovsb-thumb-bg-active); transform: translateY(var(--ovsb-thumb-y, 0px)) scaleX(1); } /* Hover direct sur la zone track → active visuelle (utile en desktop) */ .ovsb__track:hover ~ .ovsb__thumb, .ovsb__thumb:hover { background: var(--ovsb-thumb-bg-active); } /* ======================================== 4) Divers (compat & accessibilité visuelle) ======================================== */ /* Améliore la réactivité GPU (Edge/Chrome) */ .ovsb__track, .ovsb__thumb { backface-visibility: hidden; } /* Quand overlay affiché, éviter le scroll chaining vers parent si souhaité */ .ovsb-host { overscroll-behavior: contain; } /* Cas tactile : permettre drag propre sans scroll involontaire horizontal */ .ovsb__thumb, .ovsb__track { touch-action: none; }