mirror of
https://github.com/andwati/pwnforfunandprofit.xyz.git
synced 2026-10-18 02:12:52 +00:00
feat: Add a scroll-to-top button with accompanying styles and JavaScript functionality.
This commit is contained in:
@@ -4,6 +4,7 @@ node_modules/
|
||||
|
||||
# Ignore Zola HTML templates (Tera syntax breaks standard HTML parsing)
|
||||
templates/**/*.html
|
||||
templates/**/*.json
|
||||
|
||||
# Ignore minified static files
|
||||
static/**/*.min.js
|
||||
|
||||
@@ -33,7 +33,7 @@ Feature backlog for the blog. Check off items as they're implemented.
|
||||
|
||||
## UI / UX Polish
|
||||
|
||||
- [ ] **Scroll-to-top button** — floating button that appears after scrolling down, smooth-scrolls back to top
|
||||
- [x] **Scroll-to-top button** — floating button that appears after scrolling down, smooth-scrolls back to top
|
||||
- [ ] **Table of contents active-section highlight** — as you scroll, the current heading is highlighted in the TOC
|
||||
- [ ] **Terminal-style command palette** — `Ctrl+K` opens a fuzzy-search palette over posts and nav links
|
||||
- [ ] **Animated hero terminal prompt** — typewriter effect on the homepage subtitle / profile section
|
||||
@@ -43,12 +43,6 @@ Feature backlog for the blog. Check off items as they're implemented.
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
## Distribution & Reach
|
||||
|
||||
- [ ] **Newsletter / email subscription** — integrate Buttondown or self-hosted Listmonk with a subscribe form
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -36,7 +36,7 @@
|
||||
letter-spacing: 0.04em;
|
||||
|
||||
&::after {
|
||||
content: "→";
|
||||
content: '→';
|
||||
transition: transform 0.2s ease;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
// Scroll to Top Floating Button
|
||||
.scroll-to-top {
|
||||
position: fixed;
|
||||
bottom: 2rem;
|
||||
right: 2rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
border-radius: 50%;
|
||||
background: var(--bg-color);
|
||||
color: var(--accent-color);
|
||||
border: 1px solid var(--accent-color);
|
||||
cursor: pointer;
|
||||
z-index: 100;
|
||||
|
||||
// Hidden state by default
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transform: translateY(1rem);
|
||||
transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
|
||||
|
||||
// Visible state triggered by JS
|
||||
&.visible {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
@media (hover: hover) {
|
||||
&:hover {
|
||||
background: var(--accent-color);
|
||||
color: var(--bg-color);
|
||||
transform: translateY(-0.25rem);
|
||||
box-shadow: 0 4px 12px rgba(16, 185, 129, 0.2); // Emerald glow matching accent
|
||||
}
|
||||
|
||||
&:hover svg {
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
outline: 2px solid var(--accent-color);
|
||||
outline-offset: 4px;
|
||||
}
|
||||
|
||||
svg {
|
||||
transition: transform 0.2s ease;
|
||||
}
|
||||
|
||||
// Responsive padding for mobile devices
|
||||
@media (max-width: 768px) {
|
||||
bottom: 1.5rem;
|
||||
right: 1.5rem;
|
||||
width: 2.75rem;
|
||||
height: 2.75rem;
|
||||
}
|
||||
}
|
||||
@@ -13,3 +13,4 @@
|
||||
@import 'footer';
|
||||
@import 'search';
|
||||
@import 'utilities';
|
||||
@import 'scroll-top';
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const scrollToTopBtn = document.getElementById('scroll-to-top');
|
||||
|
||||
if (!scrollToTopBtn) return;
|
||||
|
||||
// Show button when scrolled down 400px
|
||||
const toggleVisibility = () => {
|
||||
if (window.scrollY > 400) {
|
||||
scrollToTopBtn.classList.add('visible');
|
||||
} else {
|
||||
scrollToTopBtn.classList.remove('visible');
|
||||
}
|
||||
};
|
||||
|
||||
// Smooth scroll to top when clicked
|
||||
scrollToTopBtn.addEventListener('click', () => {
|
||||
window.scrollTo({
|
||||
top: 0,
|
||||
behavior: 'smooth',
|
||||
});
|
||||
});
|
||||
|
||||
// Throttle the scroll event listener a tiny bit for performance
|
||||
let isTicking = false;
|
||||
window.addEventListener('scroll', () => {
|
||||
if (!isTicking) {
|
||||
window.requestAnimationFrame(() => {
|
||||
toggleVisibility();
|
||||
isTicking = false;
|
||||
});
|
||||
isTicking = true;
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -245,9 +245,15 @@
|
||||
|
||||
{% include "partials/footer.html" %}
|
||||
</div>
|
||||
<button id="scroll-to-top" class="scroll-to-top" aria-label="Scroll to top">
|
||||
<svg viewBox="0 0 24 24" width="24" height="24" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="18 15 12 9 6 15"></polyline>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<script src="{{ get_url(path="js/theme.js") }}"></script>
|
||||
<script src="{{ get_url(path="js/copy-code.js") }}"></script>
|
||||
<script defer src="{{ get_url(path="js/scroll-top.js") }}"></script>
|
||||
{% block scripts %}{% endblock scripts %}
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user