Files
blowfish/layouts/_default/single.html
T
Morten Victor Nordbye 1f82608cd6 Add reading progress bar for articles (#3014)
* Add reading progress bar for articles

* Let page-level showReadingProgress override the site setting

Hugo's default treats false as empty, so a page-level false could not
disable the bar once it was enabled site-wide. Resolve the value by
initialising from the site setting and overriding it only when the
front matter parameter is actually present, and use the same resolved
boolean in single.html and head.html.

* Guard the reading progress handler and mark the listener passive

The script loads in the head without defer, so the scroll listener is
registered before the progress bar element exists. A scroll event fired
during parsing, such as when the browser restores the scroll position on
reload, would throw. Bail out when the element is missing, matching the
guard in background-blur.js, and mark the scroll listener passive as
toc.html already does.
2026-08-16 22:14:45 +01:00

148 lines
6.1 KiB
HTML

{{ define "main" }}
{{ .Scratch.Set "scope" "single" }}
{{ $showReadingProgress := site.Params.article.showReadingProgress | default false }}
{{ if isset .Params "showreadingprogress" }}
{{ $showReadingProgress = .Params.showReadingProgress }}
{{ end }}
{{ if $showReadingProgress }}
{{ partial "reading-progress.html" . }}
{{ end }}
<article>
{{/* Hero */}}
{{ if .Params.showHero | default (site.Params.article.showHero | default false) }}
{{ $heroStyle := .Params.heroStyle }}
{{ if not $heroStyle }}{{ $heroStyle = site.Params.article.heroStyle }}{{ end }}
{{ $heroStyle := print "hero/" $heroStyle ".html" }}
{{ if templates.Exists ( printf "partials/%s" $heroStyle ) }}
{{ partial $heroStyle . }}
{{ else }}
{{ partial "hero/basic.html" . }}
{{ end }}
{{ end }}
{{/* Header */}}
<header id="single_header" class="mt-5 max-w-prose">
{{ if .Params.showBreadcrumbs | default (site.Params.article.showBreadcrumbs | default false) }}
{{ partial "breadcrumbs.html" . }}
{{ end }}
<h1 class="mt-0 text-4xl font-extrabold text-neutral-900 dark:text-neutral">
{{ .Title | emojify }}
</h1>
<div class="mt-1 mb-6 text-base text-neutral-500 dark:text-neutral-400 print:hidden">
{{ partial "article-meta/basic.html" (dict "context" . "scope" "single") }}
</div>
{{ if not (.Params.showAuthorBottom | default (site.Params.article.showAuthorBottom | default false)) }}
{{ template "SingleAuthor" . }}
{{ end }}
</header>
{{/* Body */}}
<section class="flex flex-col max-w-full mt-0 prose dark:prose-invert lg:flex-row">
{{ $enableToc := site.Params.article.showTableOfContents | default false }}
{{ $enableToc = .Params.showTableOfContents | default $enableToc }}
{{ $showToc := and $enableToc (in .TableOfContents "<ul") }}
{{ $topClass := cond (hasPrefix site.Params.header.layout "fixed") "lg:top-[140px]" "lg:top-10" }}
{{ if $showToc }}
<div class="order-first lg:ms-auto px-0 lg:order-last lg:ps-8 lg:max-w-2xs">
<div class="toc ps-5 print:hidden lg:sticky {{ $topClass }}">
{{ partial "toc.html" . }}
</div>
</div>
{{ end }}
<div class="min-w-0 min-h-0 max-w-fit">
{{ partial "series/series.html" . }}
<div class="article-content max-w-prose mb-20">
{{ .Content }}
{{ $defaultReplyByEmail := site.Params.replyByEmail }}
{{ $replyByEmail := default $defaultReplyByEmail .Params.replyByEmail }}
{{ if $replyByEmail }}
{{ $replySubject := i18n "article.reply_to" (dict "Title" .Title) | default (printf "Reply to %s" .Title) }}
<strong class="block mt-8">
<a
class="email-link m-1 rounded bg-neutral-300 p-1.5 text-neutral-700 hover:bg-primary-500 hover:text-neutral dark:bg-neutral-700 dark:text-neutral-300 dark:hover:bg-primary-400 dark:hover:text-neutral-800"
href="#"
data-email="{{ site.Params.Author.email | base64Encode }}"
data-subject="{{ replace $replySubject "\"" "'" }}">
{{ i18n "article.reply_by_email" | default "Reply by Email" }}
</a>
<noscript>
<span
class="m-1 rounded bg-neutral-300 p-1.5 text-neutral-700 dark:bg-neutral-700 dark:text-neutral-300">
{{ i18n "article.reply_by_email" | default "Reply by Email" }} (require JavaScript)
</span>
</noscript>
</strong>
{{ end }}
</div>
{{ if (.Params.showAuthorBottom | default (site.Params.article.showAuthorBottom | default false)) }}
{{ template "SingleAuthor" . }}
{{ end }}
{{ partial "series/series-closed.html" . }}
{{ partial "sharing-links.html" . }}
{{ partial "related.html" . }}
</div>
</section>
{{/* Footer */}}
<footer class="pt-8 max-w-prose print:hidden">
{{ partial "article-pagination.html" . }}
{{ if .Params.showComments | default (site.Params.article.showComments | default false) }}
{{ if templates.Exists "partials/comments.html" }}
<div class="pt-3">
<hr class="border-dotted border-neutral-300 dark:border-neutral-600" />
<div class="pt-3">
{{ partial "comments.html" . }}
</div>
</div>
{{ else }}
{{ warnf "[BLOWFISH] Comments are enabled for %s but no comments partial exists." .File.Path }}
{{ end }}
{{ end }}
</footer>
</article>
{{ end }}
{{ define "SingleAuthor" }}
{{ $authorsData := hugo.Data.authors }}
{{ $taxonomies := site.Taxonomies.authors }}
{{ $baseURL := site.BaseURL }}
{{ $taxonomyLink := 0 }}
{{ $showAuthor := 0 }}
{{ $isAuthorBottom := (.Params.showAuthorBottom | default ( site.Params.article.showAuthorBottom | default false)) }}
{{ if not (strings.HasSuffix $baseURL "/") }}
{{ $baseURL = delimit (slice $baseURL "/") "" }}
{{ end }}
{{ if .Params.showAuthor | default (site.Params.article.showAuthor | default true) }}
{{ $showAuthor = 1 }}
{{ partial "author.html" . }}
{{ end }}
{{ range $author := .Page.Params.authors }}
{{ $authorData := index $authorsData $author }}
{{- if $authorData -}}
{{ range $taxonomyname, $taxonomy := $taxonomies }}
{{ if (eq $taxonomyname $author) }}
{{ $taxonomyLink = delimit (slice $baseURL "authors/" $author "/") "" }}
{{ end }}
{{ end }}
{{ $finalLink := $taxonomyLink }}
{{ $currentLang := site.Language.Name }}
{{ if eq site.LanguagePrefix "" }}
{{ $finalLink = printf "%sauthors/%s/" $baseURL $author }}
{{ else }}
{{ $finalLink = printf "%s%s/authors/%s/" $baseURL $currentLang $author }}
{{ end }}
{{ partial "author-extra.html" (dict "context" . "data" $authorData "link" $finalLink) }}
{{- end -}}
{{ end }}
{{ if or $taxonomyLink $showAuthor }}
<div class="{{ cond $isAuthorBottom "mb-10" "mb-5" }}"></div>
{{ end }}
{{ end }}