mobile fixes
This commit is contained in:
parent
41bbbd799b
commit
c3fa26f60e
|
|
@ -291,10 +291,7 @@ nav.site-nav {
|
||||||
|
|
||||||
@media (max-width: 540px) {
|
@media (max-width: 540px) {
|
||||||
.nav-logo {
|
.nav-logo {
|
||||||
position: static;
|
display: none;
|
||||||
height: 2rem;
|
|
||||||
width: 2rem;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-row-primary {
|
.nav-row-primary {
|
||||||
|
|
@ -345,6 +342,59 @@ nav.site-nav {
|
||||||
.nav-portals a {
|
.nav-portals a {
|
||||||
padding: 0.3rem 0.55rem;
|
padding: 0.3rem 0.55rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Extra bottom padding so the last content line isn't hidden
|
||||||
|
behind the fixed mobile TOC bar. */
|
||||||
|
body {
|
||||||
|
padding-bottom: 2.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mobile TOC bar — fixed bottom strip: progress line + current
|
||||||
|
section label. toc.js sets --toc-progress and syncs the label. */
|
||||||
|
#toc-mobile-bar {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
position: fixed;
|
||||||
|
bottom: 0; left: 0; right: 0;
|
||||||
|
height: 2.25rem;
|
||||||
|
background: var(--bg-nav);
|
||||||
|
border-top: 1px solid var(--border);
|
||||||
|
padding: 0 1rem;
|
||||||
|
z-index: 90;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Progress track (faint full-width line at top of bar) */
|
||||||
|
#toc-mobile-bar::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0; left: 0; right: 0;
|
||||||
|
height: 2px;
|
||||||
|
background: var(--border);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Progress fill — grows with scroll via --toc-progress */
|
||||||
|
#toc-mobile-bar::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0; left: 0;
|
||||||
|
height: 2px;
|
||||||
|
width: calc(var(--toc-progress, 0) * 100%);
|
||||||
|
background: var(--text-muted);
|
||||||
|
transition: width 0.12s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc-mobile-label {
|
||||||
|
font-family: var(--font-sans);
|
||||||
|
font-size: 0.68rem;
|
||||||
|
font-weight: 600;
|
||||||
|
font-variant-caps: all-small-caps;
|
||||||
|
letter-spacing: 0.07em;
|
||||||
|
color: var(--text-faint);
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Row 2: portal links — hidden until nav.js adds .is-open */
|
/* Row 2: portal links — hidden until nav.js adds .is-open */
|
||||||
|
|
@ -386,6 +436,12 @@ nav.site-nav {
|
||||||
and the --toc-progress custom property for the progress bar.
|
and the --toc-progress custom property for the progress bar.
|
||||||
============================================================ */
|
============================================================ */
|
||||||
|
|
||||||
|
/* Mobile TOC bar — hidden on all viewports by default; the ≤540px
|
||||||
|
media query below makes it visible on mobile only. */
|
||||||
|
#toc-mobile-bar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
#toc {
|
#toc {
|
||||||
font-family: var(--font-sans);
|
font-family: var(--font-sans);
|
||||||
font-size: 0.85rem;
|
font-size: 0.85rem;
|
||||||
|
|
|
||||||
|
|
@ -155,3 +155,64 @@ a.footnote-ref {
|
||||||
a.footnote-ref:hover {
|
a.footnote-ref:hover {
|
||||||
color: var(--text-muted);
|
color: var(--text-muted);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ============================================================
|
||||||
|
MOBILE SIDENOTE POPUP
|
||||||
|
Bottom sheet shown when tapping a sidenote ref on narrow
|
||||||
|
viewports (where .sidenote is hidden). The overlay is
|
||||||
|
display:none by default; sidenotes.js adds .is-open to show it.
|
||||||
|
No media query needed — JS only opens the popup when the
|
||||||
|
sidenote span is hidden, so it never fires on wide viewports.
|
||||||
|
============================================================ */
|
||||||
|
|
||||||
|
.sidenote-popup-overlay {
|
||||||
|
display: none;
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
z-index: 500;
|
||||||
|
background: rgba(0, 0, 0, 0.45);
|
||||||
|
align-items: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidenote-popup-overlay.is-open {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidenote-popup {
|
||||||
|
background: var(--bg);
|
||||||
|
border-top: 1px solid var(--border);
|
||||||
|
border-radius: 0.75rem 0.75rem 0 0;
|
||||||
|
padding: 1.25rem 1.25rem 2.75rem;
|
||||||
|
width: 100%;
|
||||||
|
max-height: 65vh;
|
||||||
|
overflow-y: auto;
|
||||||
|
position: relative;
|
||||||
|
font-family: var(--font-serif);
|
||||||
|
font-size: 0.9rem;
|
||||||
|
line-height: 1.6;
|
||||||
|
color: var(--text);
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidenote-popup-close {
|
||||||
|
position: absolute;
|
||||||
|
top: 0.7rem;
|
||||||
|
right: 0.9rem;
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
line-height: 1;
|
||||||
|
color: var(--text-faint);
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 0.1rem 0.35rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidenote-popup-close:hover {
|
||||||
|
color: var(--text);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Preserve sidenote-num badge style inside the popup */
|
||||||
|
.sidenote-popup .sidenote-num {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -97,12 +97,16 @@
|
||||||
sn.addEventListener('mouseenter', function () { hovering = true; update(); });
|
sn.addEventListener('mouseenter', function () { hovering = true; update(); });
|
||||||
sn.addEventListener('mouseleave', function () { hovering = false; update(); });
|
sn.addEventListener('mouseleave', function () { hovering = false; update(); });
|
||||||
|
|
||||||
/* Click on the superscript link: sticky focus on wide viewports,
|
/* Click on the superscript link: sticky focus on wide viewports;
|
||||||
normal anchor scroll on narrow viewports (sidenote hidden). */
|
mobile popup on narrow viewports (sidenote hidden). */
|
||||||
const link = ref.querySelector('a');
|
const link = ref.querySelector('a');
|
||||||
if (link) {
|
if (link) {
|
||||||
link.addEventListener('click', function (e) {
|
link.addEventListener('click', function (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
if (getComputedStyle(sn).display === 'none') {
|
||||||
|
openMobilePopup(sn);
|
||||||
|
return;
|
||||||
|
}
|
||||||
toggleFocus();
|
toggleFocus();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -126,6 +130,65 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* Mobile popup — bottom sheet for narrow viewports where .sidenote */
|
||||||
|
/* is display:none. Created lazily on first use. */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
var mobileOverlay = null;
|
||||||
|
|
||||||
|
function ensureMobileOverlay() {
|
||||||
|
if (mobileOverlay) return;
|
||||||
|
|
||||||
|
var overlay = document.createElement('div');
|
||||||
|
overlay.className = 'sidenote-popup-overlay';
|
||||||
|
overlay.setAttribute('role', 'dialog');
|
||||||
|
overlay.setAttribute('aria-modal', 'true');
|
||||||
|
overlay.setAttribute('aria-label', 'Note');
|
||||||
|
|
||||||
|
var sheet = document.createElement('div');
|
||||||
|
sheet.className = 'sidenote-popup';
|
||||||
|
sheet.setAttribute('tabindex', '-1');
|
||||||
|
|
||||||
|
var closeBtn = document.createElement('button');
|
||||||
|
closeBtn.className = 'sidenote-popup-close';
|
||||||
|
closeBtn.setAttribute('aria-label', 'Close note');
|
||||||
|
closeBtn.textContent = '\u00d7'; /* × */
|
||||||
|
|
||||||
|
var body = document.createElement('div');
|
||||||
|
body.className = 'sidenote-popup-body';
|
||||||
|
|
||||||
|
sheet.appendChild(closeBtn);
|
||||||
|
sheet.appendChild(body);
|
||||||
|
overlay.appendChild(sheet);
|
||||||
|
document.body.appendChild(overlay);
|
||||||
|
|
||||||
|
overlay.addEventListener('click', function (e) {
|
||||||
|
if (!sheet.contains(e.target)) closeMobilePopup();
|
||||||
|
});
|
||||||
|
closeBtn.addEventListener('click', closeMobilePopup);
|
||||||
|
|
||||||
|
document.addEventListener('keydown', function (e) {
|
||||||
|
if (e.key === 'Escape' && mobileOverlay &&
|
||||||
|
mobileOverlay.classList.contains('is-open')) {
|
||||||
|
closeMobilePopup();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
mobileOverlay = overlay;
|
||||||
|
}
|
||||||
|
|
||||||
|
function openMobilePopup(sn) {
|
||||||
|
ensureMobileOverlay();
|
||||||
|
mobileOverlay.querySelector('.sidenote-popup-body').innerHTML = sn.innerHTML;
|
||||||
|
mobileOverlay.classList.add('is-open');
|
||||||
|
mobileOverlay.querySelector('.sidenote-popup').focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeMobilePopup() {
|
||||||
|
if (mobileOverlay) mobileOverlay.classList.remove('is-open');
|
||||||
|
}
|
||||||
|
|
||||||
/* Click anywhere outside a focused pair dismisses it. */
|
/* Click anywhere outside a focused pair dismisses it. */
|
||||||
document.addEventListener('click', function (e) {
|
document.addEventListener('click', function (e) {
|
||||||
if (focusedPair &&
|
if (focusedPair &&
|
||||||
|
|
|
||||||
|
|
@ -20,8 +20,10 @@
|
||||||
|
|
||||||
if (!headings.length) return;
|
if (!headings.length) return;
|
||||||
|
|
||||||
const label = toc.querySelector('.toc-active-label');
|
const label = toc.querySelector('.toc-active-label');
|
||||||
const toggleBtn = toc.querySelector('.toc-toggle');
|
const toggleBtn = toc.querySelector('.toc-toggle');
|
||||||
|
const mobileBar = document.getElementById('toc-mobile-bar');
|
||||||
|
const mobileLabel = mobileBar && mobileBar.querySelector('.toc-mobile-label');
|
||||||
|
|
||||||
const pageTitleEl = document.querySelector('#markdownBody .page-title');
|
const pageTitleEl = document.querySelector('#markdownBody .page-title');
|
||||||
const pageTitle = pageTitleEl ? pageTitleEl.textContent.trim() : 'Contents';
|
const pageTitle = pageTitleEl ? pageTitleEl.textContent.trim() : 'Contents';
|
||||||
|
|
@ -29,14 +31,15 @@
|
||||||
function activateTitle() {
|
function activateTitle() {
|
||||||
links.forEach(a => a.classList.remove('is-active'));
|
links.forEach(a => a.classList.remove('is-active'));
|
||||||
if (label) label.textContent = pageTitle;
|
if (label) label.textContent = pageTitle;
|
||||||
|
if (mobileLabel) mobileLabel.textContent = pageTitle;
|
||||||
}
|
}
|
||||||
|
|
||||||
function activate(id) {
|
function activate(id) {
|
||||||
links.forEach(a => a.classList.toggle('is-active', a.dataset.target === id));
|
links.forEach(a => a.classList.toggle('is-active', a.dataset.target === id));
|
||||||
if (label) {
|
const activeLink = linkMap.get(id);
|
||||||
const activeLink = linkMap.get(id);
|
const text = activeLink ? activeLink.textContent : pageTitle;
|
||||||
label.textContent = activeLink ? activeLink.textContent : pageTitle;
|
if (label) label.textContent = text;
|
||||||
}
|
if (mobileLabel) mobileLabel.textContent = text;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Collapse / expand. The collapsed state is hidden from
|
// Collapse / expand. The collapsed state is hidden from
|
||||||
|
|
@ -70,11 +73,13 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Progress indicator — drives the horizontal bar under .toc-header.
|
// Progress indicator — drives the horizontal bar under .toc-header
|
||||||
|
// and the mobile bar's progress line.
|
||||||
function updateProgress() {
|
function updateProgress() {
|
||||||
const scrollable = document.documentElement.scrollHeight - window.innerHeight;
|
const scrollable = document.documentElement.scrollHeight - window.innerHeight;
|
||||||
const progress = scrollable > 0 ? Math.min(1, window.scrollY / scrollable) : 0;
|
const progress = scrollable > 0 ? Math.min(1, window.scrollY / scrollable) : 0;
|
||||||
toc.style.setProperty('--toc-progress', progress);
|
toc.style.setProperty('--toc-progress', progress);
|
||||||
|
if (mobileBar) mobileBar.style.setProperty('--toc-progress', progress);
|
||||||
}
|
}
|
||||||
window.addEventListener('scroll', updateProgress, { passive: true });
|
window.addEventListener('scroll', updateProgress, { passive: true });
|
||||||
updateProgress();
|
updateProgress();
|
||||||
|
|
|
||||||
|
|
@ -14,4 +14,7 @@
|
||||||
$body$
|
$body$
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="toc-mobile-bar" aria-hidden="true">
|
||||||
|
<span class="toc-mobile-label">Contents</span>
|
||||||
|
</div>
|
||||||
$partial("templates/partials/page-footer.html")$
|
$partial("templates/partials/page-footer.html")$
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue