In jedem meiner aktuellen Projekte setze ich auf die CSS-Precompiler Sass oder Less. Dabei möchte ich den Code einfach halten und möglichst nahe an der CSS3-Syntax bleiben. Mixins für vendor-prefixes kommen daher nicht in Frage, da ich mir so wenig wie möglich zusätzliche Syntax merken will.

Stattdessen verwende ich die CSS3-Anweisungen direkt ohne Prefix und überlasse die Cross-Browser-Kompatibilität dem Tool autoprefixer. Der zusätzliche Kompilierschritt bringt auch einige Fallstricke mit sich.

Um die unterschiedlichen, auf verschiedene Dateien verteilten, Module auch sauber debuggen zu können, führt hier kein Weg an Sourcemaps vorbei.

Zielsetzung und Rahmenbedingungen

Wer wirklich faul sein will, muss eine Aufgabenstellung sauber lösen. Das bedeutet, ich will nur ein build target für das CSS und nicht zwischen Entwicklung und live unterscheiden müssen. Wenn ein Bug auftritt, muss das auch live transparent nachvollziehbar sein. Das CSS muss ich jederzeit auf das Livesystem schmeißen können ohne Performance-Einbußen für den Besucher.

Damit das auch mit mehreren Kompilierschritten sauber funktioniert, ergeben sich einige Anforderungen:

  • Ein Stylesheet muss immer minimiert vorliegen
  • Das CSS darf keine debug prints (sass) enthalten.
  • Sourcemaps dürfen nicht inline eingebunden werden, sondern müssen als externe Datei vorliegen
  • Mapping-Pfade zu den Quelldateien dürfen nur relativ angegeben werden
  • Alle Quelldateien müssen im Repository unterhalb eines Verzeichnisses liegen (global eingebundene ruby gems sind scheiße, besser lokale bundler-Installationen oder noch besser bower verwenden)

Das wichtigste dabei: Der Precompiler bestimmt das Ergebnis. Wenn die Soucemap aus sass oder less absolute Pfade auf das Dateisystem des Entwicklers enthält, kann autoprefixer damit nicht umgehen und das Mapping zeigt ins Leere. Zudem würde das Debuggen nur an einem Entwicklerrechner funkionieren.

Als Bonus erkennt Autoprefixer, ob das CSS komprimiert vorliegt oder nicht. Das bedeutet, wenn ich dem Tool minimiertes CSS übergebe, erhalte ich auch ein minimiertes Ergebnis.

Liegt die Sourcemap als separate Datei vor, lässt sich diese auch sauber aus git/svn heraushalten.

Gulp setup:

Folgende npm-module werden verwendet:

  • gulp
  • gulp-sourcemaps
  • gulp-livereload
  • gulp-autoprefixer
  • gulp-minify-css
  • stream-combiner2

Workaround für gulp

Nacheinander werden dabei die Streams von less/sass, minifycss und autoprefixer kombiniert. Die Reihenfolge spielt in dem Fall eine große Rolle: Wird minifycss erst nach dem autoprefixer aufgerufen, ist die Sourcemap futsch und zeigt auf das kompilierte Stylesheet (github issue)

Gulp, Sass, Autoprefixer

Sass-Compiler: gulp-sass

gulp/sass.js view raw
var combiner = require('stream-combiner2'),
    gulp = require('gulp'),
    sass = require('gulp-sass'),
    sourcemaps = require('gulp-sourcemaps' ),
    livereload = require('gulp-livereload'),
    autoprefixer = require('gulp-autoprefixer'),
    minifyCSS = require('gulp-minify-css');

module.exports = function() {
    var stream = combiner.obj([
        gulp.src('scss/style.scss'),
        sourcemaps.init(),
        sass(),
        minifyCSS(),
        autoprefixer(),
        sourcemaps.write('.', {
            includeContent: false,
            sourceRoot : '../scss/'
        }),
        gulp.dest('css/'),
        livereload()
    ]);

    stream.on('error', console.error.bind(console));

    return stream;
};

Gulp, Less, Autoprefixer

Less-Compiler: gulp-less

gulp/less.js view raw
var combiner = require('stream-combiner2'),
    gulp = require('gulp'),
    less = require('gulp-less'),
    sourcemaps = require('gulp-sourcemaps' ),
    livereload = require('gulp-livereload'),
    autoprefixer = require('gulp-autoprefixer'),
    minifyCSS = require('gulp-minify-css');

module.exports = function() {
    var stream = combiner.obj([
        gulp.src('less/style.less'),
        sourcemaps.init(),
        less(),
        minifyCSS(),
        autoprefixer(),
        sourcemaps.write('.', {
            includeContent: false,
            sourceRoot : '../less/'
        }),
        gulp.dest('css/'),
        livereload()
    ]);

    stream.on('error', console.error.bind(console));

    return stream;
};

Grunt setup:

Folgende npm-module werden verwendet:

  • load-grunt-config
  • grunt
  • grunt-autoprefixer
  • grunt-concurrent
  • grunt-contrib-watch

Mithilfe von load-grunt-config lassen sich die Module auf verschiedene Dateien aufteilen. Bei dem Setup kann bei beiden Precompilern der gleiche autoprefixer-task verwendet werden.

Grunt, Sass, Autoprefixer

Sass-Compiler: grunt-contrib-sass

Funkioniert ähnlich wie die gulp-Lösung sauber und einfach. Autoprefixer kommt bei dem Setup auch dann klar, wenn Sass keinen komprimierten Output liefert.

grunt/sass.js view raw
module.exports = {
    all: {
        options: {
            sourcemap : 'auto',
            style : 'compressed'
        },
        files: {
            '<%=srcDir%>/css/raw/style.css': '<%=srcDir%>/scss/style.scss'
        }
    }
};

Grunt, Less, Autoprefixer

Less-Compiler: grunt-contrib-less

Der grunt task verwendet fürs Mapping absolute Pfade, relative Pfade muss man sich selbst bauen.

grunt/less.js view raw
module.exports = {
    all: {
        options: {
            sourceMap : true,
            sourceMapFilename : 'css/raw/style.css.map',
            sourceMapRootpath : '../../',
            sourceMapBasepath : '<%=srcDir%>',
            sourceMapURL : 'style.css.map',
            compress : true
        },
        files: {
            '<%=srcDir%>/css/raw/style.css': '<%=srcDir%>/less/style.less'
        }
    }
};
  • sourceMapRootpath: gibt den relativen Pfad zu den Quelldateien aus Sicht der Sourcemap an.
  • sourceMapBasepath: der absolute Pfad auf die Quelldateien, der in der Sourcemap von der Pfadangabe subtrahiert werden soll.
  • sourceMapFilename: Zielangabe für das Dateisystem, erzwingt die externe SourceMap
  • sourceMapURL: relative URL der Map aus Sicht des Stylesheets

Fazit

Am Rundesten lief die Konfiguration mit grunt-contrib-sass. Das less-Pendant war mit Abstand am ekligsten zu konfigurieren. Die gulp-tasks sind am schnellsten formuliert, jedoch ist die Reihenfolge der Streams nicht eingängig und die Sourcemap erstmal nicht zu gebrauchen gewesen.