<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Design on kenji.blog</title><link>http://kenji.blog/de/categories/design/</link><description>Recent content in Design on kenji.blog</description><generator>Hugo -- gohugo.io</generator><language>de</language><copyright>kenjinote</copyright><lastBuildDate>Sun, 13 Sep 2026 03:00:00 +0900</lastBuildDate><atom:link href="http://kenji.blog/de/categories/design/index.xml" rel="self" type="application/rss+xml"/><item><title>Hugo Theme-Anpassung: Wie man Tailwind CSS einführt</title><link>http://kenji.blog/de/p/hugo-tailwind-css-customization-guide/</link><pubDate>Sun, 13 Sep 2026 03:00:00 +0900</pubDate><guid>http://kenji.blog/de/p/hugo-tailwind-css-customization-guide/</guid><description>&lt;img src="http://kenji.blog/p/hugo-tailwind-css-customization-guide/img/eyecatch.jpg" alt="Featured image of post Hugo Theme-Anpassung: Wie man Tailwind CSS einführt" />&lt;h1 id="einführung-die-starke-synergie-zwischen-dem-static-site-generator-hugo-und-tailwind-css">Einführung: Die starke Synergie zwischen dem Static Site Generator Hugo und Tailwind CSS
&lt;/h1>&lt;p>In der modernen Web-Frontend-Entwicklung ist die Vereinbarkeit von Leistung und Entwicklererfahrung (DX: Developer Experience) eines der wichtigsten Anliegen in jedem Projekt. Die Kombination von &lt;strong>Hugo&lt;/strong>, das unter den Static Site Generatoren (SSG) zu den schnellsten der Welt gehört, mit &lt;strong>Tailwind CSS&lt;/strong>, das das innovative Paradigma &amp;ldquo;Utility-First&amp;rdquo; eingeführt hat, kann als ultimative Lösung für dieses Problem angesehen werden.&lt;/p>
&lt;p>Hugo ist in Go geschrieben und bietet eine unglaubliche Leistung, mit der Builds selbst für Websites mit Tausenden von Seiten in nur wenigen Sekunden oder Millisekunden abgeschlossen werden. Andererseits ermöglicht Tailwind CSS das direkte Schreiben unzähliger vordefinierter Utility-Klassen (z. B. &lt;code>flex&lt;/code>, &lt;code>text-center&lt;/code>, &lt;code>mt-4&lt;/code>) in HTML, wodurch der Kontextwechsel zwischen CSS- und HTML-Dateien entfällt und Design-Iterationen beschleunigt werden.&lt;/p>
&lt;p>In diesem Artikel werden wir die Schritte zur Integration von Tailwind CSS in ein Hugo-Theme und zum Aufbau einer fortschrittlichen Asset-Pipeline (Hugo Pipes) mit PostCSS im Detail erläutern – von den Grundlagen der Architektur bis hin zu mathematischen Leistungsoptimierungen.&lt;/p>
&lt;hr>
&lt;h2 id="1-die-entwicklung-von-utility-first-css-und-komponentenbasiertem-design">1. Die Entwicklung von Utility-First CSS und komponentenbasiertem Design
&lt;/h2>&lt;p>Bevor wir zu den Schritten für die Integration von Tailwind CSS übergehen, ist es sehr hilfreich, die Geschichte und Entwicklung der CSS-Design-Philosophie zu verstehen, um zu begreifen, warum wir Tailwind CSS verwenden sollten.&lt;/p>
&lt;h3 id="die-grenzen-des-traditionellen-css-designs-bem-und-oocss">Die Grenzen des traditionellen CSS-Designs (BEM und OOCSS)
&lt;/h3>&lt;p>In der Vergangenheit galt es in der Webentwicklung als Best Practice, semantische Klassennamen zu vergeben. Wenn man beispielsweise eine Kartenkomponente erstellte, trennte man HTML und CSS wie folgt:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">div&lt;/span> &lt;span class="na">class&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;card&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">img&lt;/span> &lt;span class="na">class&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;card__image&amp;#34;&lt;/span> &lt;span class="na">src&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;image.jpg&amp;#34;&lt;/span> &lt;span class="na">alt&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;thumbnail&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">div&lt;/span> &lt;span class="na">class&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;card__content&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">h2&lt;/span> &lt;span class="na">class&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;card__title&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>Titel&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">h2&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">p&lt;/span> &lt;span class="na">class&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;card__description&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>Die Beschreibung wird hier eingefügt.&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">p&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">div&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">div&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-css" data-lang="css">&lt;span class="line">&lt;span class="cl">&lt;span class="p">.&lt;/span>&lt;span class="nc">card&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">border-radius&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">8&lt;/span>&lt;span class="kt">px&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">box-shadow&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">0&lt;/span> &lt;span class="mi">4&lt;/span>&lt;span class="kt">px&lt;/span> &lt;span class="mi">6&lt;/span>&lt;span class="kt">px&lt;/span> &lt;span class="nb">rgba&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="mf">0.1&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">background-color&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#ffffff&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">overflow&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">hidden&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">.&lt;/span>&lt;span class="nc">card__title&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">font-size&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mf">1.5&lt;/span>&lt;span class="kt">rem&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">font-weight&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">bold&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">color&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#333333&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">/* Im Folgenden würden detailliertere Stile stehen */&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Dieser auf BEM (Block Element Modifier) basierende Entwurf funktioniert bei kleinen Projekten gut, führt aber häufig zu den folgenden Problemen:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Erschöpfung und Ermüdung bei der Namensgebung&lt;/strong>: Jedes Mal, wenn eine ähnliche Komponente erstellt wird, muss man sich neue Klassennamen ausdenken (z. B. &lt;code>card-news&lt;/code>, &lt;code>card-featured&lt;/code>).&lt;/li>
&lt;li>&lt;strong>Aufblähen von CSS&lt;/strong>: Die Anzahl der CSS-Zeilen wächst mit jeder neuen Funktion, und einmal geschriebenes CSS wird selten gelöscht, aus Angst, man wisse nicht, &amp;ldquo;wo es verwendet wird&amp;rdquo;, wodurch sich toter Code ansammelt.&lt;/li>
&lt;li>&lt;strong>Kontextwechsel&lt;/strong>: Da HTML-Struktur und CSS-Stile in separaten Dateien verwaltet werden, steigt die Anzahl der Tab-Wechsel im Editor exponentiell an.&lt;/li>
&lt;/ol>
&lt;h3 id="paradigmenwechsel-durch-tailwind-css">Paradigmenwechsel durch Tailwind CSS
&lt;/h3>&lt;p>Tailwind CSS löst diese Probleme durch einen Ansatz, der auf der &amp;ldquo;Kombination von Utility-Klassen&amp;rdquo; basiert. Wenn wir Tailwind CSS auf die obige Kartenkomponente anwenden, sieht das so aus:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">div&lt;/span> &lt;span class="na">class&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;rounded-lg shadow-md bg-white overflow-hidden&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">img&lt;/span> &lt;span class="na">class&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;w-full h-48 object-cover&amp;#34;&lt;/span> &lt;span class="na">src&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;image.jpg&amp;#34;&lt;/span> &lt;span class="na">alt&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;thumbnail&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">div&lt;/span> &lt;span class="na">class&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;p-6&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">h2&lt;/span> &lt;span class="na">class&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;text-2xl font-bold text-gray-800&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>Titel&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">h2&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">p&lt;/span> &lt;span class="na">class&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;mt-2 text-gray-600&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>Die Beschreibung wird hier eingefügt.&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">p&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">div&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">div&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Da die Klassennamen selbst spezifische Stilwerte darstellen (&lt;code>p-6&lt;/code> steht z. B. für &lt;code>padding: 1.5rem;&lt;/code> usw.), kann man allein durch das Betrachten des HTMLs das endgültige Rendering-Ergebnis vorhersagen. Darüber hinaus extrahiert der JIT (Just-In-Time) Compiler von Tailwind nur die tatsächlich verwendeten Klassen in die endgültige CSS-Datei für die Produktion, wodurch die Dateigröße des CSS auf ein Minimum reduziert wird.&lt;/p>
&lt;hr>
&lt;h2 id="2-die-architektur-von-hugo-pipes-und-postcss">2. Die Architektur von Hugo Pipes und PostCSS
&lt;/h2>&lt;p>Um Tailwind CSS in Hugo zu integrieren, muss man die Asset-Verarbeitungs-Pipeline namens &lt;strong>Hugo Pipes&lt;/strong> verstehen. Hugo Pipes ist eine leistungsstarke Funktion, die alle assetbezogenen Prozesse – wie die Kompilierung von Sass/SCSS, das Bündeln und Minifizieren von JavaScript und die Ausführung von &lt;strong>PostCSS&lt;/strong>, das wir hier verwenden werden – innerhalb von Hugo selbst abschließt.&lt;/p>
&lt;p>PostCSS ist ein Tool zum Transformieren von CSS mithilfe von JavaScript-Plugins. Tatsächlich funktioniert auch Tailwind CSS selbst als PostCSS-Plugin.&lt;/p>
&lt;h3 id="der-ast-abstract-syntax-tree-transformationsmechanismus-in-postcss">Der AST (Abstract Syntax Tree) Transformationsmechanismus in PostCSS
&lt;/h3>&lt;p>Zu verstehen, wie PostCSS CSS verarbeitet, ist bei der Fehlersuche enorm hilfreich. Das folgende Mermaid-Diagramm zeigt die Pipeline, in der PostCSS eine CSS-Datei einliest, sie mithilfe von Plugins konvertiert und das endgültige CSS ausgibt.&lt;/p>
&lt;pre class="mermaid">
flowchart TD
A[&amp;#34;Rohes CSS (styles.css)&amp;#34;] --&amp;gt;|Parser| B[&amp;#34;AST (Abstrakter Syntaxbaum)&amp;#34;]
B --&amp;gt; C[&amp;#34;Plugin 1: Tailwind CSS&amp;#34;]
C --&amp;gt; D[&amp;#34;Plugin 2: Autoprefixer&amp;#34;]
D --&amp;gt; E[&amp;#34;Plugin N: cssnano&amp;#34;]
E --&amp;gt;|Stringifier| F[&amp;#34;Kompiliertes &amp;amp; optimiertes CSS&amp;#34;]
&lt;/pre>
&lt;ol>
&lt;li>&lt;strong>Parser&lt;/strong>: Analysiert die eingegebene rohe CSS-Zeichenfolge und wandelt sie in einen AST (Abstrakter Syntaxbaum) um, eine Datenstruktur, die programmgesteuert manipuliert werden kann.&lt;/li>
&lt;li>&lt;strong>Plugins&lt;/strong>:
&lt;ul>
&lt;li>&lt;strong>Tailwind CSS&lt;/strong>: Scannt Vorlagendateien (HTML oder Markdown) und fügt dem AST Knoten für die verwendeten Utility-Klassen hinzu. Es löst auch &lt;code>@tailwind&lt;/code>-Direktiven auf.&lt;/li>
&lt;li>&lt;strong>Autoprefixer&lt;/strong>: Bezieht sich auf die &lt;code>Can I Use&lt;/code>-Datenbank und fügt dem AST bei Bedarf Vendor-Präfixe (&lt;code>-webkit-&lt;/code>, &lt;code>-moz-&lt;/code> usw.) als Eigenschaften hinzu.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>Stringifier&lt;/strong>: Konvertiert den transformierten AST zurück in eine CSS-Zeichenfolge, die vom Browser interpretiert werden kann, und gibt diese aus.&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="3-umgebungseinrichtung-und-voraussetzungen">3. Umgebungseinrichtung und Voraussetzungen
&lt;/h2>&lt;p>Lassen Sie uns nun mit dem eigentlichen Integrationsprozess beginnen. Zuerst überprüfen wir, ob die benötigte Software installiert ist.&lt;/p>
&lt;h3 id="voraussetzungen">Voraussetzungen
&lt;/h3>&lt;ol>
&lt;li>
&lt;p>&lt;strong>Hugo Extended Version&lt;/strong>:
Sie benötigen die &lt;strong>Extended-Version&lt;/strong>, die Sass/SCSS-Verarbeitungsfunktionen und native PostCSS-Integration enthält, anstelle der regulären Hugo-Version. Führen Sie den folgenden Befehl im Terminal aus und stellen Sie sicher, dass die Versionsinformationen die Zeichenfolge &lt;code>extended&lt;/code> enthalten.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">hugo version
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Erwartete Ausgabe:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># hugo v0.121.2-4146... windows/amd64 BuildDate=... VendorInfo=gohugoio +extended&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;/li>
&lt;li>
&lt;p>&lt;strong>Node.js und npm&lt;/strong>:
Abhängigkeiten wie Tailwind CSS und PostCSS laufen auf Node.js. Stellen Sie sicher, dass Node.js (LTS-Version empfohlen) installiert ist.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">node -v
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm -v
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;/li>
&lt;/ol>
&lt;h3 id="installation-der-npm-pakete">Installation der npm-Pakete
&lt;/h3>&lt;p>Initialisieren Sie npm im Stammverzeichnis Ihres Projekts (der Ebene, auf der sich Hugos Konfigurationsdatei &lt;code>hugo.toml&lt;/code> befindet) und installieren Sie die benötigten Pakete.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Erzeugen der package.json&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm init -y
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Installieren von Tailwind CSS, PostCSS und Autoprefixer als Entwicklungsabhängigkeiten&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm install -D tailwindcss postcss postcss-cli autoprefixer
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;blockquote>
&lt;p>[!IMPORTANT]
Wenn &lt;code>postcss-cli&lt;/code> nicht installiert ist, kann ein Fehler auftreten, wenn Hugo versucht, PostCSS intern aufzurufen. Hugo Pipes verwendet intern &lt;code>postcss-cli&lt;/code>, also stellen Sie sicher, dass es installiert ist.&lt;/p>
&lt;/blockquote>
&lt;hr>
&lt;h2 id="4-konfigurationsdateien-erstellen-postcss--tailwind-css">4. Konfigurationsdateien erstellen (PostCSS &amp;amp; Tailwind CSS)
&lt;/h2>&lt;p>Nachdem die Pakete installiert sind, erstellen wir zwei wichtige Konfigurationsdateien, die das Verhalten des Projekts steuern. Platzieren Sie diese im Stammverzeichnis Ihres Projekts.&lt;/p>
&lt;h3 id="erstellen-von-tailwindconfigjs">Erstellen von tailwind.config.js
&lt;/h3>&lt;p>Wenn Sie den folgenden Befehl in Ihrem Terminal ausführen, wird eine Standardkonfigurationsdatei generiert.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">npx tailwindcss init
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Öffnen Sie die generierte Datei &lt;code>tailwind.config.js&lt;/code> in Ihrem Editor und konfigurieren Sie die Eigenschaft &lt;code>content&lt;/code>. Dies ist sehr wichtig. Tailwind analysiert die Dateien in den hier angegebenen Pfaden und extrahiert die verwendeten Klassen. Geben Sie die Layout- und Inhaltsdateien entsprechend der Projektstruktur von Hugo genau an.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="cm">/** @type {import(&amp;#39;tailwindcss&amp;#39;).Config} */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">module&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">exports&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Scanziele entsprechend Hugos Verzeichnisstruktur angeben
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">content&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;./content/**/*.md&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;./content/**/*.html&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;./layouts/**/*.html&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;./assets/**/*.js&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Wenn Sie ein Theme verwenden, müssen Sie auch dessen Verzeichnis einschließen
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// &amp;#34;./themes/my-theme/layouts/**/*.html&amp;#34;,
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">theme&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">extend&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Erweitern Sie hier benutzerdefinierte Farben oder Schriftarten
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">colors&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;brand-primary&amp;#39;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;#3490dc&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;brand-secondary&amp;#39;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;#ffed4a&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">fontFamily&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;sans&amp;#39;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;Helvetica Neue&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;Arial&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;Hiragino Kaku Gothic ProN&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;Meiryo&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;sans-serif&amp;#39;&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">plugins&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Bei Bedarf offizielle Plugins hinzufügen (z. B. das Typography-Plugin)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// require(&amp;#39;@tailwindcss/typography&amp;#39;),
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="erstellen-von-postcssconfigjs">Erstellen von postcss.config.js
&lt;/h3>&lt;p>Als Nächstes erstellen Sie &lt;code>postcss.config.js&lt;/code> im Stammverzeichnis des Projekts. Darin wird definiert, welche Plugins PostCSS in welcher Reihenfolge ausführt.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">module&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">exports&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">plugins&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">tailwindcss&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">autoprefixer&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Mit dieser Konfiguration führt Hugo, wenn es PostCSS aufruft, zuerst die Verarbeitung durch Tailwind CSS aus und wendet anschließend Autoprefixer an, um Vendor-Präfixe hinzuzufügen.&lt;/p>
&lt;hr>
&lt;h2 id="5-aufbau-der-css-asset-pipeline-in-hugo">5. Aufbau der CSS-Asset-Pipeline in Hugo
&lt;/h2>&lt;p>Sobald die Konfiguration abgeschlossen ist, ist es an der Zeit, Tailwind CSS in das Hugo-Theme zu integrieren.&lt;/p>
&lt;h3 id="5-1-erstellen-der-css-datei-als-einstiegspunkt">5-1. Erstellen der CSS-Datei als Einstiegspunkt
&lt;/h3>&lt;p>Erstellen Sie eine CSS-Datei als Einstiegspunkt im Verzeichnis &lt;code>assets/css/&lt;/code> (erstellen Sie es, falls es nicht existiert). Hier nennen wir sie &lt;code>main.css&lt;/code>.&lt;/p>
&lt;p>&lt;strong>Dateipfad: &lt;code>assets/css/main.css&lt;/code>&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-css" data-lang="css">&lt;span class="line">&lt;span class="cl">&lt;span class="c">/* Laden der Basisstile von Tailwind (Reset CSS usw.) */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">@&lt;/span>&lt;span class="k">tailwind&lt;/span> &lt;span class="nt">base&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">/* Laden der Komponentenklassen */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">@&lt;/span>&lt;span class="k">tailwind&lt;/span> &lt;span class="nt">components&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">/* Laden der Utility-Klassen */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">@&lt;/span>&lt;span class="k">tailwind&lt;/span> &lt;span class="nt">utilities&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">/* Wenn Sie eigenes benutzerdefiniertes CSS benötigen, können Sie es hier hinzufügen,
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c"> es wird jedoch empfohlen, dafür wenn möglich die extend-Option in der tailwind.config.js zu nutzen */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">@&lt;/span>&lt;span class="k">layer&lt;/span> &lt;span class="nt">components&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nc">btn-primary&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="err">@apply&lt;/span> &lt;span class="err">bg-blue-500&lt;/span> &lt;span class="n">hover&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">bg-blue-700&lt;/span> &lt;span class="kc">text&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="kc">white&lt;/span> &lt;span class="k">font&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="kc">bold&lt;/span> &lt;span class="n">py-2&lt;/span> &lt;span class="n">px-4&lt;/span> &lt;span class="n">rounded&lt;/span> &lt;span class="k">transition&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">colors&lt;/span> &lt;span class="n">duration-300&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="5-2-bearbeiten-der-layout-datei-headhtml">5-2. Bearbeiten der Layout-Datei (head.html)
&lt;/h3>&lt;p>Als Nächstes weisen wir Hugos Template an, die oben genannte CSS-Datei zu laden, und definieren die Pipeline, um sie mit PostCSS zu verarbeiten. Im Allgemeinen wird das Partial-Template, das das &lt;code>&amp;lt;head&amp;gt;&lt;/code>-Tag definiert, (z. B. &lt;code>layouts/partials/head.html&lt;/code>) bearbeitet.&lt;/p>
&lt;p>&lt;strong>Dateipfad: &lt;code>layouts/partials/head.html&lt;/code>&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-go-html-template" data-lang="go-html-template">&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">head&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">meta&lt;/span> &lt;span class="na">charset&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;utf-8&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">meta&lt;/span> &lt;span class="na">name&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;viewport&amp;#34;&lt;/span> &lt;span class="na">content&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;width=device-width, initial-scale=1&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">title&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>&lt;span class="cp">{{&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="na">.Title&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="cp">}}&lt;/span> | &lt;span class="cp">{{&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="na">.Site.Title&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="cp">}}&lt;/span>&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">title&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c">&amp;lt;!-- Abrufen von assets/css/main.css --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cp">{{&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nx">$css&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">:=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nx">resources&lt;/span>&lt;span class="na">.Get&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;css/main.css&amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="cp">}}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c">&amp;lt;!-- Definieren der PostCSS-Optionen --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cp">{{&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nx">$options&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">:=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nx">dict&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;inlineImports&amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">true&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="cp">}}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cp">{{&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nx">$css&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nx">$css&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">|&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nx">resources&lt;/span>&lt;span class="na">.PostCSS&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nx">$options&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="cp">}}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c">&amp;lt;!-- Asset-Optimierungspipeline für die Produktionsumgebung --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cp">{{&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nx">hugo&lt;/span>&lt;span class="na">.IsProduction&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="cp">}}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c">&amp;lt;!-- 1. Minify (Komprimierung) --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cp">{{&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nx">$css&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nx">$css&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">|&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nx">minify&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="cp">}}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c">&amp;lt;!-- 2. Fingerprint (Hinzufügen eines Hashes für Cache-Busting) --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cp">{{&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nx">$css&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nx">$css&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">|&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nx">fingerprint&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;sha512&amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="cp">}}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c">&amp;lt;!-- 3. Ausgeben des Tags mit SRI (Subresource Integrity) --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">link&lt;/span> &lt;span class="na">rel&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;stylesheet&amp;#34;&lt;/span> &lt;span class="na">href&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="cp">{{&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nx">$css&lt;/span>&lt;span class="na">.RelPermalink&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="cp">}}&lt;/span>&lt;span class="s">&amp;#34;&lt;/span> &lt;span class="na">integrity&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="cp">{{&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nx">$css&lt;/span>&lt;span class="na">.Data.Integrity&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="cp">}}&lt;/span>&lt;span class="s">&amp;#34;&lt;/span> &lt;span class="na">crossorigin&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;anonymous&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cp">{{&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">else&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="cp">}}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c">&amp;lt;!-- In der Entwicklungsumgebung unkomprimiert ausgeben (Priorität auf Build-Geschwindigkeit) --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">link&lt;/span> &lt;span class="na">rel&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;stylesheet&amp;#34;&lt;/span> &lt;span class="na">href&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="cp">{{&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nx">$css&lt;/span>&lt;span class="na">.RelPermalink&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="cp">}}&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cp">{{&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">end&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="cp">}}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">head&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h4 id="erläuterung-der-pipeline-und-mermaid-diagramm">Erläuterung der Pipeline und Mermaid-Diagramm
&lt;/h4>&lt;p>Im Folgenden wird erläutert, wie der obige Go-Template-Code die CSS-Datei verarbeitet, illustriert durch eine Reihe von Pipeline-Prozessen.&lt;/p>
&lt;pre class="mermaid">
flowchart TD
A[&amp;#34;assets/css/main.css&amp;#34;] --&amp;gt;|resources.Get| B[&amp;#34;Hugo-Speicherbereich&amp;#34;]
B --&amp;gt;|resources.PostCSS| C[&amp;#34;PostCSS + Tailwind JIT-Prozess&amp;#34;]
C --&amp;gt; D{&amp;#34;hugo.IsProduction ?&amp;#34;}
D -- Ja (Produktionsumgebung) --&amp;gt; E[&amp;#34;minify (Leerzeichen/Zeilenumbrüche entfernen)&amp;#34;]
E --&amp;gt; F[&amp;#34;fingerprint (Hash berechnen)&amp;#34;]
F --&amp;gt; G[&amp;#34;public/css/main.[hash].css (mit SRI)&amp;#34;]
D -- Nein (Entwicklungsumgebung) --&amp;gt; H[&amp;#34;public/css/main.css&amp;#34;]
&lt;/pre>
&lt;ol>
&lt;li>&lt;strong>&lt;code>resources.Get&lt;/code>&lt;/strong>: Sucht die angegebene Datei im Verzeichnis &lt;code>assets&lt;/code> und lädt sie als Ressourcenobjekt in den Speicher.&lt;/li>
&lt;li>&lt;strong>&lt;code>resources.PostCSS&lt;/code>&lt;/strong>: Verweist auf die &lt;code>postcss.config.js&lt;/code> im Stammverzeichnis des Projekts und wendet Tailwind CSS und Autoprefixer auf den CSS-Quellcode an. In der Entwicklungsumgebung (&lt;code>hugo server&lt;/code>) arbeitet der JIT-Modus und generiert beim Ändern von Dateien schnell nur die benötigten Klassen.&lt;/li>
&lt;li>&lt;strong>&lt;code>minify&lt;/code>&lt;/strong>: Entfernt beim Build für die Produktion (z. B. &lt;code>hugo --environment production&lt;/code>) unnötige Leerzeichen und Kommentare und minimiert die Dateigröße.&lt;/li>
&lt;li>&lt;strong>&lt;code>fingerprint&lt;/code>&lt;/strong>: Berechnet basierend auf dem Dateiinhalt einen SHA-Hash und hängt ihn an den Dateinamen an (z. B. &lt;code>main.ab12cd...css&lt;/code>). Dies ermöglicht ein &amp;ldquo;Cache-Busting&amp;rdquo;, was sicherstellt, dass neue Dateien zuverlässig geladen werden, wenn das CSS aktualisiert wird, während der leistungsstarke Cache des Browsers genutzt wird.&lt;/li>
&lt;li>&lt;strong>&lt;code>integrity&lt;/code>&lt;/strong>: Verwendet den durch den Fingerprint berechneten Hash-Wert, um ein SRI-Attribut auszugeben, das Manipulationen durch CDNs usw. verhindert.&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="6-mathematische-leistungsanalyse-in-der-css-optimierung">6. Mathematische Leistungsanalyse in der CSS-Optimierung
&lt;/h2>&lt;p>Einer der größten Vorteile der Einführung von Tailwind CSS ist die Minimierung der ausgelieferten CSS-Dateigröße. Analysieren wir dies quantitativ mithilfe eines mathematischen Modells, um zu verstehen, wie sich dies auf die Web-Performance (insbesondere den First Contentful Paint: FCP) auswirkt.&lt;/p>
&lt;h3 id="modell-zur-reduzierung-der-css-dateigröße">Modell zur Reduzierung der CSS-Dateigröße
&lt;/h3>&lt;p>Bei traditionellen CSS-Frameworks (wie Bootstrap) wird das gesamte Framework geladen, einschließlich der nicht verwendeten Stile, was zu einer großen Dateigröße $S_{original}$ führt (ca. 150 KB bis 200 KB).
Wenn die Dateigröße nach Anwendung des Purge-Prozesses durch den JIT-Compiler von Tailwind CSS $S_{purged}$ ist, kann dies mit der Reduzierungsrate $R_{purge}$ wie folgt ausgedrückt werden:&lt;/p>
$$
S_{purged} = S_{original} \times (1 - R_{purge})
$$&lt;p>In einem typischen Projekt erreicht $R_{purge}$ nahezu $0.9$ (90% Reduzierung), wodurch $S_{purged}$ auf nur noch etwa 10 KB bis 20 KB sinkt.&lt;/p>
&lt;p>Zusätzlich erfolgt bei der Auslieferung eine Komprimierung auf der Serverseite mittels Brotli oder Gzip. Wenn die Komprimierungsrate $R_{compress}$ (typischerweise etwa 0,7 bis 0,8) beträgt, wird die endgültige Nutzlastgröße $S_{final}$, die über das Netzwerk übertragen wird, nach folgender Formel berechnet:&lt;/p>
$$
S_{final} = S_{purged} \times (1 - R_{compress})
$$&lt;h3 id="kritischer-rendering-pfad-und-netzwerklatenz">Kritischer Rendering-Pfad und Netzwerklatenz
&lt;/h3>&lt;p>Die Zeit, die der Browser benötigt, um die ersten Inhalte auf dem Bildschirm zu zeichnen (FCP), kann durch die Summe aus der HTML-Download-Zeit, der CSS-Download-Zeit und der Rendering-Zeit angenähert werden.&lt;/p>
$$
T_{FCP} \approx RTT + \frac{S_{HTML}}{BW} + RTT + \frac{S_{final}}{BW} + T_{render}
$$&lt;p>Hierbei ist:&lt;/p>
&lt;ul>
&lt;li>$RTT$ : Round Trip Time (Netzwerkverzögerung zwischen Client und Server)&lt;/li>
&lt;li>$BW$ : Netzwerkbandbreite (Bandwidth)&lt;/li>
&lt;/ul>
&lt;p>In Umgebungen mit schmaler $BW$ und hohem $RTT$ (hohe Latenz), wie z. B. in mobilen Netzwerken, minimiert der Ansatz von Tailwind CSS $S_{final}$ auf nur wenige Kilobyte. Dies treibt den Term $\frac{S_{final}}{BW}$ nahe Null und ist die treibende Kraft für das Erreichen unglaublicher Ergebnisse bei Tests (wie Google PageSpeed Insights).&lt;/p>
&lt;hr>
&lt;h2 id="7-starten-des-entwicklungsservers-und-bestätigung-des-hot-reloadings">7. Starten des Entwicklungsservers und Bestätigung des Hot-Reloadings
&lt;/h2>&lt;p>Nachdem alle Einstellungen vorgenommen wurden, starten Sie den Hugo-Entwicklungsserver und überprüfen Sie, ob Tailwind CSS ordnungsgemäß funktioniert.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">hugo server -D
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Rufen Sie &lt;code>http://localhost:1313/&lt;/code> in Ihrem Browser auf und vergewissern Sie sich, dass die Website angezeigt wird.
Öffnen Sie eine Markdown-Inhaltsdatei oder ein Hugo-Template (die Dateien unter &lt;code>layouts/&lt;/code>) und versuchen Sie, einige Klassen hinzuzufügen.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="c">&amp;lt;!-- Beispiel für die Anwendung von Tailwind-Klassen zum Testen --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">div&lt;/span> &lt;span class="na">class&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;bg-gradient-to-r from-blue-500 to-purple-600 text-white p-8 rounded-xl shadow-2xl text-center transform transition duration-500 hover:scale-105&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">h1&lt;/span> &lt;span class="na">class&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;text-4xl font-extrabold tracking-tight&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>Tailwind CSS + Hugo ist großartig!&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">h1&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">p&lt;/span> &lt;span class="na">class&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;mt-4 text-lg font-medium&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>Vergewissern Sie sich, dass das Hot-Reloading sofort übernommen wird.&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">p&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">div&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Sobald Sie die Datei speichern, arbeiten Hugos leistungsstarker File Watcher und der JIT-Compiler von Tailwind zusammen, um das CSS im Millisekundenbereich neu zu erstellen. Sie werden die Freude erleben, wenn der Browser automatisch neu geladen wird (Hot-Reload).&lt;/p>
&lt;h3 id="fehlerbehebung-wenn-stile-nicht-angewendet-werden">Fehlerbehebung: Wenn Stile nicht angewendet werden
&lt;/h3>&lt;p>Sollten die Änderungen nicht sichtbar sein, prüfen Sie bitte die folgenden Punkte:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Die Einstellung des &lt;code>content&lt;/code>-Pfads in &lt;code>tailwind.config.js&lt;/code>&lt;/strong>
Wenn der Pfad zu den zu scannenden Dateien falsch ist, kann Tailwind die in diesen Dateien verwendeten Klassen nicht erkennen und fügt sie dem CSS nicht hinzu. Überprüfen Sie insbesondere bei Verwendung eines Themes, ob der Pfad zum Theme-Verzeichnis fehlt.&lt;/li>
&lt;li>&lt;strong>PostCSS-Fehler&lt;/strong>
Wenn Sie in den Protokollen des Hugo-Servers in Ihrem Terminal Fehler wie &lt;code>Error: failed to transform resource: PostCSS not found&lt;/code> sehen, ist &lt;code>npm install&lt;/code> möglicherweise nicht erfolgreich ausgeführt worden, oder &lt;code>postcss-cli&lt;/code> fehlt.&lt;/li>
&lt;li>&lt;strong>Hugos Cache leeren&lt;/strong>
In seltenen Fällen kann durch Hugos Caching ein altes CSS erhalten bleiben. Versuchen Sie, den Server zu stoppen, ihn mit &lt;code>hugo server --ignoreCache&lt;/code> neu zu starten oder das temporäre Verzeichnis des Betriebssystems (z. B. &lt;code>/tmp/hugo_cache/&lt;/code>) zu löschen.&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="8-build-für-die-produktion-und-weitere-verbesserungen">8. Build für die Produktion und weitere Verbesserungen
&lt;/h2>&lt;p>Wenn Sie Ihre Website auf einem Produktionsserver (z. B. Netlify, Vercel, GitHub Pages, Cloudflare Pages) bereitstellen, müssen Sie Umgebungsvariablen setzen und die Optimierungspipeline für die Produktion ausführen.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Beispiel für einen Produktions-Build-Befehl&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">NODE_ENV&lt;/span>&lt;span class="o">=&lt;/span>production hugo --minify --environment production
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Durch das Hinzufügen des Flags &lt;code>--environment production&lt;/code> wird der &lt;code>{{ if hugo.IsProduction }}&lt;/code>-Block in der Datei &lt;code>head.html&lt;/code> ausgeführt, woraufhin das CSS minifiziert und der Fingerprint hinzugefügt wird.&lt;/p>
&lt;h3 id="markdown-styling-mit-dem-typography-plugin">Markdown-Styling mit dem Typography-Plugin
&lt;/h3>&lt;p>Auf Blog- oder Dokumentationsseiten wie bei Hugo können Klassen nicht direkt an die aus Markdown generierten reinen HTML-Elemente (&lt;code>&amp;lt;h1&amp;gt;&lt;/code>, &lt;code>&amp;lt;p&amp;gt;&lt;/code>, &lt;code>&amp;lt;ul&amp;gt;&lt;/code> usw.) angehängt werden. In solchen Fällen ist das offizielle &lt;strong>Typography-Plugin&lt;/strong> von Tailwind sehr nützlich.&lt;/p>
&lt;ol>
&lt;li>
&lt;p>Installieren Sie das Plugin&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">npm install -D @tailwindcss/typography
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;/li>
&lt;li>
&lt;p>Fügen Sie es zu &lt;code>tailwind.config.js&lt;/code> hinzu&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">module&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">exports&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// ...
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">plugins&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">require&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;@tailwindcss/typography&amp;#39;&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;/li>
&lt;li>
&lt;p>Anwendung im Template
Durch einfaches Hinzufügen der Klasse &lt;code>prose&lt;/code> (sowie optionaler Farb- und Größenvarianten) zum Container-Element, das den Artikelinhalt ausgibt, werden schöne Standardstile angewendet.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-go-html-template" data-lang="go-html-template">&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">article&lt;/span> &lt;span class="na">class&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;prose prose-lg prose-blue mx-auto mt-10&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cp">{{&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="na">.Content&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="cp">}}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">article&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;/li>
&lt;/ol>
&lt;p>Dadurch müssen keine komplexen CSS-Selektoren (z. B. &lt;code>.article-content h2 { ... }&lt;/code>) manuell geschrieben werden, und die Modularität der Komponenten bleibt vollständig erhalten.&lt;/p>
&lt;hr>
&lt;h2 id="9-fazit-vollendung-eines-hochgradig-wartbaren-frontend-ökosystems">9. Fazit: Vollendung eines hochgradig wartbaren Frontend-Ökosystems
&lt;/h2>&lt;p>Gut gemacht! Damit ist die perfekte Webentwicklungs-Asset-Pipeline fertiggestellt, die die extrem schnelle Engine zur Generierung statischer Webseiten von Hugo mit den modernen Styling-Funktionen von Tailwind CSS und der Erweiterbarkeit von PostCSS vereint.&lt;/p>
&lt;p>Das Tolle an dieser Architektur ist, &lt;strong>&amp;ldquo;dass Sie sie nur einmal einrichten müssen&amp;rdquo;&lt;/strong>. Sobald die Pipeline eingerichtet ist, können Entwickler komplexe Benutzeroberflächen in rasanter Geschwindigkeit erstellen, ohne jemals eine CSS-Datei öffnen zu müssen, indem sie einfach intuitive Utility-Klassen in ihre HTML- oder Markdown-Templates schreiben.&lt;/p>
&lt;p>Da zudem die ausgegebene CSS-Größe stets minimiert wird, führt dies direkt zu besseren Core Web Vitals-Scores und ist aus SEO-Sicht sehr vorteilhaft.&lt;/p>
&lt;p>Die Kombination aus Hugo und Tailwind CSS wird für jedes Projekt, vom persönlichen Tech-Blog bis zur großen Unternehmens-Website, weiterhin eine der &amp;ldquo;besten Entscheidungen&amp;rdquo; sein. Nutzen Sie diese mächtige Toolchain auf jeden Fall und genießen Sie ein komfortables Web-Entwicklungsleben!&lt;/p></description></item><item><title>Design-Prinzipien für CSS bei der Dark-Mode-Unterstützung von Blogs</title><link>http://kenji.blog/de/p/dark-mode-css-design-patterns/</link><pubDate>Sat, 12 Sep 2026 12:00:00 +0900</pubDate><guid>http://kenji.blog/de/p/dark-mode-css-design-patterns/</guid><description>&lt;img src="http://kenji.blog/p/dark-mode-css-design-patterns/img/eyecatch.jpg" alt="Featured image of post Design-Prinzipien für CSS bei der Dark-Mode-Unterstützung von Blogs" />&lt;p>In der modernen Webentwicklung hat sich die Unterstützung des Dark Mode (Dunkelmodus) von einer bloßen „Nice-to-have“-Funktion zu einer „Must-have“-Anforderung zur Verbesserung der User Experience (UX) gewandelt. Besonders bei Medien wie Blogs und Dokumentationsseiten, die auf langes Lesen von Texten ausgelegt sind, ist die Bedeutung der Dark-Mode-Unterstützung extrem hoch, da sie die Augenbelastung der Nutzer verringert und den Akkuverbrauch der Geräte senkt.&lt;/p>
&lt;p>In diesem Artikel werden wir die technischen Herausforderungen, die bei der Dark-Mode-Unterstützung von Blogs unweigerlich auftreten, sowie die Punkte eines hochgradig wartbaren CSS-Designs aus der Perspektive eines Frontend-Entwicklers sehr tiefgehend beleuchten. Wir decken alles rund um die Implementierung des Dark Mode ab: von der Nutzung von CSS Custom Properties (CSS-Variablen), fortgeschrittener JavaScript-Steuerung und SSR-Integration zur Vermeidung von FOUC (Flash of Unstyled Content), über Farbdesign (RGB, HSL und das neueste OKLCH) zur Gewährleistung der Barrierefreiheit (WCAG 2.1 AAA), bis hin zu praktischen Code-Beispielen mit Tailwind CSS.&lt;/p>
&lt;hr>
&lt;h2 id="1-grundlagen-des-theme-designs-mit-css-custom-properties-css-variablen">1. Grundlagen des Theme-Designs mit CSS Custom Properties (CSS-Variablen)
&lt;/h2>&lt;p>Der aktuell standardmäßigste und leistungsstärkste Ansatz zur Implementierung des Dark Mode ist die Verwendung von &lt;strong>CSS Custom Properties (CSS-Variablen)&lt;/strong>. Während Variablen (&lt;code>$color&lt;/code>) in CSS-Präprozessoren wie Sass zur Kompilierzeit statisch aufgelöst werden, werden CSS-Variablen zur Laufzeit des Browsers dynamisch aufgelöst und überschrieben. Dies macht es möglich, die Farbgebung der gesamten Seite sofort zu ändern, indem einfach Klassen über JavaScript umgeschaltet werden.&lt;/p>
&lt;h3 id="11-definition-des-grundlegenden-farb-themes">1.1 Definition des grundlegenden Farb-Themes
&lt;/h3>&lt;p>Zunächst definieren wir die Farbpalette für den Light Mode (Standard) mithilfe der Pseudoklasse &lt;code>:root&lt;/code>. Das klassische Designmuster besteht dann darin, diese Variablen zu überschreiben, wenn ein Attribut wie &lt;code>[data-theme='dark']&lt;/code> (oder eine &lt;code>.dark&lt;/code>-Klasse) hinzugefügt wird.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-css" data-lang="css">&lt;span class="line">&lt;span class="cl">&lt;span class="c">/* Variablen-Definition für den Light Mode (Standard) */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">:&lt;/span>&lt;span class="nd">root&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-bg-primary&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#ffffff&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-bg-secondary&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#f3f4f6&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-text-primary&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#111827&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-text-secondary&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#4b5563&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-accent&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#3b82f6&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-border&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#e5e7eb&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">/* Variablen-Überschreibung im Dark Mode */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">[&lt;/span>&lt;span class="nt">data-theme&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s1">&amp;#39;dark&amp;#39;&lt;/span>&lt;span class="o">]&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-bg-primary&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#111827&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-bg-secondary&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#1f2937&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-text-primary&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#f9fafb&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-text-secondary&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#9ca3af&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-accent&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#60a5fa&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-border&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#374151&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">/* Tatsächliche Anwendung */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">body&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">background-color&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nf">var&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">--&lt;/span>&lt;span class="kc">color&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">bg&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">primary&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">color&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nf">var&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">--&lt;/span>&lt;span class="kc">color&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="kc">text&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">primary&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">transition&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="k">background-color&lt;/span> &lt;span class="mf">0.3&lt;/span>&lt;span class="kt">s&lt;/span> &lt;span class="kc">ease&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kc">color&lt;/span> &lt;span class="mf">0.3&lt;/span>&lt;span class="kt">s&lt;/span> &lt;span class="kc">ease&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">a&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">color&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nf">var&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">--&lt;/span>&lt;span class="kc">color&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">accent&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Durch die vollständige Trennung von Layout- oder Typografie-Spezifikationen und Farb-(Theme-)Spezifikationen auf diese Weise wird die Wartbarkeit von CSS drastisch verbessert.&lt;/p>
&lt;h3 id="12-nutzung-von-media-prefers-color-scheme-dark">1.2 Nutzung von @media (prefers-color-scheme: dark)
&lt;/h3>&lt;p>Wenn der Dark Mode auf Betriebssystemebene eingestellt ist, ist es aus UX-Sicht wünschenswert, das dunkle Theme ab dem ersten Besuch der Website automatisch anzuwenden. Dies wird durch die Media-Query &lt;code>@media (prefers-color-scheme: dark)&lt;/code> erreicht.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-css" data-lang="css">&lt;span class="line">&lt;span class="cl">&lt;span class="c">/* Fallback, wenn das Betriebssystem auf Dark Mode eingestellt ist */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">@&lt;/span>&lt;span class="k">media&lt;/span> &lt;span class="o">(&lt;/span>&lt;span class="nt">prefers-color-scheme&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nt">dark&lt;/span>&lt;span class="o">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">:&lt;/span>&lt;span class="nd">root&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="nd">not&lt;/span>&lt;span class="o">([&lt;/span>&lt;span class="nt">data-theme&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s1">&amp;#39;light&amp;#39;&lt;/span>&lt;span class="o">])&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-bg-primary&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#111827&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-bg-secondary&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#1f2937&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-text-primary&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#f9fafb&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-text-secondary&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#9ca3af&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-accent&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#60a5fa&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-border&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#374151&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Bei dieser Schreibweise wird die Dark-Mode-Einstellung des Betriebssystems respektiert und die Variablen werden überschrieben, es sei denn, der Benutzer hat explizit den Light Mode gewählt (&lt;code>data-theme='light'&lt;/code>).&lt;/p>
&lt;hr>
&lt;h2 id="2-verständnis-von-farbräumen-und-barrierefreiheit-wcag-21-aaa">2. Verständnis von Farbräumen und Barrierefreiheit (WCAG 2.1 AAA)
&lt;/h2>&lt;p>Beim Farbdesign für den Dark Mode reicht es nicht aus, einfach „den Hintergrund schwarz und den Text weiß zu machen“. Wenn der Kontrast zu stark ist, kann dies zu einer Überstrahlung führen und das Lesen erschweren; ist der Kontrast zu gering, wird die Sichtbarkeit beeinträchtigt. In den Web Content Accessibility Guidelines (WCAG) ist das Kontrastverhältnis zur Gewährleistung der Sichtbarkeit streng definiert.&lt;/p>
&lt;h3 id="21-berechnungsformel-für-das-wcag-kontrastverhältnis">2.1 Berechnungsformel für das WCAG-Kontrastverhältnis
&lt;/h3>&lt;p>Das Kontrastverhältnis (Contrast Ratio) $CR$ in den WCAG wird anhand der relativen Leuchtdichte (Relative Luminance) von Hintergrund- und Vordergrundfarbe wie folgt definiert:&lt;/p>
$$CR = \frac{L_{lighter} + 0.05}{L_{darker} + 0.05}$$&lt;p>Hierbei ist $L_{lighter}$ die relative Leuchtdichte der helleren Farbe und $L_{darker}$ die relative Leuchtdichte der dunkleren Farbe (der Wertebereich liegt zwischen 0,0 und 1,0). Um das Level AAA der WCAG 2.1 zu erreichen, ist ein Kontrastverhältnis von &lt;strong>mindestens 7:1&lt;/strong> für normalen Text und &lt;strong>mindestens 4.5:1&lt;/strong> für großen Text erforderlich.&lt;/p>
&lt;p>Die relative Leuchtdichte $L$ wird aus den RGB-Werten des sRGB-Farbraums mit der folgenden komplexen Formel berechnet:&lt;/p>
$$L = 0.2126 \times R + 0.7152 \times G + 0.0722 \times B$$&lt;p>Für jede Komponente ($R, G, B$) wird der normalisierte Wert verwendet, der durch Teilen des ursprünglichen 8-Bit-Werts ($R_{sRGB}$) durch 255 ermittelt wird, und die folgende Transformation zur Aufhebung der Gammakorrektur durchgeführt:&lt;/p>
$$
R, G, B =
\begin{cases}
\frac{C_{sRGB}}{12.92} &amp; \text{if } C_{sRGB} \le 0.03928 \\
\left( \frac{C_{sRGB} + 0.055}{1.055} \right)^{2.4} &amp; \text{otherwise}
\end{cases}
$$&lt;p>Es ist schwierig, diese Berechnung manuell durchzuführen, aber durch den Einsatz von Farbdesign-Tools können mechanisch Farben ausgewählt werden, die das Kontrastverhältnis von 7:1 ($CR \ge 7.0$) erfüllen.&lt;/p>
&lt;h3 id="22-hsl-vs-rgb-vs-oklch">2.2 HSL vs. RGB vs. OKLCH
&lt;/h3>&lt;p>Früher waren RGB und HSL die vorherrschenden Methoden bei der Erstellung von Farbpaletten. Diese weisen jedoch in Bezug auf die „wahrnehmungsbezogene Gleichmäßigkeit“ große Mängel auf.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>RGB&lt;/strong>: Es handelt sich um mechanische primäre Lichtfarben, was es für Menschen schwierig macht, intuitiv Anpassungen wie „heller machen“ oder „dunkler machen“ vorzunehmen.&lt;/li>
&lt;li>&lt;strong>HSL&lt;/strong>: Verwendet Farbton (Hue), Sättigung (Saturation) und Helligkeit (Lightness), aber die „Helligkeit (L)“ in HSL stimmt nicht mit der wahrgenommenen Helligkeit des menschlichen Auges überein. Beispielsweise haben ein reines Gelb und ein reines Blau mit einer Helligkeit von 50 % in HSL numerisch die gleiche Helligkeit, aber für das menschliche Auge erscheint das Gelb überwältigend heller.&lt;/li>
&lt;li>&lt;strong>OKLCH&lt;/strong>: Der neueste Farbraum, der kürzlich in das CSS Color Module Level 4 aufgenommen wurde. Er besteht aus Lightness (wahrgenommene Helligkeit), Chroma (Sättigung) und Hue (Farbton) und ist &lt;strong>vollständig auf die menschlichen Seheigenschaften abgestimmt (wahrnehmungsbezogen gleichmäßig)&lt;/strong>.&lt;/li>
&lt;/ul>
&lt;p>Durch die Verwendung von OKLCH kann die gleiche wahrgenommene Helligkeit (Lightness) auch dann beibehalten werden, wenn sich der Farbton (Hue) ändert, was die Generierung von Farbpaletten für den Dark Mode extrem vorhersehbar und sicher macht.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-css" data-lang="css">&lt;span class="line">&lt;span class="cl">&lt;span class="c">/* Beispiel für die Definition von CSS-Variablen mit OKLCH */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">:&lt;/span>&lt;span class="nd">root&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c">/* Höhere Basishelligkeit und geringere Sättigung für den Light Mode */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--bg-base&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nf">oklch&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mf">0.98&lt;/span> &lt;span class="mf">0.01&lt;/span> &lt;span class="mi">250&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--text-base&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nf">oklch&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mf">0.25&lt;/span> &lt;span class="mf">0.02&lt;/span> &lt;span class="mi">250&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--primary-brand&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nf">oklch&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mf">0.65&lt;/span> &lt;span class="mf">0.15&lt;/span> &lt;span class="mi">250&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">[&lt;/span>&lt;span class="nt">data-theme&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;dark&amp;#34;&lt;/span>&lt;span class="o">]&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c">/* Im Dark Mode wird nur die Helligkeit umgekehrt, um den wahrgenommenen Kontrast leicht beizubehalten */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--bg-base&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nf">oklch&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mf">0.20&lt;/span> &lt;span class="mf">0.02&lt;/span> &lt;span class="mi">250&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--text-base&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nf">oklch&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mf">0.95&lt;/span> &lt;span class="mf">0.01&lt;/span> &lt;span class="mi">250&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--primary-brand&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nf">oklch&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mf">0.75&lt;/span> &lt;span class="mf">0.15&lt;/span> &lt;span class="mi">250&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c">/* Etwas heller für den Dark Mode, um die Sichtbarkeit zu gewährleisten */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Durch die Einführung von OKLCH auf diese Weise lässt sich eine einfache Logik aufbauen, um ein konsistentes Kontrastverhältnis (WCAG AAA-Niveau) über mehrere Themes hinweg zu garantieren.&lt;/p>
&lt;hr>
&lt;h2 id="3-vermeidung-von-fouc-flash-of-unstyled-content-und-ssr-hydratisierung">3. Vermeidung von FOUC (Flash of Unstyled Content) und SSR-Hydratisierung
&lt;/h2>&lt;p>Das Problem, das Entwickler bei der Unterstützung des Dark Mode am meisten plagt, ist das Flackern des Bildschirms, bekannt als &lt;strong>FOUC (Flash of Unstyled Content)&lt;/strong>.&lt;/p>
&lt;h3 id="31-die-falle-des-theme-wechsels-via-clientseitigem-js">3.1 Die Falle des Theme-Wechsels via clientseitigem JS
&lt;/h3>&lt;p>Bei SPAs wie React oder Vue (oder statischen Seiten durch SSG) ist es üblich, die Einstellungen des Nutzers im &lt;code>localStorage&lt;/code> zu speichern, sie mit JavaScript auszulesen und das Theme umzuschalten. Wenn dieser Prozess jedoch z. B. im &lt;code>useEffect&lt;/code> von React durchgeführt wird, treten die folgenden Probleme auf:&lt;/p>
&lt;ol>
&lt;li>Der Browser rendert das HTML/CSS des Light Mode.&lt;/li>
&lt;li>Das JS-Bundle wird geladen und ausgeführt.&lt;/li>
&lt;li>Die &lt;code>dark&lt;/code>-Einstellung wird aus dem &lt;code>localStorage&lt;/code> gelesen.&lt;/li>
&lt;li>Die Klasse &lt;code>dark&lt;/code> wird dem HTML hinzugefügt und der Bildschirm wird plötzlich dunkel (Flackern).&lt;/li>
&lt;/ol>
&lt;h3 id="32-die-perfekte-maßnahme-gegen-fouc-einsatz-von-cookies-und-ssr">3.2 Die perfekte Maßnahme gegen FOUC: Einsatz von Cookies und SSR
&lt;/h3>&lt;p>Die Best Practice, um FOUC vollständig zu vermeiden und Hydratisierungsfehler zu verhindern, besteht darin, &lt;strong>die Theme-Einstellungen des Nutzers in &lt;code>document.cookie&lt;/code> zu speichern und das HTML mit den entsprechenden Klassen bereits in der Phase des serverseitigen Renderings (SSR) zurückzugeben&lt;/strong>.&lt;/p>
&lt;p>Das folgende Sequenzdiagramm zeigt den idealen Ablauf der Theme-Initialisierung mithilfe von Cookies.&lt;/p>
&lt;pre class="mermaid">
sequenceDiagram
participant User as &amp;#34;Benutzer&amp;#34;
participant Browser as &amp;#34;Browser OS&amp;#34;
participant Server as &amp;#34;Server SSR&amp;#34;
participant DOM as &amp;#34;Client DOM&amp;#34;
User-&amp;gt;&amp;gt;Browser: &amp;#34;Seite anfordern&amp;#34;
Browser-&amp;gt;&amp;gt;Server: &amp;#34;GET-Anfrage mit Cookie (theme=dark)&amp;#34;
Server--&amp;gt;&amp;gt;Browser: &amp;#34;HTML mit &amp;lt;html data-theme=&amp;#39;dark&amp;#39;&amp;gt; zurückgeben&amp;#34;
Browser-&amp;gt;&amp;gt;DOM: &amp;#34;Initiales HTML rendern (Kein FOUC)&amp;#34;
DOM-&amp;gt;&amp;gt;DOM: &amp;#34;Hydratisierungs-Skript ausführen&amp;#34;
alt &amp;#34;Cookie fehlt (Erster Besuch)&amp;#34;
DOM-&amp;gt;&amp;gt;Browser: &amp;#34;OS prefers-color-scheme prüfen&amp;#34;
Browser--&amp;gt;&amp;gt;DOM: &amp;#34;matchMedia-Ergebnis (z.B. dark)&amp;#34;
DOM-&amp;gt;&amp;gt;DOM: &amp;#34;data-theme=&amp;#39;dark&amp;#39; via blockierendem Inline-Skript setzen&amp;#34;
end
&lt;/pre>
&lt;h3 id="33-verteidigungslinie-durch-inline-skripte-für-statische-seiten-die-keine-cookies-verwenden-können">3.3 Verteidigungslinie durch Inline-Skripte (Für statische Seiten, die keine Cookies verwenden können)
&lt;/h3>&lt;p>Bei Blogs, die reines SSG (Static Site Generation) ohne SSR-Möglichkeit nutzen (wie statische Exporte von Hugo, Gatsby oder Astro), ist es zwingend erforderlich, ein blockierendes Inline-JavaScript innerhalb des &lt;code>&amp;lt;head&amp;gt;&lt;/code>-Tags zu platzieren, das die Klasse unmittelbar vor dem Rendern des DOM hinzufügt.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="c">&amp;lt;!-- Am Ende innerhalb von &amp;lt;head&amp;gt; platzieren --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">script&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="kd">function&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">try&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">var&lt;/span> &lt;span class="nx">localTheme&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">localStorage&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">getItem&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;theme&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">var&lt;/span> &lt;span class="nx">osTheme&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">window&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">matchMedia&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;(prefers-color-scheme: dark)&amp;#39;&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">matches&lt;/span> &lt;span class="o">?&lt;/span> &lt;span class="s1">&amp;#39;dark&amp;#39;&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;light&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">var&lt;/span> &lt;span class="nx">theme&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">localTheme&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="nx">osTheme&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">document&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">documentElement&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">setAttribute&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;data-theme&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">theme&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">catch&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">e&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">})();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">script&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Da dieses kleine Skript das Rendering des Browsers blockiert und sofort ausgeführt wird, ist das Attribut &lt;code>data-theme&lt;/code> bereits zum Zeitpunkt des Zeichnens des Bildschirms gesetzt, wodurch das Bildschirmflackern (FOUC) vollständig vermieden wird.&lt;/p>
&lt;hr>
&lt;h2 id="4-implementierungsansätze-mit-tailwind-css-und-rohem-scsscss">4. Implementierungsansätze mit Tailwind CSS und rohem SCSS/CSS
&lt;/h2>&lt;p>Wenn Sie den Dark Mode in ein reales Projekt integrieren, müssen Sie die werkzeugspezifischen Ansätze verstehen.&lt;/p>
&lt;h3 id="41-dark-mode-in-tailwind-css">4.1 Dark Mode in Tailwind CSS
&lt;/h3>&lt;p>Tailwind CSS bietet standardmäßig die Variante &lt;code>dark:&lt;/code>, was die Implementierung des Dark Mode extrem einfach macht. Setzen Sie die Eigenschaft &lt;code>darkMode&lt;/code> in der Konfigurationsdatei (&lt;code>tailwind.config.js&lt;/code>).&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// tailwind.config.js
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="nx">module&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">exports&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// &amp;#39;media&amp;#39; (abhängig von OS-Einstellung) oder &amp;#39;class&amp;#39; (manuell umschaltbar)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">darkMode&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;class&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">theme&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">extend&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">colors&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cm">/* Erweitern Sie die Tailwind-Farbpalette mithilfe von CSS-Variablen */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">primary&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;rgb(var(--color-primary) / &amp;lt;alpha-value&amp;gt;)&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">background&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;rgb(var(--color-background) / &amp;lt;alpha-value&amp;gt;)&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Auf der HTML-Seite müssen Sie lediglich die Klassen wie folgt hinzufügen:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">div&lt;/span> &lt;span class="na">class&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">h1&lt;/span> &lt;span class="na">class&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;text-2xl font-bold&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>Hallo Welt&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">h1&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">p&lt;/span> &lt;span class="na">class&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;mt-2&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>Tailwind macht den Dark Mode unglaublich einfach.&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">p&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">div&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Es ist jedoch eine Ursache für aufgeblähte Komponenten, &lt;code>dark:bg-xxx&lt;/code> auf alle Elemente anzuwenden. In großen Blogs und Apps wird ein hybrides Design (semantisches Farbdesign) empfohlen, &lt;strong>das auf CSS-Variablen basiert und von Tailwind aus auf diese CSS-Variablen verweist&lt;/strong>.&lt;/p>
&lt;p>Hier ist ein Klassendiagramm, das die Vererbung und Anwendungsebenen von CSS-Variablen zeigt:&lt;/p>
&lt;pre class="mermaid">
classDiagram
class GlobalCSSVariables {
&amp;#34;--color-brand-500&amp;#34;
&amp;#34;--color-gray-900&amp;#34;
}
class SemanticVariables {
&amp;#34;--bg-primary&amp;#34;
&amp;#34;--text-base&amp;#34;
&amp;#34;--accent&amp;#34;
}
class TailwindConfig {
&amp;#34;theme.colors.background&amp;#34;
&amp;#34;theme.colors.primary&amp;#34;
}
class UIComponents {
&amp;#34;class=&amp;#39;bg-background text-primary&amp;#39;&amp;#34;
}
GlobalCSSVariables &amp;lt;|-- SemanticVariables : &amp;#34;:root &amp;amp; .dark&amp;#34;
SemanticVariables &amp;lt;|-- TailwindConfig : &amp;#34;tailwind.config.js&amp;#34;
TailwindConfig &amp;lt;.. UIComponents : &amp;#34;Wendet Utility-Klassen an&amp;#34;
&lt;/pre>
&lt;h3 id="42-implementierung-mit-raw-scsscss-nutzung-von-mixins">4.2 Implementierung mit Raw SCSS/CSS (Nutzung von Mixins)
&lt;/h3>&lt;p>In Projekten, die SCSS selbst schreiben, ohne Tailwind zu verwenden, kapseln wir die Dark-Mode-Stile mithilfe von &lt;code>@mixin&lt;/code>.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-scss" data-lang="scss">&lt;span class="line">&lt;span class="cl">&lt;span class="cm">/* Definition des SCSS Mixins */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">@mixin&lt;/span>&lt;span class="nf"> dark-mode&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cm">/* Unterstützt sowohl das [data-theme=&amp;#39;dark&amp;#39;] Attribut als auch OS-Einstellungen */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">[&lt;/span>&lt;span class="nt">data-theme&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s1">&amp;#39;dark&amp;#39;&lt;/span>&lt;span class="o">]&lt;/span> &lt;span class="k">&amp;amp;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">@content&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">@media&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">prefers-color-scheme&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="ni">dark&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nd">:root:not&lt;/span>&lt;span class="o">([&lt;/span>&lt;span class="nt">data-theme&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s1">&amp;#39;light&amp;#39;&lt;/span>&lt;span class="o">])&lt;/span> &lt;span class="k">&amp;amp;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">@content&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm">/* Anwendungsbeispiel */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nc">.card&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">background-color&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mh">#ffffff&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">color&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mh">#333333&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">border&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="kt">px&lt;/span> &lt;span class="ni">solid&lt;/span> &lt;span class="mh">#eeeeee&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">@include&lt;/span>&lt;span class="nd"> dark-mode&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">background-color&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mh">#1a202c&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">color&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mh">#e2e8f0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">border-color&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mh">#2d3748&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Diese Methode ist intuitiv, aber da die Dateigröße des CSS nach der Kompilierung dazu neigt, aufzublähen (Media-Queries werden für jeden Selektor dupliziert), ist der aktuelle Trend die Umstellung auf ein Design, das sich auf CSS-Variablen (Custom Properties) konzentriert.&lt;/p>
&lt;hr>
&lt;h2 id="5-dark-mode-optimierung-für-bilder-images-und-svgs">5. Dark-Mode-Optimierung für Bilder (Images) und SVGs
&lt;/h2>&lt;p>Auch wenn das Farbdesign von Text und Hintergrund abgeschlossen ist, wirken als Inhalt platzierte Bilder und Icons (SVGs), wenn sie im Light Mode verbleiben, extrem blendend und isoliert im Dark Mode. Eine Optimierung auch für diese ist unerlässlich.&lt;/p>
&lt;h3 id="51-css-filter-zur-reduzierung-der-bildhelligkeit">5.1 CSS-Filter zur Reduzierung der Bildhelligkeit
&lt;/h3>&lt;p>Bitmap-Bilder wie Fotos können zu hell sein, wenn sie unverändert im Dark Mode angezeigt werden. Durch die Verwendung der CSS-Eigenschaft &lt;code>filter&lt;/code>, um die Helligkeit (brightness) und den Kontrast (contrast) des Bildes leicht zu verringern, kann es natürlich in die Dark-Theme-UI integriert werden.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-css" data-lang="css">&lt;span class="line">&lt;span class="cl">&lt;span class="o">[&lt;/span>&lt;span class="nt">data-theme&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s1">&amp;#39;dark&amp;#39;&lt;/span>&lt;span class="o">]&lt;/span> &lt;span class="nt">img&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="nd">not&lt;/span>&lt;span class="o">([&lt;/span>&lt;span class="nt">src&lt;/span>&lt;span class="o">*=&lt;/span>&lt;span class="s2">&amp;#34;.svg&amp;#34;&lt;/span>&lt;span class="o">])&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c">/* Helligkeit verringern, Kontrast leicht erhöhen */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">filter&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">brightness&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mf">0.8&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="nb">contrast&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mf">1.1&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">transition&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="k">filter&lt;/span> &lt;span class="mf">0.3&lt;/span>&lt;span class="kt">s&lt;/span> &lt;span class="kc">ease&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">[&lt;/span>&lt;span class="nt">data-theme&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s1">&amp;#39;dark&amp;#39;&lt;/span>&lt;span class="o">]&lt;/span> &lt;span class="nt">img&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="nd">hover&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c">/* Beim Hovern zur ursprünglichen Helligkeit zurückkehren (falls der Benutzer Details sehen möchte) */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">filter&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">brightness&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="nb">contrast&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="52-bildunterscheidung-mit-dem-picture-tag">5.2 Bildunterscheidung mit dem &lt;code>&amp;lt;picture&amp;gt;&lt;/code>-Tag
&lt;/h3>&lt;p>Logo-Bilder oder erklärende Illustrationen (wie JPEGs mit festem weißem Hintergrund) können nicht allein durch Filterung behandelt werden. In diesem Fall ist es die richtige Lösung, das HTML-Element &lt;code>&amp;lt;picture&amp;gt;&lt;/code> und Media-Queries zu verwenden, um eine andere Bilddatei für den Dark Mode anzuzeigen.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">picture&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c">&amp;lt;!-- Wird für Benutzer mit Dark-Mode-OS-Einstellung angezeigt --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">source&lt;/span> &lt;span class="na">srcset&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;/img/logo-dark.png&amp;#34;&lt;/span> &lt;span class="na">media&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;(prefers-color-scheme: dark)&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c">&amp;lt;!-- Standard (Light Mode) --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">img&lt;/span> &lt;span class="na">src&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;/img/logo-light.png&amp;#34;&lt;/span> &lt;span class="na">alt&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;Website-Logo&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">picture&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;em>Hinweis: Da diese Methode nicht mit manuellem Umschalten über &lt;code>localStorage&lt;/code> etc. verknüpft ist (sie hängt nur von den OS-Einstellungen ab), müssen Sie bei der Implementierung einer manuellen Umschaltung die Bild-&lt;code>src&lt;/code> dynamisch mit JS umschreiben oder &lt;code>display: none&lt;/code> mit CSS-Klassen umschalten.&lt;/em>&lt;/p>
&lt;h3 id="53-currentcolor-unterstützung-für-svg-icons">5.3 &lt;code>currentColor&lt;/code>-Unterstützung für SVG-Icons
&lt;/h3>&lt;p>Für Inline-SVGs, die z. B. in Icons verwendet werden, ist es am elegantesten, die Füllfarbe an die Textfarbe des übergeordneten Elements zu koppeln. Geben Sie &lt;code>currentColor&lt;/code> für die Attribute &lt;code>fill&lt;/code> oder &lt;code>stroke&lt;/code> des SVG an.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="c">&amp;lt;!-- Der Wert der CSS color Eigenschaft (z.B. var(--text-primary)) wird automatisch angewendet --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">svg&lt;/span> &lt;span class="na">viewBox&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;0 0 24 24&amp;#34;&lt;/span> &lt;span class="na">fill&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;currentColor&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">path&lt;/span> &lt;span class="na">d&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;M12 2L2 22h20L12 2z&amp;#34;&lt;/span> &lt;span class="p">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">svg&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Wenn nun in den Dark Mode gewechselt wird und die Textfarbe des übergeordneten Elements in das weiße Spektrum wechselt, ändert sich auch das SVG-Icon automatisch in das weiße Spektrum.&lt;/p>
&lt;hr>
&lt;h2 id="6-fazit-auf-dem-weg-zu-einem-nachhaltigen-dark-mode-design">6. Fazit: Auf dem Weg zu einem nachhaltigen Dark-Mode-Design
&lt;/h2>&lt;p>Um in Blogs und Webanwendungen einen qualitativ hochwertigen Dark Mode zu implementieren, ist ein CSS-Design, das die folgenden Punkte abdeckt, unerlässlich:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Nutzung von CSS Custom Properties&lt;/strong>: Vermeiden Sie die feste Kodierung von Farbspezifikationen und abstrahieren Sie auf semantische Variablennamen (z.B. &lt;code>--bg-primary&lt;/code>).&lt;/li>
&lt;li>&lt;strong>Einsatz des OKLCH-Farbraums&lt;/strong>: Entwerfen Sie ein sehr zugängliches Kontrastverhältnis (7:1 oder höher), das WCAG 2.1 AAA erfüllt, logisch in einem wahrnehmungsbezogen gleichmäßigen Farbraum.&lt;/li>
&lt;li>&lt;strong>Konsequente FOUC-Verhinderung&lt;/strong>: Eliminieren Sie das anfängliche Bildschirmflackern beim Laden vollständig durch die Kombination von SSR und Cookies oder durch blockierende Inline-Skripte im &lt;code>&amp;lt;head&amp;gt;&lt;/code>.&lt;/li>
&lt;li>&lt;strong>Medien- und Asset-Optimierung&lt;/strong>: Nutzen Sie &lt;code>filter: brightness()&lt;/code>, &lt;code>currentColor&lt;/code> und &lt;code>&amp;lt;picture&amp;gt;&lt;/code>-Tags, um auch Nicht-Text-Elemente in das dunkle Theme zu harmonisieren.&lt;/li>
&lt;/ol>
&lt;p>Diese feinen Anpassungen, die über eine bloße &amp;ldquo;Farbinvertierung&amp;rdquo; hinausgehen, sind die Voraussetzung für einen modernen Blog, der von den Nutzern geliebt wird und ein exzellentes, augenschonendes Leseerlebnis (Reading Experience) bietet. Entwickler, die den Dark Mode implementieren möchten, sollten die Designmuster in diesem Artikel als Referenz verwenden.&lt;/p></description></item><item><title>Grundlagen der Softwarearchitektur: Was ist Clean Architecture?</title><link>http://kenji.blog/de/p/clean-architecture-basics-guide/</link><pubDate>Sat, 12 Sep 2026 12:00:00 +0900</pubDate><guid>http://kenji.blog/de/p/clean-architecture-basics-guide/</guid><description>&lt;img src="http://kenji.blog/p/clean-architecture-basics-guide/img/eyecatch.jpg" alt="Featured image of post Grundlagen der Softwarearchitektur: Was ist Clean Architecture?" />&lt;p>In der modernen Softwareentwicklung ist der Aufbau eines &amp;ldquo;veränderungsresistenten Systems&amp;rdquo; eine ständige Herausforderung. Änderungen der Geschäftsanforderungen, das Aufkommen neuer Frameworks, UI-Erneuerungen, Datenbankmigrationen. Für all diese Änderungen wird eine Architektur benötigt, die sich flexibel anpassen kann, ohne das gesamte System neu aufbauen zu müssen. Eine Antwort darauf ist die von Robert C. Martin (bekannt als Uncle Bob) vorgeschlagene &lt;strong>Clean Architecture&lt;/strong>.&lt;/p>
&lt;p>In diesem Artikel werden wir uns mit dem Wesen der Clean Architecture befassen, indem wir ihre Geschichte, ihren Zweck, die Details ihrer vier Schichten, die Abhängigkeitsregel und konkrete Implementierungsbeispiele betrachten. Wir werden eine sehr tiefe und detaillierte technische Erklärung liefern.&lt;/p>
&lt;h2 id="1-probleme-traditioneller-architekturen-und-die-geschichte-der-clean-architecture">1. Probleme traditioneller Architekturen und die Geschichte der Clean Architecture
&lt;/h2>&lt;p>Historisch gesehen hat die Softwarearchitektur verschiedene Paradigmenwechsel erlebt. In frühen Systemen waren Geschäftslogik, UI und Datenzugriffscode eng miteinander gekoppelt (sogenannter Spaghetti-Code). Später, mit dem Ziel der Trennung von Anliegen (Separation of Concerns), wurde die 3-Schichten-Architektur (Präsentationsschicht, Geschäftslogikschicht, Datenzugriffsschicht) populär.&lt;/p>
&lt;p>Die traditionelle 3-Schichten-Architektur hatte jedoch ein großes Problem: &amp;ldquo;&lt;strong>Die Domäne (Geschäftslogik) wird von der Datenbank und dem Framework abhängig.&lt;/strong>&amp;rdquo;&lt;/p>
&lt;p>Wenn beispielsweise die Geschäftslogikschicht die Datenzugriffsschicht (wie einen ORM) direkt aufruft, wirken sich Änderungen am Datenbankschema oder am ORM auf die Geschäftslogik aus. Das heißt, es entstand der Widerspruch, dass die &amp;ldquo;Geschäftsregeln&amp;rdquo;, die am wichtigsten sind und nicht geändert werden sollten, von der &amp;ldquo;Infrastruktur&amp;rdquo; abhängig wurden, in der technische Änderungen am wahrscheinlichsten auftreten.&lt;/p>
&lt;p>Als Lösung dafür wurden Architekturen wie die folgenden konzipiert:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Hexagonale Architektur (Ports and Adapters)&lt;/strong> - Alistair Cockburn&lt;/li>
&lt;li>&lt;strong>Zwiebelarchitektur (Onion Architecture)&lt;/strong> - Jeffrey Palermo&lt;/li>
&lt;li>&lt;strong>DCI (Data, Context and Interaction)&lt;/strong> - James Coplien, Trygve Reenskaug&lt;/li>
&lt;li>&lt;strong>BCE (Boundary-Control-Entity)&lt;/strong> - Ivar Jacobson&lt;/li>
&lt;/ul>
&lt;p>Alle diese Architekturen haben dasselbe Ziel: die &amp;ldquo;&lt;strong>Trennung von Anliegen (Separation of Concerns)&lt;/strong>&amp;rdquo;. Es geht darum, die Software in Schichten zu unterteilen, eine Umgebung zu schaffen, in der jede unabhängig testbar ist, und einen Zustand zu erreichen, in dem sie unabhängig von externen Agenten (UI, DB, Frameworks) ist.&lt;/p>
&lt;p>Robert C. Martin integrierte diese hervorragenden architektonischen Konzepte, fasste sie zu einer einzigen praktischen Regel zusammen und nannte sie &amp;ldquo;&lt;strong>Clean Architecture&lt;/strong>&amp;rdquo;.&lt;/p>
&lt;h2 id="2-zweck-und-merkmale-der-clean-architecture">2. Zweck und Merkmale der Clean Architecture
&lt;/h2>&lt;p>Ein System, das die Clean Architecture übernimmt, weist die folgenden Merkmale auf:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Unabhängig von Frameworks (Independent of Frameworks)&lt;/strong>: Die Architektur hängt nicht von der Existenz von funktionsreichen Softwarebibliotheken ab. Dadurch können Frameworks als &amp;ldquo;Werkzeuge&amp;rdquo; verwendet werden, ohne das System in die Einschränkungen des Frameworks zwingen zu müssen.&lt;/li>
&lt;li>&lt;strong>Testbar (Testable)&lt;/strong>: Die Geschäftsregeln können ohne die UI, Datenbank, den Webserver oder andere externe Elemente getestet werden.&lt;/li>
&lt;li>&lt;strong>Unabhängig von der UI (Independent of UI)&lt;/strong>: Die UI kann leicht geändert werden, ohne den Rest des Systems zu ändern. Beispielsweise kann eine Web-UI durch eine Konsolen-UI ersetzt werden, ohne die Geschäftsregeln zu ändern.&lt;/li>
&lt;li>&lt;strong>Unabhängig von der Datenbank (Independent of Database)&lt;/strong>: Oracle oder SQL Server können gegen Mongo, BigTable, CouchDB usw. ausgetauscht werden. Geschäftsregeln sind nicht an die Datenbank gebunden.&lt;/li>
&lt;li>&lt;strong>Unabhängig von externen Agenten (Independent of any external agency)&lt;/strong>: Tatsächlich wissen die Geschäftsregeln überhaupt nichts über die Außenwelt.&lt;/li>
&lt;/ol>
&lt;h2 id="3-die-4-schichten-layers-der-clean-architecture">3. Die 4 Schichten (Layers) der Clean Architecture
&lt;/h2>&lt;p>Die Clean Architecture wird typischerweise durch konzentrische Kreise dargestellt. Je näher man dem Zentrum kommt, desto mehr repräsentiert die Software Richtlinien auf höherer Ebene (hochabstrakte Geschäftsregeln). Je weiter man nach außen geht, desto mehr wird sie zu Mechanismen (konkreten Details).&lt;/p>
&lt;pre class="mermaid">
graph TD
subgraph &amp;#34;External Interfaces (Frameworks &amp;amp; Drivers)&amp;#34;
A[&amp;#34;Web&amp;#34;]
B[&amp;#34;Benutzeroberfläche (UI)&amp;#34;]
C[&amp;#34;Datenbank&amp;#34;]
D[&amp;#34;Externe APIs&amp;#34;]
end
subgraph &amp;#34;Interface Adapters&amp;#34;
E[&amp;#34;Controller&amp;#34;]
F[&amp;#34;Gateways&amp;#34;]
G[&amp;#34;Presenter&amp;#34;]
end
subgraph &amp;#34;Application Business Rules (Use Cases)&amp;#34;
H[&amp;#34;Use-Case-Interaktoren&amp;#34;]
end
subgraph &amp;#34;Enterprise Business Rules (Entities)&amp;#34;
I[&amp;#34;Entitäten&amp;#34;]
end
A --&amp;gt; E
B --&amp;gt; E
C --&amp;gt; F
D --&amp;gt; F
E --&amp;gt; H
F --&amp;gt; H
G --&amp;gt; H
H --&amp;gt; I
&lt;/pre>
&lt;h3 id="31-entitäten-entities">3.1. Entitäten (Entities)
&lt;/h3>&lt;p>Entitäten kapseln die unternehmensweiten Geschäftsregeln (Enterprise Business Rules). Eine Entität kann ein Objekt mit Methoden sein oder eine Menge von Datenstrukturen und Funktionen. Es sind die allgemeinsten und auf höchster Ebene angesiedelten Regeln, die von mehreren verschiedenen Anwendungen im Unternehmen wiederverwendet werden können.
Selbst wenn Sie nur eine einzige Anwendung erstellen, sind die Entitäten die Geschäftsobjekte dieser Anwendung. Entitäten sind von externen Änderungen (wie Änderungen in der Seitennavigation oder bei der Sicherheit) absolut nicht betroffen.&lt;/p>
&lt;h3 id="32-anwendungsfälle-use-cases">3.2. Anwendungsfälle (Use Cases)
&lt;/h3>&lt;p>Die Use-Case-Schicht enthält anwendungsspezifische Geschäftsregeln (Application Business Rules). Hier werden alle Anwendungsfälle des Systems gekapselt und implementiert. Use Cases orchestrieren den Datenfluss zu und von den Entitäten und weisen diese an, ihre Geschäftsregeln anzuwenden, um die Ziele des Anwendungsfalls zu erreichen.
Änderungen in dieser Schicht dürfen sich nicht auf die Entitäten auswirken. Ebenso wenig dürfen externe Änderungen an der Datenbank, der UI oder den Frameworks diese Schicht beeinflussen. Die Use Cases sind vollständig von diesen Belangen isoliert.&lt;/p>
&lt;h3 id="33-schnittstellenadapter-interface-adapters">3.3. Schnittstellenadapter (Interface Adapters)
&lt;/h3>&lt;p>Die Schicht der Schnittstellenadapter ist eine Sammlung von Adaptern, die Daten von einem Format, das für die Use Cases und Entitäten bequem ist, in ein Format umwandeln, das für externe Agenten wie Datenbanken oder das Web am besten geeignet ist.
Zum Beispiel gehören die Elemente der MVC (Model-View-Controller) Architektur einer GUI für das Web hierher. Der Controller nimmt die Eingaben des Benutzers entgegen, reicht sie an den Use Case weiter, und der Presenter nimmt die Ausgabe des Use Cases entgegen und formatiert sie für die Ansicht (UI).
Es ist auch die Aufgabe dieser Schicht, Daten in ein Format zu konvertieren, das die Datenbank (wie SQL) versteht. Code, der weiter innen als diese Schicht liegt, sollte überhaupt nichts über die Datenbank wissen.&lt;/p>
&lt;h3 id="34-frameworks-und-treiber-frameworks--drivers">3.4. Frameworks und Treiber (Frameworks &amp;amp; Drivers)
&lt;/h3>&lt;p>Die äußerste Schicht besteht aus Werkzeugen wie Datenbanken und Web-Frameworks. Hier wird in der Regel wenig Code geschrieben, abgesehen von &amp;ldquo;Verbindungscode&amp;rdquo; (Glue-Code), um mit den inneren Kreisen zu kommunizieren.
In dieser Schicht werden alle Details aufbewahrt. Das Web ist ein Detail. Die Datenbank ist ein Detail. Um den Schaden so gering wie möglich zu halten, platzieren wir diese Details auf der Außenseite.&lt;/p>
&lt;h2 id="4-die-abhängigkeitsregel-the-dependency-rule">4. Die Abhängigkeitsregel (The Dependency Rule)
&lt;/h2>&lt;p>Es gibt eine wichtigste Regel, die niemals gebrochen werden darf, damit die Clean Architecture funktioniert. Dies ist die &amp;ldquo;&lt;strong>Abhängigkeitsregel (The Dependency Rule)&lt;/strong>&amp;rdquo;.&lt;/p>
&lt;blockquote>
&lt;p>Quellcode-Abhängigkeiten dürfen nur nach innen (auf Richtlinien höherer Ebene) gerichtet sein.&lt;/p>
&lt;/blockquote>
&lt;p>Code in den inneren Kreisen darf absolut nichts über Code in den äußeren Kreisen wissen. Namen (Funktionen, Klassen, Variablen usw.), die in einem äußeren Kreis deklariert sind, dürfen von Code in einem inneren Kreis nicht erwähnt werden.
Ebenso dürfen Datenformate, die in einem äußeren Kreis verwendet werden, nicht in einem inneren Kreis verwendet werden. Dies gilt insbesondere, wenn diese Formate durch ein Framework im äußeren Kreis generiert werden.&lt;/p>
&lt;pre class="mermaid">
graph LR
A[&amp;#34;Frameworks &amp;amp; Treiber&amp;#34;] --&amp;gt;| Abhängigkeit | B[&amp;#34;Schnittstellenadapter&amp;#34;]
B --&amp;gt;| Abhängigkeit | C[&amp;#34;Anwendungsfälle&amp;#34;]
C --&amp;gt;| Abhängigkeit | D[&amp;#34;Entitäten&amp;#34;]
style A fill:#f9f9f9,stroke:#333,stroke-width:2px
style B fill:#e6f7ff,stroke:#333,stroke-width:2px
style C fill:#fff0f6,stroke:#333,stroke-width:2px
style D fill:#f6ffed,stroke:#333,stroke-width:2px
&lt;/pre>
&lt;p>Mathematisch ausgedrückt: Wenn wir den Schichtindex als $L_i$ bezeichnen und $i=0$ als Entität (innerste Schicht) sowie $i=3$ als Framework (äußerste Schicht) definieren, dann muss für jede bestehende Abhängigkeit von einer Schicht $L_m$ zu $L_n$ immer die folgende Ungleichung gelten:&lt;/p>
$$ m > n $$&lt;p>Das heißt, der Vektor der Abhängigkeit $\vec{D}$ zeigt immer zum Zentrum.&lt;/p>
&lt;h2 id="5-grenzen-überschreiten-das-dependency-inversion-principle-dip">5. Grenzen überschreiten: Das Dependency Inversion Principle (DIP)
&lt;/h2>&lt;p>Wenn man versucht, die Abhängigkeitsregel einzuhalten, stößt man sofort auf ein großes Problem: &amp;ldquo;&lt;strong>Was tun, wenn der Use Case Daten aus der Datenbank abrufen muss?&lt;/strong>&amp;rdquo;&lt;/p>
&lt;p>Wenn die Use-Case-Schicht (innen) direkt die Schnittstellenadapter-Schicht (die äußere Implementierung des Repositories) aufruft, würde die Abhängigkeit nach außen zeigen, was eine Verletzung der Abhängigkeitsregel wäre.&lt;/p>
&lt;p>Die Lösung für dieses Problem ist das &amp;ldquo;D&amp;rdquo; aus den SOLID-Prinzipien (Dependency Inversion Principle: Prinzip der Abhängigkeitsumkehr).&lt;/p>
&lt;h3 id="definition-des-dependency-inversion-principle-dip">Definition des Dependency Inversion Principle (DIP)
&lt;/h3>&lt;ol>
&lt;li>High-Level-Module sollten nicht von Low-Level-Modulen abhängen. Beide sollten von Abstraktionen abhängen.&lt;/li>
&lt;li>Abstraktionen sollten nicht von Details abhängen. Details sollten von Abstraktionen abhängen.&lt;/li>
&lt;/ol>
&lt;p>Um dies zu erreichen, definieren wir eine &lt;strong>Schnittstelle (Abstraktion)&lt;/strong> in der Use-Case-Schicht und &lt;strong>implementieren&lt;/strong> diese Schnittstelle in der äußeren Schicht (Schnittstellenadapter). Die Use-Case-Schicht hängt nur von der selbst definierten Schnittstelle ab und nicht von konkreten Implementierungen auf der Außenseite.&lt;/p>
&lt;pre class="mermaid">
classDiagram
class UseCaseInteractor {
- UserRepository repository
+ execute()
}
class UserRepository {
&amp;lt;&amp;lt;Interface&amp;gt;&amp;gt;
+ findById(id)
+ save(user)
}
class UserRepositoryImpl {
+ findById(id)
+ save(user)
}
UseCaseInteractor --&amp;gt; UserRepository : &amp;#34;Hängt ab von&amp;#34;
UserRepositoryImpl ..|&amp;gt; UserRepository : &amp;#34;Implementiert&amp;#34;
note for UseCaseInteractor &amp;#34;Innere Schicht (Use Case)&amp;#34;
note for UserRepository &amp;#34;Innere Schicht (Use Case)&amp;#34;
note for UserRepositoryImpl &amp;#34;Äußere Schicht (Schnittstellenadapter)&amp;#34;
&lt;/pre>
&lt;p>Im obigen Diagramm verläuft der Kontrollfluss (Control Flow) zur Laufzeit von &lt;code>UseCaseInteractor&lt;/code> $\rightarrow$ &lt;code>UserRepositoryImpl&lt;/code>. Die Quellcode-Abhängigkeit (Source Code Dependency) verläuft jedoch von &lt;code>UserRepositoryImpl&lt;/code> $\rightarrow$ &lt;code>UserRepository&lt;/code> (nach innen). Durch den Einsatz von Polymorphismus konnte die Quellcode-Abhängigkeit entgegen dem Kontrollfluss ausgerichtet werden. Das ist der Grund, warum es als &amp;ldquo;&lt;strong>Umkehrung&lt;/strong> der Abhängigkeit&amp;rdquo; bezeichnet wird.&lt;/p>
&lt;h2 id="6-konkretes-implementierungsbeispiel-mit-typescript">6. Konkretes Implementierungsbeispiel mit TypeScript
&lt;/h2>&lt;p>Hier zeigen wir ein einfaches Implementierungsbeispiel (Benutzerregistrierungsfunktion) der Clean Architecture unter Verwendung von TypeScript.&lt;/p>
&lt;h3 id="61-entitäten-entities">6.1. Entitäten (Entities)
&lt;/h3>&lt;p>Dies sind die zentralsten Geschäftsregeln.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// src/domain/entities/User.ts
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">export&lt;/span> &lt;span class="kr">class&lt;/span> &lt;span class="nx">User&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">constructor&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">public&lt;/span> &lt;span class="kr">readonly&lt;/span> &lt;span class="nx">id&lt;/span>: &lt;span class="kt">string&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">public&lt;/span> &lt;span class="kr">readonly&lt;/span> &lt;span class="nx">name&lt;/span>: &lt;span class="kt">string&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">public&lt;/span> &lt;span class="kr">readonly&lt;/span> &lt;span class="nx">email&lt;/span>: &lt;span class="kt">string&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">public&lt;/span> &lt;span class="kr">readonly&lt;/span> &lt;span class="nx">createdAt&lt;/span>: &lt;span class="kt">Date&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span> &lt;span class="p">{}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Entitätsspezifische Geschäftsregeln (z.B. Überprüfung der Namenslänge usw.)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kr">public&lt;/span> &lt;span class="nx">isValid&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kr">boolean&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">name&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">length&lt;/span> &lt;span class="o">&amp;gt;=&lt;/span> &lt;span class="mi">3&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">email&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">includes&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;@&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="62-anwendungsfälle-use-cases">6.2. Anwendungsfälle (Use Cases)
&lt;/h3>&lt;p>In der Use-Case-Schicht definieren wir die Datenstrukturen für Eingabe und Ausgabe (DTOs) sowie die Repository-Schnittstelle, um die Abhängigkeit umzukehren.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;span class="lnt">48
&lt;/span>&lt;span class="lnt">49
&lt;/span>&lt;span class="lnt">50
&lt;/span>&lt;span class="lnt">51
&lt;/span>&lt;span class="lnt">52
&lt;/span>&lt;span class="lnt">53
&lt;/span>&lt;span class="lnt">54
&lt;/span>&lt;span class="lnt">55
&lt;/span>&lt;span class="lnt">56
&lt;/span>&lt;span class="lnt">57
&lt;/span>&lt;span class="lnt">58
&lt;/span>&lt;span class="lnt">59
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// src/application/repositories/UserRepository.ts
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">User&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;../../domain/entities/User&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Schnittstelle, die von der Use-Case-Schicht definiert wird
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">export&lt;/span> &lt;span class="kr">interface&lt;/span> &lt;span class="nx">UserRepository&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">findByEmail&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">email&lt;/span>: &lt;span class="kt">string&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">Promise&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">User&lt;/span> &lt;span class="err">|&lt;/span> &lt;span class="na">null&lt;/span>&lt;span class="p">&amp;gt;;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">save&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">user&lt;/span>: &lt;span class="kt">User&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">Promise&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">void&lt;/span>&lt;span class="p">&amp;gt;;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// src/application/usecases/RegisterUser/RegisterUserDTO.ts
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">export&lt;/span> &lt;span class="kr">interface&lt;/span> &lt;span class="nx">RegisterUserInputDTO&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">name&lt;/span>: &lt;span class="kt">string&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">email&lt;/span>: &lt;span class="kt">string&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="kr">interface&lt;/span> &lt;span class="nx">RegisterUserOutputDTO&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">id&lt;/span>: &lt;span class="kt">string&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">name&lt;/span>: &lt;span class="kt">string&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">email&lt;/span>: &lt;span class="kt">string&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">createdAt&lt;/span>: &lt;span class="kt">Date&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// src/application/usecases/RegisterUser/RegisterUserUseCase.ts
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">User&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;../../domain/entities/User&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">UserRepository&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;../../repositories/UserRepository&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">RegisterUserInputDTO&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">RegisterUserOutputDTO&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;./RegisterUserDTO&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="kr">class&lt;/span> &lt;span class="nx">RegisterUserUseCase&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Hängt von der Abstraktion (Schnittstelle) ab. Nicht von der konkreten Implementierung.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kr">constructor&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kr">private&lt;/span> &lt;span class="kr">readonly&lt;/span> &lt;span class="nx">userRepository&lt;/span>: &lt;span class="kt">UserRepository&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">public&lt;/span> &lt;span class="kr">async&lt;/span> &lt;span class="nx">execute&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">input&lt;/span>: &lt;span class="kt">RegisterUserInputDTO&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">Promise&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">RegisterUserOutputDTO&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">existingUser&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">await&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">userRepository&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">findByEmail&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">input&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">email&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">existingUser&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">throw&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nb">Error&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;User already exists&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">newUser&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">User&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">crypto&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">randomUUID&lt;/span>&lt;span class="p">(),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">input&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">name&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">input&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">email&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">new&lt;/span> &lt;span class="nb">Date&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="nx">newUser&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">isValid&lt;/span>&lt;span class="p">())&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">throw&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nb">Error&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Invalid user data&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Ruft den Speichervorgang in der äußeren Datenbank auf, aber die Abhängigkeit zeigt nach innen (zur Schnittstelle)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">await&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">userRepository&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">save&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">newUser&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">id&lt;/span>: &lt;span class="kt">newUser.id&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">name&lt;/span>: &lt;span class="kt">newUser.name&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">email&lt;/span>: &lt;span class="kt">newUser.email&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">createdAt&lt;/span>: &lt;span class="kt">newUser.createdAt&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="63-schnittstellenadapter-interface-adapters">6.3. Schnittstellenadapter (Interface Adapters)
&lt;/h3>&lt;p>Wir erstellen die konkrete Zugriffsbearbeitung für die Datenbank (Repository-Implementierung) und den Controller zur Verarbeitung von HTTP-Anfragen.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// src/adapters/repositories/PostgresUserRepository.ts
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">UserRepository&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;../../application/repositories/UserRepository&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">User&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;../../domain/entities/User&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Nimmt einen DB-Client als äußere Schicht (Treiber) an
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">DatabaseClient&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;../../infrastructure/database/DatabaseClient&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="kr">class&lt;/span> &lt;span class="nx">PostgresUserRepository&lt;/span> &lt;span class="kr">implements&lt;/span> &lt;span class="nx">UserRepository&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">constructor&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kr">private&lt;/span> &lt;span class="kr">readonly&lt;/span> &lt;span class="nx">dbClient&lt;/span>: &lt;span class="kt">DatabaseClient&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">public&lt;/span> &lt;span class="kr">async&lt;/span> &lt;span class="nx">findByEmail&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">email&lt;/span>: &lt;span class="kt">string&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">Promise&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">User&lt;/span> &lt;span class="err">|&lt;/span> &lt;span class="na">null&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">record&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">await&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">dbClient&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">query&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;SELECT * FROM users WHERE email = $1&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="nx">email&lt;/span>&lt;span class="p">]);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="nx">record&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="kc">null&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">User&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">record&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">id&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">record&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">name&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">record&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">email&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">record&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">created_at&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">public&lt;/span> &lt;span class="kr">async&lt;/span> &lt;span class="nx">save&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">user&lt;/span>: &lt;span class="kt">User&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">Promise&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">void&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">await&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">dbClient&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">query&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;INSERT INTO users (id, name, email, created_at) VALUES ($1, $2, $3, $4)&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">[&lt;/span>&lt;span class="nx">user&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">id&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">user&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">name&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">user&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">email&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">user&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">createdAt&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// src/adapters/controllers/UserController.ts
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">RegisterUserUseCase&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;../../application/usecases/RegisterUser/RegisterUserUseCase&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="kr">class&lt;/span> &lt;span class="nx">UserController&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">constructor&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kr">private&lt;/span> &lt;span class="kr">readonly&lt;/span> &lt;span class="nx">registerUserUseCase&lt;/span>: &lt;span class="kt">RegisterUserUseCase&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">public&lt;/span> &lt;span class="kr">async&lt;/span> &lt;span class="nx">register&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">req&lt;/span>: &lt;span class="kt">any&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">res&lt;/span>: &lt;span class="kt">any&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">Promise&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">void&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">try&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">input&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">name&lt;/span>: &lt;span class="kt">req.body.name&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">email&lt;/span>: &lt;span class="kt">req.body.email&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">output&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">await&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">registerUserUseCase&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">execute&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">input&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">res&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">status&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">201&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">json&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">output&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">catch&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">error&lt;/span>: &lt;span class="kt">any&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">res&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">status&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">400&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">json&lt;/span>&lt;span class="p">({&lt;/span> &lt;span class="nx">message&lt;/span>: &lt;span class="kt">error.message&lt;/span> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="64-hauptkomponente-dependency-injection-di">6.4. Hauptkomponente (Dependency Injection: DI)
&lt;/h3>&lt;p>Beim Starten der Anwendung werden alle Abhängigkeiten konstruiert (Wiring). Dies wird als Composition Root bezeichnet.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// src/infrastructure/web/server.ts
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">import&lt;/span> &lt;span class="nx">express&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;express&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">DatabaseClient&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;../database/DatabaseClient&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">PostgresUserRepository&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;../../adapters/repositories/PostgresUserRepository&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">RegisterUserUseCase&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;../../application/usecases/RegisterUser/RegisterUserUseCase&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">UserController&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;../../adapters/controllers/UserController&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">app&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">express&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">app&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">use&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">express&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">json&lt;/span>&lt;span class="p">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 1. Initialisierung des Treibers
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">const&lt;/span> &lt;span class="nx">dbClient&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">DatabaseClient&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="cm">/* Verbindungsinformationen */&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 2. Initialisierung des Adapters (Instanziierung konkreter Klassen)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">const&lt;/span> &lt;span class="nx">userRepository&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">PostgresUserRepository&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">dbClient&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 3. Initialisierung des Use Cases (Injektion des Konkreten in die Schnittstelle = DI)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">const&lt;/span> &lt;span class="nx">registerUserUseCase&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">RegisterUserUseCase&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">userRepository&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 4. Initialisierung des Controllers
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">const&lt;/span> &lt;span class="nx">userController&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">UserController&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">registerUserUseCase&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Routing
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="nx">app&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">post&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;/users&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">req&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">res&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="nx">userController&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">register&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">req&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">res&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">app&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">listen&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">3000&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Server is running on port 3000&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Auf diese Weise übernimmt das äußerste &amp;ldquo;Startskript&amp;rdquo; die schmutzigen Details (Instanziierung konkreter Klassen) und übergibt den inneren Schichten nur saubere Schnittstellen. Diese Struktur stellt sicher, dass die Geschäftslogik vollständig von der Außenwelt isoliert ist.&lt;/p>
&lt;h2 id="7-mathematische-betrachtung-von-kopplung-coupling-und-kohäsion-cohesion">7. Mathematische Betrachtung von Kopplung (Coupling) und Kohäsion (Cohesion)
&lt;/h2>&lt;p>Im Software-Engineering dienen &lt;strong>Kopplung (Coupling)&lt;/strong> und &lt;strong>Kohäsion (Cohesion)&lt;/strong> als Metriken zur Bewertung der Qualität einer Architektur.&lt;/p>
&lt;p>Die Kopplung $C$ stellt die Stärke der Abhängigkeiten zwischen Modulen dar. Wenn Modul $A$ von Modul $B$ abhängt, die Gesamtzahl der Abhängigkeiten im System $N_{dep}$ ist und die Anzahl der Module $N_{mod}$ ist, kann einer der Indikatoren für die Komplexität wie folgt ausgedrückt werden:&lt;/p>
$$ Complexity \propto \frac{N_{dep}}{N_{mod}} $$&lt;p>In der Clean Architecture lenkt die Anwendung des DIP die physikalischen Abhängigkeitspfeile hin zu Abstraktionen. Die Änderungshäufigkeit (Instabilität: $I$) der Abstraktionen (Schnittstellen) ist so konzipiert, dass sie sehr gering ist.&lt;/p>
&lt;p>Die Instabilität $I$ wird mit der folgenden Formel berechnet (definiert von Robert C. Martin):&lt;/p>
&lt;ul>
&lt;li>$C_e$ (Efferent Coupling): Ausgehende Kopplung (Anzahl der Dinge, von denen ich abhänge)&lt;/li>
&lt;li>$C_a$ (Afferent Coupling): Eingehende Kopplung (Anzahl der Dinge, die von mir abhängen)&lt;/li>
&lt;/ul>
$$ I = \frac{C_e}{C_e + C_a} $$&lt;ul>
&lt;li>Wenn $I = 0$, ist die Komponente völlig stabil (sie hängt von nichts ab und wird von anderen als Abhängigkeit genutzt).&lt;/li>
&lt;li>Wenn $I = 1$, ist die Komponente völlig instabil (niemand hängt von ihr ab, aber sie hängt von anderen ab).&lt;/li>
&lt;/ul>
&lt;p>Die &amp;ldquo;Entitätsschicht&amp;rdquo; der Clean Architecture hat $C_e = 0$ (keine Abhängigkeit nach außen), also ist $I = 0$. Das bedeutet, es ist die stabilste Schicht.
Umgekehrt haben die &amp;ldquo;UI-Schicht&amp;rdquo; und die &amp;ldquo;DB-Schicht&amp;rdquo; $C_a \approx 0$ und $C_e > 0$, also ist $I \approx 1$, was sie zu leicht veränderbaren Schichten (instabilen Schichten) macht.&lt;/p>
&lt;p>Ein wichtiges architektonisches Prinzip, das SDP (Stable Dependencies Principle), besagt: &amp;ldquo;&lt;strong>Abhängigkeiten müssen in Richtung stabilerer Komponenten zeigen (Komponenten mit einem kleineren $I$)&lt;/strong>&amp;rdquo;. Die konzentrischen Kreise der Clean Architecture visualisieren genau dieses SDP und sind so gestaltet, dass die Abhängigkeiten von außen ($I=1$) nach innen ($I=0$) weisen.&lt;/p>
&lt;h2 id="8-teststrategie-und-clean-architecture">8. Teststrategie und Clean Architecture
&lt;/h2>&lt;p>Einer der größten Vorteile der Clean Architecture ist ihre &lt;strong>Testbarkeit&lt;/strong>. Da die Schichten getrennt sind, können Tests für jede Schicht unabhängig geschrieben werden.&lt;/p>
&lt;h3 id="81-testen-von-entitäten-unit-test">8.1. Testen von Entitäten (Unit Test)
&lt;/h3>&lt;p>Da es sich um reine Logik ohne externe Abhängigkeiten handelt, werden weder eine DB noch Mocks benötigt. Es sind zuverlässige Tests, die extrem schnell ausgeführt werden können.&lt;/p>
&lt;h3 id="82-testen-von-use-cases-unit-test-with-mocks">8.2. Testen von Use Cases (Unit Test with Mocks)
&lt;/h3>&lt;p>Da alle externen Abhängigkeiten wie Repositories als Schnittstellen definiert sind, müssen Sie beim Testen lediglich &lt;strong>Test-Mocks oder In-Memory-Implementierungen (Fakes)&lt;/strong> injizieren (DI). Es ist nicht erforderlich, eine echte Datenbank zu starten. Dies ermöglicht ein schnelles Testen komplexer Verzweigungen und Ausnahmen in der Geschäftslogik.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Beispiel für einen Use-Case-Test (unter Annahme von Jest)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="nx">test&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Es sollte ein Fehler auftreten, wenn versucht wird, sich mit einer bestehenden E-Mail-Adresse zu registrieren&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kr">async&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Erstellung eines Fake-Repositories
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kr">const&lt;/span> &lt;span class="nx">mockRepo&lt;/span>: &lt;span class="kt">UserRepository&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">findByEmail&lt;/span>: &lt;span class="kt">async&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">email&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">User&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;1&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;Test&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">email&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nb">Date&lt;/span>&lt;span class="p">()),&lt;/span> &lt;span class="c1">// Gibt einen bestehenden Benutzer zurück
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">save&lt;/span>: &lt;span class="kt">async&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">user&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">useCase&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">RegisterUserUseCase&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">mockRepo&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Ausführung des Use Cases und Fehler-Assertion
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">await&lt;/span> &lt;span class="nx">expect&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">useCase&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">execute&lt;/span>&lt;span class="p">({&lt;/span> &lt;span class="nx">name&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;Bob&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">email&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;test@example.com&amp;#39;&lt;/span> &lt;span class="p">}))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">rejects&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">toThrow&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;User already exists&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="83-testen-von-adaptern-integration-test">8.3. Testen von Adaptern (Integration Test)
&lt;/h3>&lt;p>Die Implementierungsklasse des Repositories stellt eine tatsächliche Verbindung zur Datenbank her, um zu testen, ob das SQL korrekt ist. Der Test für den Controller überprüft den Teil, der eine HTTP-Anfrage empfängt und JSON zurückgibt. Hier wird keine detaillierte Überprüfung der Geschäftslogik durchgeführt; es wird nur bestätigt, dass die &amp;ldquo;Konvertierung&amp;rdquo; und die &amp;ldquo;Kommunikation&amp;rdquo; korrekt sind.&lt;/p>
&lt;h2 id="9-nachteile-der-clean-architecture-und-wann-man-sie-übernehmen-sollte">9. Nachteile der Clean Architecture und wann man sie übernehmen sollte
&lt;/h2>&lt;p>Obwohl die Clean Architecture allmächtig erscheint, ist sie keine Silberkugel. Die folgenden Nachteile (Kompromisse) existieren:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Erhöhte anfängliche Lern- und Entwicklungskosten&lt;/strong>: Die Anzahl der Dateien und Schnittstellen (Abstraktionen) steigt signifikant an. Es gibt viel &amp;ldquo;Boilerplate-Code&amp;rdquo; (Standardcode) wie z.B. das Umpacken von DTOs.&lt;/li>
&lt;li>&lt;strong>Overkill für kleine Projekte&lt;/strong>: Für einen Prototyp, der in wenigen Tagen erstellt wird, oder ein einmaliges Werkzeug, bei dem kaum Änderungen zu erwarten sind, ist die Übernahme dieser Architektur oft mit unnötigen Kosten verbunden. Auch für einfache APIs, die nur CRUD-Operationen ausführen, ist sie ungeeignet.&lt;/li>
&lt;/ol>
&lt;p>&lt;strong>Wann man sie übernehmen sollte&lt;/strong>:&lt;/p>
&lt;ul>
&lt;li>Produkte, die voraussichtlich über einen langen Zeitraum (mehrere Jahre oder länger) gewartet und betrieben werden.&lt;/li>
&lt;li>Systeme, in denen die Geschäftsregeln komplex sind und sich Spezifikationen häufig ändern.&lt;/li>
&lt;li>Wenn eine Arbeitsteilung (Frontend, Backend, Infrastruktur usw.) in einem großen Entwicklungsteam gefördert werden soll.&lt;/li>
&lt;li>Wenn komplexe Geschäftsbereiche in Kombination mit Domain-Driven Design (DDD) modelliert werden sollen.&lt;/li>
&lt;/ul>
&lt;h2 id="10-fazit">10. Fazit
&lt;/h2>&lt;p>Die Clean Architecture ist eine Designphilosophie, die den Kern des Systems, nämlich die &amp;ldquo;Geschäftsregeln&amp;rdquo;, vor den &amp;ldquo;Details&amp;rdquo; wie der UI, Datenbanken und Frameworks schützt.&lt;/p>
&lt;p>Ihr Kern besteht aus der &lt;strong>Abhängigkeitsregel&lt;/strong> und dem &lt;strong>Dependency Inversion Principle (DIP)&lt;/strong>. Durch deren korrekte Anwendung wird die Software flexibel gegenüber Änderungen, einfacher zu testen und kann ihren Wert über einen langen Zeitraum erhalten.&lt;/p>
&lt;p>Wichtig ist nicht, die Verzeichnisstruktur der Clean Architecture blind zu imitieren, sondern das Wesentliche zu verstehen – &amp;ldquo;&lt;strong>warum es so aufgeteilt ist&lt;/strong>&amp;rdquo; und &amp;ldquo;&lt;strong>wohin die Abhängigkeitspfeile zeigen&lt;/strong>&amp;rdquo; – und sie entsprechend der Größe und Komplexität des eigenen Projekts angemessen anzuwenden.&lt;/p>
&lt;hr>
&lt;p>&lt;em>Referenz: &amp;ldquo;Clean Architecture: A Craftsman&amp;rsquo;s Guide to Software Structure and Design&amp;rdquo; von Robert C. Martin&lt;/em>&lt;/p></description></item></channel></rss>