Eleventy: PostCSS und Tailwind CSS integrieren

In meinem Eleventy Tutorial habe ich ja bereits gezeigt, wie man einen Blog mit Eleventy aufsetzen kann und auch, wie man CSS in eine Eleventy Webseite integieren kann.

Da ich gerne mit Tailwind CSS arbeite, möchte ich dieses natürlich auch in meinen privaten Projekten einsetzen und habe hier auch einige Setups ausprobiert. Die meisten setzen hierbei PostCSS und TailwindCSS "klassisch" auf und passen dann die Skripte in der package.json so an, dass erst die Eleventy Seite gebaut wird und dann das passende CSS generiert wird. Das kann man so tun, machte in meinen Augen aber das Setup in der package.json komplizierter.

Meine Alternative nutzt Eleventy Bordmittel, um das CSS zu generieren und fügt sich mit wenigen Zeilen Code in die bestehende Konfiguration ein.

Das ganze könnt ihr Euch in meinem Tutorial Projekt im tailwind Branch ansehen (commit).

Die Idee ist die folgende: Statt einem nachgelagerten Prozess, nutzen wir während des Builds über einen asynchronen Filter PostCss und erzeugen so über Eleventy auch direkt unser CSS.

Damit wir PostCSS und Tailwind CSS nutzen können, müssen wir es im ersten Schritt installieren:

npm i -D tailwindcss postcss autoprefixer cssnano

In unserer Eleventy Konfiguration benötigen wir jetzt noch einen asynchronen Filter:

const tailwind = require('tailwindcss');
const postCss = require('postcss');
const autoprefixer = require('autoprefixer');
const cssnano = require('cssnano');

const postcssFilter = (cssCode, done) => {
// wir rufen hier PostCSS auf.
postCss([tailwind(require('./tailwind.config')), autoprefixer(), cssnano({ preset: 'default' })])
.process(cssCode, {
// Pfad zu unserer CSS Datei
from: './src/_includes/styles/tailwind.css'
})
.then(
(r) => done(null, r.css),
(e) => done(e, null)
);
};

Natürlich könnt ihr mit wenigen Anpassungen auch nur PostCSS nutzen. Lasst dann einfach den Tailwind CSS spezifischen Code raus.

Den Filter fügen wir jetzt in der Konfiguration, zusammen mit einem "watch target", hinzu

module.exports = function (config) {
// ...
config.addWatchTarget('./src/_includes/styles/tailwind.css');
config.addNunjucksAsyncFilter('postcss', postcssFilter);
// ...
};

Über addWatchTarget teilen wir Eleventy mit, dass Änderungen an dieser Datei einen erneute build auslösen sollen, wenn wir gerade die Anwendung lokal mit --serve laufen lassen. Der asynchrone Filter sorgt dafür, dass wir unser CSS umwandeln können.

Jetzt fehlt nur noch das CSS, welches wir unter ./src/_includes/styles/tailwind.css im Projekt hinzufügen. Dort kann natürlich ganz normal mit Tailwind CSS gearbeitet werden:

@tailwind base;
@tailwind components;
@tailwind utilities;

Wenn wir jetzt unser CSS in der Seite ergänzen möchten, haben wir zwei Möglichkeiten:

  1. als inline style an beliebiger Stelle in unsererm template
  2. als eigene CSS Datei

Welche Variante hier die richtige ist, hängt stark vom Projekt ab und ist wahrscheinlich einen eigenen Blogpost wert. Ich zeige hier schnell beide Varianten:

Im head der Seite (in unserem Beispiel in der Datei src/_includes/layout.njk):

<style>
{% set css %}
{% include "styles/tailwind.css" %}
{% endset %}
{{css | postcss | safe}}
</style>

Wir setzen hier zunächst den Wert css im Template und nutzen dann den postcss Filter. Über den safe Filter wird das ganze dann in unserem Template eingefügt. Für mich die richtige Lösung, wenn ich nur wenig CSS habe und die erzeugte HTML Datei nicht zu groß wird.

Wenn man lieber eine eigene CSS Datei erzeugen möchte, kann man dies über ein eigenes Template machen (z. B. src/style.njk)

---
permalink: style.css
---
{% set css %}
{% include "styles/tailwind.css" %}
{% endset %}
{{css | postcss | safe}}

Dank dem permalink in der FrontMatter wird diese Datei als style.css erzeugt und kann dann in unserem Template eingebettet werden (src/_includes/layout.njk).

<link rel="stylesheet" href="/style.css" />

Kennt ihr einen besseren Weg, um CSS in Eleventy zu erzeugen und integrieren? Habt ihr weitere Fragen zu Eleventy? Schreibt mir gerne auf Mastodon oder Twitter.