<?xml version="1.0" encoding="utf-8" standalone="yes"?><feed xmlns="http://www.w3.org/2005/Atom" xml:base="https://foundata.com/" xml:lang="de"><title>Hugo auf foundata</title><id>https://foundata.com/de/tags/hugo/feed-atom.xml</id><link rel="self" type="application/atom+xml" hreflang="de" href="https://foundata.com/de/tags/hugo/feed-atom.xml" title="atom"/><link rel="alternate" type="text/html" hreflang="de" href="https://foundata.com/de/tags/hugo/" title="html"/><link rel="alternate" type="application/rss+xml" hreflang="de" href="https://foundata.com/de/tags/hugo/feed-rss.xml" title="rss"/><link rel="alternate" type="application/atom+xml" hreflang="en" href="https://foundata.com/en/tags/hugo/feed-atom.xml" title="atom, English"/><link rel="alternate" type="text/html" hreflang="en" href="https://foundata.com/en/tags/hugo/feed-atom.xml" title="html, English"/><link rel="alternate" type="application/rss+xml" hreflang="en" href="https://foundata.com/en/tags/hugo/feed-atom.xml" title="rss, English"/><updated>2023-08-28T17:06:00Z</updated><author><name>foundata GmbH</name><email>webmaster@foundata.com</email><uri>https://foundata.com/</uri></author><rights>© 2023-2026, foundata GmbH (https://foundata.com)</rights><icon>https://foundata.com/images/feed-icon.67ff83c698af1511552374e80cf5f6ff26d497ef21f04186cf058859d535ca75.svg</icon><logo>https://foundata.com/images/feed-logo.9138f24a120dabc7e3d34003662131cc9c7f2ff153ec39d2f6dea3a48c35b4bf.svg</logo><entry><title type="html">Hugo Themes mit Tailwind CSS v3.x (inklusive Live-Reload)</title><id>tag:foundata.com,2023-08-28:/de/blog/2023/hugo-theme-tailwind-css-jit-livereload/</id><published>2023-08-28T17:06:00Z</published><updated>2023-08-28T17:06:00Z</updated><link href="https://foundata.com/de/blog/2023/hugo-theme-tailwind-css-jit-livereload/?utm_source=feed-atom" rel="alternate" type="text/html"/><link href="https://foundata.com/en/blog/2023/hugo-theme-tailwind-css-jit-livereload/?utm_source=feed-atom" rel="alternate" type="text/html" hreflang="en"/><author><name>Andreas Haerter</name><uri>https://andreashaerter.com/</uri></author><summary type="html">Tipps zur Nutzung von Tailwind CSS v3.x und dessen JIT-Compiler in Hugo Themes, inklusive Live-Reload.</summary><content type="html" xml:base="https://foundata.com/"><![CDATA[<p>Wir haben im August 2023 <a href="https://foundata.com/de/" target="_blank" rel="noreferrer noopener">foundata.com</a> neu gestaltet. Wir nutzen den <a href="https://en.wikipedia.org/wiki/Static_site_generator" target="_blank" rel="noreferrer noopener">Static Site Generator</a> <a href="https://gohugo.io/" target="_blank" rel="noreferrer noopener">Hugo</a> mit einem eigens entwickeltem Theme. Als CSS-Framework kommt dabei <a href="https://tailwindcss.com/blog/tailwindcss-v3" target="_blank" rel="noreferrer noopener">Tailwind CSS v3</a> zum Einsatz.</p>
<p>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 <a href="https://tailwindcss.com/blog/just-in-time-the-next-generation-of-tailwind-css" target="_blank" rel="noreferrer noopener">JIT-Compiler</a> nutzen wollen.</p>
<p>Das Web ist bereits voll von nützlichen Ressourcen zu Hugo und Tailwind CSS.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> 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.<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup></p>
<h2 id="beispielseite-und-live-reload-bei-änderungen-mit-hugo-server" class="scroll-mt-20 md:scroll-mt-24 wrap-break-word group/heading "><a href="#beispielseite-und-live-reload-bei-%c3%a4nderungen-mit-hugo-server" class="group-hover/heading:after:content-['#'] group-hover/heading:after:ml-1.5 group-hover/heading:after:text-content-400" id="beispielseite-und-live-reload-bei-änderungen-mit-hugo-server">Beispielseite und Live-Reload bei Änderungen mit <code>hugo server</code></a></h2><p><a href="https://gohugo.io/commands/hugo_server/" target="_blank" rel="noreferrer noopener">Hugo bietet einen eingebauten Web-Server</a>. Dies bei der Template-Entwicklung so nützlich, da dieser automatisch <a href="https://github.com/livereload/livereload-js" target="_blank" rel="noreferrer noopener">liverelaod-js</a> 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.</p>
<p>Das zu nutzende Content-Verzeichnis und das Theme können über die Parameter <code>--source</code> und <code>--themesDir</code> 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 <code>exampleSite</code> getan.</p>
<p>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 <a href="https://gohugo.io/commands/hugo_server/" target="_blank" rel="noreferrer noopener">Dokumentation zu <code>hugo server</code></a>):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">hugo server <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  --source ./exampleSite <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  --themesDir ../.. <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  --baseURL http://localhost/ <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  --port <span class="m">1313</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  --buildDrafts <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  --cleanDestinationDir <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  --logLevel debug <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  --disableFastRender <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  --gc <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  --minify <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  --noHTTPCache <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  --printI18nWarnings <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  --templateMetrics <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  --templateMetricsHints
</span></span></code></pre></div><p>Zu beachten: <code>--logLevel debug</code> wurde mit <a href="https://github.com/gohugoio/hugo/releases/tag/v0.114.0" target="_blank" rel="noreferrer noopener">v0.114.0</a> eingeführt. Wenn die eigene Hugo-Version älter ist, muss man stattdessen <code>--debug</code> nutzen.</p>
<h2 id="neugenerierung-der-tailwind-css-styles-bei-änderungen-via-jit-compiler" class="scroll-mt-20 md:scroll-mt-24 wrap-break-word group/heading "><a href="#neugenerierung-der-tailwind-css-styles-bei-%c3%a4nderungen-via-jit-compiler" class="group-hover/heading:after:content-['#'] group-hover/heading:after:ml-1.5 group-hover/heading:after:text-content-400" id="neugenerierung-der-tailwind-css-styles-bei-änderungen-via-jit-compiler">Neugenerierung der Tailwind CSS-Styles bei Änderungen via JIT-Compiler</a></h2><p>Tailwind CSS wird üblicherweise <a href="https://tailwindcss.com/docs/installation" target="_blank" rel="noreferrer noopener">via NodeJS installiert</a>. Das <code>npx tailwindcss</code>-Kommando bietet ebenfalls eine Dateiänderungsüberwachung (<code>--watch</code>-Parameter) mit automatischer Neugenerierung der Styles über den <a href="https://tailwindcss.com/blog/just-in-time-the-next-generation-of-tailwind-css" target="_blank" rel="noreferrer noopener">JIT-Compiler</a>.</p>
<p>Die CSS-Quelldatei unseres Hugo-Themes liegt unter <code>./assets/styles/tw-main-src.css</code>, die Ausgabedatei wird unter <code>./assets/styles/tw-main-dist.css</code> generiert. Hierzu dient das folgende Kommando:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nv">NODE_ENV</span><span class="o">=</span>development npx tailwindcss <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  -i ./assets/styles/tw-main-src.css <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  -o ./assets/styles/tw-main-dist.css <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  --watch --minify
</span></span></code></pre></div><p>Die Datei <code>tailwind.config.js</code> definiert, welche Verzeichnisse des Hugo-Templates nach Styles durchsucht werden sollen. In unseren Template sieht dies wie folgt aus:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="c1">//[...]
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="err">module.exports</span> <span class="err">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="err">/*</span> <span class="err">Content</span> <span class="err">sources</span> <span class="err">to</span> <span class="err">tell</span> <span class="err">Tailwind</span> <span class="err">which</span> <span class="err">files</span> <span class="err">to</span> <span class="err">scan</span> <span class="err">for</span> <span class="err">used</span> <span class="err">class</span> <span class="err">names</span> <span class="err">for</span>
</span></span><span class="line"><span class="cl">     <span class="err">generating</span> <span class="err">the</span> <span class="err">needed</span> <span class="err">CSS.</span> <span class="err">More</span> <span class="err">info:</span>
</span></span><span class="line"><span class="cl">     <span class="err">-</span> <span class="err">https:</span><span class="c1">//tailwindcss.com/docs/content-configuration#configuring-source-paths
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="err">*/</span>
</span></span><span class="line"><span class="cl">  <span class="err">content:</span> <span class="err">[</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;./layouts/**/*.html&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;./content/**/*.{html,md}&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;./themes/foundata/layouts/**/*.html&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;./exampleSite/**/*.{html,md}&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="err">],</span>
</span></span><span class="line"><span class="cl">  <span class="c1">//[...]
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></div><p>In der Datei <code>./themes/foundata/layouts/_default/baseof.html</code> sind dabei die folgende Zeilen für das Laden der so generierten CSS-Datei zuständig:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-golang" data-lang="golang"><span class="line"><span class="cl"><span class="w">  </span><span class="p">{{</span><span class="cm">/* Use a Hugo scratch to store files and content for bundling CSS and JS assets */</span><span class="p">}}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="p">{{</span><span class="w"> </span><span class="err">$</span><span class="nx">assets</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">newScratch</span><span class="w"> </span><span class="p">}}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="p">{{</span><span class="cm">/* TailwindCSS */</span><span class="p">}}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="p">{{</span><span class="w"> </span><span class="err">$</span><span class="nx">assets</span><span class="p">.</span><span class="nx">Add</span><span class="w"> </span><span class="s">&#34;css&#34;</span><span class="w"> </span><span class="p">(</span><span class="nf">slice</span><span class="w"> </span><span class="p">(</span><span class="nx">resources</span><span class="p">.</span><span class="nx">Get</span><span class="w"> </span><span class="s">&#34;styles/tw-main-dist.css&#34;</span><span class="p">))</span><span class="w"> </span><span class="p">}}</span><span class="w"> </span><span class="p">{{</span><span class="cm">/* output file for distribution */</span><span class="p">}}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="p">[</span><span class="o">...</span><span class="p">]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="p">{{</span><span class="cm">/* Get the scratch&#39;s CSS content as bundled, optimized file */</span><span class="p">}}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="p">{{</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="err">$</span><span class="nx">assets</span><span class="p">.</span><span class="nx">Get</span><span class="w"> </span><span class="s">&#34;css&#34;</span><span class="w"> </span><span class="p">}}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">{{</span><span class="w"> </span><span class="err">$</span><span class="nx">bundleCSS</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="err">$</span><span class="nx">assets</span><span class="p">.</span><span class="nx">Get</span><span class="w"> </span><span class="s">&#34;css&#34;</span><span class="w"> </span><span class="p">|</span><span class="w"> </span><span class="nx">resources</span><span class="p">.</span><span class="nx">Concat</span><span class="w"> </span><span class="s">&#34;styles/bundle.css&#34;</span><span class="w"> </span><span class="p">|</span><span class="w"> </span><span class="nx">resources</span><span class="p">.</span><span class="nx">Minify</span><span class="w"> </span><span class="p">|</span><span class="w"> </span><span class="nx">resources</span><span class="p">.</span><span class="nx">Fingerprint</span><span class="w"> </span><span class="s">&#34;sha256&#34;</span><span class="p">)</span><span class="w"> </span><span class="p">}}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">&lt;</span><span class="nx">link</span><span class="w"> </span><span class="kd">type</span><span class="p">=</span><span class="s">&#34;text/css&#34;</span><span class="w"> </span><span class="nx">rel</span><span class="p">=</span><span class="s">&#34;stylesheet&#34;</span><span class="w"> </span><span class="nx">href</span><span class="p">=</span><span class="s">&#34;{{ $bundleCSS.RelPermalink }}&#34;</span><span class="w"> </span><span class="nx">integrity</span><span class="p">=</span><span class="s">&#34;{{ $bundleCSS.Data.Integrity }}&#34;</span><span class="p">&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="p">{{</span><span class="w"> </span><span class="nx">end</span><span class="w"> </span><span class="p">}}</span><span class="w">
</span></span></span></code></pre></div><h2 id="alles-zusammen-nutzen-via-packagesjson" class="scroll-mt-20 md:scroll-mt-24 wrap-break-word group/heading "><a href="#alles-zusammen-nutzen-via-packagesjson" class="group-hover/heading:after:content-['#'] group-hover/heading:after:ml-1.5 group-hover/heading:after:text-content-400" id="alles-zusammen-nutzen-via-packagesjson">Alles zusammen nutzen via <code>packages.json</code></a></h2><p>Da NodeJS genutzt wird um die Tailwind CSS-Installation zu verwalten, liegt im root unseres Templates eine <code>packages.json</code>. Diese kann auch genutzt werden, um die oberen Befehle komfortabel zusammenzuführen.</p>
<p>Wir haben diese dazu in einzelne <code>scripts</code> gepackt, die via <code>npm run &lt;name&gt;</code> aufgerufen werden können. Das <a href="https://www.npmjs.com/package/npm-run-all" target="_blank" rel="noreferrer noopener"><code>npm-run-all</code>-Paket</a> gibt uns die Möglichkeit, alles komfortabel ohne Blockaden zu verketten. In unserer <code>packages.json</code> sieht dies wie folgt aus:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="c1">//[...]
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;hugo-foundata-theme&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;version&#34;</span><span class="p">:</span> <span class="s2">&#34;1.0.0&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;description&#34;</span><span class="p">:</span> <span class="s2">&#34;foundata theme for Hugo&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;scripts&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="err">[...]</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;dev&#34;</span><span class="p">:</span> <span class="s2">&#34;npm-run-all --parallel dev:tailwindcss dev:example --print-name --race&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;dev:tailwindcss&#34;</span><span class="p">:</span> <span class="s2">&#34;touch ./assets/styles/tw-main-dist.css &amp;&amp; NODE_ENV=development npx tailwindcss -i ./assets/styles/tw-main-src.css -o ./assets/styles/tw-main-dist.css --watch --minify&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;dev:example&#34;</span><span class="p">:</span> <span class="s2">&#34;hugo server --source ./exampleSite --themesDir ../.. --baseURL http://localhost/ --port 1313 --buildDrafts --cleanDestinationDir  --disableFastRender --gc --minify --noHTTPCache --printI18nWarnings --templateMetrics --templateMetricsHints&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;build:vendorlibs&#34;</span><span class="p">:</span> <span class="s2">&#34;npm-run-all preinstall postinstall --print-name&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;build:tailwindcss&#34;</span><span class="p">:</span> <span class="s2">&#34;NODE_ENV=production npx tailwindcss -i ./assets/styles/tw-main-src.css -o ./assets/styles/tw-main-dist.css --minify&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="c1">//[...]
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></div><p>Das war&rsquo;s. Um am Theme zu arbeiten wechselt man einfach in das Theme-Verzeichnis, führt <code>npm run dev</code> aus und kann unter <code>http://localhost:1313</code> die Hugo-Beispielseite aufrufen. Sie wird automatisch bei jeder Änderung an einem Style, <a href="https://gohugo.io/templates/" target="_blank" rel="noreferrer noopener">Template</a> oder einem Beispielinhalt direkt neu generiert und dann im Browser automatisch neu geladen. Dies ist insbesondere wegen des <a href="https://adamwathan.me/css-utility-classes-and-separation-of-concerns/" target="_blank" rel="noreferrer noopener">Utility-First-Ansatzes</a> von Tailwind CSS sehr passend.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Neben der offiziellen Dokumentation fand ich insbesondere <a href="https://www.regisphilibert.com/tags/hugo/" target="_blank" rel="noreferrer noopener">https://www.regisphilibert.com/tags/hugo/</a> sehr hilfreich.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>Die <a href="https://github.com/gohugoio/hugo/releases/tag/v0.112.0" target="_blank" rel="noreferrer noopener">Hugo Version v0.112.0</a> hat ebenfalls eine neue Option zur besseren Einbindung von Tailwind CSS v3.x eingeführt. Wir verfolgen aber einen etwas anderen Ansatz.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content><category scheme="taxonomy:tags" term="foundata" label="foundata"/><category scheme="taxonomy:tags" term="webdev" label="webdev"/><category scheme="taxonomy:tags" term="hugo" label="hugo"/><category scheme="taxonomy:tags" term="tailwind-css" label="tailwind-css"/></entry></feed>