5 Commits

Author SHA1 Message Date
Dome 40001ac757 Update toc.js 2026-04-22 01:30:12 +02:00
Dome 1fc6cd3009 Update style.css 2026-04-22 01:27:05 +02:00
Dome 84fdfe85ac TOC Refinment
feat(toc): improve footer collision handling and initial render

- implement transform-based footer collision (no layout shift)
- add dynamic trigger offset for smoother early interaction
- introduce soft easing for natural TOC push-back
- fix initial TOC visibility with requestAnimationFrame
- ensure stable positioning without top/position overrides

Result: smooth, flicker-free TOC behavior with proper footer boundary handling
2026-04-22 01:25:10 +02:00
Dome a44fe2958e Update toc.js 2026-04-21 23:42:27 +02:00
Dome fdbc8c30c7 Update style.css 2026-04-21 23:42:24 +02:00
2 changed files with 86 additions and 43 deletions
+60 -22
View File
@@ -2,7 +2,7 @@ document.addEventListener('DOMContentLoaded', function () {
var toc = document.getElementById('zeitfresser-floating-toc'); var toc = document.getElementById('zeitfresser-floating-toc');
var title = document.querySelector('.zeitfresser-article-heading .page-title, .zeitfresser-article-heading .entry-title, .entry-header .entry-title'); var title = document.querySelector('.zeitfresser-article-heading .page-title, .zeitfresser-article-heading .entry-title, .entry-header .entry-title');
var progressBar = document.getElementById('zeitfresser-floating-toc-progress'); var progressBar = document.getElementById('zeitfresser-floating-toc-progress');
var nav = toc.querySelector('.zeitfresser-floating-toc__nav'); var nav = toc ? toc.querySelector('.zeitfresser-floating-toc__nav') : null;
if (!toc || !title) { if (!toc || !title) {
return; return;
@@ -44,10 +44,24 @@ document.addEventListener('DOMContentLoaded', function () {
var titleRect = title.getBoundingClientRect(); var titleRect = title.getBoundingClientRect();
var scrollTop = window.scrollY || window.pageYOffset || 0; var scrollTop = window.scrollY || window.pageYOffset || 0;
var contentColumn = document.querySelector('.inside-page .main-wrapper > *:first-child, .inside-page .main-wrapper .primary-content, .inside-page .main-wrapper #primary, .inside-page .main-wrapper main');
var sidebar = document.querySelector('.inside-page .main-wrapper > aside, .inside-page .main-wrapper .widget-area, .inside-page .main-wrapper #secondary, .inside-page .main-wrapper .sidebar'); var contentColumn = document.querySelector(
'.inside-page .main-wrapper > *:first-child, ' +
'.inside-page .main-wrapper .primary-content, ' +
'.inside-page .main-wrapper #primary, ' +
'.inside-page .main-wrapper main'
);
var sidebar = document.querySelector(
'.inside-page .main-wrapper > aside, ' +
'.inside-page .main-wrapper .widget-area, ' +
'.inside-page .main-wrapper #secondary, ' +
'.inside-page .main-wrapper .sidebar'
);
var contentRect = contentColumn ? contentColumn.getBoundingClientRect() : titleRect; var contentRect = contentColumn ? contentColumn.getBoundingClientRect() : titleRect;
var sidebarRect = sidebar ? sidebar.getBoundingClientRect() : null; var sidebarRect = sidebar ? sidebar.getBoundingClientRect() : null;
var mirroredGap = 56; var mirroredGap = 56;
if (sidebarRect) { if (sidebarRect) {
@@ -64,6 +78,32 @@ document.addEventListener('DOMContentLoaded', function () {
document.documentElement.style.setProperty('--zeitfresser-toc-width', tocWidth + 'px'); document.documentElement.style.setProperty('--zeitfresser-toc-width', tocWidth + 'px');
} }
function handleFooterCollision() {
if (!isDesktop()) {
toc.style.transform = '';
return;
}
var footer = document.querySelector('footer.site-footer, footer, #colophon');
if (!footer) return;
var footerRect = footer.getBoundingClientRect();
var tocRect = toc.getBoundingClientRect();
var offset = 40;
var triggerOffset = Math.min(200, window.innerHeight * 0.2);
var overlap = tocRect.bottom - (footerRect.top - triggerOffset);
if (overlap > -offset) {
var strength = 0.85;
var correction = Math.max(0, (overlap + offset) * strength);
toc.style.transform = 'translateY(-' + correction + 'px)';
} else {
toc.style.transform = '';
}
}
function setActiveLink(id) { function setActiveLink(id) {
links.forEach(function (link) { links.forEach(function (link) {
var active = link.getAttribute('data-target') === id; var active = link.getAttribute('data-target') === id;
@@ -78,11 +118,13 @@ document.addEventListener('DOMContentLoaded', function () {
} }
function updateProgress() { function updateProgress() {
if (!progressBar) { if (!progressBar) return;
return;
} var article = document.querySelector(
'.single-post .post-content article, ' +
'.single-post .post-content, article.post, article'
);
var article = document.querySelector('.single-post .post-content article, .single-post .post-content, article.post, article');
if (!article) { if (!article) {
progressBar.style.width = '0%'; progressBar.style.width = '0%';
return; return;
@@ -98,9 +140,7 @@ document.addEventListener('DOMContentLoaded', function () {
function updateActiveHeading() { function updateActiveHeading() {
var headings = getHeadings(); var headings = getHeadings();
if (!headings.length) { if (!headings.length) return;
return;
}
var currentId = headings[0].target.id; var currentId = headings[0].target.id;
var triggerY = headingOffset + 24; var triggerY = headingOffset + 24;
@@ -115,14 +155,13 @@ document.addEventListener('DOMContentLoaded', function () {
} }
function onViewportChange() { function onViewportChange() {
if (ticking) { if (ticking) return;
return;
}
ticking = true; ticking = true;
window.requestAnimationFrame(function () { window.requestAnimationFrame(function () {
syncPosition(); syncPosition();
handleFooterCollision();
updateProgress(); updateProgress();
updateActiveHeading(); updateActiveHeading();
ticking = false; ticking = false;
@@ -132,10 +171,7 @@ document.addEventListener('DOMContentLoaded', function () {
links.forEach(function (link) { links.forEach(function (link) {
link.addEventListener('click', function (event) { link.addEventListener('click', function (event) {
var target = getTarget(link); var target = getTarget(link);
if (!target) return;
if (!target) {
return;
}
event.preventDefault(); event.preventDefault();
@@ -150,14 +186,10 @@ document.addEventListener('DOMContentLoaded', function () {
}); });
}); });
if (nav) { if (nav) {
nav.addEventListener('wheel', function (event) { nav.addEventListener('wheel', function (event) {
var canScroll = nav.scrollHeight > nav.clientHeight; var canScroll = nav.scrollHeight > nav.clientHeight;
if (!canScroll) return;
if (!canScroll) {
return;
}
var atTop = nav.scrollTop <= 0; var atTop = nav.scrollTop <= 0;
var atBottom = Math.ceil(nav.scrollTop + nav.clientHeight) >= nav.scrollHeight; var atBottom = Math.ceil(nav.scrollTop + nav.clientHeight) >= nav.scrollHeight;
@@ -169,10 +201,16 @@ document.addEventListener('DOMContentLoaded', function () {
}, { passive: false }); }, { passive: false });
} }
// Initial run
syncPosition(); syncPosition();
handleFooterCollision();
updateProgress(); updateProgress();
updateActiveHeading(); updateActiveHeading();
requestAnimationFrame(function () {
toc.classList.add('is-visible');
});
window.addEventListener('scroll', onViewportChange, { passive: true }); window.addEventListener('scroll', onViewportChange, { passive: true });
window.addEventListener('resize', onViewportChange, { passive: true }); window.addEventListener('resize', onViewportChange, { passive: true });
}); });
+26 -21
View File
@@ -5,7 +5,7 @@ Author: Zeitfresser
Author URI: https://ztfr.eu/ Author URI: https://ztfr.eu/
Theme URI: https://ztfr.eu/ Theme URI: https://ztfr.eu/
Description: Zeitfresser Wordpress Theme Description: Zeitfresser Wordpress Theme
Version: 1.0 Version: 1.1
Tested up to: 6.2 Tested up to: 6.2
Requires PHP: 7.0 Requires PHP: 7.0
License: GNU General Public License v2 or later License: GNU General Public License v2 or later
@@ -36,35 +36,39 @@ html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:b
--zeitfresser-toc-width: 230px; --zeitfresser-toc-width: 230px;
} }
.zeitfresser-article-heading{
display:flex;
align-items:flex-start;
gap:0;
}
.zeitfresser-toc-toggle,
.zeitfresser-floating-toc__close{
display:none !important;
}
.zeitfresser-floating-toc{ .zeitfresser-floating-toc{
position:fixed; position:fixed;
top:calc(var(--zeitfresser-toc-top, 100px) + var(--zeitfresser-toc-top-offset, 0px)) !important; top:calc(var(--zeitfresser-toc-top, 100px) + var(--zeitfresser-toc-top-offset, 0px)) !important;
left:var(--zeitfresser-toc-left, 24px) !important; left:var(--zeitfresser-toc-left, 24px) !important;
width:var(--zeitfresser-toc-width) !important; width:var(--zeitfresser-toc-width) !important;
max-height:calc(100vh - var(--zeitfresser-toc-top, 100px) - var(--zeitfresser-toc-top-offset, 0px) - 36px) !important; max-height:calc(100vh - var(--zeitfresser-toc-top, 100px) - var(--zeitfresser-toc-top-offset, 0px) - 36px) !important;
padding:0 !important; padding:0 !important;
border:none !important; border:none !important;
border-radius:0 !important; border-radius:0 !important;
background:transparent !important; background:transparent !important;
background-color:transparent !important; background-color:transparent !important;
backdrop-filter:none !important; backdrop-filter:none !important;
box-shadow:none !important; box-shadow:none !important;
overflow:hidden !important; overflow:hidden !important;
opacity:1 !important;
transform:none !important; transform:none;
pointer-events:auto !important; pointer-events:auto !important;
z-index:80; z-index:80;
contain: layout style;
opacity: 0;
transition: opacity 0.12s ease-out;
will-change: opacity, transform;
}
.zeitfresser-floating-toc.is-visible{
opacity: 1;
} }
.zeitfresser-floating-toc__header{ .zeitfresser-floating-toc__header{
@@ -134,17 +138,19 @@ html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:b
margin:0 !important; margin:0 !important;
padding:0 0 0 14px !important; padding:0 0 0 14px !important;
border-left:3px solid transparent !important; border-left:3px solid transparent !important;
border-radius:0 !important;
background:none !important; background:none !important;
background-color:transparent !important;
background-image:none !important;
box-shadow:none !important; box-shadow:none !important;
color:#f7f7fa !important; color:#f7f7fa !important;
font-size:.92rem !important; font-size:.92rem !important;
line-height:1.35 !important; line-height:1.35 !important;
font-weight:400 !important; font-weight:400 !important;
text-decoration:none !important; text-decoration:none !important;
transition:color .18s ease, border-color .18s ease !important; transition:color .18s ease, border-color .18s ease !important;
word-break:normal !important; word-break:normal !important;
overflow-wrap:normal !important; overflow-wrap:normal !important;
} }
@@ -171,13 +177,12 @@ html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:b
color:#bd93f9 !important; color:#bd93f9 !important;
border-left-color:#bd93f9 !important; border-left-color:#bd93f9 !important;
background:none !important; background:none !important;
background-color:transparent !important;
background-image:none !important;
outline:none !important; outline:none !important;
box-shadow:none !important; box-shadow:none !important;
text-decoration:none !important; text-decoration:none !important;
} }
/* Remove default anchor jump highlight */
.single-post .entry-content :target, .single-post .entry-content :target,
.post-content :target, .post-content :target,
.inner-article-content :target, .inner-article-content :target,
@@ -185,12 +190,12 @@ html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:b
.post-content :focus, .post-content :focus,
.inner-article-content :focus{ .inner-article-content :focus{
background:transparent !important; background:transparent !important;
background-color:transparent !important;
outline:none !important; outline:none !important;
box-shadow:none !important; box-shadow:none !important;
border:none !important; border:none !important;
} }
/* Offset for anchor scroll */
.single-post .inner-article-content h2, .single-post .inner-article-content h2,
.single-post .inner-article-content h3, .single-post .inner-article-content h3,
.single-post .inner-article-content h4, .single-post .inner-article-content h4,
@@ -200,9 +205,9 @@ html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:b
scroll-margin-top:88px; scroll-margin-top:88px;
} }
/* Disable TOC on smaller screens */
@media only screen and (max-width:1650px){ @media only screen and (max-width:1650px){
.zeitfresser-floating-toc{ .zeitfresser-floating-toc{
display:none !important; display:none !important;
} }
} }