Block > v12.0.0
66 min (13919 Wörter, 94487 Zeichen)
Inhaltsverzeichnis
Hinweis
Dieser Beitrag wird ständig aktualisiert, erkennbar an den Updates 😉
Yeah, ich bin zurueck 🎉
Das waren lange 3 Jahre, aber ich glaube es hat sich gelohnt
😊
Angefangen hat alles Anfang 2019 mit Treat Your Blog as Code bei noqqe .
Ich war direkt Feuer und Flamme.
Tooling / Gedanken #
Allerdings alles der Reihe nach, denn Tooling ist ebenfalls wichtig, und
Hugo kannte ich damals noch nicht. CI/CD nutzten wir auf Arbeit
schon mit GitLab , markdownlint
war mir ebenfalls
neu.
Statische Generatoren #
Bisher hatte ich meine Website immer nur mit selbst geschriebenen Bashscripts in einem selbst gehosteten Gitrepo verwaltet. Das Bash nicht die beste Loesung ist, war mir bewusst, denn Bilder einpflegen erforderte immer etwas Aufwand und die Laufzeit zum Bauen der Website mit allen Komponenten war auch eher unschoen. Zudem nicht wirklich crossplattform und teilweise buggy …
Klar war mir, dass ich immer noch eine statische Website haben wollte, nur eben mit einem besseren/schnelleren/sauberen “Backend”.
Mein erster Schritt war daher die Suche nach einem geeigneten Generator und ein bisschen auch die Ueberlegung, was ich ueberhaupt noch veraendern oder neu einbinden wollte. Laut diesen Listen gibt es ja so einige Generatoren …
Ins Auge gefasst hatte ich damals konkret:
-
Hugo ist in Go geschrieben und erschien 2013. Ueber Hugo bin ich oefters schon gestolpert, weil darueber einige Leute in den Planeten drueber geschrieben hatten.
-
Nanoc ist in Ruby geschrieben und erschien ca. 2007 . Nanoc kannte ich vorher noch nicht.
-
Jekyll setzt auf Ruby und erschien 2008. Jekyll kannte ich schon vorher, da es die treibende Kraft hinter GitHub Pages ist.
Leider weiss ich nicht mehr die konkreten Gruende (evtl. weil Hugo schon einen Webserver mitbringt, was fuer die lokale Entwicklung natuerlich hervorragend ist), aber vermutlich war es die Anzahl an GitHub Sternen , die vielen verschiedenen Themes (darunter auch viele kostenpflichtige ), die angepriesene Schnelligkeit, Flexibilitaet sowie dass Hugo Open Source und Crossplattform ist, dass die Wahl auf Hugo fiel 🎉
Auszeichnungssprachen #
Auszeichnungssprachen gibt es einige (die bekannteste duerfte HTML sein), Hugo unterstuetzt davon auch ein paar und bisher hatte ich meine Beitraege immer in Markdown geschrieben.
-
Wenn ich tatsaechlich doch mal noch mehr will , kann ich auch einfach ein Text in AsciiDoc erstellen. Bisher war das allerdings noch nicht notwendig, da ich alles relevante in Markdown schreiben konnte oder mit Hilfe von Shortcodes .
AsciiDoc eignet sich eben mehr fuer Leute, die ein Buch schreiben wollen . Evtl. ist es ja sogar ein geeignetes Tool fuer ein Kochbuch.
-
Markdown ist standardmaessig recht limitiert, was z.B. den HTML-Tag
sub
odersup
angeht. Je nach Parser gibt es aber auch Moeglichkeiten mehr als den Standard zu nutzen. Hugo nutzt z.B. Goldmark , was CommonMark konform ist, und mit dem ich auch Tabellen und Checklisten generieren kann. Fussnoten und Durchgestrichenes sind auch drin.
Da nur die Endung (md
vs adoc
) letztendlich den Unterschied ausmacht
(Front Matter bleibt gleich), bleibe ich erstmal bei Markdown.
Umwandeln kann ich ja Markdown in AsciiDoc mit z.B. Pandoc recht
schnell.
Front Matter / Config #
Mein Front Matter , also ein paar Zeilen, die z.B. den Titel und die Tags angeben, war bisher “eigen” (1. Zeile = Titel, 2. Zeile = kommaseparierte Liste mit Tags). Bei Hugo gibt es hauptsaechlich 3 Moeglichkeiten (die Unterschiede sind in diesem Vergleich schoen zu sehen):
-
“JavaScript Object Notation” mag ich persoenlich nicht so gerne, nutze sie aber zwangsweise dauernd (API als Stichwort).
-
“Tom’s Obvious, Minimal Language” kannte ich vorher noch nicht wirklich, aber die Syntax ist ja auch sehr simpel.
-
“YAML Ain’t Markup Language” nutze ich schon recht haeufig auf Arbeit (z.B.
docker-compose.yaml
oder Kubernetes), daher bin ich damit schon vertraut.
Ausser JSON ist mir die Nutzung recht egal, und so habe ich mich fuer
YAML im Front Matter entschieden (weil ---
schoener aussieht und
leichter zu tippen ist als +++
) und fuer TOML in meinen Hugo
Konfigurationsdateien .
Stylesheet languages #
Mit “Cascading Style Sheets” (CSS ) wird eine Website erst so richtig bunt und attraktiv. Allerdings gibt es nicht nur das reine CSS, sondern auch Varianten, die u.a. Variablen und Funktionen nutzen und dann in CSS umgewandelt werden koennen.
Wikipedia listet einige auf, angeschaut habe ich mir:
-
“Leaner Style Sheets” gibt es seit 2009 und ich hatte schon zumindest mal davon gehoert. Ein Vorteil von Less ist, dass es eine Obermenge von CSS ist, d.h. CSS-Code ist gleichzeitig auch gueltiger Less-Code.
-
“Syntactically awesome style sheets” gibt es seit 2007 und war mir auch schon gelaeufig. Neben Sass gibt es auch noch “Sassy CSS” (SCSS), welches statt Einrueckungen die CSS Formatierung nutzt.
-
Stylus kannte ich vorher noch nicht, gibt es aber schon seit 2010. Statt der Nutzung von geschweiften Klammern wird hier auf Einrueckung gesetzt, Zeichen wie
:
,;
und,
sind optional und koennen weggelassen werden.
Die Entscheidung zur Nutzung von SCSS fiel mir nicht schwer, da Hugo
schon Support fuer Sass/SCSS mitbringt, was bei
Less/Stylus (noch?) nicht der Fall ist .
Zudem finde ich die Nutzung von geschweiften Klammern wie bei CSS
ueblich schoener als Einrueckungen 😉
Hosting und Konfiguration #
Irgendwo muss die Website ja auch laufen. Bisher teilte ich mir einen Server bei Hetzner mit Flo . Allerdings wollte ich mir mal einen eigenen Server (VPS reicht) goennen und auch dort moeglichst viel nach dem Prinzip von Infrastructure as code (IaC) umsetzen.
Zudem sollte nicht nur die Website dort laufen, sondern u.a. auch Git,
Seafile , newsboat , weechat , neomutt , …
Und natuerlich alles unter Arch Linux mit root-Zugriff. Daher
fielen direkt schonmal einige Hostingideen raus, die ich hier aber
trotzdem mal aufliste:
-
Ist zwar bestimmt spannend zu nutzen um mal tiefer in das Thema Kubernetes einzusteigen, aber etwas Overkill fuer meine kleine Website. Fuer AWS gibt es Arch Linux Images .
-
Kein root moeglich, aber generell empfehlenswert.
-
Ebenfalls kein root moeglich, aber ebenfalls empfehlenswert.
-
Kein root, kein Arch Linux, kein ssh. Aber generell sinnvoll fuer Menschen, die nur ihre Website online bringen wollen. Auch erwaehenswert: Netlify CMS und Front Matter
-
Bieten Arch Linux nativ an 🎉
Kurz: ich habe mich fuer Netcup entschieden, einfach weil ich mit
denen schon gute Erfahrungen beruflich (und auch vor Jahren privat)
gesammelt habe und sie Arch Linux schon als ISO anbieten 😉
Zudem ist die Verwaltung der Server (inkl. Umzuege) sehr einfach.
Mit der Klaerung des Hostings stellte sich mir die Frage, wie ich auch das System dahinter moeglichst gut via IaC abbilden kann.
Bisher (berufliche) Erfahrung gesammelt habe ich mit:
-
Bisher nur spaerlich eingesetzt, gefaellt mir nicht so gut.
-
Nutzen wir quasi ueberall, finde ich super.
-
Bisher nur in Verbindung mit AWS und S3 Buckets , ansonsten noch wenig Ansatzpunkte, obwohl es Provider fuer netcup gibt.
Das ist allerdings wesentlich komplexer, fuer mich alleine und mein kleines VPS. Im Zusammenhang mit Arch Linux bin ich dann noch auf folgende Moeglichkeiten gestossen:
-
Klingt erstmal sehr gut, auch die Beschreibung im README und der Vergleich zu Puppet, Ansible, NixOS usw.
Tatsaechlich bin ich erst spaeter darauf gestossen, als ich schon eine Loesung hatte 😉 -
Ein sehr interessanter Ansatz, einfach ein ISO zu erstellen und bei Netcup hochzuladen. Allerdings waere das ISO dann erstmal nur auf den Server zugeschnitten. Aenderungen sind natuerlich nachtraeglich machbar, ohne nochmal den ganzen Server aufzusetzen. Trotzdem, irgendwie aufwaendig.
-
Sehr coole Moeglichkeit, mehrere Maschinen einfach mit den vorhandenen Boardwerkzeugen bzw. also Textdateien aufzusetzen. Darauf gekommen bin ich durch diesen Blogeintrag .
Letztendlich entschieden habe ich mich fuer meta packages, da sie einfach zu erstellen und warten, und auch mit CI/CD ausspielbar sind.
Ein Beitrag ueber die Suche und die Technik dahinter sind in Arbeit.
CI/CD #
Die Grundlage von CI /CD ist Versionsverwaltung (bei mir Git ), klar. Aber wie bekomme ich den Code jetzt gebaut und auf den Server? Hierbei habe ich mir folgende Loesungen angeschaut:
-
GitHub Actions wurden 2019 eingefuehrt und fand ich natuerlich direkt spannend. Irgendwann habe ich es dann auch mal fuer ein paar Repos eingebunden und fuer gut befunden.
-
GitLab gibt es seit 2014 und waberte immer mal wieder durch die Blogosphaere. Seit wann CI/CD integriert ist, konnte ich nicht herausfinden. Jedenfalls nutze ich GitLab CI seit Jahren beruflich.
-
Travis gibt es seit 2011. Ich bin immer wieder in diversen Repos darueber gestolpert und habe es dann selbst kurz ausprobiert, bevor GitHub Actions rauskam.
Mein Code fuer die Website liegt aktuell bei GitHub, daher nutze ich auch die GitHub Actions fuer das Testing und Deployment der Website. GitLab CI waere aber auch eine gute Alternative.
Anforderungen / Wuensche #
Was wollte ich ueberhaupt mit dem ganzen Umbau? Warum nicht alles so wie bisher weiterbetreiben nach dem Motto never change a running system ?
Ich war schon sehr lange nicht mehr zufrieden mit meiner Loesung, aber es hat erstmal funktioniert. Doch mit dem Anreiz, mal alles neu zu machen, habe ich mir eine Liste zusammengestellt bzw. mal einfach drauf los rumgewerkelt.
Hier also eine Liste mit Dingen, die ich mit dem neuen statischen Generator (also Hugo) umsetzen wollte bzw. will. Einige Dinge habe ich noch nicht umgesetzt oder bin noch dabei, daher die Checkboxen.
- Alles muss textbasiert sein
Also so wie auch vorher schon und damit auch fuer Git geeignet - abgesehen natuerlich von Bildern etc. - Markdown++ (siehe Configure markup )
- Tabellen
Beispiel Yeah 🎉 - Codebloecke / Syntaxhervorhebung , siehe Codebloecke
- Fussnoten 1
- Heading IDs , siehe Render Hooks
-
Durchgestrichener Text - Checklisten [x]
- Emojis 😉 🎉
Sehr einfach dankenableEmoji = true
in den Einstellungen . Als Cheat Sheet kann ich Emojipedia sehr empfehlen. - “Smartypants ” …
- Sub & Sup
- Tabellen
- Moeglichst wenig “Breaking Changes”
- Minify everything!
- schoenes Template / Design
- Table Of Contents / Inhaltsverzeichnis
- Sitemap
- robots.txt / humans.txt
- Errorcodes
- RSS
- Aehnliche Beitraege
- Naechste / Vorherige Seite
- Tagcloud
- URL-Schema
- Suche
- Statistik
- Changelog / Version
- DSGVO-konforme Einbindung von Medien
- Mehrsprachigkeit / i18n
- Structured Data
- neue Domain
- Workflow mit GitHub Actions
Migration / Umsetzung #
Nach der sehr einfachen Installation von Hugo ging es daran, meine bestehende Website zu migrieren.
Wo und Wie anfangen waren meine ersten Fragen 😉
Im Prinzip habe ich mich an einigen Blogposts und dem Quick Start entlang gehangelt.
hugo new site uxgch
hugo new theme uxgch
Wobei der letzte Befehl nicht ganz richtig ist, erst habe ich eins der vielen Themes kopiert (XMin ) und nach und nach durch eigenen Code ersetzt, wobei ich mich erstmal an dem Stil der alten Version orientiert habe.
Im Prinzip habe ich nach und nach jede Datei angepasst, sei es wegen Markdown-Anpassungen (Codebloecke z.B.), Emojis, Aktualisierungen/Ergaenzungen/Streichungen; das Hinzufuegen/Anpassen von Templates oder den Einbau von Shortcodes (Bilder z.B.).
Inspirationen gibt es dabei genug: im Forum , dank neuer Versionen , Code auf GitHub oder Blogbeitraegen .
Breaking Changes #
Beim Wechsel zu Hugo wollte ich so wenig wie moeglich an der alten Struktur veraendern. Und wenn doch, moeglichst alles umleiten, damit alte Links immer noch ans Ziel kommen.
Moeglich machen das hauptsaechlich Aliases im Frontmatter :
title: "Sinn des Lebens"
date: 2007-05-27T13:10:31+02:00
draft: false
tags: [ "tv", "sinn", "leben" ]
aliases: [ "/1180264231.htm" ] # magic
Beim Bauen von Hugo wird nun ein Ordner 1180264231.htm
mit der Datei
index.html
angelegt, die ein meta http-equiv="refresh"
zu der neuen
Location beinhaltet. Das ist auch schon die ganze Magie dahinter. Laut
Hugo habe ich so ca. 288 Aliase in meinen Dateien … 🙈
Fuer den Rest habe ich folgendes in meiner nginx Config:
location / {
rewrite ^/(tagcloud|holidays|recipes)\.fcgi$ /$1 permanent;
rewrite ^/tagcloud_(.*)\.htm$ /tags/$1 permanent;
rewrite ^/linkdump\.xml$ /linkdump/rss.xml permanent;
rewrite ^/(linux|vegan)\.xml$ /tags/$1/rss.xml permanent;
rewrite ^/yh\.xml$ /block/rss.xml permanent;
rewrite ^/all\.xml$ /rss.xml permanent;
}
Diese sorgen fuer die notwendigen Weiterleitungen, gerade fuer die Feeds in den Planeten.
Das Einzige, was ich leider nicht weiterleiten konnte, sind die einzelnen Linkdumps, das waren naemlich einfach nur Anker zu einer jaehrlichen Uebersichtsseite …
Zumindest wird linkdump_2012.htm
weitergeleitet zur jetzigen
Uebersichtsseite … Naja, es gibt schlimmeres 😉
Minify everything! #
Es sollte eigtl. Standard sein, dass alle JavaScript- und CSS-Dateien gebuendelt und “minifiziert ” werden, bevor sie ausgespielt werden. Zudem sollte auch HTML “gecrunched” werden. All das ist moeglich mit Hugo Pipes 🎉
Hier als Beispiel mein Code im <head>
von
layouts/_default/baseof.html
, in dem CSS gebuendelt und
minifiziert wird, unter Verwendung von
Pipes :
{{ $style := resources.Get "css/style.scss" | toCSS }}
{{ $lightbox := resources.Get "css/lightbox2/lightbox.min.css" }}
{{ $fontawesome := resources.Get "fontawesome.scss" | toCSS }}
{{ $opensans := resources.Get "open-sans.scss" | toCSS }}
{{ $css := slice $fontawesome $opensans $lightbox $style | resources.Concat "css/bundle.css" | minify }}
{{ if ne hugo.Environment "development" -}}
{{ $css = $css | fingerprint -}}
{{ end -}}
<link rel="stylesheet" href="{{ $css.RelPermalink }}">
Aehnlich verhaelt es sich mit JavaScript im <footer>
. Im Letzten
Schritt kann hier noch die Data Integrity dazukommen:
{{ $js_scratch := newScratch }}
{{ $js_scratch.Set "js" slice -}}
{{ $libs := slice "js/jquery/jquery.min.js" "js/lightbox2/lightbox.min.js" -}}
{{ range $libs -}}
{{ $js_scratch.Add "js" (resources.Get . ) -}}
{{ end -}}
{{ $js := $js_scratch.Get "js" | resources.Concat "js/bundle.js" | resources.Minify | resources.Fingerprint -}}
{{ if hugo.IsServer -}}
<script async defer src="{{ $js.RelPermalink }}"></script>
{{ else -}}
<script async defer src="{{ $js.RelPermalink }}" integrity="{{ $js.Data.Integrity }}"></script>
{{ end -}}
Schoen waere nun noch eine Anpassung der Content Security Policy
in nginx , wenn die Data Integrity bekannt ist. Das
steht noch auf meiner TODO-Liste 😊
Die CSP kann uebrigens mit einem Evaluator getestet werden.
HTML kann schliesslich mit Hugo selbst minifiziert werden (letzter Satz):
hugo --minify
Ansonsten ist es sicher sinnvoll, auch die statischen Dateien moeglichst klein zu halten, z.B. durch Nutzung von WebP (siehe Bilder) oder WebM ).
Design/Layout #
Mein Ziel war es, ein simples, aber ansprechendes Layout hinzubekommen, und nicht so weit entfernt vom alten Design. Inspirationen gibt es ja alleine mit den Hugo Themes genug, aber ganz cool fand ich die Iterationen der Motherfucking Website (Suche , Reddit ):
- Better Motherfucking Website
- Even Better Motherfucking Website
- Best Motherfucking Website
- The Best Motherfucking Website
- Perfect Motherfucking Website
Zusaetzlich zur Farbpalette (was eine Wisschenschaft fuer sich ist) kommen dann noch “Feinheiten” wie “neue” HTML-Tags , typographische Elemente wie Smart Quotes , und sonstige schoene Elemente. Davon hat die Designwelt ja recht viel 🤓
Gute Anlaufstellen bieten die Front-End Checklist , das SELFHTML-Wiki , Google Style Guides und generell diverse Standards .
Am Ende habe ich einfach das Standard-CSS in etwas abgewandelter Form verwendet. Die Farben sind bei mir jetzt also Weiss(-isch) als Hintergrundfarbe (#fafafa), Schwarz(-isch) als Schriftfarbe (#27272a), Gruen(-isch) als Linkfarbe (#04815D). Ich habe nur kein Lila als “Visited” verwendet, aber Underline als “Hover” .
Wichtig waren mir dabei Farben, die die folgenden Kontrasttests bestehen (und ich hab mir mal eben schnell noch neue Shortcodes gebaut 😉):
-
Contrast Checker (testet Hintergrundfarbe und Schriftfarbe)
ratio AA AALarge AAA AAALarge ✅ 14.2 ✅ pass ✅ pass ✅ pass ✅ pass -
Link Contrast Checker (testet Hintergrundfarbe, Schriftfarbe und Linkfarbe)
conformance ratio Body Text to Background ✅ pass ✅ 14.2 Link to Background ✅ pass ✅ 4.67 Link to Body Text ✅ pass ✅ 3.05
CSS #
Das schwierigste (zumindest fuer mich) ist ein sauberes (S)CSS. Zudem habe ich ja mehrere Fremdsysteme mit aufgenommen (z.B. Lightbox und Font Awesome), die ja auch noch (S)CSS mitbringen. Das alles unter einen Hut zu bringen ist nicht immer ganz einfach, aber nur so lerne ich auch dazu 😉
Etwas aufgehalten hat mich auch der selbst gebastelte,
datenschutzfreundliche Gist-Shortcode, denn der passt nicht mit
meinen anderen Styles (besonders pre
und code
) zusammen. Allerdings
koennen Dinge ja ueberschrieben werden 😉
Zur Vorbereitung auf den Dark Mode habe ich noch etwas
anpassen bzw. hinzufuegen muessen. Und zwar habe ich erst in
config/_default/markup.toml
die Parameter zu
Highlight wie folgt gesetzt:
|
|
Mit dieser Einstellung muss ich die entsprechenden Klassen nun selbst
hinzufuegen (damit kann ich sie aber z.B. spaeter fuer den Dark
Mode einfach ueberschreiben). Ich habe mich fuer den Style
solarized-light
entschieden, eine Uebersicht gibt es in der Chroma
Style Gallery . Die entsprechenden Klassen kann ich mit
dem folgenden Befehl an meine assets/css/style.scss
anhaengen:
hugo gen chromastyles --style 'solarized-light' >>assets/css/style.scss
Dark Mode #
Hauptmotivation: Dem Darktheme Club beitreten zu koennen 😅
Eine dunkle Variante hatte ich sogar schon eingebaut, allerdings bin ich
mit dem (S)CSS noch nicht zufrieden gewesen. Das kommt also noch
😉
Update (2024-07-07):
Ok, das hat etwas gebraucht. Und fertig bin ich immer noch nicht
😅
Aber hey, eine erste Iteration ist da!
Ich hatte in meinen Notizen schon sehr viele (ok, nach Ausduennung dann doch nicht mehr so viele) Informationen bzw. Inspirationen (z.B. Hugo Themes mit dem Tag “dark mode” ) zu diesem Thema gesammelt, aber es kommt ja dann doch irgendwie immer ganz anders.
Jedenfalls bin ich dank Bryce Wray ueber Tailwind CSS gestolpert (im Gegensatz zu anderen CSS Frameworks ) und fand die Idee nicht schlecht (finden auch andere gut ). Zumindest kann ich damit relativ leicht einen Dark Mode mit erstellen (durch Nutzung von prefers-color-scheme durch das Betriebssystem).
Installation und Nutzung von Tailwind CSS #
Es gibt sogar mittlerweile zwei Repos vom Hugo Entwickler Bjørn Erik Pedersen selbst, die eine Integration von Tailwind CSS mit Hugo beschreiben (eins fuer v3 und eins fuer v4 ). Ich habe mich aber fuer die “normale” Installation (siehe Tailwind CSS Doku ) entschieden:
npm install -D tailwindcss # installiert tailwindcss und speichert Abhaengigkeiten als devDependencies
npx tailwindcss init # erstellt eine initiale tailwind.config.js
Die Config sieht bei mir nun so aus:
Durch die content
-Zeile gebe ich an, welche Dateien von tailwindcss
beachtet werden sollen, um CSS zu generieren. Zusaetzlich habe ich durch
die preflight
-Zeile Preflight ausgeschaltet ,
damit ich nicht alle Standard-Tags nochmal neu stylen muss.
Einige Einstellungen habe ich dann in der assets/css/tailwind.css
gesetzt:
assets/css/tailwind.css
|
|
Das sind hauptsaechlich Tags, die ich so nicht einfach setzen kann, weil
sie via Markdown einfach generiert werden (z.B. blockquote
und
code
). Zudem habe ich dort auf alle img
-Tags im Dark Modus einen
Filter angewendet (siehe Dark Mode Images ), damit
die Bilder nicht so hell sind im Vergleich.
Und dann kann folgender Befehl ausgefuehrt werden, um eine
assets/css/main.css
zu erstellen. Diese Datei kann dann wie gewohnt in
die layouts/_default/baseof.html
aufgenommen werden (siehe
Minifizierung):
npx tailwindcss -i ./assets/css/tailwind.css -o ./assets/css/main.css --watch --minify
In der Pipeline ist dieser Befehl natuerlich auch enthalten (siehe GitHub Action).
SCSS zu Tailwind CSS #
Tja, und dann kann die Nutzung losgehen. Ich habe einfach mal mit der
layouts/_default/baseof.html
angefangen (mit dem <body>
) und quasi
alles aus der SCSS in Tailwind CSS uebersetzt. Geholfen haben mir da
sehr diese zwei Converter: SCSS to CSS und CSS to
Tailwind CSS .
Und bin dann nach und nach alle Shortcodes etc. durchgegangen. Die
meisten Probleme hatte ich mit der Syntaxhervorhebung. Dazu habe ich
erstmal eine weitere Variante (naemlich solarized-dark
) zu meiner
assets/css/style.scss
hinzugefuegt:
hugo gen chromastyles --style 'solarized-dark' >>assets/css/style.scss
Aber seit der letzten Generierung vor ein paar Jahren hat sich die Software dahinter weiterentwickelt und meine kompletten Codekaesten zerschossen. Nach langem Probieren und Rumsuchen bin ich dann dank des Hugo Entwicklers Joe Mooring auf ein paar Zeilen Loesung (noch mehr dazu von ihm ) gestossen (danke dafuer!):
.chroma .lntable .lnt,
.chroma .lntable .hl {
display: flex;
}
.chroma .lntable .lntd + .lntd {
width: 100%;
}
.chroma {
overflow-x: auto;
margin: 0;
}
Und damit ist nun fast alles in die Shortcodes usw. gewandert, aber eben
auch ein paar Sachen in assets/css/tailwind.css
bzw. noch in
assets/css/style.scss
geblieben (zumindest den Chroma Kram wuerde ich
auch dort drin lassen).
Eine coole Sache noch, die mir via diesem Guide aufgefallen ist:
<meta name="color-scheme" content="dark light">
Das im Header (also in layouts/_default/baseof.html
) sorgt dafuer,
dass Formularfelder wie bei meiner Suche auch entsprechend dem
Dark oder Light Mode eingefaerbt werden. The more you know 😉
Accessibility #
Ich hatte mir beim ersten Rumspielen mit Tailwind CSS so schoene Farben fuer den Dark Mode ausgesucht, doch dann kam der Link Contrast Checker 🥲
Nach einigem Rumspielen (danke an HTML Color Codes , Colour accessibility test , Contrast Grid und eine Toolsammlung ) habe ich endlich Farben fuer den Dark Mode gefunden, die auch die Tests bestehen:
- Hintergrundfarbe: #21212C
- Schriftfarbe: #f4f4f5
- Linkfarbe: #1B9D69
- Contrast Checker:
ratio AA AALarge AAA AAALarge ✅ 14.4 ✅ pass ✅ pass ✅ pass ✅ pass - Link Contrast Checker:
conformance ratio Body Text to Background ✅ pass ✅ 14.4 Link to Background ✅ pass ✅ 4.60 Link to Body Text ✅ pass ✅ 3.14
Was noch fehlt #
- Dark/Light Mode Umschalter
- https://radu-matei.com/blog/dark-mode/
- https://yonkov.github.io/post/add-dark-mode-toggle-to-hugo/
- https://css-tricks.com/a-complete-guide-to-dark-mode-on-the-web/#storing-preferences
- https://oostens.me/posts/hugo--css-dark/light-theme/
- https://derekkedziora.com/blog/dark-mode-revisited
- https://www.brycewray.com/posts/2023/06/great-take-toggle/
- Bilder?
- schoenere Farben/Zusammenspiele?
- viele kleinere Dinge wie Emojis und Icons
Responsive Design #
Eigentlich dachte ich ja, dass es sich mit dem folgenden Meta-Tag erledigt hat:
<meta name="viewport" content="width=device-width, initial-scale=1.0">
Aber so einfach ist es leider nicht 🥲
Mit Tailwind CSS hatte ich die Gelegenheit, von Grund auf direkt damit zu starten , habe diese aber nicht genutzt. Also steht das noch auf meiner TODO Liste.
Fonts #
Fuer die kleinen Icons nutze ich Font Awesome , Alternativen sind z.B. Feather , Fork Awesome , Simple Icons , oder Health Icons .
Ich habe lange herumprobiert und mich dann letztendlich dazu
entschieden, nur die Woff2-Dateien durch etwas CSS einzubinden. Also
kein JavaScript und auch keine SVG Sprites.
Zwar kann IE damit nicht umgehen , aber … who cares? 😉
Fuer das Einbinden nutze ich npm
und Hugo Mounts ,
es geht aber auch direkt mit einem Hugo Module .
Alternativ geht das auch via Shortcode .
Meine Loesung wie folgt (von hier ). Erstmal das Paket installieren:
npm i @fortawesome/fontawesome-free
Danach folgendes in die config/_default/module.toml
mit aufnehmen:
[[mounts]]
source = "node_modules/@fortawesome/fontawesome-free/scss"
target = "assets/css/fontawesome"
[[mounts]]
source = "node_modules/@fortawesome/fontawesome-free/webfonts"
target = "static/fonts/fontawesome"
Dann eine kleine Datei assets/fontawesome.scss
erstellen:
$fa-font-path: "../fonts/fontawesome";
@import "css/fontawesome/fontawesome.scss";
@import "css/fontawesome/solid.scss";
@import "css/fontawesome/brands.scss";
Und diese dann in die Minifizierungspipeline mit aufnehmen:
{{ $fontawesome := resources.Get "fontawesome.scss" | toCSS }}
{{ $css := slice $fontawesome $opensans $lightbox $style | resources.Concat "css/bundle.css" | minify }}
Die Nutzung erfolgt entweder direkt (also mit HTML Code) oder via
Shortcode . Ich habe dazu einfach selbst einen
gebastelt (layouts/shortcodes/icon.html
):
|
|
Genutzt werden kann es nun z.B. auf einer Seite content/test.md
wie
folgt:
{{< icon size="fa-lg" anim="fa-spin" col="#abcdef" >}}
Info
Der obige Code musste auskommentiert werden, damit er nicht ausgefuehrt wird. Das geht, indem nach dem<
ein /*
und vor dem >
ein */
hinzugefuegt wird.
Dieser Code erzeugt dann das hier: .
Wichtig ist hierbei, dass die Datei keine neue Zeile am Ende enthaelt, denn sonst wird das Icon mit einem Leerzeichen dahinter dargestellt.
Sofern eine .editorconfig
genutzt wird, kann folgendes
fuer .html
-Dateien hinzugefuegt werden:
[*.html]
insert_final_newline = false
Alternativ kann das Leerzeichen laut Stackexchange mit perl -pi -e 'chomp if eof' /path/to/file
entfernt werden.
Noch auf meiner TODO-Liste ist die Unterscheidung zwischen dekorativen und semantischen Icons und die entsprechende Umsetzung.
Ansonsten nutze ich als Schriftart Open Sans , was auch via
npm
und Hugo Mounts eingebunden und ausgeliefert wird.
Bilder #
Die Bilder wollte ich wie bisher auch mit einem
Polaroid-Effekt versehen (zudem Lightbox zum schoener
darstellen und navigieren), diesmal aber via CSS, statt vorgeneriert mit
convert
.
Inspirationen fuer die Umsetzung habe ich ein
paar gefunden und damit folgenden SCSS-Code in
assets/css/style.scss
gebastelt:
figure {
img {
display: block;
margin-left: auto;
margin-right: auto;
}
&.lb {
margin: 2em auto;
background: white;
background: linear-gradient(110deg, white, oldlace);
box-shadow: 4px 4px 15px gray;
max-width: 280px;
vertical-align: top;
position: relative;
transform: rotate(4deg);
transition: all ease 0.6s;
text-align: center;
a {
text-decoration: none;
}
img {
display: inline;
max-width: 100%;
height: auto;
margin: 5% 5% 0 5%;
}
figcaption {
width: 90%;
min-height: 50px;
margin: 0 5% 5% 5%;
text-align: center;
}
}
}
Bisher habe ich vorwiegend Bilder in den Formaten JPEG und PNG
verwendet. Allerdings gibt es seit Version v.83.0
in Hugo
Support fuer WebP , was laut Can I use… in den meisten
derzeit genutzten Browsern verwendet werden kann.
Ein paar der bisher genutzten Bilder habe ich nachtraeglich mit
cwebp
(alternativ convert
) umgewandelt. Dank
Hugo kann ich aber zumindest die Thumbnails automatisch mit einem
Shortcode umwandeln lassen 🎉
[Hugo kann noch viel mehr, zum Beispiel koennen auch automatisch Wasserzeichen hinzugefuegt werden oder Exif-Informationen ausgelesen und dargestellt werden. Sehr cool z.B. fuer Bildergalerien.]
Ich glaube, der Shortcode fuer Bilder ist eins der am haeufigsten angepassten Shortcodes, ein Beispiel gibt es im Forum . Mein Shortcode ist auch angepasst und hat Lightbox-Code schon integriert, zudem noch ein paar andere Dinge wie Unterschriften, Qualitaet, Linkziele, Format, …
Auf meiner TODO-Liste steht noch lazy loading , was aber etwas komplexer ist.
Favicon #
Ein Favicon hatte ich ja vorher schon, nur die
Einbindung war noch nicht ganz perfekt. Ueber die Website
RealFaviconGenerator habe fuer ca. alle Plattformen das richtige
Bild erstellen und in layouts/_default/baseof.html
einbinden koennen:
<!-- realfavicongenerator.net -->
<link rel="apple-touch-icon" sizes="180x180" href="{{ "apple-touch-icon.png" | absURL }}">
<link rel="icon" type="image/png" sizes="32x32" href="{{ "favicon-32x32.png" | absURL }}">
<link rel="icon" type="image/png" sizes="192x192" href="{{ "android-chrome-192x192.png" | absURL }}">
<link rel="icon" type="image/png" sizes="16x16" href="{{ "favicon-16x16.png" | absURL }}">
<link rel="manifest" crossorigin="use-credentials" href="{{ "manifest.webmanifest" | absURL }}">
<link rel="mask-icon" href="{{ "safari-pinned-tab.svg" | absURL }}" color="#5bbad5">
<link rel="shortcut icon" href="{{ "favicon.ico" | absURL }}">
<meta name="apple-mobile-web-app-title" content="{{ site.Title }}">
<meta name="application-name" content="{{ site.Title }}">
<meta name="msapplication-TileColor" content="#da532c">
<meta name="msapplication-config" content="{{ "browserconfig.xml" | absURL }}">
<meta name="theme-color" content="#000000">
Die Dateien von der Website habe ich alle im Ordner static
abgelegt.
Codebloecke / Syntaxhighlighting #
Ich verwende viele Codebloecke, daher sollen die natuerlich auch
schoen aussehen. Hugo bringt sowas schon mit und
funktioniert mit Code Fences und
Shortcodes . Im Hintergrund verwendet Hugo
Chroma , was sehr viele Sprachen
unterstuetzt . Abweichend von den
Standardeinstellungen habe ich mich fuer
solarized-light
als Style (siehe CSS) bzw.
solarized-dark
(siehe Dark Mode) entschieden.
Die Inspiration, vor einen Codeblock die Sprache “anzupinnen” habe ich mir hier geholt .
Beispiele fuer den Einsatz gibt es weiter unten bei Gist oder GitHub.
Blockquotes #
Zitate verwende ich immer mal wieder und wollte die auch entsprechend aufhuebschen. Fuer das CSS habe ich mich ein bisschen an den “BQ Patterns” und “Classy Blockquotes” (ohne Bilder) orientiert und mich auch noch ein bisschen inspirieren lassen von einer anderen Website .
Mein Shortcode in layouts/shortcodes/blockquote.html
ist recht simpel
gestaltet:
layouts/shortcodes/blockquote.html
|
|
Verwendet werden kann es wie folgt:
{{< blockquote src="Wikipedia" dst="s://de.wikipedia.org/wiki/Blockzitat" >}}
Ein **Blockzitat** ist eine typografische Darstellung eines Zitats, bei dem eine längere zitierte Passage als eigener Absatz herausgestellt wird.
{{< /blockquote >}}
Mein SCSS in assets/css/style.scss
im Folgenden enthaelt Mixins (quasi
Funktionen) und Variablen, da ich es z.B. auch fuer Twitter
in aehnlicher Form verwendet habe:
$color: #575757;
$color_blockquote_before: #e6e6e6;
$color_blockquote_footer: #d3d3cf;
$font_serif: serif;
@mixin blockquote_before($color: $color_blockquote_before) {
position: absolute;
color: $color;
z-index: -1;
}
blockquote {
position: relative;
&:before {
font-family: $font_serif;
content: '\201C';
top: -50px;
left: -15px;
font-size: 5em;
@include blockquote_before();
}
footer {
color: $color_blockquote_footer;
&:before {
content: '\2015';
}
}
}
Das Resultat mit Quelle (bzw. Footer) sieht nun so aus:
Ein Blockzitat ist eine typografische Darstellung eines Zitats, bei dem eine längere zitierte Passage als eigener Absatz herausgestellt wird.
Render Hooks #
Das Feature Render Hooks gibt es seit Version
v0.62.0 und erlaubt es, bei Headern (also z.B. <h2>
) den
Permalink daneben hinzuzufuegen (bei mir durch ein #
gekennzeichnet)
oder Links zu anderen Websites mit einem Icon (bei mir ) kenntlich zu machen.
Der Code in layouts/_default/_markup/render-heading.html
fuer die
Header sieht so aus:
<h{{ .Level }} id="{{ .Anchor | safeURL }}">{{ .Text | safeHTML }} <a href="#{{ .Anchor | safeURL }}">#</a></h{{ .Level }}>
Fuer die Icons neben den Links sieht der Code in
layouts/_default/_markup/render-link.html
wie folgt aus:
<a href="{{ .Destination | safeURL }}"{{ with .Title}} title="{{ . }}"{{ end }}>{{ .Text | safeHTML }}{{ if strings.HasPrefix .Destination "http" }} <i class='fa-solid fa-up-right-from-square fa-sm'></i>{{ end }}</a>
Durch die Abfrage mit dem Prefix http
werden nur externe Links mit dem
Icon versehen, interne Links sind relativ und werden daher nicht mit gekennzeichnet.
CSS Icons #
Eine Alternative (?) ist die Nutzung von CSS , z.B. um Links zu einem RSS-Feed mit dem RSS-Icon () zu kennzeichnen.
Dazu reicht folgendes im CSS (mit der Nutzung von Fonts):
a[href$="rss.xml"]::before {
content: '\f143';
color: #f26522;
font-family: "Font Awesome 5 Free";
margin-right: 5px;
}
Verlinke ich jetzt einen RSS-Feed, erscheint das Icon: Link zu einem
RSS-Feed. Das funktioniert auch mit externen Feeds, dann
wird zusaetzlich noch der render-link.html
angehaengt: Link zu
externem “RSS-Feed” .
Back to top #
Gerade bei langen Texten und/oder auf dem Mobilgeraet ist ein Button, mit dem zum Anfang “gescrollt” werden kann recht sinnvoll.
Daher nun jetzt auch auf dieser Website, dank vanilla-back-to-top . Installiert habe ich es ganz einfach ueber npm :
npm i vanilla-back-to-top
Danach hab ich es in die config/_default/module.toml
mit aufgenommen:
[[mounts]]
source = "node_modules/vanilla-back-to-top/dist/vanilla-back-to-top.min.js"
target = "assets/js/b2t/b2t.min.js"
Zum Schluss noch eine kleine Datei layouts/assets/js/b2t.js
erstellt
(was dem 2. Beispiel entspricht ):
Und diese dann mit der gemounteten layouts/assets/js/b2t/b2t.min.js
in
die Minifizierungspipeline mit aufgenommen:
{{ $libs := slice "js/jquery/jquery.min.js" "js/lightbox2/lightbox.min.js" "js/b2t/b2t.min.js" "js/b2t.js" -}}
LaTeX #
Update (2024-01-28): Eine Integration von \(\LaTeX\) wollte ich schon seit langer Zeit haben (einfach weil ich es kann, schoen und einfach finde (git !), damit meine Bachelor Arbeit erstellt habe und immer noch zum Briefe schreiben nutze) und hatte dazu auch schon mehrere Vorgehensweisen entdeckt:
- How to use LaTex in Markdown (mit MathJax , ich glaube damit fing auch alles an)
- Math Typesetting in Hugo (mit KaTeX )
- Render LaTeX with KaTex in Hugo Blog (mit KaTeX , allerdings auch mit mmark , was durch goldmark ersetzt wurde)
- Better TeX math typesetting in Hugo (mit KaTeX , beruft sich auf ein nicht mehr existierendes Hugo Tutorial und macht den Case “LaTeX in RSS” auf, was ich allerdings erstmal™ auslasse)
- Hugo KaTeX shortcode (mit KaTeX , als Shortcode)
- KaTeX in HUGO einbinden (mit KaTeX , macht aber noch Beispiele fuer nicht-markdown-Dateien und wie sowas auch in Cactus.chat eingebunden werden kann)
- und zu guter Letzt KaTeX in Hugo (mit KaTeX , aus dem offiziellen Forum)
Seit Hugo v0.122.0 gibt es allerdings dazu eine offizielle Anleitung und das ist doch Grund genug, das endlich mal zu integrieren, wa?
Zuerst habe ich meine markup.toml
und params.toml
um die jeweiligen
Konfigurationen ergaenzt, allerdings ist der Default fuer math
bei
mir auf false
gesetzt (muss dann also im Frontmatter auf true
gesetzt werden, wenn LaTeX aktiviert sein soll). Zudem habe ich nur
jeweils eine Konfiguration fuer “block” und “inline”, da ich die eine
“block”-Syntax auch in einem anderen Kontext nutze:
[markup]
[markup.goldmark]
[markup.goldmark.extensions]
[markup.goldmark.extensions.passthrough]
enable = true
[markup.goldmark.extensions.passthrough.delimiters]
block = [['$$', '$$']]
inline = [['\(', '\)']]
[params]
math = false
Allen Anleitungen gemein ist die blinde Einbindung von JavaScript
ueber ein Content Delivery Network . Da ich das aus
Datenschutzgruenden nicht nutzen will, werde ich auch hier die
benoetigten Dateien via npm
und Hugo Mounts
einbinden.
Ich habe mich fuer MathJax und die tex-chtml.js
-Variante (es gibt
noch andere ) entschieden, die auch in der Hugo Doku
verwendet wird.
Also installieren wir erstmal das Paket:
npm i mathjax --save
Und binden 2 Dinge via Modules ein:
[[mounts]]
source = "node_modules/mathjax/es5/tex-chtml.js"
target = "assets/js/mathjax/mathjax.js"
[[mounts]]
source = "node_modules/mathjax/es5/output/chtml/fonts/woff-v2"
target = "static/js/output/chtml/fonts/woff-v2"
Zur Konfiguration legen wir noch eine weitere JavaScript Datei an:
Zu guter Letzt werden die beiden Dateien moeglichst umstaendlich™ in
layouts/_default/baseof.html
im <footer>
eingebunden, analog zu den
anderen JavaScript-Dateien (ich habe hier noch keine bessere
Moeglichkeit gefunden, abhaengig von der Konfiguration im Frontmatter
unterschiedliche JavaScript Dateien usw. einzubinden, daher wird es nun
als weiteres Bundle nur nachgeladen, wenn der Parameter math
gesetzt
ist):
<footer>
...
{{- if .Param "math" -}}
{{ $js_mathjax_scratch := newScratch -}}
{{ $js_mathjax_scratch.Set "jsm" slice -}}
{{ $js_mathjax_scratch.Add "jsm" (resources.Get "js/mathjax/mathjax.js") -}}
{{ $js_mathjax_scratch.Add "jsm" (resources.Get "js/mathjax.js") -}}
{{ $jsm := $js_mathjax_scratch.Get "jsm" | resources.Concat "js/bundle-mathjax.js" -}}
{{ if hugo.IsServer -}}
<script async defer src="{{ $jsm.Permalink }}"></script>
{{ else -}}
{{ $jsm := $jsm | minify | fingerprint }}
<script async defer src="{{ $jsm.Permalink }}" integrity="{{ $jsm.Data.Integrity }}" crossorigin="anonymous"></script>
{{ end -}}
{{- end -}}
...
</footer>
Und damit kann ich jetzt endlich \(\LaTeX\) nutzen, woop woop (an das
math: true
im Frontmatter denken) 🎉
So zum Beispiel den Satz vom Igel , an den ich mich noch aus meinem Studium erinnern kann:
Für jedes \(m\in \mathbb {N}\) und für jede stetige Abbildung \(f\colon \mathbb {S} ^{2m}\to \mathbb {R} ^{2m+1}\) existiert ein \(x_{0}\in \mathbb {S} ^{2m}\) und ein \(\lambda \in \mathbb {R}\) mit \(f(x_{0})=\lambda \cdot x_{0}\).
Table Of Contents / Inhaltsverzeichnis #
Der Code in der Doku reicht leider nicht fuer meine
Beduerfnisse, denn ich will eigentlich immer ein Inhaltsverzeichnis
haben, wenn der Text 400 oder mehr Woerter enthaelt und nicht der Flag
toc: false
gesetzt ist.
Ansonsten soll das Inhaltsverzeichnis nur angezeigt werden, wenn es 2
oder mehr Header gibt (egal welche).
Mein Code in partials/block/toc.html
sieht daher wie folgt aus (mit
Nutzung von <details>
):
layouts/partials/block/toc.html
|
|
Ein Problem bestand noch darin, dass das erstellte Inhaltsverzeichnis
auch den ersten Header (<h1>
) mit dargestellt hat. Dank Goldmark kann
das nun via startLevel = 2
eingestellt werden (das ging
vorher mit Blackfriday naemlich nicht ).
Sitemap #
Hugo liefert schon von Haus aus eine Sitemap mit (hier ist meine), der Code dafuer ist auf GitHub . Die Einbindung in den Header ist in diesem Thread beschrieben .
Soll eine Seite/Beitrag oder was auch immer nicht in der Sitemap
erscheinen, kann die Loesung von Fryboyter verwendet
werden. Eine Alternative Loesung bei Mert Bakır . Meine
Loesung in layouts/_default/sitemap.xml
sieht jetzt einfach so aus:
{{ printf "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?>" | safeHTML }}
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
xmlns:xhtml="http://www.w3.org/1999/xhtml">
{{ range where .Data.Pages ".Params.exclude" "!=" true }} // statt {{ range .Data.Pages }}
{{- if .Permalink -}}
<url>
<loc>{{ .Permalink }}</loc>{{ if not .Lastmod.IsZero }}
<lastmod>{{ safeHTML ( .Lastmod.Format "2006-01-02T15:04:05-07:00" ) }}</lastmod>{{ end }}{{ with .Sitemap.ChangeFreq }}
<changefreq>{{ . }}</changefreq>{{ end }}{{ if ge .Sitemap.Priority 0.0 }}
<priority>{{ .Sitemap.Priority }}</priority>{{ end }}{{ if .IsTranslated }}{{ range .Translations }}
<xhtml:link
rel="alternate"
hreflang="{{ .Language.Lang }}"
href="{{ .Permalink }}"
/>{{ end }}
<xhtml:link
rel="alternate"
hreflang="{{ .Language.Lang }}"
href="{{ .Permalink }}"
/>{{ end }}
</url>
{{- end -}}
{{ end }}
</urlset>
Nun kann im Frontmatter eine Seite via exclude: true
in der Seite
ausgeschlossen werden.
Konfigurieren lassen sich ansonsten ein paar Einstellungen via Config:
Das XML-Schema einer Sitemap ist hier spezifiziert und kann mit einem Validator ueberprueft werden.
Warnung
Dieser Fehler hat mich mein halbes Leben gekostet … 🙄
Wird in config/_default/outputs.toml
bei home
die Sitemap
inkludiert, wird einfach das Default-Template genutzt:
home = [ "html", "rss", "manifest", "sitemap" ]
Somit greifen jegliche Aenderungen in layouts/_default/sitemap.xml
nicht.
robots.txt / humans.txt #
Eine robots.txt
ist ja ueblich, aber auf der anderen Seite
gibt es auch humans.txt (mehr dazu ). Ein
lustiges Projekt, fuer mich aber nicht notwendig, da ich hier ja alles
selbst mache, daher leite ich es einfach auf meine
Kontakt-Seite weiter.
Natuerlich laesst sich die robots.txt
auch anpassen .
Ein Forumsbeitrag enthaelt z.B. die Moeglichkeit,
einzelne Seiten/Beitraege nicht zu erlauben (aehnlich zur
Sitemap). Meine sieht z.B. so aus (layouts/robots.txt
),
indem ich einach den gleichen Parameter wie bei der Sitemap nutze:
Wichtig bei einer eigenen robots.txt
ist folgende Einstellung in
config/_default/config.toml
:
enableRobotsTXT = true
Werden bestimmte Seiten ausgeschlossen , sollten sie auch
im <head>
ausgeschlossen werden. Daher sieht meine
layouts/_default/baseof.html
so aus:
<head>
<!-- [..] -->
<meta name="robots" content="{{ if .Params.exclude }}noindex,nofollow{{ else }}index,follow{{ end }}">
</head>
Errorcodes #
Normalerweise sehen die Errorcodes-Seiten ziemlich langweilig aus. Mit
Hugo ist es moeglich, diese auch wie eine normale Webseite
auszugeben und mit z.B. nginx zu nutzen
(content/403/index.md
):
Wichtig ist hier die Zeile mit url: 403.html
, denn ansonsten wuerde
Hugo dies als /403/
ausgeben, womit nginx Schwierigkeiten hat, daher
reicht nun einfach:
error_page 403 /403.html;
Ebenso wichtig ist die Zeile mit exclude: true
, denn diese
Fehlerseiten sollen weder in der Sitemap auftauchen, noch in
der robots.txt
bzw. <head>
“erlaubt”
sein .
Und auch wichtig ist, dass CSS-, JavaScript und sonstige Dateien absolut
eingebunden werden. Bisher hatte ich in meinem <head>
alles
auf relURL
und relPermalink
stehen (siehe
Minifizierung), doch dann koennen Resourcen nicht geladen
werden, wenn z.B. https://uxg.ch/bewusst/falscher/pfad aufgerufen
wird. Daher habe ich nun alles einiges auf absURL
und
Permalink
umgestellt.
Meine Fehlerseiten: 403 und 404. Weitere schoene gibt es bei http.cat 😸
RSS #
Da ich in mehreren Planeten gelistet bin, brauche ich natuerlich auch einen ordentlichen RSS-Feed , wenn nicht sogar mehrere 😉
Allerdings ist das Default-Template nicht ausreichend genug,
denn ich will im Feed nicht nur ein Summary eines Beitrags drin haben,
sondern eben den ganzen Beitrag . Zudem sollen nicht alle
Beitraege im Feed sein, sondern nur die neuesten 10, was mit
einem limit = 10
gesetzt werden kann.
Update (2024-03-30):
Dazu habe ich das Template in layouts/_default/rss.xml
mit einer Zeile modifiziert (mit Einhaltung der Standards ).
Es gibt uebrigens auch ein Issue und schon einen Pull
request :
<description>{{ .Content | transform.XMLEscape | safeHTML }}</description> // .Content statt .Summary
Das funktioniert schonmal prima fuer die Tags 🎉
Allerdings soll die Tags-Seite kein RSS ausspucken (wozu auch?). Dazu
ist eine Anpassung der Outputs in
config/_default/outputs.toml
notwendig:
page = [ "html" ]
home = [ "html", "rss", "manifest" ]
section = [ "html", "rss" ]
# taxonomy soll kein rss beinhalten
taxonomy = [ "html" ]
term = [ "html", "rss" ]
Hugo wirft jetzt erstmal einen Fehler bei dieser Aenderung aus:
ERROR 2021/12/13 17:12:50 You have configured output formats for 'taxonomy' in your site configuration. In Hugo 0.73.0 we fixed these to be what most people expect (taxonomy and term).
But this also means that your site configuration may not do what you expect. If it is correct, you can suppress this message by following the instructions below.
If you feel that this should not be logged as an ERROR, you can ignore it by adding this to your site config:
ignoreErrors = ["error-output-taxonomy"]
Da ich mir sicher bin, dass ich kein RSS-Feed fuer meine Tags-Seite
haben will, kann ich den Fehler in der config/_default/config.toml
ignorieren:
ignoreErrors = ["error-output-taxonomy"]
Falls ihr nicht sicher seid, was alles noch einen RSS-Feed ausspuckt,
und ob das wirklich notwendig ist, koennt ihr eure Seite erstmal bauen
und dann nach rss.xml
suchen. Bei mir hatten sich ein paar Dateien
eingeschlichen, die durch Sektionen generiert wurden, obwohl das gar
keine Section sein muss. Also mal schnell ein git mv _index.md index.md
gemacht in den entsprechenden Ordnern und bis auf eine Sektion
war alles gut (steht noch auf meiner TODO-Liste das zu fixen) 😉
Update (2024-04-05)
Das ist mittlerweile gefixt, indem ich
einfach im Frontmatter outputs
ueberschreibe (und rss
einfach
weglasse):
outputs:
- html
Eingebunden werden koennen die Feeds (und ggf. andere
Formate) in den <head>
in layouts/_default/baseof.html
so:
{{ with .OutputFormats.Get "rss" -}}
{{ printf `<link rel=%q type=%q href=%q title=%q>` .Rel .MediaType.Type .Permalink site.Title | safeHTML }}
{{ end }}
Uebrigens gibt es auch Validatoren fuer RSS-Feeds .
Alternativen: Atom und JSON Feed #
Zusaetzlich zu RSS gibt es auch noch Atom und JSON Feed , was wir natuerlich auch in Hugo realisieren koennen.
Darauf habe ich aber (erst einmal) verzichtet, liste aber hier ein paar Quellen auf, wie das realisiert werden kann.
- https://ig.nore.me/2017/05/json-feed-and-hugo-output-formats/
- https://www.bitquabit.com/post/json-feed-with-hugo/
- https://www.raymondcamden.com/2017/05/18/creating-a-json-feed-for-hugo
- https://xdeb.org/post/2017/06/11/make-hugo-generate-a-json-search-index-and-json-feed/
- https://discourse.gohugo.io/t/atom-and-json-feeds/13572
Related Posts / Aehnliche Beitraege #
Wie in jeder Standardwordpressinstallation gibt es auch bei Hugo die Moeglichkeit, aehnliche Beitraege z.B. nach einem Blockpost anzuzeigen.
Meine Config (config/_default/related.toml
) sieht so aus:
config/_default/related.toml
|
|
Und mein Partial (layouts/partials/block/related.html
), welcher in
layouts/block/single.html
eingebunden wird, so:
layouts/partials/block/related.html
Naechste / Vorherige Seite #
Auch das wollte ich schon immer mal haben, eine einfache Navigation zum vorherigen bzw. naechsten Beitrag oder Rezept. Und auch das ist mit Hugo recht einfach moeglich:
Hierzu nutzen wir einfach die Variablen , die Hugo uns
fuer eine Seite zur Verfuegung stellt. Meine Datei
layouts/partials/page/prev_next.html
, die z.B. in
layouts/block/single.html
eingebunden wird, sieht so aus (unter
Nutzung von Icons):
layouts/partials/page/prev_next.html
|
|
Tagcloud #
Wie in der alten Version, generiert durch ein paar Bashskripte, wollte ich wieder eine Tagcloud . Das ist natuerlich auch mit Hugo moeglich (Taxonomy als Stichwort ).
Diesmal allerdings nicht direkt nativ, d.h. ich habe mir den Code von verschiedenen Quellen angeguckt und schliesslich von Artem Sidorenko uebernommen . Danke!
Die Seite an sich ist die Uebersichtsseite der Tags, die via
layouts/_default/terms.html
modifiziert werden kann. Dort habe ich
einfach ein Partial namens layouts/partials/term_cloud.html
angelegt
mit folgendem Inhalt:
layouts/partials/term_cloud.html
|
|
Mit ausgelagertem CSS waere das natuerlich ebenso moeglich gewesen …
Weitere Inspirationen fuer Tagclouds .
Update (2024-07-07): Da ich letztens ueber Kategorien vs Schlagwörter gestolpert bin: Ich habe den Einsatz von Kategorien in Hugo noch nie verstanden und habe deshalb auch keine im Einsatz. Tags reichen mir vollkommen aus.
URL-Schema #
Dank Hugo ist das sehr einfach moeglich. Generell nutze ich nun keine
Endung (genannt Ugly URLs ) mehr bei den
Eintraegen/Seiten (also z.B. kein $timestamp.htm
bei den
Blockeintraegen), was in Hugo der Default ist. Allerdings
sollten die Blockposts anders dargestellt werden. Das geht in der
Config so :
[permalinks]
block = "/block/:year/:month/:day/:slug/"
Suche #
Update (2022-08-06): Seit Jahren habe ich mich auf externe Suchmaschinen verlassen, wollte aber schon immer eine “eigene” Suche haben. Ich hatte auch schon des oefteren angefangen zu recherchieren, was ich denn nun eigentlich einsetzen will, habe mich aber nicht zu einer Loesung durchringen koennen. Im Raum standen z.B. eine Suche mit Lunr oder mit einem JSON Feed .
Der Nachteil dabei wurde in den letzten Tagen auf Brain Baking beschrieben (dieser Feed oder Index kann je nach Umfang der Website dann natuerlich auch selbst sehr gross sein und muss komplett client-seitig heruntergeladen werden).
Und daher setze ich nun, wie auch viele andere in den letzten Wochen auf das noch recht junge Projekt Pagefind , was die Suche auf allen Seiten vereinfacht, da hier mit Fragmenten gearbeitet wird und der Index erstellt wird, nachdem die Website gebaut wurde 😊
Der Einbau war sehr einfach, ich habe einfach ein Partial
layouts/partials/search-pagefind.html
erstellt und es auf einer Seite
eingebunden:
layouts/partials/search-pagefind.html
|
|
Zusaetzlich habe ich in meinem GitHub Workflow die folgenden Zeilen ergaenzt (der gesamte Workflow steht weiter unten):
- name: Run Pagefind
run: npx -y pagefind --site "public"
Und das wars auch schon, die Suche war nach wenigen Stunden live 🎉
Update (2024-04-02): Ein lang ersehntes Feature war der Autofocus in der Suchleiste (siehe Issue dazu ), was ich nun dank Pagefind v1.1.0 auch endlich einbauen konnte.
Update (2024-10-06):
Um Seiten von der Suche
auszuschliessen habe ich folgendes in
layouts/_default/baseof.html
drin stehen (mehr zu
Onlyfeed):
<main{{ if and (not (in .Params.Tags "onlyfeed")) (ne .Kind "term") }} data-pagefind-body{{ end }}>
Statistik #
Bisher wurde die Seite Statistik mit einem Bashscript gebaut (via Cronjob).
Dabei wurden nicht nur Eintraege, Linkdumps, Rezepte usw. gezaehlt, sondern auch die Haeufigkeit von Suchmaschineneingaben (ausgelesen ueber die Webserverlogs). Das funktioniert mit der Nutzung von GitHub Actions jetzt nicht mehr so einfach (auch die vorherigen Zaehlungen koennen mit Hugo nur schwierig abgebildet werden). Daher halt eben nur eine kleinere Statistik. 😊
Allerdings wuerde ich gerne noch ein paar kleine Dinge mit einbauen:
- die Hugo Statistik, die beim Bauen angezeigt wird
- eine Auflistung aller Dateiendungen sortiert nach der Haeufigkeit
Ersteres laesst sich generieren ueber einen normalen Durchlauf von Hugo, wobei der Output in eine Datei geschrieben wird.
Zweiteres funktioniert mit einem find
-Befehl , der
ebenfalls in eine Datei geschrieben wird.
Beide Dateien werden anschliessend bei einem (zweiten) Durchlauf mit Hilfe
von readfile
eingebunden.
# Hugo Statistik
$ hugo | grep -v "^INFO" | sed '1d'
hugo v0.90.1+extended darwin/amd64 BuildDate=unknown
INFO 2021/12/11 20:14:31 syncing static files to /
| DE
-------------------+-------
Pages | 1099
Paginator pages | 0
Non-page files | 907
Static files | 125
Processed images | 805
Aliases | 287
Sitemaps | 1
Cleaned | 0
Built in 1370 ms
# Auflistung aller Dateiendungen sortiert nach der Haeufigkeit (erstmal nur die letzten 10)
$ find . -type f | grep -oE '\.(\w+)$' | sort | uniq -c | sort | tail
26 .woff
26 .woff2
195 .png
272 .xml
278 .js
512 .md
1057 .html
1663 .svg
1786 .webp
2215 .jpg
Beides habe ich noch nicht umgesetzt, das steht noch auf meiner
TODO-Liste.
Update (2024-04-05):
Beides habe ich mittlerweile umgesetzt.
Hierbei kommt das Update v0.124.0
mit Segments zum
Einsatz. Die genauen Befehle sind im GitHub Workflow
ersichtlich. In meiner hugo.toml
habe ich lediglich folgendes
definiert:
[segments]
[segments.statistics]
[[segments.statistics.includes]]
path = '{/statistics}'
Changelog / Version #
Bisher habe ich Aenderungen in einer Datei namens changelog.txt
festgehalten, diese wurde nun durch eine regulaere Seite
ersetzt. Zusaetzlich gab es noch eine Datei namens version.txt
, die
ist nun aber auch im Changelog aufgegangen.
Lange Zeit hatte ich nur Major- und Minor-Versionen festgehalten, nun kehre ich wieder zu Semantic Versioning zurueck, also zusaetzlich mit Patch-Version.
Schoen waere noch ein automatischer Changelog, zusammengebaut via Git
Log und/oder Tags. Aber mal gucken, steht als Low Prio auf meiner
TODO-Liste.
Update (2024-04-05): Diese Idee habe ich mittlerweile komplett verworfen, siehe dazu den Eintrag zum Changelog.
DSGVO #
Es gibt zwar ein paar Moeglichkeiten, die Standard-Shortcodes etwas datenschutzfreundlicher zu gestalten (Bountysource ), allerdings gehen diese mir nicht weit genug. So wird bei einem Seitenaufruf immer noch Zeugs von anderen Websites nachgeladen, was ich natuerlich nicht will (und ihr wohl auch nicht).
Daher habe ich mir die Muehe gemacht, selber was zu basteln.
gist
und twitter
kommen ohne sonstige Skripte
aus, bei youtube
und vimeo
ist noch ein Skript
notwendig, was das Thumbnail runterlaedt und auf die passende Groesse
von 560x560
(siehe geometry
bei imagemagick
)
umwandelt. instagram
habe ich bisher nicht gebraucht und
github
(als Alternative zu gist
) sowie
osm
habe ich neu gebaut. spotify
wollte ich auch
noch bauen, aber die API ist doof 😉
Ansonsten kann noch durch interne Templates Google Analytics sowie Disqus verwendet werden. Ersteres ist bei mir nicht im Einsatz und selbst wenn sollte lieber Matomo , umami , Plausible Analytics oder GoatCounter verwendet werden. Auf Zweiteres gehe ich unter Kommentare naeher drauf ein.
Gist #
Die Einbindung von Gists ist mir nicht datensparsam genug
(es werden Sachen von GitHub nachgeladen). Zum Glueck gibt es die
Moeglichkeit, per API u.a. den Code abzufragen. Im Gegensatz
zu dem offiziellen Hugo Shortcode benoetigt mein
Shortcode (layouts/shortcodes/gist.html
) mit “Positional
Parameters” nicht den Namen, sondern nur die Gist ID und
ggf. einen Dateinamen, wenn der Gist mehrere Dateien enthaelt, aber nur
eine angezeigt werden soll:
layouts/shortcodes/gist.html
|
|
Die Magie passiert via getJSON
, das Styling passiert
via CSS bzw. Tailwind CSS und ist in
Codebloecke naeher beschrieben.
Ein Beispiel Gist mit mehreren Dateien , wovon nur eine angezeigt werden soll, sieht dann so aus:
{{< gist a900acbf7140c18217dc7a1679c52114 "example-bower.json" >}}
|
|
GitHub #
Ganz aehnlich zu dem Shortcode fuer ein Gist ist
layouts/shortcodes/github.html
mit “Named Parameters”
aufgebaut:
layouts/shortcodes/github.html
|
|
In diesem Shortcode muss die Sprache mit angegeben werden, da die
API diese leider nicht mit ausgibt. Zusaetzlich gibt es
noch die Moeglichkeit, dem Shortcode highlight
Optionen
mitzugeben:
{{< github repo="tohn/aa3d-tools" file="dm2txt.py" lang="py3" >}}
|
|
Eine Alternative kommt vom Hugo Entwickler Joe Mooring : Syntax highlighting for GitHub files .
Instagram #
Der Standard-Shortcode von Instagram will ich nicht einsetzen, da er direkt Daten von Instagram nachlaedt. Allerdings braucht die API viel zu viele Daten von mir und daher ganz ehrlich: war mir zuviel Arbeit, kommt vielleicht irgendwann™.
Kommentare #
Vor Jahren habe ich mich ja eigentlich gegen Kommentare entschieden. Allerdings bringt Hugo schon Support fuer Disqus mit , was die Einbindung natuerlich recht einfach macht.
Wobei Disqus aus Datenschutzgruenden natuerlich nicht die beste Wahl ist. Auf der Seite im Wiki werden aber Alternativen aufgelistet .
Isso kenne ich schon sehr lange und hatte es probeweise auch schon im Einsatz, allerdings habe ich es noch nicht mit Docker am Laufen. Und die Python-Dependency-Hell will ich mir nicht nativ antun.
tl;dr Kommentare will ich haben, allerdings habe ich mich noch nicht fuer eine Loesung entschieden, daher ist das noch ein TODO auf meiner Liste 😉
OpenStreetMaps #
Statt Google Maps sollte OpenStreetMaps verwendet werden. Doch wie datenschutzfreundlich einbinden? Denn der Standard Embed-Code laedt $Dinge direkt von OpenStreetMaps …
<iframe width="425" height="350" frameborder="0" scrolling="no" marginheight="0" marginwidth="0" src="https://www.openstreetmap.org/export/embed.html?bbox=-10.315990447998049%2C51.53373644524688%2C-10.150337219238283%2C51.62846065474101&layer=mapnik&marker=51.5810798959352%2C-10.233279168605804" style="border: 1px solid black"></iframe><br/><small><a href="https://www.openstreetmap.org/?mlat=51.5811&mlon=-10.2333#map=13/51.5811/-10.2332">View Larger Map</a></small>
Daher habe ich folgenden Shortcode layout/shortcodes/osm.html
gebaut:
layouts/shortcodes/osm.html
|
|
Hierbei wird erstmal nur ein Bild mit Text im Vordergrund angezeigt.
Erst bei Klick auf das Icon () in der Mitte
wird die Karte in einem Iframe nachgeladen. Alternativ kann der Link
unter dem <div>
genutzt werden, der direkt auf die
OpenStreetMap-Website verlinkt.
Den Code dafuer habe ich fuer youtube
uebernommen aus
einem Beitrag von Florian Meier (Code auf GitLab )
mit einigen Anpassungen und ihn dann fuer OpenStreetMap nochmals
angepasst:
-
Nutzung von
getJSON
bzw. der YouTube-API , um Informationen ueber das Video zu bekommen und in dem Container darzustellen (in diesem Fall nicht notwendig) und damit auch Anpassung des Textes -
Nutzung eines Icons von Font Awesome als Nachlade-Button
-
Nutzung eines Scriptes, um das Vorschaubild im Hintergrund runterzuladen (mehr dazu in
vimeo
); in diesem Fall wird ein statisches Bild genutzt, siehe unten
Der notwendige CSS-Code zum Stylen ist dank Tailwind CSS direkt im Shortcode enthalten.
Und hier noch das Bild, was im Hintergrund angezeigt wird (abgewandelt vom Original ):
Abgeleitet vom Embed-Code sieht mein Shortcode dann wie folgt aus:
{{< osm "-10.315990447998049%2C51.53373644524688%2C-10.150337219238283%2C51.62846065474101" "51.5810798959352%2C-10.233279168605804" "51.5811" "-10.2333" "13" >}}
Die Karte wird von OpenStreetMap eingebettet.
Es gelten die Datenschutzerklaerungen von OpenStreetMap .
Spotify #
Aehnlich zu osm
, vimeo
und youtube
habe ich mir auch das einfach selbst gebastelt bzw. es erstmal nur
versucht.
Denn das Problem war: Hugo konnte noch nicht mit
POST-Requests umgehen. Zuerst hatte ich mir daher mit
curl
einen Access Token besorgt und den dann mit Hugo genutzt.
Allerdings ist all das jetzt mit resources.GetRemote
(seit Version 0.91.0 ) moeglich 🎉
Dank der kurzen Haltbarkeit des access_token
s von nur einer Stunde ist
die lokale Entwicklung aber sehr nervig. Daher im Folgenden nur ein
Proof of Concept .
Nach etwas Hilfe aus der Community hatte ich dann
auch meinen Shortcode layouts/shortcodes/spotify.html
zusammengebaut:
layouts/shortcodes/spotify.html
|
|
Um mit der Spotify-API zu reden, brauchen wir eine ID
und ein Secret . Die koennen wir in Hugo mit
Environment Variablen einbinden. Ich habe dafuer eine gute
Loesung gefunden, diese Variablen abhaengig vom Ordner einzubinden,
naemlich mit direnv
🎉
Nach der Installation und der Einbindung in die
Shell , erstellen wir im aktuellen Hugo-Verzeichnis eine
Datei .envrc
:
export HUGO_SECRET_SPOTIFY_ID="012"
export HUGO_SECRET_SPOTIFY_SECRET="789"
Diese Datei erlauben wir jetzt mit direnv allow .
und koennen danach
Hugo starten:
hugo server -v -D
Nun werden die Variablen genutzt und die API kann angesprochen werden 🎉
Um jetzt noch die Hintergrundbilder runterladen zu koennen, habe ich
folgendes Script in bin/sm.sh
gebaut, was rg
(ripgrep, einfach
viel schneller als grep
), youtube-dl
(nur fuer
Vimeo und YouTube) und convert
(imagemagick
) benoetigt:
|
|
Das Styling ist nun auch dank Tailwind CSS direkt im Shortcode
enthalten. Als Hover Farbe des Buttons habe ich #1db954
im Einsatz (um die genaue Farbe von Logos/Brands herauszufinden,
kann z.B. BrandColors genutzt werden).
Insgesamt sieht der Shortcode dann wie folgt aus:
{{< spotify album 25r7pEf31viAbsoVHC6bQ4 >}}
Da wie oben beschrieben der access_token
nur fuer eine Stunde gueltig
ist, muesste ich lokal entweder jede Stunde Hugo neu starten oder den
Parameter --ignoreCache=true
anhaengen:
hugo server -v -D --ignoreCache=true
Da beides mega nervig ist, gibt es den Spotify Shortcode nur als Bilder 😉
Update (2024-07-07): Vom Hugo Entwickler Joe Mooring gibt es Spotify widgets , die ich aber so nicht nutzen wuerde (DSGVO und so).
Twitter #
Twitter habe ich dank Elon Musk nicht mehr im Einsatz.
Vimeo #
Wie bei osm
schon beschrieben, habe ich den Code fuer
youtube
auch fuer Vimeo verwendet, da mir auch hier der
Standard-Shortcode nicht weit
genug geht. Angepasst fuer Vimeo sieht
layouts/shortcodes/vimeo.html
so aus:
layouts/shortcodes/vimeo.html
|
|
Das Styling ist wieder direkt im Shortcode, als Hover Farbe des Buttons
habe ich #1ab7ea
genutzt.
Um mit der Vimeo-API zu reden, brauchen wir ein
Secret . Das koennen wir in Hugo mit Environment
Variablen einbinden. Hierzu kann die Loesung, die ich bei
Spotify beschrieben habe ebenfalls verwendet werden. Die
.envrc
habe ich einfach mit der folgenden Zeile erweitert:
export HUGO_SECRET_VIMEO="abc"
Um jetzt noch die Hintergrundbilder runterladen zu koennen, habe ich das
Script bin/sm.sh
von Spotify erweitert:
|
|
Insgesamt sieht der Shortcode dann wie folgt aus:
{{< vimeo 22439234 >}}
Das Video The Mountain (ID: 22439234) wird von Vimeo eingebettet.
Es gelten die Datenschutzerklaerungen von Vimeo .
Optional kann noch die Startzeit in Sekunden mit angegeben werden:
{{< vimeo 22439234 83 >}}
Das Video The Mountain (ID: 22439234) wird von Vimeo eingebettet.
Es gelten die Datenschutzerklaerungen von Vimeo .
YouTube #
Im Prinzip wurde ja alles schon weiter oben beschrieben. Der
Standard-Shortcode gefaellt mir nicht ,
daher habe ich selbst was gebastelt in
layouts/shortcodes/youtube.html
, mit den hier
folgenden Influenzen , wobei ich mich mehr an dem Beitrag
von Florian Meier (siehe osm
) orientiert habe:
layouts/shortcodes/youtube.html
|
|
Und auch hier ist das Styling dank Tailwind CSS direkt im Shortcode
drin. Als Hover Farbe des Buttons habe ich #ff0000
genutzt.
Um mit der YouTube-API zu reden, brauchen wir ein
Secret . Auch dieses Mal koennen wir mit Environment
Variablen und direnv (siehe vimeo
) das Secret
mit Hugo nutzen und erweitern die .envrc
um folgende Zeile:
export HUGO_SECRET_YOUTUBE="xyz"
Und ebenfalls wie bei vimeo
kann auch das Shellscript zum Runterladen
der Hintergruende genutzt werden. Das waere noch was fuer meine
TODO-Liste, dass das via Pipeline passieren kann (der Durchlauf wuerde
dann aber laenger dauern).
Und so sieht dann der Shortcode aus:
{{< youtube beTqiiV5zhI >}}
Das Video Kinicles (ID: beTqiiV5zhI) wird von YouTube eingebettet.
Es gelten die Datenschutzerklaerungen von Google .
Optional kann noch die Startzeit in Sekunden mit angegeben werden:
{{< youtube beTqiiV5zhI 83 >}}
Das Video Kinicles (ID: beTqiiV5zhI) wird von YouTube eingebettet.
Es gelten die Datenschutzerklaerungen von Google .
Mehrsprachigkeit / i18n #
Mehrsprachigkeit ist mit Hugo recht leicht moeglich . Eine
Loesung fuer einzelne Artikel/Seiten etc. wird bei Fryboyter
beschrieben . Noch bin ich unsicher, ob ich das
ueberhaupt will, daher lasse ich das erstmal offen.
Zu ueberlegen waere dann noch, wie die URL aufgebaut sein soll, z.B.
/en/artikel/
oder /artikel-en/
oder /artikel/en/
oder …
Structured Data / Schema.org #
Weil ich meine Rezepte bei Tandoor importieren wollte, ist mir aufgefallen, dass es dafuer eine Loesung namens structured data gibt, damit das auch einfach gelingt und schoen aussieht.
Also habe ich mal mit ein wenig Hilfe meine Rezepte umgebaut, sodass sie auch valides structured data ausspucken. Die Kategorien usw. habe ich mir ein bisschen bei der Rezepteingabe bei Chefkoch abgeguckt. Weitere Quellen:
- https://developers.google.com/search/docs/advanced/structured-data/recipe
- https://gohugo-amp.gohugohq.com/schema/recipe/
Testen laesst sich das dann mit dem Test fuer
Rich-Suchergebnisse .
Bei der weiteren Beschaeftigung damit, habe ich es mal zumindest noch
fuer die Startseite eingebaut. Weitere Seiten werden vermutlich folgen.
Die Einbindung ist recht simpel und habe ich mir in diesem
hervorragenden Repository etwas abgeguckt. Ich habe
einfach in meiner _default/baseof.html
folgendes vor dem schliessenden
</head>
hinzugefuegt:
{{ partial "schema/schema.html" . }}
</head>
Das Partial layouts/partials/schema/schema.html
sieht dann so aus:
Das Schema fuer Website sieht dann z.B. so aus:
{{ $author := or (.Params.author) (.Site.Params.Author.name) }}
{{ $description := .Site.Params.description }}
<script type="application/ld+json">
{
"@context": "http://schema.org",
"@type": "WebSite",
"name": "{{ .Site.Title }}",
"url": {{ .Site.BaseURL }},
"description": "{{ $description }}",
"thumbnailUrl": {{ .Site.Params.logo | absURL }},
"license": "{{ .Site.Params.copyright }}"
}
</script>
Hier noch weitere Quellen:
- https://code.luasoftware.com/tutorials/hugo/schema-markup-for-hugo/
- https://codingnconcepts.com/hugo/structure-data-json-ld-hugo/
- https://dev.to/pdwarkanath/adding-structured-data-to-your-hugo-site-58db
- https://discourse.gohugo.io/t/marking-up-json-ld/1154/14
- https://discourse.gohugo.io/t/semantic-web-for-hugo-for-testing/35219/2
- https://github.com/gohugoio/hugo/blob/master/tpl/tplimpl/embedded/templates/schema.html bzw. https://gohugo.io/templates/internal/#the-internal-templates
- https://semweb.youronly.one
Neue Domain #
Im Laufe der Jahre fielen mir immer wieder neue und vor allem kuerzere
Domains ein. Schliesslich habe ich mich fuer uxg.ch
entschieden.
Warum? Auf vielen Geraeten lautet mein Username seit langem benjo
, was
(abgeleitet von der Caesar-Verschluesselung ) ein ROT1
von admin
ist. Da es keine TLD .jo
gibt, habe ich einfach mal
durchrotiert und bin bei “ROT20” haengen geblieben. Die Domain war
verfuegbar, ist kurz (gerade im Vergleich zu yhaupenthal.org
),
unterstuetzt DNSSEC und war nicht mega teuer, also habe ich
sie gekauft 🎉
Workflow #
So, und wie funktioniert nun das alles im Zusammenspiel?
GitHub Action #
Zuerst legen wir einen GitHub Workflow an (im Repo in
.github/workflows/test_build_deploy.yml
):
.github/workflows/test_build_deploy.yml
|
|
Im Repo selbst muessen wir nun noch Secrets anlegen , die die entsprechenden Daten enthalten. Also z.B. das Secret fuer die YouTube API oder die SSH-Daten zum Server.
Update (2024-03-30): Bei jedem Push werden erst 2 Testjobs ausgefuehrt (die ich jetzt via reusable Workflow einbinde) und bei erfolgreichem Status der 3. Job (der ein bisschen von Composite Actions Gebrauch macht):
-
Fortlaufend den Code mit Lintern etc. zu ueberpruefen sollte eigtl. Standard sein. Ich nutze in neovim dazu ALE , was Support fuer Markdown via markdownlint mitbringt.
Meine
.markdownlintrc
sieht so aus:{ "comment": "https://github.com/DavidAnson/markdownlint / https://github.com/noqqe/noqqe.de/blob/master/.markdownlintrc", "MD013": false, "MD026": true, "MD033": true }
-
Auch meine Shellscripts will ich fortlaufend ueberpruefen und nutze hierzu wieder ALE mit dem Tool shellcheck .
-
Build & Deploy
Waren beide vorherigen Jobs erfolgreich, folgt der dritte. Dieser checkt erstmal das Repo aus (mit History fuer Git Info Variablen ), installiert dann node.js , Hugo und die npm-Pakete , die in der
packages.json
angegeben sind. Schliesslich wird unsere Website mit Minifizierung von Hugo gebaut und in einem weiteren Schritt auf unseren Webserver hochgeladen. Auf entsprechende Vorkehrungen auf dem Server gehe ich jetzt nicht ein, aber ein Artikel dazu (siehe Hosting) ist in Arbeit. Ebenso das Ersetzen vonnpm
durch Hugo Modules . -
…
-
Profit 🎉
GitLab CI #
Als Alternative hier die Vorgehensweise mit der GitLab CI (trotz Repo bei GitHub ). Wie auch bei GitHub muessen wir unsere Secrets im Repo hinterlegen .
Ansonsten funktioniert die ganze Magie ueber die .gitlab-ci.yml
, was
im Prinzip die gleichen Steps durchlaeuft wie die Action bei
GitHub (nur mit evtl. alten Paketen, weil ich die
Docker Images schon laenger nicht mehr aktualisiert
habe):
.gitlab-ci.yml
|
|
Lokal #
Dank Archetypes und Frontmatter legen wir anschliessend eine
Datei namens archetypes/block.md
an:
Es reicht dann folgender Befehl, um eine Datei anzulegen:
$ hugo new block/beispiel.md
/path/to/website/content/block/beispiel.md created
Diese Datei koennen wir nun oeffnen und beliebig aendern. Die Datei wird
anschliessend zum Git Index hinzugefuegt (git add content/block/beispiel.md
), commited (git commit -va
) und die
Aenderungen gepusht (git push origin main
).
Der Push loest dann auf GitHub die oben beschriebene Action aus und wir sollten nach kurzer Zeit eine neue Version auf der Website sehen 🎉
Die ganzen technischen Dinge im Hintergrund (also Server an sich, ssh
,
nginx
usw.) versuche ich noch in einem anderen Beitrag zu beschreiben.
Und … das wars! Ueber 3 Jahre Arbeit, um meine Website auf einen neuen technischen Stand zu heben 🙈 😅 🎉
Influenzen / Nuetzliches #
- https://github.com/noqqe/noqqe.de
- https://grh.am/2018/deploying-a-hugo-static-site-using-gitlab-ci-cd-and-ssh/
- https://noqqe.de/blog/2015/04/11/hugo-templating/
- https://alison.rbind.io/post/2017-06-12-up-and-running-with-blogdown/#
- https://robjhyndman.com/about/
- https://livefreeordichotomize.com/2016/12/15/hill-for-the-data-scientist-an-xkcd-story/
- https://laurakalbag.com/processing-responsive-images-with-hugo/
- https://zwbetz.com/make-a-hugo-blog-from-scratch/
- https://www.henryskup.com/pages/colophon/
- https://css-tricks.com/advice-for-technical-writing/
- https://github.com/henriksommerfeld/blog-hugo
- https://www.jakewiesler.com/blog/hugo-directory-structure/
- https://hugo-multiauthor-example.netlify.com/authors/cupcake-lover/
- https://github.com/imorente/hugo-multiauthor-example/blob/master/site/config.toml
- https://fn.lc/post/hugo-multiple-lists/
- https://gohugo.io/content-management/taxonomies
- https://solar.lowtechmagazine.com/2018/09/how-to-build-a-lowtech-website/
- https://www.ii.com/portals/
- https://github.com/gohugoio/hugo/tree/master/docs/layouts/shortcodes
- https://github.com/spf13/spf13.com/tree/master/layouts/shortcodes
- https://github.com/gohugoio/hugo/tree/master/tpl/tplimpl/embedded/templates/shortcodes
- https://fossies.org/linux/hugo/docs/content/en/content-management/shortcodes.md
- https://kollitsch.dev/blog/2022/gitignore-gohugo/
- https://www.brycewray.com/posts/2022/07/really-getting-started-hugo-four-steps/
-
Laeuft 🎉 ↩︎