EmberJS & SASS & Bootstrap
Because I have figured this out more than once... I present my notes on how to setup an ember-cli project with sass css.
Additionally I will add the bootstrap framework and a pattern to be able to override it's .scss sources. I focus on the Bootstrap framework but the above should work with any library that have sources in sass.
Finally I describe a tiny pattern I have found useful regarding directories and css.
First we'll init a ember project. You can do this with any existing ember project, but please note that ember-cli only allows to use one styling language.
mkdir new-app
cd new-app
ember init
npm uninstall ember-welcome-page --save-dev
SASS
It is not difficult to switch to SASS support to in ember project. First some dependencies.
npm install broccoli-funnel --save-dev
npm install broccoli-merge-trees --save-dev
bower install bootstrap-sass --save
ember install ember-cli-sass
Then it is necessary must modify the ember-cli-build.js
file to be able to generate css from .sass (or .scss) files.
I define the includePaths
option of ember-cli-sass
to bower_components/bootstrap-sass/assets/stylesheets
. This simply makes it possible to do @import 'bootstrap/*';
within our application styles file.
A 'Funnel' is created to be able to use the bootstrap icon fonts.
/*jshint node:true*/
/* global require, module */
var EmberApp = require('ember-cli/lib/broccoli/ember-app');
var Funnel = require('broccoli-funnel');
var MergeTrees = require('broccoli-merge-trees');
module.exports = function (defaults) {
var app = new EmberApp(defaults, {
sassOptions: {
includePaths: [
'bower_components/bootstrap-sass/assets/stylesheets'
]
}
});
// Use `app.import` to add additional libraries to the generated
// output files.
//
// If you need to use different assets in different
// environments, specify an object as the first parameter. That
// object's keys should be the environment name and the values
// should be the asset to use in that environment.
//
// If the library that you are including contains AMD or ES6
// modules that you would like to import into your application
// please specify an object with the list of modules as keys
// along with the exports of each module as its value.
// @see https://ember-cli.com/user-guide/#using-broccoli-funnel
var bootstrapAssets = new Funnel(app.bowerDirectory + '/bootstrap-sass/assets/fonts/bootstrap', {
srcDir: '/',
include: ['**/*.*'],
destDir: '/fonts/bootstrap'
});
return app.toTree(new MergeTrees([bootstrapAssets]));
};
Finally renaming app/styles/app.css
to app/styles/app.scss
enables us to do something like this @import "bootstrap/normalize";
and have the sass preprocessor find the correct file.
mv app/styles/app.css app/styles/app.scss
A directory pattern for styles
Organizing style segments is kind of hard. I usually start out cleanly but things get muddier as I build my app. Here is a directory structure as a suggestion.
mkdir app/styles/bootstrap
mkdir app/styles/bootstrap/overrides
mkdir app/styles/mixins
mkdir app/styles/body-classes
And some files
echo '// application variables' > app/styles/variables.scss
echo '@import "variables";' > app/styles/app.scss
echo '@import "bootstrap/bootstrap";' >> app/styles/app.scss
cp ./bower_components/bootstrap-sass/assets/stylesheets/bootstrap/_variables.scss app/styles/bootstrap/overrides/variables.scss
Add the following (as needed) to app/styles/app.scss
@import "variables";
@import "bootstrap/variables";
@import "bootstrap/overrides/variables";
@import "bootstrap/mixins";
// Reset and dependencies
@import "bootstrap/normalize";
@import "bootstrap/print";
@import "bootstrap/glyphicons";
// Core CSS
@import "bootstrap/scaffolding";
@import "bootstrap/type";
@import "bootstrap/code";
@import "bootstrap/grid";
@import "bootstrap/tables";
@import "bootstrap/forms";
@import "bootstrap/buttons";
// Components
@import "bootstrap/component-animations";
@import "bootstrap/dropdowns";
@import "bootstrap/button-groups";
@import "bootstrap/input-groups";
@import "bootstrap/navs";
@import "bootstrap/navbar";
@import "bootstrap/breadcrumbs";
@import "bootstrap/pagination";
@import "bootstrap/pager";
@import "bootstrap/labels";
@import "bootstrap/badges";
@import "bootstrap/jumbotron";
@import "bootstrap/thumbnails";
@import "bootstrap/alerts";
@import "bootstrap/progress-bars";
@import "bootstrap/media";
@import "bootstrap/list-group";
@import "bootstrap/panels";
@import "bootstrap/responsive-embed";
@import "bootstrap/wells";
@import "bootstrap/close";
// Components w/ JavaScript
@import "bootstrap/modals";
@import "bootstrap/tooltip";
@import "bootstrap/popovers";
@import "bootstrap/carousel";
// Utility classes
@import "bootstrap/utilities";
@import "bootstrap/responsive-utilities";
@import "bootstrap/theme";
Copy any bootstrap scss file so you can override properties and css definitions in each of them.
You'll need to import them from app/styles/app.scss
. Beware of the order you import them.
Body classes
A pattern that I found useful makes use of routes that upon activation add classes to the body tag.
ember install ember-body-class
All routes have a classNames attribute of type Array. If you wanted to add a body class page-special
to your route, it would look like this:
export default Ember.Route.extend({
classNames: ["page-common", "page-special"]
})
Then I create a file per use body class and import them in app/styles/app.scss
.
// app/styles/pages/page-common.scss
body.page-common {
// a things are black
}
// app/styles/pages/page-special.scss
body.page-special {
// special things are red
}
The Ember way to Bootstrap
Since we've now added bootstrap css compilation to our workflow it might be an idea to also add these ember-bootstrap integrations.
ember install ember-bootstrap
ember install ember-bootstrap-cp-validations
To avoid adding ember-bootstrap compiled css modify ember-cli-build.js
to instruct it not to include it.
'ember-bootstrap': {
'importBootstrapCSS': false,
'importBootstrapTheme': false
},
Vendor CSS Prefixing
Completely free bonus step
Install the ember-cli-autoprefixer addon so you can simply write the css you know and forget about vendor prefixing.
ember install ember-cli-autoprefixer
Then add a list of browsers (per environment target) to your packages.json so the prefixer (and other npm modules) can do its magic.
See browserlist for more information on this subject.
"browserslist": {
"production": [
"last 2 version",
"ie 10"
],
"testing": [
"last 2 versions"
],
"development": [
"last 2 versions"
]
}