feat(website): add back-to-top button

Adds a fixed-position button that fades in after scrolling 600px and
smoothly scrolls back to the top on click. Hidden by default via the
HTML hidden attribute so noscript users never see it.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Vinta Chen
2026-03-22 02:17:57 +08:00
parent 4f297a5301
commit 7aeb8fbb65
3 changed files with 66 additions and 0 deletions

View File

@@ -279,6 +279,25 @@ if (searchInput) {
});
}
// Back to top
var backToTop = document.querySelector('.back-to-top');
if (backToTop) {
backToTop.hidden = false;
var scrollTicking = false;
window.addEventListener('scroll', function () {
if (!scrollTicking) {
requestAnimationFrame(function () {
backToTop.classList.toggle('visible', window.scrollY > 600);
scrollTicking = false;
});
scrollTicking = true;
}
});
backToTop.addEventListener('click', function () {
window.scrollTo({ top: 0, behavior: 'smooth' });
});
}
// Restore state from URL
(function () {
var params = new URLSearchParams(location.search);

View File

@@ -527,6 +527,51 @@ th[data-sort].sort-asc::after {
font-weight: 600;
}
/* === Back to Top === */
.back-to-top {
position: fixed;
bottom: 2rem;
right: max(1.5rem, calc(50vw - 700px + 0.5rem));
background: var(--bg);
border: 1px solid var(--border-strong);
border-radius: 4px;
padding: 0.4rem 0.75rem;
font-family: var(--font-body);
font-size: var(--text-xs);
font-weight: 600;
color: var(--text-muted);
cursor: pointer;
opacity: 0;
transform: translateY(0.5rem);
transition: opacity 0.25s ease, transform 0.25s ease, border-color 0.15s, color 0.15s;
z-index: 50;
}
.back-to-top[hidden] { display: none; }
.back-to-top.visible {
opacity: 1;
transform: translateY(0);
}
.back-to-top:hover {
border-color: var(--accent);
color: var(--accent);
}
.back-to-top:active {
transform: scale(0.95);
}
.back-to-top:focus-visible {
outline: 2px solid var(--accent);
outline-offset: 2px;
}
@media (max-width: 640px) {
.back-to-top { right: 1.25rem; bottom: 1.25rem; }
}
/* === Noscript === */
.noscript-msg {
text-align: center;

View File

@@ -37,6 +37,8 @@
<main id="content">{% block content %}{% endblock %}</main>
<button class="back-to-top" aria-label="Back to top" hidden>&uarr; Top</button>
<footer class="footer">
<span
>Made by