Aurelia, Bootstrap, and Sass

Today’s objective was to override Bootstrap variables to permit some theme customization. Overall the process had a few pitfalls and I found that many references to this combination of tools were now out of date, presumably the result of changes to Aurelia’s project structure since they were written.

Sass Preprocessor

I started with a default Aurelia setup. This supports CSS and doesn’t include a preprocessor for Sass. To help, I generated an Aurelia project using au new with the Sass preprocessor selected. This highlighted the first change needed which was to replace the cssProcessor in the aurelia.json file:

  "cssProcessor": {
    "id": "sass",
    "displayName": "Sass",
    "fileExtension": ".scss",
    "source": "src/**/*.scss"
  },

To pre-process Sass a pre-processor is required. I used gulp-sass, obtained via npm install gulp-sass --save-dev. save-dev is used because this is a build tool and not required at runtime. To introduce this into the build process, I changed tasks/process-css.ts

import * as gulp from 'gulp';
import * as changedInPlace from 'gulp-changed-in-place';
import * as sourcemaps from 'gulp-sourcemaps';
import * as sass from 'gulp-sass';
import * as project from '../aurelia.json';
import {build} from 'aurelia-cli';

export default function processCSS() {
  return gulp.src(project.cssProcessor.source)
    .pipe(changedInPlace({firstPass:true}))
    .pipe(sourcemaps.init())
    .pipe(sass().on('error', sass.logError))
    .pipe(build.bundle());
};

Then I added an .scss file and referenced it from the app.html with a .css extension: . At this point doing au run and opening the browser showed the scss styles applied.

Adding Bootstrap

Aurelia doesn’t create a physical CSS file, instead the build.bundle() call in tasks/process-css.ts adds it to the app-bundle.js. As the goal was to customize Bootstrap before the CSS is generated, then the generated CSS, and therefore Bootstrap, must be included in the app-bundle rather than the vendor-bundle (where the contact manager tutorial puts it). This meant removing jquery and Bootstrap from the vendor-bundle section of the aurelia.json and putting it in the app-bundle section as follows:

"bundles": [
      {
        "name": "app-bundle.js",
        "source": [
          "[**/*.js]",
          "**/*.{css,html}"
        ],
        "dependencies": [
          "jquery",
          "bootstrap-sass"
        ]
      },
      {
        "name": "vendor-bundle.js",
        ...

The json above is slightly out of order because it references bootstrap-sass, the Sass version of bootstrap. This was obtained using the command npm install bootstrap-sass --save. I also had to clear out the original Bootstrap by deleting it from the package.json and running npm prune, and then doing similar steps for Typings.
At this point the scss file was as follows:

$navbar-default-bg: #800;
@import '../node_modules/bootstrap-sass/assets/stylesheets/bootstrap';
div {
    border: 1px solid green;
}

Building this with au build resulted in fairly verbose and non-illuminating errors because in order to work Bootstrap depends on gulp-autoprefixer which needed to be added to the process-css.ts as follows:

import * as gulp from 'gulp';
import * as changedInPlace from 'gulp-changed-in-place';
import * as autoprefixer from 'gulp-autoprefixer';
import * as sourcemaps from 'gulp-sourcemaps';
import * as sass from 'gulp-sass';
import * as project from '../aurelia.json';
import {build} from 'aurelia-cli';

export default function processCSS() {
  return gulp.src(project.cssProcessor.source)
    .pipe(changedInPlace({firstPass:true}))
    .pipe(sourcemaps.init())
    .pipe(sass().on('error', sass.logError))
    .pipe(autoprefixer())
    .pipe(build.bundle());
};

With that, I successfully overwrote a Bootstrap variable.