Replace posts/001/slug nesting with issue taxonomy

Use Hugo's `issue = "issues"` taxonomy so each post carries
`issues: ["01"]` in frontmatter. Issue pages live at /issues/01/
with a dedicated _index.md holding title, season, and description.
The /issues/ archive page uses data/issues.json for forthcoming
entries that have no posts yet.

Layouts:
- layouts/issues/list.html → plate grid for a single issue term
- layouts/issues/terms.html → issues archive (new)
- _default/single.html → scopes grid to issue term page, back-link to /issues/01/
- baseof.html → ribbon links to /issues/01/, JSON island includes issue field
- partials/header.html → Issues link → /issues/

JS:
- lbClose() navigates to /issues/${issueId}/ on a single-post page
- lightbox meta panel shows linked Issue fact

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-18 17:09:39 +02:00
parent a4463f03e3
commit 0ae8afbf7b
111 changed files with 334 additions and 70 deletions
+11 -4
View File
@@ -1,13 +1,20 @@
{{ define "main" }}
{{/* Individual post page — grid renders in background, JS opens lightbox for this slug */}}
{{- $issue := index (.Params.issues | default (slice "01")) 0 }}
{{- $issueURL := printf "/issues/%s/" $issue }}
<section class="hero" id="hero">
<div class="hero__eyebrow">{{ index (.Params.categories | default (slice "Plate")) 0 }}</div>
<div class="hero__eyebrow">
№ {{ $issue }} · {{ index (.Params.categories | default (slice "Plate")) 0 }}
</div>
<h1 class="hero__title"><em>{{ .Title }}</em></h1>
<p class="hero__lede">{{ .Params.description }} <a href="/" style="border-bottom:1px solid currentColor">Return to archive →</a></p>
<p class="hero__lede">
{{ .Params.description }}
<a href="{{ $issueURL }}" style="border-bottom:1px solid currentColor">Return to issue →</a>
</p>
</section>
<section class="grid" id="grid" data-density="default">
{{- $posts := where .Site.RegularPages "Section" "posts" }}
{{- $termPage := $.Site.GetPage (printf "/issues/%s" $issue) }}
{{- $posts := cond (ne $termPage nil) $termPage.Pages .Site.RegularPages }}
{{- range $i, $p := $posts }}
{{- $img := $p.Resources.GetMatch "*.png" }}
<a class="card"
+2 -1
View File
@@ -15,7 +15,7 @@
{{- partial "lightbox.html" . }}
<div class="ribbon" id="ribbon">
<span>Roux № {{ .Site.Params.issueNumber }} — out now. <a href="/categories/gothic/">See the plates →</a></span>
<span>Roux № {{ .Site.Params.issueNumber }} — out now. <a href="/issues/01/">See the plates →</a></span>
<button id="ribbonClose" aria-label="Dismiss">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>
</button>
@@ -37,6 +37,7 @@
{{- $post := dict
"id" .Params.plate
"slug" .Params.slug
"issue" (index (.Params.issues | default (slice "01")) 0)
"title" .Title
"description" .Params.description
"categories" .Params.categories
+37 -55
View File
@@ -1,63 +1,45 @@
{{ define "main" }}
{{- $issues := .Site.Data.issues }}
{{- $issueNum := .Params.issueNumber | default (printf "№ %s" .Title) }}
<section class="hero" id="hero">
<div class="hero__eyebrow">Archive · Every issue</div>
<h1 class="hero__title">The <em>issues</em></h1>
<p class="hero__lede">Roux publishes one hundred photographs at a time, once a season. Each issue is a complete thing.</p>
<div class="hero__eyebrow">
{{ $issueNum }} · {{ .Params.season }}
· <a href="/issues/" style="border-bottom:1px solid currentColor;margin-left:6px">All issues</a>
</div>
<h1 class="hero__title"><em>{{ .Title }}</em></h1>
<p class="hero__lede">{{ .Params.description }}</p>
</section>
<div class="issues-grid">
{{- range $i, $iss := $issues }}
{{- $delay := mul $i 80 }}
{{- $isCurrent := eq $iss.status "current" }}
{{- $isForth := eq $iss.status "forthcoming" }}
{{- if $isCurrent }}
<a href="/posts/" class="issue-card{{ if $isForth }} issue-card--forthcoming{{ end }}" style="--d:{{ $delay }}ms">
{{- else if $isForth }}
<div class="issue-card issue-card--forthcoming" style="--d:{{ $delay }}ms">
{{- else }}
<a href="/posts/" class="issue-card" style="--d:{{ $delay }}ms">
{{- end }}
<div class="issue-card__cover">
{{- if $isForth }}
<div class="issue-card__forth">
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>
Forthcoming
</div>
{{- else }}
{{/* Use first post image as cover */}}
{{- $posts := where $.Site.RegularPages "Section" "posts" }}
{{- $first := index $posts 0 }}
{{- if $first }}
{{- $img := $first.Resources.GetMatch "*.png" }}
{{- if $img }}
{{- $cover := $img.Resize "480x720 webp" }}
<picture>
<source srcset="{{ $cover.RelPermalink }}" type="image/webp" />
<img src="{{ $cover.RelPermalink }}" alt="Issue {{ $iss.number }} cover" loading="lazy" />
</picture>
{{- end }}
{{- end }}
{{- end }}
</div>
<div class="issue-card__meta">
<div class="issue-card__num">{{ $iss.number }}</div>
<h2 class="issue-card__title">{{ $iss.title | replaceRE `^(.+)$` "$1" }}</h2>
<p class="issue-card__blurb">{{ $iss.blurb }}</p>
<div class="issue-card__foot">
<span>{{ $iss.season }}</span>
{{- if $isCurrent }}
<span class="issue-card__cta">Read the issue →</span>
{{- else if $isForth }}
<span class="issue-card__cta issue-card__cta--muted">Subscribe →</span>
<section class="grid" id="grid" data-density="default">
{{- range $i, $p := .Pages }}
{{- $img := $p.Resources.GetMatch "*.png" }}
<a class="card"
href="{{ $p.RelPermalink }}"
style="--d:{{ mul (math.Min $i 18) 24 }}ms"
data-id="{{ $p.Params.plate }}"
data-slug="{{ $p.Params.slug }}">
<div class="card__frame">
{{- if $img }}
{{- $w := $img.Resize "600x900 webp" }}
<picture>
<source srcset="{{ $w.RelPermalink }}" type="image/webp" />
<img class="card__img"
loading="{{ if lt $i 8 }}eager{{ else }}lazy{{ end }}"
src="{{ $w.RelPermalink }}"
alt="{{ $p.Title }}" />
</picture>
{{- end }}
<span class="card__num">PLATE №{{ $p.Params.plate }}</span>
<span class="card__cat">{{ index ($p.Params.categories | default (slice "")) 0 }}</span>
</div>
</div>
{{- if or $isCurrent (not $isForth) }}</a>{{- else }}</div>{{- end }}
<div class="card__meta">
<h2 class="card__title">{{ $p.Title }}</h2>
<div class="card__sub">
<span>{{ index ($p.Params.categories | default (slice "")) 0 }}</span>
{{- if $p.Params.tags }}<span class="dot"></span><span>{{ index $p.Params.tags 0 }}</span>{{ end }}
</div>
<p class="card__desc">{{ $p.Params.description }}</p>
</div>
</a>
{{- end }}
</div>
</section>
{{ end }}
+61
View File
@@ -0,0 +1,61 @@
{{ define "main" }}
{{- $issues := .Site.Data.issues }}
<section class="hero" id="hero">
<div class="hero__eyebrow">Archive · Every issue</div>
<h1 class="hero__title">The <em>issues</em></h1>
<p class="hero__lede">Roux publishes one hundred photographs at a time, once a season. Each issue is a complete thing.</p>
</section>
<div class="issues-grid">
{{- range $i, $iss := $issues }}
{{- $delay := mul $i 80 }}
{{- $isCurrent := eq $iss.status "current" }}
{{- $isForth := eq $iss.status "forthcoming" }}
{{- $issueURL := printf "/issues/%s/" $iss.id }}
{{- if $isForth }}
<div class="issue-card issue-card--forthcoming" style="--d:{{ $delay }}ms">
{{- else }}
<a href="{{ $issueURL }}" class="issue-card" style="--d:{{ $delay }}ms">
{{- end }}
<div class="issue-card__cover">
{{- if $isForth }}
<div class="issue-card__forth">
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>
Forthcoming
</div>
{{- else }}
{{- $termPage := $.Site.GetPage (printf "/issues/%s" $iss.id) }}
{{- $firstPost := cond (ne $termPage nil) (index $termPage.Pages 0) nil }}
{{- if $firstPost }}
{{- $img := $firstPost.Resources.GetMatch "*.png" }}
{{- if $img }}
{{- $cover := $img.Resize "480x720 webp" }}
<picture>
<source srcset="{{ $cover.RelPermalink }}" type="image/webp" />
<img src="{{ $cover.RelPermalink }}" alt="Issue {{ $iss.number }} cover" loading="lazy" />
</picture>
{{- end }}
{{- end }}
{{- end }}
</div>
<div class="issue-card__meta">
<div class="issue-card__num">{{ $iss.number }}</div>
<h2 class="issue-card__title">{{ $iss.title }}</h2>
<p class="issue-card__blurb">{{ $iss.blurb }}</p>
<div class="issue-card__foot">
<span>{{ $iss.season }}</span>
{{- if $isCurrent }}
<span class="issue-card__cta">Read the issue →</span>
{{- else if $isForth }}
<span class="issue-card__cta issue-card__cta--muted">Subscribe →</span>
{{- end }}
</div>
</div>
{{- if $isForth }}</div>{{- else }}</a>{{- end }}
{{- end }}
</div>
{{ end }}
+1 -1
View File
@@ -13,7 +13,7 @@
</a>
<div class="masthead__right">
<a href="/issues/" class="mh-link">Issues</a>
<a href="/issue/" class="mh-link">Issues</a>
<span class="mh-sep" style="opacity:.5">·</span>
<span class="mh-city">Paris</span>
</div>