adding a left sidebar for article summary. changed right sidebar behavior between homepage and in-articles
This commit is contained in:
1 parent
c9cff79d3b
commit
7585802e57
4 files changed
+165
-14
No files matched your search
@@ -1027,6 +1027,64 @@ footer.footer-modern a:hover {
|
|||||||
margin-right: 0.4rem;
|
margin-right: 0.4rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ========================================
|
||||||
|
TABLE OF CONTENTS (left sidebar, article view)
|
||||||
|
======================================== */
|
||||||
|
.toc-sidebar {
|
||||||
|
margin-top: var(--space-xl);
|
||||||
|
position: sticky;
|
||||||
|
top: var(--space-lg);
|
||||||
|
max-height: calc(100vh - var(--space-xl));
|
||||||
|
overflow-y: auto;
|
||||||
|
font-size: var(--font-size-sm);
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc-title {
|
||||||
|
font-size: var(--font-size-xs);
|
||||||
|
font-weight: 700;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.08em;
|
||||||
|
color: var(--text-muted);
|
||||||
|
margin-bottom: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc-list {
|
||||||
|
list-style: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc-item {
|
||||||
|
padding: 0.15rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc-h3 {
|
||||||
|
padding-left: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc-h4 {
|
||||||
|
padding-left: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc-link {
|
||||||
|
color: var(--text-muted);
|
||||||
|
text-decoration: none;
|
||||||
|
line-height: 1.5;
|
||||||
|
display: block;
|
||||||
|
transition: color 0.15s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc-link:hover {
|
||||||
|
color: var(--text-primary);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc-link.active {
|
||||||
|
color: var(--accent);
|
||||||
|
font-weight: 600;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
/* ========================================
|
/* ========================================
|
||||||
RESPONSIVE
|
RESPONSIVE
|
||||||
======================================== */
|
======================================== */
|
||||||
|
|||||||
@@ -65,6 +65,7 @@
|
|||||||
'priority' => $priority,
|
'priority' => $priority,
|
||||||
'index' => $sidebarIndex++,
|
'index' => $sidebarIndex++,
|
||||||
'search' => $isSearch,
|
'search' => $isSearch,
|
||||||
|
'nav' => $isNav,
|
||||||
'html' => $out,
|
'html' => $out,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -78,32 +79,48 @@
|
|||||||
return $a['priority'] - $b['priority'];
|
return $a['priority'] - $b['priority'];
|
||||||
});
|
});
|
||||||
|
|
||||||
$sidebarFullHtml = ''; // all plugins, ordered (used off the homepage)
|
$sidebarFullHtml = ''; // all plugins, ordered (used on article/page views)
|
||||||
$sidebarNoSearchHtml = ''; // same, minus search (used on the homepage)
|
$sidebarNoSearchHtml = ''; // same, minus search (kept for potential future use)
|
||||||
|
$sidebarHomeHtml = ''; // about + categories + hit counter only (homepage, no nav/search)
|
||||||
foreach ($sidebarItems as $item) {
|
foreach ($sidebarItems as $item) {
|
||||||
$sidebarFullHtml .= $item['html'];
|
$sidebarFullHtml .= $item['html'];
|
||||||
if (!$item['search']) {
|
if (!$item['search']) {
|
||||||
$sidebarNoSearchHtml .= $item['html'];
|
$sidebarNoSearchHtml .= $item['html'];
|
||||||
}
|
}
|
||||||
|
if (!$item['search'] && !$item['nav']) {
|
||||||
|
$sidebarHomeHtml .= $item['html'];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// The hero (and the relocated search) only appear on the blog front page.
|
// The hero (and the relocated search) only appear on the blog front page.
|
||||||
$heroVisible = ($WHERE_AM_I === 'home' && Paginator::currentPage() == 1);
|
$heroVisible = ($WHERE_AM_I === 'home' && Paginator::currentPage() == 1);
|
||||||
|
// True when viewing a single non-static article.
|
||||||
|
$isArticlePage = ($WHERE_AM_I === 'page' && isset($page) && !$page->isStatic() && !$url->notFound());
|
||||||
|
// Show 3-column layout (left ToC + content + right sidebar) only when the article has headings.
|
||||||
|
$hasHeadings = $isArticlePage && (bool) preg_match('/<h[234]/i', $page->content());
|
||||||
?>
|
?>
|
||||||
|
|
||||||
<!-- Content -->
|
<!-- Content -->
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
|
||||||
<!-- Blog Posts -->
|
<?php if ($hasHeadings): ?>
|
||||||
|
|
||||||
|
<!-- Left ToC Sidebar — visible on md+ only; hidden on mobile -->
|
||||||
|
<div class="col-md-3 d-none d-md-block align-self-start">
|
||||||
|
<?php include(THEME_DIR_PHP.'toc.php'); ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Article Content (narrower when ToC is present) -->
|
||||||
|
<div class="col-md-6">
|
||||||
|
<?php include(THEME_DIR_PHP.'page.php'); ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php else: ?>
|
||||||
|
|
||||||
|
<!-- Blog Posts / Page (full width when no ToC) -->
|
||||||
<div class="col-md-8">
|
<div class="col-md-8">
|
||||||
<?php
|
<?php
|
||||||
// Bludit content are pages.
|
|
||||||
// Ordered by date, these pages behave like posts.
|
|
||||||
//
|
|
||||||
// $WHERE_AM_I detects where the visitor is:
|
|
||||||
// "page" -> viewing a single page/post
|
|
||||||
// "home" -> viewing the front page
|
|
||||||
if ($WHERE_AM_I == 'page') {
|
if ($WHERE_AM_I == 'page') {
|
||||||
include(THEME_DIR_PHP.'page.php');
|
include(THEME_DIR_PHP.'page.php');
|
||||||
} else {
|
} else {
|
||||||
@@ -112,9 +129,11 @@
|
|||||||
?>
|
?>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<?php endif ?>
|
||||||
|
|
||||||
<!-- Right Sidebar (align-self-start: card fits its content, doesn't
|
<!-- Right Sidebar (align-self-start: card fits its content, doesn't
|
||||||
stretch to match the main column's height) -->
|
stretch to match the main column's height) -->
|
||||||
<div class="col-md-3 offset-md-1 align-self-start">
|
<div class="<?php echo $hasHeadings ? 'col-md-3' : 'col-md-3 offset-md-1'; ?> align-self-start">
|
||||||
<?php include(THEME_DIR_PHP.'sidebar.php'); ?>
|
<?php include(THEME_DIR_PHP.'sidebar.php'); ?>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -213,6 +232,74 @@
|
|||||||
})();
|
})();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<!-- Table of Contents: generate links from article headings + scroll-spy -->
|
||||||
|
<script>
|
||||||
|
(function () {
|
||||||
|
var tocNav = document.getElementById('toc-nav');
|
||||||
|
if (!tocNav) return;
|
||||||
|
|
||||||
|
var content = document.querySelector('.content');
|
||||||
|
if (!content) return;
|
||||||
|
|
||||||
|
var headings = content.querySelectorAll('h2, h3, h4');
|
||||||
|
if (headings.length === 0) return;
|
||||||
|
|
||||||
|
// Ensure every heading has a stable anchor ID
|
||||||
|
var usedIds = {};
|
||||||
|
Array.prototype.forEach.call(headings, function (h) {
|
||||||
|
if (!h.id) {
|
||||||
|
var base = h.textContent.trim().toLowerCase()
|
||||||
|
.replace(/[^a-z0-9\s-]/g, '')
|
||||||
|
.replace(/\s+/g, '-')
|
||||||
|
.replace(/^-+|-+$/g, '') || 'heading';
|
||||||
|
var id = base, n = 2;
|
||||||
|
while (usedIds[id]) { id = base + '-' + (n++); }
|
||||||
|
usedIds[id] = true;
|
||||||
|
h.id = id;
|
||||||
|
} else {
|
||||||
|
usedIds[h.id] = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Build the list
|
||||||
|
var ul = document.createElement('ul');
|
||||||
|
ul.className = 'toc-list';
|
||||||
|
Array.prototype.forEach.call(headings, function (h) {
|
||||||
|
var li = document.createElement('li');
|
||||||
|
li.className = 'toc-item toc-' + h.tagName.toLowerCase();
|
||||||
|
var a = document.createElement('a');
|
||||||
|
a.href = '#' + h.id;
|
||||||
|
a.textContent = h.textContent;
|
||||||
|
a.className = 'toc-link';
|
||||||
|
li.appendChild(a);
|
||||||
|
ul.appendChild(li);
|
||||||
|
});
|
||||||
|
tocNav.appendChild(ul);
|
||||||
|
|
||||||
|
// Scroll-spy: highlight the last heading that has scrolled past the top
|
||||||
|
var headingArr = Array.prototype.slice.call(headings);
|
||||||
|
|
||||||
|
function updateActive() {
|
||||||
|
var scrollY = window.scrollY || window.pageYOffset;
|
||||||
|
var threshold = scrollY + 120; // offset for navbar height
|
||||||
|
var active = null;
|
||||||
|
headingArr.forEach(function (h) {
|
||||||
|
var top = h.getBoundingClientRect().top + scrollY;
|
||||||
|
if (top <= threshold) active = h;
|
||||||
|
});
|
||||||
|
var links = tocNav.querySelectorAll('.toc-link');
|
||||||
|
Array.prototype.forEach.call(links, function (a) { a.classList.remove('active'); });
|
||||||
|
if (active) {
|
||||||
|
var link = tocNav.querySelector('a[href="#' + active.id + '"]');
|
||||||
|
if (link) link.classList.add('active');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener('scroll', updateActive, { passive: true });
|
||||||
|
updateActive();
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
|
||||||
<!-- Load Bludit Plugins: Site Body End -->
|
<!-- Load Bludit Plugins: Site Body End -->
|
||||||
<?php Theme::plugins('siteBodyEnd'); ?>
|
<?php Theme::plugins('siteBodyEnd'); ?>
|
||||||
|
|
||||||
|
|||||||
+4
-4
@@ -20,10 +20,10 @@
|
|||||||
<?php
|
<?php
|
||||||
// Plugins are rendered in a fixed order (see index.php):
|
// Plugins are rendered in a fixed order (see index.php):
|
||||||
// Blog/About > Search > Navigation > Category > … > Hit Counter (bottom)
|
// Blog/About > Search > Navigation > Category > … > Hit Counter (bottom)
|
||||||
// On the homepage front page the search box is shown under the hero, so
|
// On the homepage the navigation is redundant (posts are already listed),
|
||||||
// it is omitted from the sidebar there.
|
// and the search box lives under the hero, so show only about/categories/counters.
|
||||||
if ($heroVisible) {
|
if ($WHERE_AM_I === 'home') {
|
||||||
echo $sidebarNoSearchHtml;
|
echo $sidebarHomeHtml;
|
||||||
} else {
|
} else {
|
||||||
echo $sidebarFullHtml;
|
echo $sidebarFullHtml;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
<aside class="toc-sidebar">
|
||||||
|
<div class="toc-title">On this page</div>
|
||||||
|
<nav id="toc-nav" aria-label="Table of contents">
|
||||||
|
<!-- Filled by the ToC script in index.php -->
|
||||||
|
</nav>
|
||||||
|
</aside>
|
||||||
Reference in new issue
Block a user