Modularize <head> and baseof templates (#639)

* Add extension points for template customization

Today, if you use the hugo-coder template, you can't modify <head>
without having to write the entire template from scratch. This makes
small point modifications or adjustments impractical and means that
downstream consumers will quickly be out of sync with upstream
changes.

To remedy this, we split up the template into more modular extension
points. This commit adds one extension point to <body> for arbitrary
scripting and several more to <head> for different existing
touchpoints.

* Add John Feminella to CONTRIBUTORS.md

* Modularize home.html template

Co-authored-by: Luiz F. A. de Prá <luizdepra@users.noreply.github.com>
This commit is contained in:
John Feminella
2022-05-29 14:46:30 -04:00
committed by GitHub
parent 5e4e500cdf
commit 3762d3a24c
16 changed files with 200 additions and 172 deletions

View File

@@ -1,160 +1,71 @@
<!DOCTYPE html>
<html lang="{{ .Site.Language.Lang }}">
{{ partial "head.html" . }}
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="color-scheme" content="light dark">
{{ $csClass := "colorscheme-light" }}
{{ if eq .Site.Params.colorScheme "dark" }}
{{ $csClass = "colorscheme-dark" }}
{{ else if eq .Site.Params.colorScheme "auto" }}
{{ $csClass = "colorscheme-auto" }}
{{ end }}
{{ if .Site.Params.csp }}
{{ partial "csp.html" . }}
{{ end }}
<body class="preload-transitions {{ $csClass }}{{ if .Site.Params.rtl }} rtl{{ end }}">
{{ partial "float" . }}
<main class="wrapper">
{{ partial "header.html" . }}
{{ with .Site.Params.author }}<meta name="author" content="{{ . }}">{{ end }}
<meta name="description" content="{{ .Description | default (.Summary | default .Site.Params.description ) }}">
<meta name="keywords" content="{{ (delimit .Keywords ",") | default .Site.Params.keywords }}">
<div class="content">
{{ block "content" . }}{{ end }}
</div>
{{ template "_internal/twitter_cards.html" . }}
{{ template "_internal/opengraph.html" . }}
{{ partial "footer.html" . }}
</main>
<title>{{ block "title" . }}{{ .Site.Title }}{{ end }}</title>
{{ if .Permalink }}
<link rel="canonical" href="{{ .Permalink }}">
{{ end }}
<link rel="preload" href="/fonts/forkawesome-webfont.woff2?v=1.2.0" as="font" type="font/woff2" crossorigin>
{{ if .Site.IsServer }}
{{ $cssOpts := (dict "targetPath" "css/coder.css" "enableSourceMap" true ) }}
{{ $styles := resources.Get "scss/coder.scss" | resources.ExecuteAsTemplate "style.coder.css" . | toCSS $cssOpts }}
<link rel="stylesheet" href="{{ $styles.RelPermalink }}" media="screen">
{{ else }}
{{ $cssOpts := (dict "targetPath" "css/coder.css" ) }}
{{ $styles := resources.Get "scss/coder.scss" | resources.ExecuteAsTemplate "style.coder.css" . | toCSS $cssOpts | minify | fingerprint }}
<link rel="stylesheet" href="{{ $styles.RelPermalink }}" integrity="{{ $styles.Data.Integrity }}" crossorigin="anonymous" media="screen" />
{{ end }}
{{ if .Site.Params.rtl }}
{{ if .Site.IsServer }}
{{ $cssOpts := (dict "targetPath" "css/coder-rtl.css" "enableSourceMap" true ) }}
{{ $styles := resources.Get "scss/coder-rtl.scss" | resources.ExecuteAsTemplate "style.coder-rtl.css" . | toCSS $cssOpts }}
<link rel="stylesheet" href="{{ $styles.RelPermalink }}" media="screen">
{{ else }}
{{ $cssOpts := (dict "targetPath" "css/coder-rtl.css" ) }}
{{ $styles := resources.Get "scss/coder-rtl.scss" | resources.ExecuteAsTemplate "style.coder-rtl.css" . | toCSS $cssOpts | minify | fingerprint }}
<link rel="stylesheet" href="{{ $styles.RelPermalink }}" integrity="{{ $styles.Data.Integrity }}" crossorigin="anonymous" media="screen" />
{{ end }}
{{ end }}
{{ if or (eq .Site.Params.colorScheme "auto") (eq .Site.Params.colorScheme "dark") }}
{{ if .Site.IsServer }}
{{ $cssOpts := (dict "targetPath" "css/coder-dark.css" "enableSourceMap" true ) }}
{{ $styles := resources.Get "scss/coder-dark.scss" | resources.ExecuteAsTemplate "style.coder-dark.css" . | toCSS $cssOpts }}
<link rel="stylesheet" href="{{ $styles.RelPermalink }}" media="screen">
{{ else }}
{{ $cssOpts := (dict "targetPath" "css/coder-dark.css" ) }}
{{ $styles := resources.Get "scss/coder-dark.scss" | resources.ExecuteAsTemplate "style.coder-dark.css" . | toCSS $cssOpts | minify | fingerprint }}
<link rel="stylesheet" href="{{ $styles.RelPermalink }}" integrity="{{ $styles.Data.Integrity }}" crossorigin="anonymous" media="screen" />
{{ end }}
{{ end }}
{{ range .Site.Params.customCSS }}
{{ if $.Site.IsServer }}
{{ $styles := resources.Get . }}
<link rel="stylesheet" href="{{ $styles.RelPermalink }}" media="screen">
{{ else }}
{{ $styles := resources.Get . | minify | fingerprint }}
<link rel="stylesheet" href="{{ $styles.RelPermalink }}" integrity="{{ $styles.Data.Integrity }}" crossorigin="anonymous" media="screen" />
{{ end }}
{{ end }}
{{ range .Site.Params.customSCSS }}
{{/* We don't change the targetPath to because it's transparent to users */}}
{{ if $.Site.IsServer }}
{{ $cssOpts := (dict "enableSourceMap" true ) }}
{{ $styles := resources.Get . | toCSS $cssOpts }}
<link rel="stylesheet" href="{{ $styles.RelPermalink }}" media="screen">
{{ else }}
{{ $styles := resources.Get . | toCSS | minify | fingerprint }}
<link rel="stylesheet" href="{{ $styles.RelPermalink }}" integrity="{{ $styles.Data.Integrity }}" crossorigin="anonymous" media="screen" />
{{ end }}
{{ end }}
<link rel="icon" type="image/png" href="{{ .Site.Params.favicon_32 | default "/images/favicon-32x32.png" | relURL }}" sizes="32x32">
<link rel="icon" type="image/png" href="{{ .Site.Params.favicon_16 | default "/images/favicon-16x16.png" | relURL }}" sizes="16x16">
<link rel="apple-touch-icon" href="{{ .Site.Params.touchicon | default "/images/apple-touch-icon.png" | relURL }}">
<link rel="apple-touch-icon" sizes="180x180" href="{{ .Site.Params.touchicon | default "/images/apple-touch-icon.png" | relURL }}">
{{ range .AlternativeOutputFormats -}}
{{ printf `<link rel="%s" type="%s" href="%s" title="%s" />` .Rel .MediaType.Type .RelPermalink $.Site.Title | safeHTML }}
{{ end -}}
{{ hugo.Generator }}
</head>
{{ $csClass := "colorscheme-light" }}
{{ if eq .Site.Params.colorScheme "dark" }}
{{ $csClass = "colorscheme-dark" }}
{{ else if eq .Site.Params.colorScheme "auto" }}
{{ $csClass = "colorscheme-auto" }}
{{ if .Site.IsServer }}
{{ $script := resources.Get "js/coder.js" }}
<script src="{{ $script.RelPermalink }}"></script>
{{ else }}
{{ $script := resources.Get "js/coder.js" | minify | fingerprint }}
<script src="{{ $script.RelPermalink }}" integrity="{{ $script.Data.Integrity }}"></script>
{{ end }}
<body class="preload-transitions {{ $csClass }}{{ if .Site.Params.rtl }} rtl{{ end }}">
{{ partial "float" . }}
<main class="wrapper">
{{ partial "header.html" . }}
<div class="content">
{{ block "content" . }}{{ end }}
</div>
{{ range .Site.Params.customJS }}
{{ if $.Site.IsServer }}
{{ $script := resources.Get . }}
<script src="{{ $script.RelPermalink }}"></script>
{{ else }}
{{ $script := resources.Get . | minify | fingerprint }}
<script src="{{ $script.RelPermalink }}" integrity="{{ $script.Data.Integrity }}"></script>
{{ end }}
{{ end }}
{{ partial "footer.html" . }}
</main>
{{ template "_internal/google_analytics.html" . }}
{{ if .Site.IsServer }}
{{ $script := resources.Get "js/coder.js" }}
<script src="{{ $script.RelPermalink }}"></script>
{{ else }}
{{ $script := resources.Get "js/coder.js" | minify | fingerprint }}
<script src="{{ $script.RelPermalink }}" integrity="{{ $script.Data.Integrity }}"></script>
{{ end }}
{{ if and .Site.Params.fathomAnalytics .Site.Params.fathomAnalytics.siteID }}
{{- partial "analytics/fathom" . -}}
{{ end }}
{{ range .Site.Params.customJS }}
{{ if $.Site.IsServer }}
{{ $script := resources.Get . }}
<script src="{{ $script.RelPermalink }}"></script>
{{ else }}
{{ $script := resources.Get . | minify | fingerprint }}
<script src="{{ $script.RelPermalink }}" integrity="{{ $script.Data.Integrity }}"></script>
{{ end }}
{{ end }}
{{ if and .Site.Params.plausibleAnalytics .Site.Params.plausibleAnalytics.domain }}
{{- partial "analytics/plausible" . -}}
{{ end }}
{{ template "_internal/google_analytics.html" . }}
{{ if and .Site.Params.goatCounter .Site.Params.goatCounter.code }}
{{- partial "analytics/goatcounter" . -}}
{{ end }}
{{ if and .Site.Params.fathomAnalytics .Site.Params.fathomAnalytics.siteID }}
{{- partial "analytics/fathom" . -}}
{{ end }}
{{ if and .Site.Params.cloudflare .Site.Params.cloudflare.token }}
{{- partial "analytics/cloudflare" . -}}
{{ end }}
{{ if and .Site.Params.plausibleAnalytics .Site.Params.plausibleAnalytics.domain }}
{{- partial "analytics/plausible" . -}}
{{ end }}
{{ if and .Site.Params.matomo .Site.Params.matomo.serverURL }}
{{- partial "analytics/matomo" . -}}
{{ end }}
{{ if and .Site.Params.goatCounter .Site.Params.goatCounter.code }}
{{- partial "analytics/goatcounter" . -}}
{{ end }}
{{ if and .Site.Params.googleTagManager .Site.Params.googleTagManager.id }}
{{- partial "analytics/googletagmanager" . -}}
{{ end }}
{{ if and .Site.Params.cloudflare .Site.Params.cloudflare.token }}
{{- partial "analytics/cloudflare" . -}}
{{ end }}
{{ if and .Site.Params.matomo .Site.Params.matomo.serverURL }}
{{- partial "analytics/matomo" . -}}
{{ end }}
{{ if and .Site.Params.googleTagManager .Site.Params.googleTagManager.id }}
{{- partial "analytics/googletagmanager" . -}}
{{ end }}
</body>
{{- partial "body/extensions" . -}}
</body>
</html>

View File

@@ -0,0 +1,5 @@
{{/*
This extension point occurs just before the end of each page's <body> tag.
You can add further theme extensions or customizations here if needed.
*/}}

View File

@@ -0,0 +1,23 @@
<head>
{{ partial "head/meta-tags.html" . }}
<title>{{ block "title" . }}{{ .Site.Title }}{{ end }}</title>
{{ if .Permalink }}
<link rel="canonical" href="{{ .Permalink }}">
{{ end }}
{{ partialCached "head/theme-styles.html" . }}
{{ partialCached "head/color-scheme.html" . }}
{{ partialCached "head/custom-styles.html" . }}
{{ partialCached "head/custom-icons.html" . }}
{{ partialCached "head/alternative-output-formats.html" . }}
{{ partialCached "head/hugo-generator.html" . }}
{{ partial "head/extensions.html" . }}
</head>

View File

@@ -0,0 +1,3 @@
{{ range .AlternativeOutputFormats -}}
{{ printf `<link rel="%s" type="%s" href="%s" title="%s" />` .Rel .MediaType.Type .RelPermalink $.Site.Title | safeHTML }}
{{ end -}}

View File

@@ -0,0 +1,11 @@
{{ if or (eq .Site.Params.colorScheme "auto") (eq .Site.Params.colorScheme "dark") }}
{{ if .Site.IsServer }}
{{ $cssOpts := (dict "targetPath" "css/coder-dark.css" "enableSourceMap" true ) }}
{{ $styles := resources.Get "scss/coder-dark.scss" | resources.ExecuteAsTemplate "style.coder-dark.css" . | toCSS $cssOpts }}
<link rel="stylesheet" href="{{ $styles.RelPermalink }}" media="screen">
{{ else }}
{{ $cssOpts := (dict "targetPath" "css/coder-dark.css" ) }}
{{ $styles := resources.Get "scss/coder-dark.scss" | resources.ExecuteAsTemplate "style.coder-dark.css" . | toCSS $cssOpts | minify | fingerprint }}
<link rel="stylesheet" href="{{ $styles.RelPermalink }}" integrity="{{ $styles.Data.Integrity }}" crossorigin="anonymous" media="screen" />
{{ end }}
{{ end }}

View File

@@ -0,0 +1,5 @@
<link rel="icon" type="image/png" href="{{ .Site.Params.favicon_32 | default "/images/favicon-32x32.png" | relURL }}" sizes="32x32">
<link rel="icon" type="image/png" href="{{ .Site.Params.favicon_16 | default "/images/favicon-16x16.png" | relURL }}" sizes="16x16">
<link rel="apple-touch-icon" href="{{ .Site.Params.touchicon | default "/images/apple-touch-icon.png" | relURL }}">
<link rel="apple-touch-icon" sizes="180x180" href="{{ .Site.Params.touchicon | default "/images/apple-touch-icon.png" | relURL }}">

View File

@@ -0,0 +1,21 @@
{{ range .Site.Params.customCSS }}
{{ if $.Site.IsServer }}
{{ $styles := resources.Get . }}
<link rel="stylesheet" href="{{ $styles.RelPermalink }}" media="screen">
{{ else }}
{{ $styles := resources.Get . | minify | fingerprint }}
<link rel="stylesheet" href="{{ $styles.RelPermalink }}" integrity="{{ $styles.Data.Integrity }}" crossorigin="anonymous" media="screen" />
{{ end }}
{{ end }}
{{ range .Site.Params.customSCSS }}
{{/* We don't change the targetPath to because it's transparent to users */}}
{{ if $.Site.IsServer }}
{{ $cssOpts := (dict "enableSourceMap" true ) }}
{{ $styles := resources.Get . | toCSS $cssOpts }}
<link rel="stylesheet" href="{{ $styles.RelPermalink }}" media="screen">
{{ else }}
{{ $styles := resources.Get . | toCSS | minify | fingerprint }}
<link rel="stylesheet" href="{{ $styles.RelPermalink }}" integrity="{{ $styles.Data.Integrity }}" crossorigin="anonymous" media="screen" />
{{ end }}
{{ end }}

View File

@@ -0,0 +1,4 @@
{{/*
You can add further theme extensions or customizations here if they should
appear in <head>.
*/}}

View File

@@ -0,0 +1 @@
{{ hugo.Generator }}

View File

@@ -0,0 +1,15 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="color-scheme" content="light dark">
{{ if .Site.Params.csp }}
{{ partial "csp.html" . }}
{{ end }}
{{ with .Site.Params.author }}
<meta name="author" content="{{ . }}">{{ end }}
<meta name="description" content="{{ .Description | default (.Summary | default .Site.Params.description ) }}">
<meta name="keywords" content="{{ (delimit .Keywords " ,") | default .Site.Params.keywords }}">
{{ template "_internal/twitter_cards.html" . }}
{{ template "_internal/opengraph.html" . }}

View File

@@ -0,0 +1,23 @@
<link rel="preload" href="/fonts/forkawesome-webfont.woff2?v=1.2.0" as="font" type="font/woff2" crossorigin>
{{ if .Site.IsServer }}
{{ $cssOpts := (dict "targetPath" "css/coder.css" "enableSourceMap" true ) }}
{{ $styles := resources.Get "scss/coder.scss" | resources.ExecuteAsTemplate "style.coder.css" . | toCSS $cssOpts }}
<link rel="stylesheet" href="{{ $styles.RelPermalink }}" media="screen">
{{ else }}
{{ $cssOpts := (dict "targetPath" "css/coder.css" ) }}
{{ $styles := resources.Get "scss/coder.scss" | resources.ExecuteAsTemplate "style.coder.css" . | toCSS $cssOpts | minify | fingerprint }}
<link rel="stylesheet" href="{{ $styles.RelPermalink }}" integrity="{{ $styles.Data.Integrity }}" crossorigin="anonymous" media="screen" />
{{ end }}
{{ if .Site.Params.rtl }}
{{ if .Site.IsServer }}
{{ $cssOpts := (dict "targetPath" "css/coder-rtl.css" "enableSourceMap" true ) }}
{{ $styles := resources.Get "scss/coder-rtl.scss" | resources.ExecuteAsTemplate "style.coder-rtl.css" . | toCSS $cssOpts }}
<link rel="stylesheet" href="{{ $styles.RelPermalink }}" media="screen">
{{ else }}
{{ $cssOpts := (dict "targetPath" "css/coder-rtl.css" ) }}
{{ $styles := resources.Get "scss/coder-rtl.scss" | resources.ExecuteAsTemplate "style.coder-rtl.css" . | toCSS $cssOpts | minify | fingerprint }}
<link rel="stylesheet" href="{{ $styles.RelPermalink }}" integrity="{{ $styles.Data.Integrity }}" crossorigin="anonymous" media="screen" />
{{ end }}
{{ end }}

View File

@@ -1,35 +1,9 @@
<section class="container centered">
<div class="about">
{{ if and (isset .Site.Params "avatarurl") (not (isset .Site.Params "gravatar")) }}
{{ with .Site.Params.avatarURL }}
<div class="avatar"><img src="{{ . | relURL }}" alt="avatar"></div>
{{ end }}
{{ end }}
{{ with .Site.Params.gravatar }}
<div class="avatar"><img src="https://www.gravatar.com/avatar/{{md5 .}}?s=240&d=mp" alt="gravatar"></div>
{{ end }}
<h1>{{ .Site.Params.author }}</h1>
{{ if reflect.IsSlice .Site.Params.info }}
<h2>{{ range .Site.Params.info }}{{.}}<br>{{ end}}</h2>
{{ else }}
<h2>{{ .Site.Params.info }}</h2>
{{ end }}
{{ with .Site.Params.social }}
<ul>
{{ range sort . "weight" }}
{{ if .icon }}
<li>
<a href="{{ .url | safeURL }}" aria-label="{{ .name }}" {{ if .rel }}rel="{{ .rel }}"{{ end }} {{ if .target }}target="{{ .target }}"{{ end }} {{ if .type }}type="{{ .type }}"{{ end }}>
<i class="{{ .icon }}" aria-hidden="true"></i>
</a>
</li>
{{ else }}
<li>
<a href="{{ .url | safeURL }}" aria-label="{{ .name }}" {{ if .rel }}rel="{{ .rel }}"{{ end }} {{ if .target }}target="{{ .target }}"{{ end }}>{{ .name }}</a>
</li>
{{ end }}
{{ end }}
</ul>
{{ end }}
{{ partialCached "home/avatar.html" . }}
{{ partialCached "home/author.html" . }}
{{ partialCached "home/social.html" . }}
</div>
</section>

View File

@@ -0,0 +1,6 @@
<h1>{{ .Site.Params.author }}</h1>
{{ if reflect.IsSlice .Site.Params.info }}
<h2>{{ range .Site.Params.info }}{{.}}<br>{{ end}}</h2>
{{ else }}
<h2>{{ .Site.Params.info }}</h2>
{{ end }}

View File

@@ -0,0 +1,8 @@
{{ if and (isset .Site.Params "avatarurl") (not (isset .Site.Params "gravatar")) }}
{{ with .Site.Params.avatarURL }}
<div class="avatar"><img src="{{ . | relURL }}" alt="avatar"></div>
{{ end }}
{{ end }}
{{ with .Site.Params.gravatar }}
<div class="avatar"><img src="https://www.gravatar.com/avatar/{{md5 .}}?s=240&d=mp" alt="gravatar"></div>
{{ end }}

View File

@@ -0,0 +1,17 @@
{{ with .Site.Params.social }}
<ul>
{{ range sort . "weight" }}
{{ if .icon }}
<li>
<a href="{{ .url | safeURL }}" aria-label="{{ .name }}" {{ if .rel }}rel="{{ .rel }}"{{ end }} {{ if .target }}target="{{ .target }}"{{ end }} {{ if .type }}type="{{ .type }}"{{ end }}>
<i class="{{ .icon }}" aria-hidden="true"></i>
</a>
</li>
{{ else }}
<li>
<a href="{{ .url | safeURL }}" aria-label="{{ .name }}" {{ if .rel }}rel="{{ .rel }}"{{ end }} {{ if .target }}target="{{ .target }}"{{ end }}>{{ .name }}</a>
</li>
{{ end }}
{{ end }}
</ul>
{{ end }}