Skip to content

Commit 98e360f

Browse files
authored
Improve sidebar behavior on scroll and adjust styling (#3391)
1 parent 4f7ee00 commit 98e360f

File tree

2 files changed

+84
-30
lines changed

2 files changed

+84
-30
lines changed

_static/css/custom.css

+23-5
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@
2121
--navbar-level-2-color: #b8d6f0;
2222
--navbar-level-3-color: #a3c4e1;
2323
--navbar-heading-color: #ff7381;
24+
--navbar-scrollbar-color: #d45a66;
25+
--navbar-scrollbar-hover-color: #b14550;
26+
--navbar-scrollbar-active-color: #72383e;
2427
--navbar-scrollbar-background: #131e2b;
2528

2629
--link-color: #2980b9;
@@ -102,6 +105,9 @@
102105
--navbar-level-2-color: #ccc;
103106
--navbar-level-3-color: #bbb;
104107
--navbar-heading-color: #ee7381;
108+
--navbar-scrollbar-color: #be5460;
109+
--navbar-scrollbar-hover-color: #963e48;
110+
--navbar-scrollbar-active-color: #5f3034;
105111
--navbar-scrollbar-background: #1c1e21;
106112

107113
--link-color: #8cf;
@@ -845,7 +851,7 @@ kbd, .kbd {
845851

846852
/* Banner panel in sidebar */
847853
.wy-nav-side .ethical-rtd.fixed {
848-
position: fixed
854+
position: fixed;
849855
}
850856

851857
/* Version selector (only visible on Read the Docs) */
@@ -899,11 +905,16 @@ kbd, .kbd {
899905
padding: 0;
900906
}
901907

902-
/* Allows the navbar's scrollbar to be shown */
908+
/* Allows the scrollbar to be shown in the sidebar */
903909
@media only screen and (min-width: 769px) {
904910
.wy-side-scroll {
905911
overflow: hidden;
906912
}
913+
914+
.wy-nav-side .wy-side-scroll .ethical-rtd {
915+
width: calc(300px - 1.25em);
916+
padding: 0 0 0 1em;
917+
}
907918
}
908919
.wy-menu.wy-menu-vertical {
909920
overflow-y: auto;
@@ -920,9 +931,9 @@ kbd, .kbd {
920931
}
921932
}
922933

923-
/* Navbar's scrollbar styling */
934+
/* Scrollbar styling */
924935
.wy-menu.wy-menu-vertical {
925-
scrollbar-color: var(--navbar-heading-color) var(--navbar-scrollbar-background);
936+
scrollbar-color: var(--navbar-scrollbar-color) var(--navbar-scrollbar-background);
926937
}
927938
.wy-menu.wy-menu-vertical::-webkit-scrollbar {
928939
width: .75rem;
@@ -931,5 +942,12 @@ kbd, .kbd {
931942
background-color: var(--navbar-scrollbar-background);
932943
}
933944
.wy-menu.wy-menu-vertical::-webkit-scrollbar-thumb {
934-
background-color: var(--navbar-heading-color);
945+
background-color: var(--navbar-scrollbar-color);
946+
}
947+
/* Firefox does the dimming on hover automatically. We emulate it for Webkit-based browsers. */
948+
.wy-menu.wy-menu-vertical::-webkit-scrollbar-thumb:hover {
949+
background-color: var(--navbar-scrollbar-hover-color);
950+
}
951+
.wy-menu.wy-menu-vertical::-webkit-scrollbar-thumb:active {
952+
background-color: var(--navbar-scrollbar-active-color);
935953
}

_static/js/custom.js

+61-25
Original file line numberDiff line numberDiff line change
@@ -31,16 +31,25 @@ const registerOnScrollEvent = (function(){
3131
// to their initial state.
3232

3333
const $window = $(window);
34-
const $menu = $('.wy-menu-vertical');
35-
const $search = $('.wy-side-nav-search');
36-
const $ethical = $('.ethical-rtd');
34+
const $sidebar = $('.wy-side-scroll');
35+
const $search = $sidebar.children('.wy-side-nav-search');
36+
const $menu = $sidebar.children('.wy-menu-vertical');
37+
const $ethical = $sidebar.children('.ethical-rtd');
38+
39+
// This padding is needed to correctly adjust the height of the scrollable area in the sidebar.
40+
// It has to have the same height as the ethical block, if there is one.
41+
let $menuPadding = $menu.children('.wy-menu-ethical-padding');
42+
if ($menuPadding.length == 0) {
43+
$menuPadding = $('<div class="wy-menu-ethical-padding"></div>');
44+
$menu.append($menuPadding);
45+
}
3746

3847
if (mediaQuery.matches) {
3948
// Entering the "desktop" state.
4049

41-
// The scroll event handler.
50+
// The main scroll event handler.
4251
// Executed as the page is scrolled and once immediatelly as the page enters this state.
43-
const handleScroll = (currentScroll) => {
52+
const handleMainScroll = (currentScroll) => {
4453
if (currentScroll >= scrollTopPixels) {
4554
// After the page is scrolled below the threshold, we fix everything in place.
4655
$search.css('margin-top', `-${scrollTopPixels}px`);
@@ -63,24 +72,13 @@ const registerOnScrollEvent = (function(){
6372
}
6473
};
6574

66-
$search.addClass('fixed');
67-
$ethical.addClass('fixed');
68-
69-
// Adjust the inner height of navigation so that the banner can be overlaid there later.
70-
const ethicalOffsetBottom = $ethical.height() || 0;
71-
if (ethicalOffsetBottom) {
72-
$menu.css('padding-bottom', `${ethicalOffsetBottom}px`);
73-
} else {
74-
$menu.css('padding-bottom', `0px`);
75-
}
76-
77-
$window.scroll(function() {
78-
handleScroll(window.scrollY);
79-
});
80-
81-
$menu.scroll(function() {
82-
const menuScrollTop = $(this).scrollTop();
83-
const menuScrollBottom = this.scrollHeight - (menuScrollTop + this.offsetHeight);
75+
// The sidebar scroll event handler.
76+
// Executed as the sidebar is scrolled as well as after the main scroll. This is needed
77+
// because the main scroll can affect the scrollable area of the sidebar.
78+
const handleSidebarScroll = () => {
79+
const menuElement = $menu.get(0);
80+
const menuScrollTop = $menu.scrollTop();
81+
const menuScrollBottom = menuElement.scrollHeight - (menuScrollTop + menuElement.offsetHeight);
8482

8583
// As the navigation is scrolled we add a shadow to the top bar hanging over it.
8684
if (menuScrollTop > 0) {
@@ -95,9 +93,30 @@ const registerOnScrollEvent = (function(){
9593
} else {
9694
$ethical.css('margin-top', '0px');
9795
}
96+
};
97+
98+
$search.addClass('fixed');
99+
$ethical.addClass('fixed');
100+
101+
// Adjust the inner height of navigation so that the banner can be overlaid there later.
102+
const ethicalOffsetBottom = $ethical.height() || 0;
103+
if (ethicalOffsetBottom) {
104+
$menuPadding.css('height', `${ethicalOffsetBottom}px`);
105+
} else {
106+
$menuPadding.css('height', `0px`);
107+
}
108+
109+
$window.scroll(function() {
110+
handleMainScroll(window.scrollY);
111+
handleSidebarScroll();
112+
});
113+
114+
$menu.scroll(function() {
115+
handleSidebarScroll();
98116
})
99117

100-
handleScroll(window.scrollY);
118+
handleMainScroll(window.scrollY);
119+
handleSidebarScroll();
101120
} else {
102121
// Entering the "mobile" state.
103122

@@ -109,15 +128,32 @@ const registerOnScrollEvent = (function(){
109128

110129
$search.css('margin-top', `0px`);
111130
$menu.css('margin-top', `0px`);
112-
$menu.css('padding-bottom', `0px`);
113131
$menu.css('max-height', 'initial');
132+
$menuPadding.css('height', `0px`);
114133
$ethical.css('margin-top', '0px');
115134
}
116135
}
117136
})();
118137

119138
$(document).ready(() => {
139+
const sidebarContainer = document.querySelector('.wy-side-scroll');
120140
const mediaQuery = window.matchMedia('only screen and (min-width: 769px)');
141+
142+
// Subscribe to DOM changes in the sidebar container, because there is a
143+
// banner that gets added at a later point, that we might not catch otherwise.
144+
const observerConfig = { childList: true };
145+
const observerCallback = (mutationsList, observer) => {
146+
for (let mutation of mutationsList) {
147+
if (mutation.type !== 'childList') {
148+
continue;
149+
}
150+
151+
registerOnScrollEvent(mediaQuery);
152+
}
153+
};
154+
const observer = new MutationObserver(observerCallback);
155+
observer.observe(sidebarContainer, observerConfig);
156+
121157
registerOnScrollEvent(mediaQuery);
122158
mediaQuery.addListener(registerOnScrollEvent);
123159
});

0 commit comments

Comments
 (0)