feat: add Nimbus UI mode with adaptive scrollbars and layout improvements
This commit is contained in:
@@ -0,0 +1,209 @@
|
||||
/* ===============================
|
||||
Overlay Scrollbar — Cross-browser (Edge/Firefox/Chrome/Safari)
|
||||
Fichier : _overlay-scrollbar.css
|
||||
=============================== */
|
||||
|
||||
/* ---------- Thèmes & variables ---------- */
|
||||
:root {
|
||||
/* Couleurs (clair) */
|
||||
--ovsb-track-bg: rgba(15, 23, 42, 0.12);
|
||||
--ovsb-thumb-bg: rgba(100, 116, 139, 0.55);
|
||||
--ovsb-thumb-bg-active: rgba(59, 130, 246, 0.75);
|
||||
|
||||
/* 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"] {
|
||||
--ovsb-track-bg: rgba(148, 163, 184, 0.28);
|
||||
--ovsb-thumb-bg: rgba(226, 232, 240, 0.72);
|
||||
--ovsb-thumb-bg-active: rgba(241, 245, 249, 0.92);
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
Reference in New Issue
Block a user