Tony Ward

Tony Ward

CSS in Ember Engines

October 30, 2016

First off, welcome to my second post about Ember Engines! I’m working on a series of posts to talk about what I learned when migrating a traditional Ember app to an Ember Engine.

Were you previously using ember-cli-less in your Ember App? By default, the addon compiles app/styles/app.less into dist/assets/app.css. That means you have a single LESS file, importing all of your other LESS files (if applicable), that gets shoved out at build time to the right spot for Ember to pickup and use. One thing I noticed when moving to an engine, was that this didn’t work like it did in a traditional app.

The Engine Problem

When we moved over to an engine, I was wondering why none of our CSS was applied. Nothing was styled as it was in our traditional app and I was thoroughly confused. I couldn’t really find any documentation on it online, either. The other item not in my favor, was that I’m still fairly new to Ember and the build process.

I’m not a Broccoli master, so this did not come to me very quickly. Luckily, I have a lot of great Ember gurus around me. If you have a good understanding of how the ember-cli build process goes, experience working with addons already, or know a lot about Broccoli in general, you probably already have an idea of what needs to happen. The problem was that our LESS files were not being merged into the Broccoli funnel for the engine. This is required for add-ons and engines!

The Solution

One solution is to use broccoli-less-single. The key part being that this supports @imports. Using this allows you to merge your LESS files into a single CSS file, then merge that into the Broccoli funnel.

Warning: This solution is currently out of date as of 06/15 with Ember 2.12. If you are using that version, please follow the issue on GitHub here.

For the sake of this post, let’s say I have the following LESS file:

// This file resides at: addon/styles/addon.less
@import 'components/vegetables.less';
@import 'components/fruits.less';
@import 'variables.less';
.error {
  color: @error-color;
}

The first thing you’ll want to do is add broccoli-less-single and broccoli-merge-trees to your package.json.

// npm install broccoli-less-single --save
// npm install broccoli-merge-tress --save
// package.json
  "dependencies": {
    "broccoli-less-single": "~0.6.0",
    "broccoli-merge-trees": "~1.1.1",
    "ember-cli-htmlbars": "~1.0.1"
  },

After doing above, then in your index.js:

// index.js
var EngineAddon = require('ember-engines/lib/engine-addon');
var MergeTrees = require('broccoli-merge-trees');
var CompileLess = require('broccoli-less-single');
module.exports = EngineAddon.extend({
    name: ‘my-engine’,
    treeForAddon(tree) {
        var defaultTree = this._super.treeForAddon.call(this, tree);
        var compiledLessTree = new CompileLess(tree, 'styles/addon.less', this.name + '.css');
        return new MergeTrees([defaultTree, compiledLessTree]);
    }
});

So what does the above snippet actually do? We take the defaultTree, which is provided from the super call, compile our LESS file under styles/addon.less, and output it to our this.name + ‘.css’ file. After that, we merge the default tree with our new compiled tree and all of that gets shoved out to the /dist/assets/vendor.css.

That should be it to get your styles back in action!

A note about ember-cli-build.js

One side note I wanted to mention is that we aren’t doing anything in ember-cli-build.js like we would normally in our traditional Ember app. Why is that? Just read the comment and you’ll get your answer!

/*
 This build file specifies the options for the dummy test app of this
 addon, located in `/tests/dummy`
 This build file does *not* influence how the addon or the app using it
 behave. You most likely want to be modifying `./index.js` or app’s build file
 */

An untested note about SASS files

Even though the examples above are using LESS, you should be able to swap out broccoli-less-single to use broccoli-sass instead and be good to go. If not, I’ll buy you a beer.

Ta-da!

Hopefully that answers you questions about previously using ember-cli-less and the quick changes when you move to an engine. I’m sure there are other ways to handle it; however, the method I showed you worked the best for me. Happy engining!

New (4/24/2017): There’s now an EmberMap course on broccoli!