This commit is contained in:
spleenftw committed 2026-07-10 12:31:56 +02:00
1 parent 4c2ff4af61
commit 5384ae0cd1
7 files changed
+198 -11

No files matched your search

+4 -2
View File
@@ -3,7 +3,7 @@
A clean, minimal theme for [Bludit](https://www.bludit.com) inspired by the A clean, minimal theme for [Bludit](https://www.bludit.com) inspired by the
[Blowfish](https://blowfish.page/) Hugo theme. [Blowfish](https://blowfish.page/) Hugo theme.
![Bludit](https://img.shields.io/badge/Bludit-3.22+-informational) ![License](https://img.shields.io/badge/license-MIT-green) ![Version](https://img.shields.io/badge/version-1.6.0-blue) ![Bludit](https://img.shields.io/badge/Bludit-3.22+-informational) ![License](https://img.shields.io/badge/license-MIT-green) ![Version](https://img.shields.io/badge/version-1.7.0-blue)
## Features ## Features
@@ -16,6 +16,8 @@ A clean, minimal theme for [Bludit](https://www.bludit.com) inspired by the
- **Keyboard shortcuts** — `/` (search), `g h` (go home), `t` (back to top), `Esc` (close overlay). - **Keyboard shortcuts** — `/` (search), `g h` (go home), `t` (back to top), `Esc` (close overlay).
- **Randomized navigation** — the Navigation/Pages plugin list is shuffled on every load. - **Randomized navigation** — the Navigation/Pages plugin list is shuffled on every load.
- **Reading time & dates** shown on the post list and single posts. - **Reading time & dates** shown on the post list and single posts.
- **Post-list excerpts** — the page description renders as a 2-line excerpt; tags are capped at 5 with a `+N` badge.
- **Older / newer post navigation** — chronological links under every article, above the related posts.
- **Social icons** in the navbar and hero (SVG files in `img/`). - **Social icons** in the navbar and hero (SVG files in `img/`).
- **Tags & categories** rendered as pill badges. - **Tags & categories** rendered as pill badges.
- **Image lightbox** — clicking any article image opens it full-screen with a dark overlay and zoom hint. - **Image lightbox** — clicking any article image opens it full-screen with a dark overlay and zoom hint.
@@ -24,7 +26,7 @@ A clean, minimal theme for [Bludit](https://www.bludit.com) inspired by the
- **Code block header bar** — language label + copy-to-clipboard button on every code block. - **Code block header bar** — language label + copy-to-clipboard button on every code block.
- **Callout/admonition boxes** — `> [!NOTE]`, `> [!TIP]`, `> [!WARNING]`, `> [!DANGER]`, `> [!IMPORTANT]`. - **Callout/admonition boxes** — `> [!NOTE]`, `> [!TIP]`, `> [!WARNING]`, `> [!DANGER]`, `> [!IMPORTANT]`.
- **Series navigation** — posts tagged `series-*` display an ordered series box. - **Series navigation** — posts tagged `series-*` display an ordered series box.
- **SEO** — Open Graph, Twitter Card, JSON-LD (WebSite, Article, BreadcrumbList, sameAs), canonical URL, theme-color meta, cover-image preload. - **SEO** — Open Graph, Twitter Card, JSON-LD (WebSite, Article, BreadcrumbList, sameAs), canonical URL, theme-color meta, cover-image preload. Duplicate OG/Twitter/canonical tags emitted by Bludit plugins are stripped automatically.
- **Accessibility** — skip link, ARIA labels, `:focus-visible`, heading anchor links, print stylesheet. - **Accessibility** — skip link, ARIA labels, `:focus-visible`, heading anchor links, print stylesheet.
- **PWA** — web app manifest included. - **PWA** — web app manifest included.
- Fully responsive; no jQuery, no Bootstrap JS, no webfont requests. - Fully responsive; no jQuery, no Bootstrap JS, no webfont requests.
+108 -1
View File
@@ -355,13 +355,45 @@ h2.title {
margin-bottom: var(--space-md); margin-bottom: var(--space-md);
} }
/* Justify body paragraphs; hyphenate so the inter-word gaps stay tidy */ /* Justified body text on wide screens, where long lines keep the word gaps
tidy — helped by auto-hyphenation. Below 576px the lines are too short for
justification (rivers of whitespace), so mobile falls back to left-aligned. */
.content p { .content p {
text-align: justify; text-align: justify;
text-align-last: left; /* never stretch a paragraph's final line */
hyphens: auto; hyphens: auto;
-webkit-hyphens: auto; -webkit-hyphens: auto;
} }
@media (max-width: 575.98px) {
.content p {
text-align: left;
hyphens: none;
-webkit-hyphens: none;
}
}
/* In-content links: underlined so they're distinguishable from body text.
The palette is monochrome (accent ≈ text colour), so colour alone can't
signal a link — the underline does. Heading anchors and image links opt out. */
.content a:not(.heading-anchor) {
text-decoration: underline;
text-decoration-color: var(--text-muted);
text-decoration-thickness: 1px;
text-underline-offset: 0.18em;
}
.content a:not(.heading-anchor):hover {
color: var(--text-primary);
text-decoration-color: var(--accent);
text-decoration-thickness: 2px;
}
/* Links that wrap an image (lightbox) shouldn't draw an underline stub */
.content a:has(> img) {
text-decoration: none;
}
.content img { .content img {
border-radius: var(--radius-md); border-radius: var(--radius-md);
margin: var(--space-md) 0; margin: var(--space-md) 0;
@@ -847,10 +879,29 @@ img.nav-svg-icon {
margin-right: 0.25rem; margin-right: 0.25rem;
} }
/* Excerpt: 1-2 lines of the page description, clamped so long ones can't
push the list around */
.post-list-excerpt {
margin: 0.6rem 0 0;
color: var(--text-secondary);
font-size: 0.95rem;
line-height: 1.55;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
.post-list-item .post-taxonomy { .post-list-item .post-taxonomy {
margin-top: var(--space-sm); margin-top: var(--space-sm);
} }
/* "+N" overflow badge — muted, links to the post (where all tags are shown) */
.taxonomy-badge-more {
color: var(--text-muted);
border-style: dashed;
}
/* ======================================== /* ========================================
POST CARDS (single page / post) POST CARDS (single page / post)
======================================== */ ======================================== */
@@ -2043,6 +2094,62 @@ button:focus:not(:focus-visible) {
/* Separate the comments widget from the share row so it doesn't feel cramped. */ /* Separate the comments widget from the share row so it doesn't feel cramped. */
.utterances { margin-top: var(--space-xl) !important; } .utterances { margin-top: var(--space-xl) !important; }
/* ========================================
PREV / NEXT POST NAVIGATION
======================================== */
.post-nav {
display: grid;
grid-template-columns: 1fr 1fr;
gap: var(--space-sm);
margin: var(--space-lg) 0 0;
}
.post-nav-item {
display: flex;
flex-direction: column;
gap: 0.25rem;
padding: var(--space-sm) var(--space-md);
border: 1px solid var(--border-color);
border-radius: var(--radius-md);
background: var(--bg-surface);
transition: border-color 0.2s ease, background 0.2s ease;
}
.post-nav-item:hover {
background: var(--bg-hover);
border-color: var(--text-muted);
}
.post-nav-newer { text-align: right; align-items: flex-end; }
.post-nav-label {
display: inline-flex;
align-items: center;
gap: 0.3rem;
color: var(--text-muted);
font-size: var(--font-size-xs);
text-transform: uppercase;
letter-spacing: 0.04em;
}
.post-nav-title {
color: var(--text-primary);
font-weight: 600;
font-size: 0.95rem;
line-height: 1.35;
}
.post-nav-empty {
border: none;
background: transparent;
pointer-events: none;
}
@media (max-width: 575.98px) {
.post-nav { grid-template-columns: 1fr; }
.post-nav-empty { display: none; }
}
/* ======================================== /* ========================================
RELATED POSTS RELATED POSTS
======================================== */ ======================================== */
+4
View File
@@ -29,6 +29,10 @@
"Back to home": "Back to home", "Back to home": "Back to home",
"Previous": "Previous", "Previous": "Previous",
"Next": "Next", "Next": "Next",
"Older post": "Older post",
"Newer post": "Newer post",
"Post navigation": "Post navigation",
"All tags": "All tags",
"Updated": "Updated", "Updated": "Updated",
"Copy": "Copy", "Copy": "Copy",
"Copied": "Copied", "Copied": "Copied",
+3 -3
View File
@@ -2,9 +2,9 @@
"author": "spleenftw", "author": "spleenftw",
"email": "", "email": "",
"website": "https://github.com/Spleenftw/bludit-blowfish", "website": "https://github.com/Spleenftw/bludit-blowfish",
"version": "1.6.0", "version": "1.7.0",
"releaseDate": "2026-06-03", "releaseDate": "2026-07-10",
"license": "MIT", "license": "MIT",
"compatible": "3.22.0", "compatible": "3.22.0",
"notes": "A Bludit theme inspired by the Blowfish Hugo theme. Five-theme swatch picker (Light/Dark/Nord/Dracula/Catppuccin), sticky ToC sidebar on articles, image lightbox with zoom, image carousel (```carousel fence), tabbed code blocks (```tabs fence), smart sidebar per page type, and clean responsive typography. v1.3.0: SEO (Open Graph, Twitter Card, JSON-LD, theme-color, cover preload), per-theme syntax highlighting (self-hosted highlight.js), copy-to-clipboard on code blocks, numbered pagination, inline-SVG icons (dropped the Bootstrap Icons webfont), removed jQuery/Bootstrap JS, deferred cached JS bundle, reading progress bar, back-to-top, randomised sidebar selection, lazy-loaded images, accessibility (skip link, ARIA, :focus-visible) and a print stylesheet. v1.4.0: canonical URL, WebSite + BreadcrumbList + author sameAs JSON-LD, og:image:alt / twitter:site+creator, cover-image aspect-ratio (CLS), heading anchor links, prev/next article nav, related posts, share buttons (X/LinkedIn/copy-link), mailto social icon, PWA web app manifest, 'updated on' date, and outbound-link hardening (noopener noreferrer). v1.5.0: callout/admonition boxes (> [!NOTE]/[!TIP]/[!WARNING]/...), code-block header bar with language label + copy button, visual breadcrumbs, custom 404 page, image figure/figcaption from the markdown title, a mobile table-of-contents drawer, and third-party resource hints. v1.6.0: self-hosted Inter (no Google Fonts), client-side search overlay (\"/\" key + navbar button), keyboard shortcuts (/ , g h, t, Esc), a tabbed-code copy button, series navigation (series-* tag), and content typography (keycaps, task-list checkboxes, <mark>, external-link icon, footnote/definition-list styling). the navbar search icon only appears once the on-page search box scrolls out of view, and code blocks use a header bar (language + copy) with tighter line-height." "notes": "A Bludit theme inspired by the Blowfish Hugo theme. Five-theme swatch picker (Light/Dark/Nord/Dracula/Catppuccin), sticky ToC sidebar on articles, image lightbox with zoom, image carousel (```carousel fence), tabbed code blocks (```tabs fence), smart sidebar per page type, and clean responsive typography. v1.3.0: SEO (Open Graph, Twitter Card, JSON-LD, theme-color, cover preload), per-theme syntax highlighting (self-hosted highlight.js), copy-to-clipboard on code blocks, numbered pagination, inline-SVG icons (dropped the Bootstrap Icons webfont), removed jQuery/Bootstrap JS, deferred cached JS bundle, reading progress bar, back-to-top, randomised sidebar selection, lazy-loaded images, accessibility (skip link, ARIA, :focus-visible) and a print stylesheet. v1.4.0: canonical URL, WebSite + BreadcrumbList + author sameAs JSON-LD, og:image:alt / twitter:site+creator, cover-image aspect-ratio (CLS), heading anchor links, prev/next article nav, related posts, share buttons (X/LinkedIn/copy-link), mailto social icon, PWA web app manifest, 'updated on' date, and outbound-link hardening (noopener noreferrer). v1.5.0: callout/admonition boxes (> [!NOTE]/[!TIP]/[!WARNING]/...), code-block header bar with language label + copy button, visual breadcrumbs, custom 404 page, image figure/figcaption from the markdown title, a mobile table-of-contents drawer, and third-party resource hints. v1.6.0: self-hosted Inter (no Google Fonts), client-side search overlay (\"/\" key + navbar button), keyboard shortcuts (/ , g h, t, Esc), a tabbed-code copy button, series navigation (series-* tag), and content typography (keycaps, task-list checkboxes, <mark>, external-link icon, footnote/definition-list styling). the navbar search icon only appears once the on-page search box scrolls out of view, and code blocks use a header bar (language + copy) with tighter line-height. v1.7.0: readability pass - underlined in-content links, justified paragraphs with auto-hyphenation (left-aligned on mobile), post-list excerpts from the page description with a 5-tag cap (+N badge), older/newer post navigation under articles, and de-duplication of Open Graph / Twitter Card / canonical tags emitted by plugins."
} }
+17 -2
View File
@@ -261,5 +261,20 @@
?> ?>
<link rel="stylesheet" type="text/css" href="<?php echo $styleHref; ?>"> <link rel="stylesheet" type="text/css" href="<?php echo $styleHref; ?>">
<!-- Load Bludit Plugins: Site head --> <!-- Load Bludit Plugins: Site head.
<?php Theme::plugins('siteHead'); ?> The theme already emits a complete, coherent set of Open Graph / Twitter
Card / canonical tags above. Bludit's Open Graph & Twitter Cards plugins
emit their own (conflicting) set — e.g. a post image as og:image on the
homepage and multiple og:locale values — so their duplicates are stripped
here. Best fixed at the source by deactivating those plugins in the admin;
this filter just makes the markup correct either way. -->
<?php
ob_start();
Theme::plugins('siteHead');
$bdPluginHead = ob_get_clean();
// <meta property="og:…"> / <meta name="twitter:…"> / <meta property="article:…">
$bdPluginHead = preg_replace('/<meta\b[^>]*\b(?:property|name)=["\'](?:og:|twitter:|article:)[^"\']*["\'][^>]*>\s*/i', '', $bdPluginHead);
// duplicate <link rel="canonical">
$bdPluginHead = preg_replace('/<link\b[^>]*\brel=["\']canonical["\'][^>]*>\s*/i', '', $bdPluginHead);
echo $bdPluginHead;
?>
+20 -3
View File
@@ -69,8 +69,18 @@
</div> </div>
</div> </div>
<!-- Tags and Category --> <!-- Excerpt (the page description, set in the admin's SEO tab) -->
<?php $tagsList = $page->tags(true); $categoryKey = $page->categoryKey(); ?> <?php $bdExcerpt = blowfish_plain($page->description()); ?>
<?php if ($bdExcerpt !== '') : ?>
<p class="post-list-excerpt"><?php echo htmlspecialchars($bdExcerpt, ENT_QUOTES, 'UTF-8'); ?></p>
<?php endif ?>
<!-- Tags and Category — capped so the list stays scannable -->
<?php
$tagsList = $page->tags(true);
$categoryKey = $page->categoryKey();
$bdMaxTags = 5; // tags shown per post on the list; the rest collapse into "+N"
?>
<?php if (!empty($tagsList) || $categoryKey) : ?> <?php if (!empty($tagsList) || $categoryKey) : ?>
<div class="post-taxonomy"> <div class="post-taxonomy">
<?php if ($categoryKey) : ?> <?php if ($categoryKey) : ?>
@@ -78,9 +88,16 @@
<?php echo blowfish_icon('folder'); ?><?php echo blowfish_text($page->category()); ?> <?php echo blowfish_icon('folder'); ?><?php echo blowfish_text($page->category()); ?>
</a> </a>
<?php endif ?> <?php endif ?>
<?php $bdTagCount = 0; $bdTagOverflow = 0; ?>
<?php foreach ($tagsList as $tagKey => $tagName) : ?> <?php foreach ($tagsList as $tagKey => $tagName) : ?>
<a class="taxonomy-badge" href="<?php echo DOMAIN_TAGS . $tagKey; ?>"><?php echo blowfish_icon('tag'); ?><?php echo blowfish_text($tagName); ?></a> <?php if ($bdTagCount < $bdMaxTags) : $bdTagCount++; ?>
<a class="taxonomy-badge" href="<?php echo DOMAIN_TAGS . $tagKey; ?>"><?php echo blowfish_icon('tag'); ?><?php echo blowfish_text($tagName); ?></a>
<?php else : $bdTagOverflow++; ?>
<?php endif ?>
<?php endforeach ?> <?php endforeach ?>
<?php if ($bdTagOverflow > 0) : ?>
<a class="taxonomy-badge taxonomy-badge-more" href="<?php echo $page->permalink(); ?>" title="<?php echo $L->get('All tags'); ?>">+<?php echo $bdTagOverflow; ?></a>
<?php endif ?>
</div> </div>
<?php endif ?> <?php endif ?>
+42
View File
@@ -144,6 +144,48 @@
</article> </article>
<?php
// ---- Previous / next post navigation ----
// Chronological neighbours (Bludit's list is newest-first). Guarded like
// the related-posts block so an API difference degrades to nothing.
if (!$page->isStatic() && !$url->notFound()) {
$bdOlder = null; $bdNewer = null;
try {
global $pages;
if (isset($pages) && method_exists($pages, 'getList')) {
$bdNavKeys = array();
foreach ($pages->getList(1, 10000, true) as $bdNk) {
$bdNp = new Page($bdNk);
if (!$bdNp->isStatic()) { $bdNavKeys[] = $bdNp; }
}
foreach ($bdNavKeys as $bdNi => $bdNp) {
if ($bdNp->key() === $page->key()) {
if ($bdNi > 0) { $bdNewer = $bdNavKeys[$bdNi - 1]; }
if ($bdNi < count($bdNavKeys) - 1) { $bdOlder = $bdNavKeys[$bdNi + 1]; }
break;
}
}
}
} catch (\Throwable $e) { $bdOlder = null; $bdNewer = null; }
?>
<?php if ($bdOlder || $bdNewer) : ?>
<nav class="post-nav" aria-label="<?php echo $L->get('Post navigation'); ?>">
<?php if ($bdOlder) : ?>
<a class="post-nav-item post-nav-older" href="<?php echo $bdOlder->permalink(); ?>" rel="prev">
<span class="post-nav-label"><?php echo blowfish_icon('chevron-left'); ?><?php echo $L->get('Older post'); ?></span>
<span class="post-nav-title"><?php echo blowfish_text($bdOlder->title()); ?></span>
</a>
<?php else : ?><span class="post-nav-item post-nav-empty" aria-hidden="true"></span><?php endif ?>
<?php if ($bdNewer) : ?>
<a class="post-nav-item post-nav-newer" href="<?php echo $bdNewer->permalink(); ?>" rel="next">
<span class="post-nav-label"><?php echo $L->get('Newer post'); ?><?php echo blowfish_icon('chevron-right'); ?></span>
<span class="post-nav-title"><?php echo blowfish_text($bdNewer->title()); ?></span>
</a>
<?php else : ?><span class="post-nav-item post-nav-empty" aria-hidden="true"></span><?php endif ?>
</nav>
<?php endif ?>
<?php } ?>
<?php <?php
// ---- Related posts ---- // ---- Related posts ----
// Best-effort via Bludit's Pages API; wrapped so any API difference degrades // Best-effort via Bludit's Pages API; wrapped so any API difference degrades