Skip to content

Commit f3afb5f

Browse files
authored
Merge pull request #144 from posit-dev/enh-section-sidebar
enh: improve section sidebar in desktop view
2 parents 24b807f + 79ea2ea commit f3afb5f

6 files changed

Lines changed: 668 additions & 0 deletions

File tree

great_docs/assets/great-docs.scss

Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1148,6 +1148,228 @@ a.sidebar-item-text.sidebar-link.text-start > span {
11481148
padding-bottom: 8px;
11491149
}
11501150

1151+
/* ==========================================================================
1152+
Floating Sidebar Navigation (desktop only)
1153+
Transforms #quarto-sidebar into a pinned, independently-scrollable panel
1154+
with blur background, rounded border, and scroll affordances.
1155+
========================================================================== */
1156+
1157+
@media (min-width: 992px) {
1158+
#quarto-sidebar.gd-sidebar-float {
1159+
/* Sticky keeps the sidebar in the CSS grid flow.
1160+
JS sets top (stick offset) and max-height (shrinks for footer). */
1161+
display: flex;
1162+
flex-direction: column;
1163+
overflow: hidden !important; /* clip children to rounded corners; override .overflow-auto */
1164+
position: sticky;
1165+
transition: top 0.15s ease, max-height 0.15s ease;
1166+
/* Widen sidebar to reclaim space lost to border+padding.
1167+
Negative margin-right lets it extend slightly into the gap. */
1168+
margin-right: -12px;
1169+
/* align-self keeps the sidebar at the grid-row start so it
1170+
sticks near the top, not stretched to the full row height. */
1171+
align-self: start;
1172+
}
1173+
1174+
/* Suppress the old bottom border on the menu container —
1175+
it is now inside the float body. */
1176+
#quarto-sidebar.gd-sidebar-float .sidebar-menu-container {
1177+
border-bottom: none;
1178+
}
1179+
1180+
/* Remove default Bootstrap margin-bottom on the sidebar list
1181+
so the float body hugs the last item tightly. */
1182+
#quarto-sidebar.gd-sidebar-float .sidebar-menu-container > ul {
1183+
margin-bottom: 0;
1184+
}
1185+
1186+
#quarto-sidebar.gd-sidebar-float .sidebar-menu-container > ul > li:last-child {
1187+
padding-bottom: 7px;
1188+
}
1189+
1190+
/* Tighten vertical spacing between nav items inside the float */
1191+
#quarto-sidebar.gd-sidebar-float .sidebar-item {
1192+
margin-top: 0.1rem;
1193+
margin-bottom: 0.1em;
1194+
}
1195+
1196+
/* Override the ::before hover pill with a direct background on the link.
1197+
The ::before pseudo extends beyond the scroll container and gets clipped;
1198+
using the link's own background avoids that entirely. */
1199+
#quarto-sidebar.gd-sidebar-float .sidebar-link[href]::before {
1200+
display: none !important;
1201+
content: none !important;
1202+
}
1203+
1204+
#quarto-sidebar.gd-sidebar-float .sidebar-link[href] {
1205+
border-radius: 6px;
1206+
padding-left: 8px;
1207+
padding-right: 8px;
1208+
padding-top: 3px;
1209+
padding-bottom: 3px;
1210+
margin-left: -8px;
1211+
margin-right: 0;
1212+
transition: background-color 0.15s ease, box-shadow 0.15s ease;
1213+
}
1214+
1215+
#quarto-sidebar.gd-sidebar-float .sidebar-link[href]:hover {
1216+
background-color: rgba(0, 0, 0, 0.04);
1217+
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.10);
1218+
}
1219+
1220+
#quarto-sidebar.gd-sidebar-float.sidebar-navigation > * {
1221+
padding-top: 0.4em;
1222+
}
1223+
1224+
#quarto-sidebar.gd-sidebar-float > * {
1225+
padding-right: 0;
1226+
}
1227+
}
1228+
1229+
1230+
1231+
/* Scrollable body */
1232+
.gd-sidebar-float-body {
1233+
display: none; /* hidden below 992px */
1234+
}
1235+
1236+
@media (min-width: 992px) {
1237+
.gd-sidebar-float-body {
1238+
display: block;
1239+
flex: 1 1 auto;
1240+
overflow-y: auto;
1241+
overscroll-behavior: contain;
1242+
position: relative;
1243+
/* Thin scrollbar for Webkit/Blink */
1244+
scrollbar-width: thin;
1245+
scrollbar-color: rgba(0, 0, 0, 0.15) transparent;
1246+
1247+
&::-webkit-scrollbar {
1248+
width: 5px;
1249+
}
1250+
&::-webkit-scrollbar-track {
1251+
background: transparent;
1252+
}
1253+
&::-webkit-scrollbar-thumb {
1254+
background: rgba(0, 0, 0, 0.12);
1255+
border-radius: 4px;
1256+
}
1257+
}
1258+
}
1259+
1260+
/* Scroll affordance indicators (top / bottom) */
1261+
.gd-sidebar-scroll-indicator {
1262+
display: none; /* hidden below 992px */
1263+
}
1264+
1265+
@media (min-width: 992px) {
1266+
/* Indicators are inside .gd-sidebar-float-body (the scroll container)
1267+
and use position:sticky so they pin to the top/bottom of the
1268+
visible scroll area without covering the "Pages" header above. */
1269+
.gd-sidebar-scroll-indicator {
1270+
display: flex;
1271+
align-items: center;
1272+
justify-content: center;
1273+
position: sticky;
1274+
left: 0;
1275+
right: 0;
1276+
min-height: 32px;
1277+
margin-top: -32px; /* collapse out of flow so content isn't pushed */
1278+
pointer-events: none;
1279+
opacity: 0;
1280+
transition: opacity 0.2s ease;
1281+
z-index: 3;
1282+
}
1283+
1284+
.gd-sidebar-scroll-indicator.visible {
1285+
opacity: 1;
1286+
pointer-events: auto;
1287+
cursor: pointer;
1288+
}
1289+
1290+
.gd-sidebar-scroll-top {
1291+
top: 0;
1292+
background: linear-gradient(to bottom, rgba(250, 250, 253, 0.92) 0%, transparent 100%);
1293+
}
1294+
1295+
.gd-sidebar-scroll-bottom {
1296+
bottom: 0;
1297+
position: sticky;
1298+
margin-top: 0;
1299+
margin-bottom: -32px;
1300+
background: linear-gradient(to top, rgba(250, 250, 253, 0.92) 0%, transparent 100%);
1301+
}
1302+
1303+
.gd-sidebar-scroll-arrow {
1304+
display: flex;
1305+
align-items: center;
1306+
justify-content: center;
1307+
width: 24px;
1308+
height: 24px;
1309+
border-radius: 50%;
1310+
background: rgba(0, 0, 0, 0.06);
1311+
color: $gray-600;
1312+
font-size: 0.65rem;
1313+
line-height: 1;
1314+
transition: background 0.15s ease, color 0.15s ease;
1315+
}
1316+
1317+
.gd-sidebar-scroll-indicator:hover .gd-sidebar-scroll-arrow {
1318+
background: rgba(0, 0, 0, 0.12);
1319+
color: $gray-800;
1320+
}
1321+
}
1322+
1323+
/* ── Dark mode: Floating Sidebar ── */
1324+
body.quarto-dark {
1325+
@media (min-width: 992px) {
1326+
#quarto-sidebar.gd-sidebar-float {
1327+
background: rgba(30, 30, 30, 0.55);
1328+
border-color: rgba(255, 255, 255, 0.10);
1329+
box-shadow: 0 1px 6px rgba(0, 0, 0, 0.25);
1330+
}
1331+
1332+
1333+
.gd-sidebar-float-body {
1334+
scrollbar-color: rgba(255, 255, 255, 0.15) transparent;
1335+
1336+
&::-webkit-scrollbar-thumb {
1337+
background: rgba(255, 255, 255, 0.12);
1338+
}
1339+
}
1340+
1341+
#quarto-sidebar.gd-sidebar-float .sidebar-link[href]:hover {
1342+
background-color: rgba(255, 255, 255, 0.08);
1343+
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.12);
1344+
}
1345+
1346+
.gd-sidebar-scroll-top {
1347+
background: linear-gradient(to bottom, rgba(30, 30, 30, 0.92) 0%, transparent 100%);
1348+
}
1349+
1350+
.gd-sidebar-scroll-bottom {
1351+
background: linear-gradient(to top, rgba(30, 30, 30, 0.92) 0%, transparent 100%);
1352+
}
1353+
1354+
.gd-sidebar-scroll-arrow {
1355+
background: rgba(255, 255, 255, 0.08);
1356+
color: $gray-400;
1357+
}
1358+
1359+
.gd-sidebar-scroll-indicator:hover .gd-sidebar-scroll-arrow {
1360+
background: rgba(255, 255, 255, 0.15);
1361+
color: $gray-200;
1362+
}
1363+
}
1364+
}
1365+
1366+
/* Print: hide floating sidebar affordances */
1367+
@media print {
1368+
.gd-sidebar-scroll-indicator {
1369+
display: none !important;
1370+
}
1371+
}
1372+
11511373
#quarto-header > nav > div {
11521374
margin-left: 2%;
11531375
margin-right: 2%;
@@ -1353,6 +1575,10 @@ body.quarto-light .navbar .gd-navbar-icon:hover {
13531575
}
13541576
}
13551577

1578+
footer.footer .nav-footer {
1579+
margin-top: 10px;
1580+
}
1581+
13561582
.nav-footer {
13571583
position: relative;
13581584
z-index: 1;

great_docs/assets/navbar-style.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@
4242

4343
var els = document.querySelectorAll(".sidebar, .headroom-target");
4444
els.forEach(function (el) {
45+
// Skip the floating sidebar — sidebar-float.js manages its own bounds
46+
if (el.classList.contains('gd-sidebar-float')) return;
4547
el.style.top = h + "px";
4648
el.style.maxHeight = "calc(100vh - " + h + "px)";
4749
});

0 commit comments

Comments
 (0)