<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[thomaskral – Web Development]]></title><description><![CDATA[Berichte und How To's aus den Bereichen Web-Entwicklung, Analytics und Automatisierung.]]></description><link>https://thomaskral.de/</link><image><url>https://thomaskral.de/favicon.png</url><title>thomaskral – Web Development</title><link>https://thomaskral.de/</link></image><generator>Ghost 5.75</generator><lastBuildDate>Wed, 08 Apr 2026 11:04:23 GMT</lastBuildDate><atom:link href="https://thomaskral.de/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[How To: Entwicklung von Cross-Browser-Extensions mit einer Codebase und tailwindcss]]></title><description><![CDATA[In diesem Artikel lernst du, wie du eine gemeinsame Codebase für deine Chrome- und Firefox-Extension nutzen kannst. Außerdem zeige ich dir, wie du tailwindcss einbindest und sich der Code während du entwickelst für die jeweilige Extension automatisch generiert.]]></description><link>https://thomaskral.de/cross-browser-extension-mit-tailwindcss/</link><guid isPermaLink="false">656efb7ecfabb94958610068</guid><category><![CDATA[web-development]]></category><category><![CDATA[tailwindcss]]></category><category><![CDATA[browser-extensions]]></category><dc:creator><![CDATA[Thomas Kral]]></dc:creator><pubDate>Tue, 05 Dec 2023 15:50:00 GMT</pubDate><content:encoded><![CDATA[<p>F&#xFC;r einen meiner Kunden durfte ich die Pflege und Weiterentwicklung einer bestehenden Browser-Extension f&#xFC;r Firefox und Chrome &#xFC;bernehmen. Meine erste Aufgabe bestand darin Bugs zu beheben und die Extension von der Manifest V2 auf die Manifest V3 zu migrieren. Sp&#xE4;ter folgte noch ein Redesign.</p><p>Zum Zeitpunkt der &#xDC;bergabe wurde f&#xFC;r Firefox sowie f&#xFC;r Chrome jeweils die identische Codebase gepflegt (abgesehen von den Unterschieden in der <code>manifest.json</code>). Damit ich bei der Pflege und Weiterentwicklung nicht jede Code&#xE4;nderung doppelt vornehmen muss und Zeit spare, habe ich die Codebase zusammengefasst. Soweit, so gut. Sobald du aber, deine Extension lokal im Browser laufen lassen oder in den Extension-Store hochladen m&#xF6;chtest, kommst du mit der einen Codebase nicht weiter.</p><h2 id="wie-mit-der-manifestjson-umgehen">Wie mit der <code>manifest.json</code> umgehen?</h2><p>Chrome und Firefox haben unterschiedliche Anforderungen an die <code>manifest.json</code>. Durch das Zusammenfassen der Codebase m&#xFC;ssen wir die beiden <code>manifest.json</code> in <code>chrome.manifest.json</code> und <code>firefox.manifest.json</code> umbenennen. Damit k&#xF6;nnen weder Chrome noch Firefox was anfangen.<br><br><strong>Die L&#xF6;sung:</strong> Ein Skript, welches die Codebase nimmt und diese wieder in Chrome- und Firefox-spezifische Extensions aufsplittet. Beim Entwickeln kann es aber ganz sch&#xF6;n anstrengend werden bei jeder &#xC4;nderung dieses Skript erneut ausf&#xFC;hren zu m&#xFC;ssen. Wie wir das l&#xF6;sen, zeige ich dir gleich.</p><h2 id="wie-mit-tailwind-umgehen">Wie mit Tailwind umgehen?</h2><p>F&#xFC;r das Redesign habe ich mich f&#xFC;r Tailwind entschieden. Auch hier haben wir das Problem, dass wir Tailwind nicht nach jeder &#xC4;nderung am Style manuell ausf&#xFC;hren wollen. Tailwind generiert das CSS-File basierend auf den verwendeten CSS-Klassen in deinem Projekt. Bedeutet, dass unter Umst&#xE4;nden bestimmte CSS-Klassen aus deinen letzten &#xC4;nderungen im Stylesheet nicht auftauchen.</p><h2 id="die-l%C3%B6sung-tailwindbash-skript-mit-einem-watcher-ausl%C3%B6sen">Die L&#xF6;sung: Tailwind- &amp; <code>bash</code>-Skript mit einem Watcher ausl&#xF6;sen</h2><p>Okay legen wir los. F&#xFC;r das Aufsplitten bauen wir uns ein <code>bash</code>-Skript und legen es im <code>bin</code>-Verzeichnis ab. Das Skript habe ich <code>extensions-generate.sh</code> genannt. Es sieht wie folgt aus:</p>
<!--kg-card-begin: html-->
<pre><code>#!/bin/bash
DIR=&quot;dist/&quot;
if [ -d &quot;$DIR&quot; ]; then
  rm -r dist
  echo &quot;Deleted ${DIR} directory&quot;
fi

mkdir -p dist/firefox/
mkdir -p dist/chrome/
cp -r [extension-folder] dist/firefox/
cp -r [extension-folder] dist/chrome/

mv dist/firefox/[extension-folder]/firefox.manifest.json dist/firefox/[extension-folder]/manifest.json
mv dist/chrome/[extension-folder]/chrome.manifest.json dist/chrome/[extension-folder]/manifest.json</code></pre>
<!--kg-card-end: html-->
<p>Das Skript pr&#xFC;ft zuerst ob es bereits ein <code>dist</code>-Verzeichnis gibt. Falls ja, wird dieses gel&#xF6;scht. Danach legt es die folgende Verzeichnisstruktur an:</p>
<!--kg-card-begin: html-->
<pre><code>dist
  |- chrome
  |- firefox</code></pre>
<!--kg-card-end: html-->
<p>Als n&#xE4;chstes kopiert das Skript die Codebase jeweils in das Chrome- und das Firefox-Verzeichnis und benennt die <code>chrome.manifest.json</code> sowie die <code>firefox.manifest.json</code> zu <code>manifest.json</code> um. <code>[extension-folder]</code> ist ein Platzhalter. Den musst du mit dem Namen des Verzeichnisses austauschen in dem deine Codebase liegt.</p><p>Damit wir das Skript mit <code>yarn extensions:generate</code> ausf&#xFC;hren k&#xF6;nnen, hinterlegen wir es noch in der <code>package.json</code>:</p>
<!--kg-card-begin: html-->
<pre><code>&quot;scripts&quot;: {
    &quot;extensions:generate&quot;: &quot;./bin/extensions-generate.sh&quot;
},</code></pre>
<!--kg-card-end: html-->
<p>Es kann sein, dass du beim Ausf&#xFC;hren diese Fehlermeldung erh&#xE4;ltst: <code>permission denied</code>. In dem Fall f&#xFC;hrst du im Terminal folgendes aus: <code>chmod +x bin/build.sh</code>. Danach solltest du das Skript ausf&#xFC;hren k&#xF6;nnen.</p><h3 id="jetzt-noch-den-watcher-einbauen">Jetzt noch den Watcher einbauen</h3><p>Um das Skript automatisch bei Code&#xE4;nderungen auszuf&#xFC;hren, m&#xFC;ssen wir noch was erg&#xE4;nzen. Zur Hilfe installieren wir das <code>npm</code>-Paket <code>watch</code> mit <code>yarn add watch -D</code>, dann passen wir noch die <code>package.json</code> an:</p>
<!--kg-card-begin: html-->
<pre><code>&quot;scripts&quot;: {
    &quot;extensions:generate&quot;: &quot;./bin/extensions-generate.sh&quot;,
    &quot;extensions:watch&quot;: &quot;watch &apos;./bin/extensions-generate.sh&apos; ./[extension-folder]&quot;
},</code></pre>
<!--kg-card-end: html-->
<p>Okay, damit h&#xE4;tten wir den ersten Teil gel&#xF6;st. Jetzt m&#xFC;ssen wir noch das CSS mit Tailwind jedes Mal neu generieren lassen. Wie du Tailwind grunds&#xE4;tzlich einbindest, entnimmst du am besten der <a href="https://tailwindcss.com/docs/installation?ref=thomaskral.de" rel="noreferrer">Dokumentation</a> und w&#xE4;hlst den Weg &#xFC;ber die Tailwind CLI.</p><h3 id="tailwind-nicht-vergessen">Tailwind nicht vergessen</h3><p>Mit dem Befehl <code>npx tailwindcss -i ./src/input.css -o ./dist/output.css</code> wird das CSS generiert. du musst unter Umst&#xE4;nden die Pfade zu der Input- und Output-File anpassen. Bei mir sieht das so aus:</p>
<!--kg-card-begin: html-->
<pre><code>npx tailwindcss -i ./tailwind-input.css -o ./[extension-folder]/css/tailwind-output.css --minify</code></pre>
<!--kg-card-end: html-->
<p>Damit ich diesen langen Befehl nicht immer eintippen muss, habe ich meine <code>package.json</code> erg&#xE4;nzt:</p>
<!--kg-card-begin: html-->
<pre><code>&quot;scripts&quot;: {
    &quot;css:generate&quot;: &quot;npx tailwindcss -i ./tailwind-input.css -o ./[extension-folder]/css/tailwind-output.css --minify&quot;,
    &quot;extensions:generate&quot;: &quot;./bin/extensions-generate.sh&quot;,
    &quot;extensions:watch&quot;: &quot;watch &apos;./bin/extensions-generate.sh&apos; ./[extension-folder]&quot;
},</code></pre>
<!--kg-card-end: html-->
<p>Tailwind und unser <code>bash</code>-Skript m&#xFC;ssen wir nacheinander ausf&#xFC;hren. Daf&#xFC;r holen wir uns noch ein Helferlein mit <code>yarn add npm-run-all -D</code> in unser Projekt. Mit dem Befehl <code>yarn run-s ...</code> k&#xF6;nnen wir dann mehrere Skripte nacheinander ausf&#xFC;hren. Dabei ber&#xFC;cksichtigt <code>run-s</code> ob wir <code>yarn</code> oder <code>npm</code> nutzen. Jetzt erg&#xE4;nzen wir noch unsere <code>package.json</code> mit diesem Befehl:</p>
<!--kg-card-begin: html-->
<pre><code>&quot;scripts&quot;: {
    &quot;css:generate&quot;: &quot;npx tailwindcss -i ./tailwind-input.css -o ./[extension-folder]/css/tailwind-output.css --minify&quot;,
    &quot;extensions:generate&quot;: &quot;./bin/extensions-generate.sh&quot;,
    &quot;extensions:generate:all&quot;: &quot;run-s css:generate extensions:generate&quot;,
    &quot;extensions:watch&quot;: &quot;watch &apos;run-s css:generate extensions:generate&apos; ./[extension-folder]&quot;
},</code></pre>
<!--kg-card-end: html-->
<p><code>extensions:generate:all</code> generiert einmalig das CSS und die jeweiligen Extensions. <code>extensions:watch</code> beobachtet die Codebase und generiert bei jeder &#xC4;nderung neues CSS und die Extensions.</p>]]></content:encoded></item></channel></rss>