Hugo Themes mit Tailwind CSS v3.x (inklusive Live-Reload)

·4 min·foundata-Team·

Wir haben im August 2023 foundata.com neu gestaltet. Wir nutzen den Static Site Generator Hugo mit einem eigens entwickeltem Theme. Als CSS-Framework kommt dabei Tailwind CSS v3 zum Einsatz.

Anstatt mit Corporate-Sprech ĂŒber die Großartigkeit unseres Website-Relaunch zu langweilen 😃, will ich an dieser Stelle einige Tipps fĂŒr Hugo-Theme-Entwickler teilen, die ebenfalls Tailwind CSS v3.x und dessen JIT-Compiler nutzen wollen.

Das Web ist bereits voll von nĂŒtzlichen Ressourcen zu Hugo und Tailwind CSS.1 Wozu es jedoch weiterhin wenig Informationen gibt, ist die Anbindung und Nutzung von Tailwind CSS v3.x und dessen JIT-Compiler bei der Entwicklung eines Hugo-Templates. Insbesondere, wie man einen sinnvollen Entwicklungsmodus mit Live-Reload fĂŒr beides baut, ist wenig bis gar nicht beschrieben. Im folgenden will ich daher darstellen, wie wir dies in unseren Hugo-Template umsetzen.2

Beispielseite und Live-Reload bei Änderungen mit hugo server

Hugo bietet einen eingebauten Web-Server. Dies bei der Template-Entwicklung so nĂŒtzlich, da dieser automatisch liverelaod-js in die Seiten einbaut und diese so bei Änderungen nicht nur neu generiert, sondern der Browser ebenfalls die neuen Inhalte laden kann. Man kann so als Entwickler einfach seinen Browser offen lassen, am Template oder den Beispielinhalten eine Änderung speichern und die Seite lĂ€dt direkt neu. Ein sehr angenehmes entwickeln.

Das zu nutzende Content-Verzeichnis und das Theme können ĂŒber die Parameter --source und --themesDir definiert werden. Dies ermöglicht, dass man in seinem Theme eine komplette Beispiel-Website inklusive Konfiguration verwalten kann, die alle Features des Themes nutzt und verschiedene TextlĂ€ngen etc. pp. zur Optimierung der Typografie wĂ€hrend der Entwicklung bereitstellt. Wir haben dies unter exampleSite getan.

Ich nutze den folgende Aufruf, um den Webserver mit unseren Beispielinhalten und dem Theme zu starten (eine ErklÀrung der einzelnen Parameter findet sich in der Dokumentation zu hugo server):

hugo server \
  --source ./exampleSite \
  --themesDir ../.. \
  --baseURL http://localhost/ \
  --port 1313 \
  --buildDrafts \
  --cleanDestinationDir \
  --logLevel debug \
  --disableFastRender \
  --gc \
  --minify \
  --noHTTPCache \
  --printI18nWarnings \
  --renderToDisk \
  --templateMetrics \
  --templateMetricsHints

Zu beachten: --logLevel debug wurde mit v0.114.0 eingefĂŒhrt. Wenn die eigene Hugo-Version Ă€lter ist, muss man stattdessen --debug nutzen.

Neugenerierung der Tailwind CSS-Styles bei Änderungen via JIT-Compiler

Tailwind CSS wird ĂŒblicherweise via NodeJS installiert. Das npx tailwindcss-Kommando bietet ebenfalls eine DateiĂ€nderungsĂŒberwachung (--watch-Parameter) mit automatischer Neugenerierung der Styles ĂŒber den JIT-Compiler.

Die CSS-Quelldatei unseres Hugo-Themes liegt unter ./assets/styles/tw-main-src.css, die Ausgabedatei wird unter ./assets/styles/tw-main-dist.css generiert. Hierzu dient das folgende Kommando:

NODE_ENV=development npx tailwindcss \
  -i ./assets/styles/tw-main-src.css \
  -o ./assets/styles/tw-main-dist.css \
  --watch --minify

Die Datei tailwind.config.js definiert, welche Verzeichnisse des Hugo-Templates nach Styles durchsucht werden sollen. In unseren Template sieht dies wie folgt aus:

//[...]
module.exports = {
  /* Content sources to tell Tailwind which files to scan for used class names for
     generating the needed CSS. More info:
     - https://tailwindcss.com/docs/content-configuration#configuring-source-paths
  */
  content: [
    "./layouts/**/*.html",
    "./content/**/*.{html,md}",
    "./themes/foundata/layouts/**/*.html",
    "./exampleSite/**/*.{html,md}",
  ],
  //[...]
}

In der Datei ./themes/foundata/layouts/_default/baseof.html sind dabei die folgende Zeilen fĂŒr das Laden der so generierten CSS-Datei zustĂ€ndig:

  {{/* Use a Hugo scratch to store files and content for bundling CSS and JS assets */}}
  {{ $assets := newScratch }}

  {{/* TailwindCSS */}}
  {{ $assets.Add "css" (slice (resources.Get "styles/tw-main-dist.css")) }} {{/* output file for distribution */}}

  [...]

  {{/* Get the scratch's CSS content as bundled, optimized file */}}
  {{ if $assets.Get "css" }}
    {{ $bundleCSS := $assets.Get "css" | resources.Concat "styles/bundle.css" | resources.Minify | resources.Fingerprint "sha256") }}
    <link type="text/css" rel="stylesheet" href="{{ $bundleCSS.RelPermalink }}" integrity="{{ $bundleCSS.Data.Integrity }}">
  {{ end }}

Alles zusammen nutzen via packages.json

Da NodeJS genutzt wird um die Tailwind CSS-Installation zu verwalten, liegt im root unseres Templates eine packages.json. Diese kann auch genutzt werden, um die oberen Befehle komfortabel zusammenzufĂŒhren.

Wir haben diese dazu in einzelne scripts gepackt, die via npm run <name> aufgerufen werden können. Das npm-run-all-Paket gibt uns die Möglichkeit, alles komfortabel ohne Blockaden zu verketten. In unserer packages.json sieht dies wie folgt aus:

//[...]
{
  "name": "hugo-foundata-theme",
  "version": "1.0.0",
  "description": "foundata theme for Hugo",
  "scripts": {
    [...]
    "dev": "npm-run-all --parallel dev:tailwindcss dev:example --print-name --race",
    "dev:tailwindcss": "touch ./assets/styles/tw-main-dist.css && NODE_ENV=development npx tailwindcss -i ./assets/styles/tw-main-src.css -o ./assets/styles/tw-main-dist.css --watch --minify",
    "dev:example": "hugo server --source ./exampleSite --themesDir ../.. --baseURL http://localhost/ --port 1313 --buildDrafts --cleanDestinationDir  --disableFastRender --gc --minify --noHTTPCache --printI18nWarnings --renderToDisk --templateMetrics --templateMetricsHints",
    "build:vendorlibs": "npm-run-all preinstall postinstall --print-name",
    "build:tailwindcss": "NODE_ENV=production npx tailwindcss -i ./assets/styles/tw-main-src.css -o ./assets/styles/tw-main-dist.css --minify"
  },
  //[...]
}

Das war’s. Um am Theme zu arbeiten wechselt man einfach in das Theme-Verzeichnis, fĂŒhrt npm run dev aus und kann unter http://localhost:1313 die Hugo-Beispielseite aufrufen. Sie wird automatisch bei jeder Änderung an einem Style, Template oder einem Beispielinhalt direkt neu generiert und dann im Browser automatisch neu geladen. Dies ist insbesondere wegen des Utility-First-Ansatzes von Tailwind CSS sehr passend.


  1. Neben der offiziellen Dokumentation fand ich insbesondere https://www.regisphilibert.com/tags/hugo/ sehr hilfreich. ↩︎

  2. Die Hugo Version v0.112.0 hat ebenfalls eine neue Option zur besseren Einbindung von Tailwind CSS v3.x eingefĂŒhrt. Wir verfolgen aber einen etwas anderen Ansatz. ↩︎