diff options
| -rw-r--r-- | assets/js/main.js | 38 | ||||
| -rw-r--r-- | hugo.toml | 6 | ||||
| -rw-r--r-- | i18n/de.toml | 4 | ||||
| -rw-r--r-- | i18n/en.toml | 4 | ||||
| -rw-r--r-- | layouts/_default/baseof.html | 14 | ||||
| -rw-r--r-- | layouts/partials/footer.html | 4 | ||||
| -rw-r--r-- | layouts/partials/head/css.html | 3 | ||||
| -rw-r--r-- | layouts/partials/head/js.html | 7 | ||||
| -rw-r--r-- | layouts/partials/header.html | 9 | ||||
| -rw-r--r-- | layouts/partials/list/recent-posts.html | 8 | ||||
| -rw-r--r-- | layouts/partials/selectLanguage.html | 39 | ||||
| -rw-r--r-- | layouts/partials/selectTheme.html | 15 | ||||
| -rw-r--r-- | layouts/partials/single/tags.html | 2 | ||||
| -rw-r--r-- | layouts/robots.txt | 4 | ||||
| -rw-r--r-- | layouts/tags/list.html | 2 | ||||
| -rw-r--r-- | layouts/tags/term.html | 2 | ||||
| -rw-r--r-- | static/flags/de.svg | 7 | ||||
| -rw-r--r-- | static/flags/en.svg | 24 | ||||
| -rw-r--r-- | static/rss.svg | 7 |
19 files changed, 180 insertions, 19 deletions
diff --git a/assets/js/main.js b/assets/js/main.js index e69de29..c3218f1 100644 --- a/assets/js/main.js +++ b/assets/js/main.js @@ -0,0 +1,38 @@ +(function () { + "use strict"; + + // TOGGLE DARK/LIGHT MODE + function initThemeToggle() { + const rootHtml = document.documentElement; + const toggleThemeBtn = document.getElementById("theme-toggle"); + + // If no saved theme, determine user preference, otherwise default to light + const savedTheme = localStorage.getItem("theme"); + const prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches; + const initialTheme = savedTheme ?? (prefersDark ? "dark" : "light"); + + function setTheme(theme) { + const isDarkMode = theme === "dark"; + // toggleThemeBtn dataset comes with translated labels for site's language + const label = isDarkMode ? toggleThemeBtn.dataset.labelLight : toggleThemeBtn.dataset.labelDark; + + rootHtml.setAttribute("data-theme", theme); + toggleThemeBtn.setAttribute("aria-label", label); + toggleThemeBtn.querySelector(".icon-moon").style.display = isDarkMode ? "none" : "block"; + toggleThemeBtn.querySelector(".icon-sun").style.display = isDarkMode ? "block" : "none"; + } + + // Apply initial theme + setTheme(initialTheme); + + // Change theme on click and save user's choice + toggleThemeBtn.addEventListener("click", () => { + const newTheme = rootHtml.getAttribute("data-theme") === "dark" ? "light" : "dark"; + setTheme(newTheme); + localStorage.setItem("theme", newTheme); + }); + } + + initThemeToggle(); +})(); + @@ -2,6 +2,8 @@ baseURL = "https://example.org/" languageCode = "en-US" title = "Your Website Name" +enableRobotsTXT = true # Copy this line to your project's hugo.toml + [frontmatter] # {{ .Date }} will look for publishDate or aliases first, fallback to date date = ["publishdate", "pubdate", "published", "date"] @@ -32,8 +34,8 @@ title = "Your Website Name" weight = 40 [params] + # Set logo to empty string in your project's hugo.toml if you want to display site.Title instead + logo = "/logo.svg" [params.author] name = "Your Name" email = "your@email.com" - # Set logo to empty string in your hugo.toml if you want to display site.Title instead - logo = "logo.svg" diff --git a/i18n/de.toml b/i18n/de.toml index fb65774..bda9260 100644 --- a/i18n/de.toml +++ b/i18n/de.toml @@ -40,6 +40,10 @@ posts_list = "Artikel mit diesem Schlagwort:" tagged_with = "Getaggt mit:" +[theme] + toggle_dark = "Dunklen Modus aktivieren" + toggle_light = "Hellen Modus aktivieren" + [404] title = "Seite nicht gefunden" description = "Die angefragte Seite existiert nicht." diff --git a/i18n/en.toml b/i18n/en.toml index 690db3c..fbf4cb8 100644 --- a/i18n/en.toml +++ b/i18n/en.toml @@ -40,6 +40,10 @@ posts_list = "Posts with this tag:" tagged_with = "Tagged with:" +[theme] + toggle_dark = "Switch to dark mode" + toggle_light = "Switch to light mode" + [404] title = "Page not found" description = "The requested page does not exist." diff --git a/layouts/_default/baseof.html b/layouts/_default/baseof.html index 3c4865b..99f09d9 100644 --- a/layouts/_default/baseof.html +++ b/layouts/_default/baseof.html @@ -3,7 +3,7 @@ <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> - <title>{{ block "title" . }}{{ if .Title }}{{ .Title }} | {{ end }}{{ site.Title }}{{ end }}</title> + <title>{{ block "title" . }}{{ if and (not .IsHome) .Title }}{{ .Title }} | {{ end }}{{ site.Title }}{{ end }}</title> {{- with .Description }} <meta name="description" content="{{ . }}"> @@ -24,13 +24,25 @@ {{- end }} {{- end }} + {{- with .OutputFormats.Get "rss" }} + {{- printf `<link rel=%q type=%q href=%q title=%q>` .Rel .MediaType.Type .Permalink site.Title | safeHTML }} + {{- end }} + {{- partial "head/meta.html" . }} {{- partial "head/seo.html" . }} + <!-- Prevent FOUC, default light mode --> + <script> + const storedTheme = localStorage.getItem("theme"); + const prefDarkmode = window.matchMedia("(prefers-color-scheme: dark)").matches; + document.documentElement.setAttribute("data-theme", storedTheme ?? (prefDarkmode ? "dark" : "light")); + </script> {{- partialCached "head/css.html" . }} + {{ partialCached "head/js.html" . }} </head> <body class="{{ .Type | default "page" }}"> {{ partial "header.html" . }} + {{ partial "navmenu.html" (dict "menuID" "main" "page" .) }} <main id="main-content" class="site__main" role="main"> {{ block "main" . }}{{ end }} diff --git a/layouts/partials/footer.html b/layouts/partials/footer.html index a934e59..7225d09 100644 --- a/layouts/partials/footer.html +++ b/layouts/partials/footer.html @@ -1,3 +1,7 @@ <footer class="site__footer" role="contentinfo"> <p>© {{ now.Year }} {{ site.Params.author.name }}</p> + + <a href="{{ site.Home.Permalink }}index.xml" aria-label="RSS Feed" title="RSS Feed"> + <img src="/rss.svg" class="icon icon-rss" style="max-height: 32px" alt="RSS Feed" aria-hidden="true"> + </a> </footer> diff --git a/layouts/partials/head/css.html b/layouts/partials/head/css.html index 8e6c145..c3c6e33 100644 --- a/layouts/partials/head/css.html +++ b/layouts/partials/head/css.html @@ -6,5 +6,4 @@ <link rel="stylesheet" href="{{ .RelPermalink }}" integrity="{{ .Data.Integrity }}" crossorigin="anonymous"> {{- end }} {{- end }} -{{- end }} - +{{- end -}} diff --git a/layouts/partials/head/js.html b/layouts/partials/head/js.html index 6123e7f..0baf831 100644 --- a/layouts/partials/head/js.html +++ b/layouts/partials/head/js.html @@ -6,12 +6,11 @@ }} {{- with . | js.Build $opts }} {{- if hugo.IsDevelopment }} - <script src="{{ .RelPermalink }}"></script> + <script defer src="{{ .RelPermalink }}"></script> {{- else }} {{- with . | fingerprint }} - <script src="{{ .RelPermalink }}" integrity="{{ .Data.Integrity }}" crossorigin="anonymous"></script> + <script defer src="{{ .RelPermalink }}" integrity="{{ .Data.Integrity }}" crossorigin="anonymous"></script> {{- end }} {{- end }} {{- end }} -{{- end }} - +{{- end -}} diff --git a/layouts/partials/header.html b/layouts/partials/header.html index 7b728a6..9195345 100644 --- a/layouts/partials/header.html +++ b/layouts/partials/header.html @@ -1,7 +1,12 @@ <header class="site__header" role="banner"> <a href="{{ site.Home.RelPermalink }}" rel="home" class="site-{{ if site.Params.logo }}logo{{ else }}title{{ end }}"> - {{ with site.Params.logo}}{{ . }}{{ else }}{{ site.Title }}{{end}} + {{- with site.Params.logo }} + <img src="{{ . }}" alt="{{ site.Title }}" /> + {{- else }} + {{ site.Title }} + {{- end }} </a> - {{ partial "navmenu.html" (dict "menuID" "main" "page" .) }} + {{- partial "selectTheme.html" . }} + {{- partial "selectLanguage.html" . }} </header> diff --git a/layouts/partials/list/recent-posts.html b/layouts/partials/list/recent-posts.html index c1075e2..d774138 100644 --- a/layouts/partials/list/recent-posts.html +++ b/layouts/partials/list/recent-posts.html @@ -1,5 +1,5 @@ {{- /* -List of Recent Posts. Accepts a dict with the following optional parameters: +List of specified number of the most recent and published posts. Accepts a dict with the following optional parameters: @context {int} count: Number of posts to display (default: 10). @context {string} title: Section title (default: "Recent Articles"). @@ -11,7 +11,6 @@ List of Recent Posts. Accepts a dict with the following optional parameters: {{- $count := .count | default 10 -}} {{- $title := .title | default (lang.Translate "posts.recent" | default "Recent Articles") -}} {{- $showViewAll := .show_view_all | default true -}} - <section class="recent-posts" aria-labelledby="recent-posts-heading"> <header class="recent-posts__header"> <h2 id="recent-posts-heading" class="recent-posts__title"> @@ -19,7 +18,6 @@ List of Recent Posts. Accepts a dict with the following optional parameters: </h2> </header> - {{- /* Get published 10 most recent posts */ -}} {{- $recentPosts := where site.RegularPages "Date" "!=" nil }} {{- $recentPosts = where $recentPosts ".Date" "le" now }} {{- $recentPosts = first $count $recentPosts }} @@ -35,14 +33,14 @@ List of Recent Posts. Accepts a dict with the following optional parameters: </ul> {{- if $showViewAll }} - <a href="{{ with site.GetPage "/posts" }}{{ .RelPermalink }}{{ else }}/posts/{{ end }}" class="recent_posts__view-all-link"> + <a href="{{ with site.GetPage "/posts" }}{{ .RelPermalink }}{{ else }}/posts/{{ end }}" class="recent-posts__view-all-link"> {{ lang.Translate "posts.view_all" | default "View all posts" }} </a> {{- end }} </div> {{ else }} <div class="recent-posts__empty"> - <p class="recent_posts__empty-message"> + <p class="recent-posts__empty-message"> {{ lang.Translate "list.empty" | default "No posts in this section." }} </p> </div> diff --git a/layouts/partials/selectLanguage.html b/layouts/partials/selectLanguage.html new file mode 100644 index 0000000..752b605 --- /dev/null +++ b/layouts/partials/selectLanguage.html @@ -0,0 +1,39 @@ +{{- /* +Selection of available languages. +Dynamically links to corresponding page/post if a translated version exists, otherwise to homepage of that language as fallback. + +Note: `$activeLang` and `$allTranslationsForPage` are technically unnecessary variables, their values could be called with `$.Lang` and `$.Translations` respectively. +These variables - together with the explicit naming of `.` context variables in other cases - were chosen for better readability and understandability of the code. +*/ -}} + +{{- if gt (len site.Languages) 1 }} +{{- $activeLang := .Lang }} +{{- $allTranslationsForPage := .Translations }} + <div class="language-select" style="background-color: yellow;"> + <ul> + {{- range site.Languages }} + {{- $currLangSlice := . }} + {{- if ne $currLangSlice.Lang $activeLang }} + {{- $targetPage := index (where site.Home.Translations "Lang" $currLangSlice.Lang) 0 }} + {{- $translatedPage := index (where $allTranslationsForPage "Lang" $currLangSlice.Lang) 0 }} + {{- if $translatedPage }}{{- $targetPage = $translatedPage }}{{- end }} + <li> + <a + href="{{ $targetPage.RelPermalink }}" + hreflang="{{ $targetPage.Lang }}" + aria-label="{{ $targetPage.LinkTitle }} ({{ or $targetPage.Language.LanguageName $targetPage.Lang }})" + > + <img + src="/flags/{{ $targetPage.Lang }}.svg" + class="icon icon-flag" + style="max-height: 32px" + alt="{{ $targetPage.LinkTitle }} ({{ or $targetPage.Language.LanguageName $targetPage.Lang }})" + aria-hidden="true" + /> + </a> + </li> + {{- end }} + {{- end }} + </ul> + </div> +{{- end }} diff --git a/layouts/partials/selectTheme.html b/layouts/partials/selectTheme.html new file mode 100644 index 0000000..e9114cc --- /dev/null +++ b/layouts/partials/selectTheme.html @@ -0,0 +1,15 @@ +<button + type="button" + id="theme-toggle" + class="theme-toggle" + aria-label="{{ lang.Translate "theme.toggle_dark" }}" + data-label-dark="{{ lang.Translate "theme.toggle_dark" }}" + data-label-light="{{ lang.Translate "theme.toggle_light" }}" +> + <svg class="icon icon-moon" xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 16 16" aria-hidden="true" style="display: none;"> + <path fill="currentColor" d="M6 .278a.768.768 0 0 1 .08.858a7.208 7.208 0 0 0-.878 3.46c0 4.021 3.278 7.277 7.318 7.277c.527 0 1.04-.055 1.533-.16a.787.787 0 0 1 .81.316a.733.733 0 0 1-.031.893A8.349 8.349 0 0 1 8.344 16C3.734 16 0 12.286 0 7.71C0 4.266 2.114 1.312 5.124.06A.752.752 0 0 1 6 .278z"></path> + </svg> + <svg class="icon icon-sun" xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 16 16" aria-hidden="true"> + <path fill="currentColor" d="M8 12a4 4 0 1 0 0-8a4 4 0 0 0 0 8zM8 0a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 0zm0 13a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 13zm8-5a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2a.5.5 0 0 1 .5.5zM3 8a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2A.5.5 0 0 1 3 8zm10.657-5.657a.5.5 0 0 1 0 .707l-1.414 1.415a.5.5 0 1 1-.707-.708l1.414-1.414a.5.5 0 0 1 .707 0zm-9.193 9.193a.5.5 0 0 1 0 .707L3.05 13.657a.5.5 0 0 1-.707-.707l1.414-1.414a.5.5 0 0 1 .707 0zm9.193 2.121a.5.5 0 0 1-.707 0l-1.414-1.414a.5.5 0 0 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .707zM4.464 4.465a.5.5 0 0 1-.707 0L2.343 3.05a.5.5 0 1 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .708z"/> + </svg> +</button> diff --git a/layouts/partials/single/tags.html b/layouts/partials/single/tags.html index e90b909..f9c8105 100644 --- a/layouts/partials/single/tags.html +++ b/layouts/partials/single/tags.html @@ -7,7 +7,7 @@ <ul class="post__tags-list"> {{- range . }} <li class="post__tags-item"> - <a href="{{ .RelPermalink }}" class="post__tags-link" rel="tag"> + <a href="{{ .RelPermalink }}" class="post__tags-link" rel="tag"> {{ .Title }} </a> </li> diff --git a/layouts/robots.txt b/layouts/robots.txt new file mode 100644 index 0000000..fd9192c --- /dev/null +++ b/layouts/robots.txt @@ -0,0 +1,4 @@ +User-agent: * +Disallow: + +Sitemap: {{ site.BaseURL }}/sitemap.xml diff --git a/layouts/tags/list.html b/layouts/tags/list.html index d3af0e4..bfe9479 100644 --- a/layouts/tags/list.html +++ b/layouts/tags/list.html @@ -12,7 +12,7 @@ <section class="tags-index__content" aria-label="{{ lang.Translate "tags.all" | default "All Tags" }}"> {{- with site.Taxonomies.tags }} - {{ $tags := .Alphabetical }} + {{- $tags := .Alphabetical }} <ul class="tags-index__list"> {{- range $tags }} <li class="tags-index__item"> diff --git a/layouts/tags/term.html b/layouts/tags/term.html index e107123..1e0dba7 100644 --- a/layouts/tags/term.html +++ b/layouts/tags/term.html @@ -33,7 +33,7 @@ <footer class="tag-page__footer"> <nav class="tag-page__navigation" aria-label="{{ lang.Translate "tags.navigation" | default "Tag navigation" }}"> - <a href="{{ "/tags/" | relURL }}" class="tag-page__back-link"> + <a href="{{ .Parent.RelPermalink }}" class="tag-page__list-all-link"> ← {{ lang.Translate "tags.all_tags" | default "All tags" }} </a> </nav> diff --git a/static/flags/de.svg b/static/flags/de.svg new file mode 100644 index 0000000..9fc1a16 --- /dev/null +++ b/static/flags/de.svg @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Source: SVG Repo, https://www.svgrepo.com/svg/405490/flag-for-flag-germany --> +<svg width="800px" height="800px" viewBox="0 0 36 36" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--twemoji" preserveAspectRatio="xMidYMid meet"> + <path fill="#FFCD05" d="M0 27a4 4 0 0 0 4 4h28a4 4 0 0 0 4-4v-4H0v4z"></path> + <path fill="#ED1F24" d="M0 14h36v9H0z"></path> + <path fill="#141414" d="M32 5H4a4 4 0 0 0-4 4v5h36V9a4 4 0 0 0-4-4z"></path> +</svg> diff --git a/static/flags/en.svg b/static/flags/en.svg new file mode 100644 index 0000000..0ea496a --- /dev/null +++ b/static/flags/en.svg @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Source: SVG Repo, https://www.svgrepo.com/svg/508511/flag-gb --> +<svg width="800px" height="800px" viewBox="0 -4 28 28" fill="none" xmlns="http://www.w3.org/2000/svg"> + <g clip-path="url(#clip0_503_2952)"> + <rect width="28" height="20" rx="2" fill="white"/> + <mask id="mask0_503_2952" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="28" height="20"> + <rect width="28" height="20" rx="2" fill="white"/> + </mask> + <g mask="url(#mask0_503_2952)"> + <rect width="28" height="20" fill="#0A17A7"/> + <path fill-rule="evenodd" clip-rule="evenodd" d="M-1.28244 -1.91644L10.6667 6.14335V-1.33333H17.3334V6.14335L29.2825 -1.91644L30.7737 0.294324L21.3263 6.66667H28V13.3333H21.3263L30.7737 19.7057L29.2825 21.9165L17.3334 13.8567V21.3333H10.6667V13.8567L-1.28244 21.9165L-2.77362 19.7057L6.67377 13.3333H2.95639e-05V6.66667H6.67377L-2.77362 0.294324L-1.28244 -1.91644Z" fill="white"/>
+ <path d="M18.668 6.33219L31.3333 -2" stroke="#DB1F35" stroke-width="0.666667" stroke-linecap="round"/> + <path d="M20.0128 13.6975L31.3666 21.3503" stroke="#DB1F35" stroke-width="0.666667" stroke-linecap="round"/> + <path d="M8.00555 6.31046L-3.83746 -1.67099" stroke="#DB1F35" stroke-width="0.666667" stroke-linecap="round"/> + <path d="M9.29006 13.6049L-3.83746 22.3105" stroke="#DB1F35" stroke-width="0.666667" stroke-linecap="round"/> + <path fill-rule="evenodd" clip-rule="evenodd" d="M0 12H12V20H16V12H28V8H16V0H12V8H0V12Z" fill="#E6273E"/> + </g> + </g> + <defs> + <clipPath id="clip0_503_2952"> + <rect width="28" height="20" rx="2" fill="white"/> + </clipPath> + </defs> +</svg> diff --git a/static/rss.svg b/static/rss.svg new file mode 100644 index 0000000..9dbdb73 --- /dev/null +++ b/static/rss.svg @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Source: SVG Repo, https://www.svgrepo.com/svg/349492/rss --> +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"> + <rect width="512" height="512" rx="15%" fill="#f80"/> + <circle cx="145" cy="367" r="35" fill="#ffffff"/> + <path fill="none" stroke="#ffffff" stroke-width="60" d="M109 241c89 0 162 73 162 162m114 0c0-152-124-276-276-276"/> +</svg> |
