From: Danilo M. Date: Sat, 18 Apr 2026 18:54:50 +0000 (+0200) Subject: feat: add prev/next article navigation with shell prompt style X-Git-Tag: release_22042026-1342~81 X-Git-Url: https://git.danix.xyz/?a=commitdiff_plain;h=05f33a6e1059e84c309c5f674e094ed3b1105134;p=danix.xyz-2.git feat: add prev/next article navigation with shell prompt style Add top and bottom navigation between sequential articles with hacker aesthetic: - Top nav: [visitor@danix.xyz articles]$ cd - Bottom nav: [visitor@danix.xyz articles]$ ls ../ - Missing link shows dimmed placeholder (beginning/end) - Only renders on articles, not static pages - New partial: article-nav.html - Styling: monospace prompt in accent color, hover links with transition Co-Authored-By: Claude Haiku 4.5 --- diff --git a/docs/superpowers/specs/2026-04-18-prev-next-navigation-design.md b/docs/superpowers/specs/2026-04-18-prev-next-navigation-design.md new file mode 100644 index 0000000..2fa22a7 --- /dev/null +++ b/docs/superpowers/specs/2026-04-18-prev-next-navigation-design.md @@ -0,0 +1,56 @@ +# Prev/Next Article Navigation — Design Spec + +## Context + +danix.xyz articles currently have no way to navigate between sequential posts. The user wants shell-prompt-style "hacker" navigation at both the top and bottom of each article, consistent with the site's open-source/hacker soul. + +## Visual Design + +### Top nav (above breadcrumb) +``` +[visitor@danix.xyz articles]$ cd +◄ Previous Article Title Next Article Title ► +``` + +### Bottom nav (after tags section) +``` +[visitor@danix.xyz articles]$ ls ../ +◄ Previous Article Title Next Article Title ► +``` + +- Two lines, full width of the content column (`md:col-span-2`) +- Line 1: decorative shell prompt — monospace, accent purple, not a link +- Line 2: prev on left (◄ title), next on right (title ►), both clickable +- Missing side: dimmed placeholder `◄ (beginning)` or `(end) ►` +- Both sides always rendered to keep layout balanced + +## Architecture + +Single reusable partial `article-nav.html` accepting: +- `page` — current Hugo page context +- `variant` — `"top"` or `"bottom"` (controls prompt command: `cd` vs `ls ../`) + +Hugo variables: `.PrevInSection` / `.NextInSection` (nil-safe). + +## Styling + +- Font: JetBrains Mono (`font-mono`) +- Prompt color: `var(--accent)` (#a855f7 purple) +- Link hover: `hover:text-accent transition-colors` +- Placeholder: `text-text-dim opacity-40` +- Container: `border-t border-border pt-6` +- Titles truncated at `max-w-[45%]` with `truncate` to prevent collision + +## Files Affected + +- `layouts/partials/article-nav.html` — new partial (create) +- `layouts/articles/single.html` — insert partial top + bottom +- `layouts/_default/single.html` — insert partial top + bottom +- `assets/css/main.css` — add `.article-nav*` component classes + +## Accessibility + +- `aria-hidden="true"` on prompt line (decorative) +- `rel="prev"` / `rel="next"` on links (SEO + semantics) +- `title` attribute on links for full title on hover (truncation) +- `aria-label` on placeholder spans diff --git a/themes/danix-xyz-hacker/assets/css/main.css b/themes/danix-xyz-hacker/assets/css/main.css index f618f2d..298c38b 100644 --- a/themes/danix-xyz-hacker/assets/css/main.css +++ b/themes/danix-xyz-hacker/assets/css/main.css @@ -574,6 +574,28 @@ html.theme-light picture img[src="/images/default_thumbnail_dark.png"] { .section-title { font-size: clamp(1.5rem, 3vw + 0.5rem, 2.5rem); } + + /* ---- Article prev/next navigation ---- */ + .article-nav { + @apply border-t border-border pt-6; + } + + .article-nav-prompt { + @apply font-mono text-sm mb-2; + color: var(--accent); + } + + .article-nav-links { + @apply flex justify-between items-center font-mono text-sm; + } + + .article-nav-link { + @apply hover:text-accent transition-colors text-text; + } + + .article-nav-placeholder { + @apply text-text-dim opacity-40; + } } /* Prose overrides for light theme */ diff --git a/themes/danix-xyz-hacker/assets/css/main.min.css b/themes/danix-xyz-hacker/assets/css/main.min.css index d928b7a..942d401 100644 --- a/themes/danix-xyz-hacker/assets/css/main.min.css +++ b/themes/danix-xyz-hacker/assets/css/main.min.css @@ -1632,6 +1632,56 @@ article.border.border-border\/30.rounded-lg.card.group.bg-bg { /* Hero typography with fluid sizing */ +/* ---- Article prev/next navigation ---- */ + +.article-nav { + border-color: var(--border); +} + + + .frosted-bar.article-nav { + border-color: var(--border); +} + +.article-nav { + border-top-width: 1px; + border-color: var(--border); + padding-top: 1.5rem; +} + +.article-nav-prompt { + margin-bottom: 0.5rem; + font-family: JetBrains Mono, monospace; + font-size: 0.875rem; + line-height: 1.25rem; + color: var(--accent); +} + +.article-nav-links { + display: flex; + align-items: center; + justify-content: space-between; + font-family: JetBrains Mono, monospace; + font-size: 0.875rem; + line-height: 1.25rem; +} + +.article-nav-link { + color: var(--text); + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; +} + +.article-nav-link:hover { + color: var(--accent); +} + +.article-nav-placeholder { + color: var(--text-dim); + opacity: 0.4; +} + .sr-only { position: absolute; width: 1px; @@ -1935,6 +1985,10 @@ article.border.border-border\/30.rounded-lg.card.group.bg-bg { max-width: 80rem; } +.max-w-\[45\%\] { + max-width: 45%; +} + .max-w-lg { max-width: 32rem; } diff --git a/themes/danix-xyz-hacker/layouts/_default/single.html b/themes/danix-xyz-hacker/layouts/_default/single.html index 0d3c6fa..62e4a64 100644 --- a/themes/danix-xyz-hacker/layouts/_default/single.html +++ b/themes/danix-xyz-hacker/layouts/_default/single.html @@ -3,6 +3,11 @@
+ + {{ if eq .Section "articles" }} + {{ partial "article-nav.html" (dict "page" . "variant" "top") }} + {{ end }} + {{ partial "breadcrumb.html" . }} @@ -35,6 +40,11 @@
{{ end }} + + + {{ if eq .Section "articles" }} + {{ partial "article-nav.html" (dict "page" . "variant" "bottom") }} + {{ end }} diff --git a/themes/danix-xyz-hacker/layouts/articles/single.html b/themes/danix-xyz-hacker/layouts/articles/single.html index 4e97fb3..e646639 100644 --- a/themes/danix-xyz-hacker/layouts/articles/single.html +++ b/themes/danix-xyz-hacker/layouts/articles/single.html @@ -5,6 +5,9 @@
+ + {{ partial "article-nav.html" (dict "page" . "variant" "top") }} + {{ partial "breadcrumb.html" . }} @@ -35,6 +38,9 @@
{{ end }} + + + {{ partial "article-nav.html" (dict "page" . "variant" "bottom") }} diff --git a/themes/danix-xyz-hacker/layouts/partials/article-nav.html b/themes/danix-xyz-hacker/layouts/partials/article-nav.html new file mode 100644 index 0000000..d7f8ca3 --- /dev/null +++ b/themes/danix-xyz-hacker/layouts/partials/article-nav.html @@ -0,0 +1,46 @@ +{{ $page := .page }} +{{ $variant := .variant | default "bottom" }} +{{ $prev := $page.PrevInSection }} +{{ $next := $page.NextInSection }} + +{{/* Shell prompt command varies by position */}} +{{ $cmd := "ls ../" }} +{{ if eq $variant "top" }} + {{ $cmd = "cd" }} +{{ end }} + +