Last Updated: February 25, 2016
·
5.741K
· quocvu

Customize Bootstrap within MEAN.JS

Currently, MEAN.JS ships with Bootstrap but does not support extending it with LESS. To add additional styles, we need to add CSS files. To customize the default theme, we need to overwrite exiting styles with a new CSS. This layered approach is impractical for several reasons. First, we need write CSS instead of LESS. Second, we don't access to all the variables and mixins provided by Bootstrap.

I would much rather generate a single CSS file combining Bootstrap, my customizations, and additional styles together. This file is placed in public/application.min.css. Let's see how we add LESS compilation and remove the existing auto inclusion of CSS files scattered in various AngularJS modules.

Remove CSS

Edit config/env/all.js:

  • Remove from assets.lib.css arraypublic/lib/bootstrap/dist/css/bootstrap.css and public/lib/bootstrap/dist/css/bootstrap-theme.css. Make sure to leave an empty array (don't remove the css array)
  • Remove from assets.css the first element public/modules/**/css/*.css. We don't write CSS but LESS. Add public/application.min.css to this array. It will be auto included by app/views/templates/layout.server.view.html
  • Change assets.css to public/application.min.css
'use strict';

module.exports = {
    app: {
        title: 'my first MEAN.JS app',
        description: 'Try out MEAN.JS',
        keywords: 'nodejs, meanjs, trial'
    },
    port: process.env.PORT || 3000,
    templateEngine: 'swig',
    sessionSecret: 'MEAN',
    sessionCollection: 'sessions',
    assets: {
        lib: {
            js: [
                'public/lib/angular/angular.js',
                'public/lib/angular-resource/angular-resource.js', 
                'public/lib/angular-cookies/angular-cookies.js', 
                'public/lib/angular-animate/angular-animate.js', 
                'public/lib/angular-touch/angular-touch.js', 
                'public/lib/angular-sanitize/angular-sanitize.js', 
                'public/lib/angular-ui-router/release/angular-ui-router.js',
                'public/lib/angular-ui-utils/ui-utils.js',
                'public/lib/angular-bootstrap/ui-bootstrap-tpls.js'
            ],
            css: [],
        },
        css: [
            'public/application.min.css',
        ],
        js: [
            'public/config.js',
            'public/application.js',
            'public/modules/*/*.js',
            'public/modules/*/*[!tests]*/*.js'
        ],
        tests: [
            'public/lib/angular-mocks/angular-mocks.js',
            'public/modules/*/tests/*.js'
        ]
    }
};

Edit config/env/production.js and remove assets.lib.css and assets.css entries.

Combine LESS Files Into One

  • Copy the file public/lib/bootstrap/less/bootstrap.less to public/less/application.less and fix the import path of less files.
  • Add a file public/less/variables.less to overwrite Bootstrap variables and add any new variables. Import it into public/less/application.less by inserting it right after the import of bootstrap variables.
  • Add a file public/less/mixins.less to overwrite Bootstrap mixins and define your own mixins. Import it into public/less/application.less by inserting it right after the import of bootstrap mixins.
  • Include any other LESS files you need to create. Files defining styles for the entire application would go in public/less. Files with style for specific modules go in their respective less folders public/modules/xyz/less

The public/less/application.less should look like this:

// Core variables and mixins
@import "../lib/bootstrap/less/variables.less";
// -- Overwrite bootstrap and additional variables
@import "variables.less";

@import "../lib/bootstrap/less/mixins.less";
// -- Overwrite bootstrap and additional mixins
@import "mixins.less";

// Reset and dependencies
@import "../lib/bootstrap/less/normalize.less";
@import "../lib/bootstrap/less/print.less";
@import "../lib/bootstrap/less/glyphicons.less";

// Core CSS
@import "../lib/bootstrap/less/scaffolding.less";
@import "../lib/bootstrap/less/type.less";
@import "../lib/bootstrap/less/code.less";
@import "../lib/bootstrap/less/grid.less";
@import "../lib/bootstrap/less/tables.less";
@import "../lib/bootstrap/less/forms.less";
@import "../lib/bootstrap/less/buttons.less";

// Components
@import "../lib/bootstrap/less/component-animations.less";
@import "../lib/bootstrap/less/dropdowns.less";
@import "../lib/bootstrap/less/button-groups.less";
@import "../lib/bootstrap/less/input-groups.less";
@import "../lib/bootstrap/less/navs.less";
@import "../lib/bootstrap/less/navbar.less";
@import "../lib/bootstrap/less/breadcrumbs.less";
@import "../lib/bootstrap/less/pagination.less";
@import "../lib/bootstrap/less/pager.less";
@import "../lib/bootstrap/less/labels.less";
@import "../lib/bootstrap/less/badges.less";
@import "../lib/bootstrap/less/jumbotron.less";
@import "../lib/bootstrap/less/thumbnails.less";
@import "../lib/bootstrap/less/alerts.less";
@import "../lib/bootstrap/less/progress-bars.less";
@import "../lib/bootstrap/less/media.less";
@import "../lib/bootstrap/less/list-group.less";
@import "../lib/bootstrap/less/panels.less";
@import "../lib/bootstrap/less/responsive-embed.less";
@import "../lib/bootstrap/less/wells.less";
@import "../lib/bootstrap/less/close.less";

// Components w/ JavaScript
@import "../lib/bootstrap/less/modals.less";
@import "../lib/bootstrap/less/tooltip.less";
@import "../lib/bootstrap/less/popovers.less";
@import "../lib/bootstrap/less/carousel.less";

// Utility classes
@import "../lib/bootstrap/less/utilities.less";
@import "../lib/bootstrap/less/responsive-utilities.less";

// these are your LESS files
@import "style.less"
@import "../modules/one/less/style.less"
@import "../modules/two/less/style.less"

Add LESS Support

Install the grunt task to compile LESS files

% npm install grunt-contrib-less --save

Tell grunt to compile our LESS file by adding this block into gruntfile.js in the grunt.initConfig options

less: {
    production: {
        options: {
            paths: ['public/less'],
            cleancss: true,
            compress: true
        },
        files: {
            'public/application.min.css': 'public/less/application.less'
        }
    },
    development: { 
        options: { 
            sourceMap: true, 
            ieCompat:true, 
            dumpLineNumbers:true 
        },
        files: {
            'public/application.min.css': 'public/less/application.less'
        }
    }
},

Remove the uglify and cssmin sections, they are no longer needed. Less do this while compiling the combined file.

In the build task remove cssmin and uglify and add less

grunt.registerTask('build', ['lint', 'loadConfig', 'ngmin', 'less']);

Change ngAnnotate.production.files to 'public/application.js': '<%= applicationJavaScriptFiles %>'

Then load the less task installed earlier

grunt.loadNpmTasks('grunt-contrib-less');

Now, go to your shell and compile LESS by running grunt build or grunt less

Auto reload

Once you built application.min.css, you can have the browser auto reload it by editing gruntfile.js and change the watchFiles options to

clientCSS: ['public/application.min.css', 'public/modules/**/*.css'],

8 Responses
Add your response

oh one more thing would be that developers may not want to do the combination of override files in the Application.min.css file so they can implement less maps. This allows the ability to see less line numbers during browser inspection when the maps are added to the grunt.js.
e.g. -
less: {
development: {
options: {
sourceMap: true,
ieCompat:true,
dumpLineNumbers:true
},
files: {
// target.css file: source.less file

        }
    }
},
over 1 year ago ·

Thanks for the suggestion. I have updated the post accordingly.
Cheers

over 1 year ago ·

wth about css.map in public/ dir?
i see an error about css.map in my console

over 1 year ago ·

Thanks for this article.

I would like to point that You should also remove dependancy for: "bootstrap": "~3" in bower.json since there is new dependancy: "bootstrap-sass-official": "~3.3.3"

over 1 year ago ·

There is a problem with source mapping, i.e. it gives 404 - found because in application.min.css there is this URL:

/*# sourceMappingURL=public/application.min.css.map */

To fix this, add a configuration to "less.development.options" in "grunt.initConfig":
sourceMapURL: 'application.min.css.map'

This should fix the 404 error

over 1 year ago ·

Another HUGE problem is that public/application.js file, who holds AngularJS initialization code and which is created by MeanJS, gets overwritten by ngAnnotate task. This prevents angular from properly initializing!

Solution is:

  • move code from application.js a new file: public/angular-bootstrap.js;
  • in config/env/all.js, change (in "assets.js" path) 'public.application.js' to 'public/angular-bootstrap.js'
over 1 year ago ·

Last one... I hope!
There is a problem if running in production, i.e.:

TypeError: Cannot call method 'concat' of undefined

at /config/config.js:76

This is because we removed "assets.lib.css" and "assets.css" from production.js
To fix this we need to modify config/config.js:

module.exports = _.extend(
    require('./env/all'),
    require('./env/' + process.env.NODE_ENV) || {}
);

must be changed to:

module.exports = _.defaults(
    require('./env/all'),
    require('./env/' + process.env.NODE_ENV) || {}
);

So undefined properties are kept.

over 1 year ago ·

Thanks a lot megadix.
It would be best if you could fork the project and issue pull requests for the proposed fixes. That would ensure we all get that stuff in future releases.

https://github.com/meanjs/mean

over 1 year ago ·