Working with Sass, Bootstrap and Gulp

For frontend developers, the days when you manually linked css files to your index.html are over. Modern workflows needs some compilation steps before having a css file that can be use in development or production. Most notably, sass has become the most important language that extends css functionalities, while gulp has won the battle on the building tools front.

In this tutorial I’m going to show how to use gulp to create a workflow that combines sass and bootstrap the right way.

First, lets create a folder to play called “gulp” (you can call it whatever you want, it doesn’t matter):

$ mkdir gulp
$ cd gulp

Because we need to separate our “source” files from the compiled ones, it’s better two create two different folders called “src” and “dist”.

$ mkdir dist
$ mkdir src

Inside our “src” folder we should have an “index.html” and a main sass file called “main.scss” where we are going to put all the sass code.

$ touch src/index.html
$ touch src/main.scss

We are going to start with a very simple html file that we are going to improve progressively

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Gulp Test</title>
</head>
<body>  
</body>
</html>

Similarly, we are going to create a very simple sass code inside “main.scss” just to test that everything is working.

$primary-color: red;
body {
  background-color: $primary-color;
}

We are now ready to start working with gulp. The first step is to create the file “package.json” to store all of our npm dependencies using the “-y” flag to accept all the default values.

$ npm init -y

Next we install the npm packages to use gulp with sass, using the “–save-dev” flag to store a reference in package.json.

$ npm install --save-dev gulp gulp-sass

Our file package.json should now look like this:

{
  "name": "gulp",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "gulp": "^3.8.11",
    "gulp-sass": "^2.0.1"
  }
}

We can now proceed to create our gulp file to automate our building process.

$ touch gulpfile.js

Right now, our folder structure is as follow:

.
├── dist
├── gulpfile.js
├── node_modules
├── package.json
└── src
    ├── index.html
    └── main.scss

With the gulp file in place, we should create a task to compile “main.scss” and write the result to the folder “dist”.

var gulp = require('gulp');
var sass = require('gulp-sass');

gulp.task('styles', function(){
  return gulp.src('src/main.scss')
    .pipe(sass())
    .pipe(gulp.dest('dist'))
});

We can execute the task with the following command:

$ gulp styles

As a result, we get a new file called “main.css” inside the “dist” folder.

body {
  background-color: red;
}

We now have our sass files compiled to css in the public directory but we also need to move our “index.html” to the “dist” folder in order to group all relevant files in one place. To do that, we create a new gulp task called “html”.

var gulp = require('gulp');
var sass = require('gulp-sass');

gulp.task('styles', function(){
  return gulp.src('src/main.scss')
    .pipe(sass())
    .pipe(gulp.dest('dist'))
});

gulp.task('html', function(){
  return gulp.src('src/index.html')
    .pipe(gulp.dest('dist'))
});

If we execute both tasks using:

$ gulp styles
$ gulp html

We obtain the following folder structure:

.
├── dist
  ├── index.html
  └── main.css
├── gulpfile.js
├── node_modules
├── package.json
└── src
    ├── index.html
    └── main.scss

If we look inside dist/index.html we immediately see a problem: there is no reference to the css file anywhere. We need to “inject” our newly compiled css file into the html and we can do that with the npm package “gulp-inject”.

$ npm install --save-dev gulp-inject

We now need to modify our gulpfile to use this new package and, to save some time, find a way to invoke both tasks (styles and html) using a single command.

var gulp = require('gulp');
var sass = require('gulp-sass');
var inject = require('gulp-inject');

gulp.task('styles', function(){
  return gulp.src('src/main.scss')
    .pipe(sass())
    .pipe(gulp.dest('dist'));
});

gulp.task('html', ['styles'], function(){
  var injectFiles = gulp.src(['dist/main.css']);

  return gulp.src('src/index.html')
    .pipe(inject(injectFiles))
    .pipe(gulp.dest('dist'));
});

Several things changed here. First, on line 3, we require the “gulp-inject” package. Next, on line 11 we enforced that the “styles” task to run before the “html” task son we don’t need to call both all the time, calling just gulp html is enough to execute both in order. On line 12 we decide which files we want to inject, in this case the processed css file. Finally, on line 15, we tell gulp to inject the css file inside “index.html”.

But then again, something is missing. We are telling gulp to inject a file, but we are not specifying where. To do that, we have to modify the index.html file to put some special flags for the gulp-inject package to do its magic.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Gulp Test</title>
  <!-- inject:css -->
  <!-- endinject -->
</head>
<body>  
</body>
</html>

Now, if we run gulp html this is what we get:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Gulp Test</title>
  <!-- inject:css -->
  <link rel="stylesheet" href="/dist/main.css">
  <!-- endinject -->
</head>
<body>  
</body>
</html>

Ok, we managed to inject the file but it’s not quite right. “index.html” and “main.css” are both inside the same folder so the path for the css file shouldn’t be “/dist/main.css” but just “main.css”. To do that, we define some special options to the inject package inside “gulpfile.js”.

var gulp = require('gulp');
var sass = require('gulp-sass');
var inject = require('gulp-inject');

gulp.task('styles', function(){
  return gulp.src('src/main.scss')
    .pipe(sass())
    .pipe(gulp.dest('dist'));
});

gulp.task('html', ['styles'], function(){
  var injectFiles = gulp.src(['dist/main.css']);

  var injectOptions = {
    addRootSlash: false,
    ignorePath: ['src', 'dist']
  };

  return gulp.src('src/index.html')
    .pipe(inject(injectFiles, injectOptions))
    .pipe(gulp.dest('dist'));
});

When we run gulp html again, the index.html changes showing the real path for the css file.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Gulp Test</title>
  <!-- inject:css -->
  <link rel="stylesheet" href="main.css">
  <!-- endinject -->
</head>
<body>  
</body>
</html>

Our example is very simple because we have only one sass file but, what if we have multiple sass files? First we should create a new folder called “styles” inside the “src” folder to store all the sass files.

$ mkdir src/styles
$ touch src/styles/layout.scss
$ touch src/styles/panel.scss

The file “main.scss” will become the master sass file where the other files will be injected and ideally shouldn’t have css rules other than “@import”. We need to move the content of “main.scss” to “layout.scss” and set some sass rules inside “panel.scss” to verify that both files can share sass variables.

$primary-color: red;
body {
  background-color: $primary-color;
}

.panel {
  color: $primary-color;
}

Similar to what we did in “index.html” we need to define some placeholders in “main.scss” to instruct gulp-inject where to inject the others sass files.

// inject:app
// endinject

Unlike html, gulp-inject does not have a default placeholder for injecting sass files, so we have to help the injector to recognize these placeholders.

var gulp = require('gulp');
var sass = require('gulp-sass');
var inject = require('gulp-inject');

gulp.task('styles', function(){
  var injectAppFiles = gulp.src('src/styles/*.scss', {read: false});

  function transformFilepath(filepath) {
    return '@import "' + filepath + '";';
  }

  var injectAppOptions = {
    transform: transformFilepath,
    starttag: '// inject:app',
    endtag: '// endinject',
    addRootSlash: false
  };

  return gulp.src('src/main.scss')
    .pipe(inject(injectAppFiles, injectAppOptions))
    .pipe(sass())
    .pipe(gulp.dest('dist/styles'));
});

gulp.task('html', ['styles'], function(){
  var injectFiles = gulp.src(['dist/styles/main.css']);

  var injectOptions = {
    addRootSlash: false,
    ignorePath: ['src', 'dist']
  };

  return gulp.src('src/index.html')
    .pipe(inject(injectFiles, injectOptions))
    .pipe(gulp.dest('dist'));
});

First of all, we need to tell the injector which files we want to inject, in this case, we want to inject all files with the .scss extension found in the “/src/styles/” folder (line 6). Next we define the injector options, establishing how we are going to write the import statement (transformFilePath), how to recognize the start and end of the placeholders (starttag and endtag) and that we don’t want a root slash in the import path (addRootSlash).

In the streaming process, we add a new pipe right before the execution of the sass compiler, where we inject the sass files inside “main.scss” (line 20). Notice that for consistency with “src”, we now redirect the resulting css to a “styles” folder inside “dist” (line 22). If we now run gulp html we can see that the compiled css has all the rules defined in the sass files.

body {
  background-color: red;
}
.panel {
  color: red;
}

Adding Bootstrap (SASS)

So far everything works great, we can create all the sass files we want and they will always get compiled to a single css file cross referencing all variables, mixins and so forth. But, what if we wanted to use the sass version of bootstrap and have also access to bootstrap internal variables and mixins? What if we wanted to overwrite some bootstrap default variables to take complete control over the framework? I’m glad you ask…

Because we are going to use bower to manage front end dependencies, we need to create a “bower.json” file first to store all of our dependencies. A very similar step of what we did with npm.

$ bower init

If we accept all the default options, we end up with a file like this:

{
  "name": "gulp",
  "version": "0.0.0",
  "authors": [
    "David Barreto <barretollano@gmail.com>"
  ],
  "license": "MIT",
  "ignore": [
    "**/.*",
    "node_modules",
    "bower_components",
    "test",
    "tests"
  ]
}

We are ready to download our first dependency: “bootstrap-sass”. We must remember to always use the “–save” flag to store the reference in “bower.json”.

$ bower install --save bootstrap-sass

Because we want to have access to bootstrap variables and mixins, we need to find a way to inject bootstrap in our main sass file “main.scss” along with our own sass files. In fact, we should take care to inject bootstrap before our sass files to guarantee that the variables are defined before we try to use it in our sass files. Unfortunately, “gulp-inject” is not the right tool for the job and we must rely in another npm package called “wiredep”.

$ npm install --save-dev wiredep

The wiredep package works similar to gulp-inject in the sense that it looks for placeholders to put the necessary import statements in our “main.scss” file.

// bower:scss
// endbower

// inject:app
// endinject

Additionally to putting the placehoders, we must change our gulpfile.js to use the “wiredep” package and include it in the piping process.

var gulp = require('gulp');
var sass = require('gulp-sass');
var inject = require('gulp-inject');
var wiredep = require('wiredep').stream;

gulp.task('styles', function(){
  var injectAppFiles = gulp.src('src/styles/*.scss', {read: false});

  function transformFilepath(filepath) {
    return '@import "' + filepath + '";';
  }

  var injectAppOptions = {
    transform: transformFilepath,
    starttag: '// inject:app',
    endtag: '// endinject',
    addRootSlash: false
  };

  return gulp.src('src/main.scss')
    .pipe(wiredep())
    .pipe(inject(injectAppFiles, injectAppOptions))
    .pipe(sass())
    .pipe(gulp.dest('dist/styles'));
});

gulp.task('html', ['styles'], function(){
  var injectFiles = gulp.src(['dist/styles/main.css']);

  var injectOptions = {
    addRootSlash: false,
    ignorePath: ['src', 'dist']
  };

  return gulp.src('src/index.html')
    .pipe(inject(injectFiles, injectOptions))
    .pipe(gulp.dest('dist'));
});

Whit this minor changes, we are ready to run again gulp html. If we now open the compiled file we can see that bootstrap is now there on the top, and our css rules are at the bottom of the file.

/*! normalize.css v3.0.2 | MIT License | git.io/normalize */
html {
  font-family: sans-serif;
  -ms-text-size-adjust: 100%;
  -webkit-text-size-adjust: 100%; 
}
body {
  margin: 0; 
}
body {
  background-color: red; 
}
.panel {
  color: red;
}

We now can now safely use all bootstrap variables, mixins and classes in our own code. But there’s still something to be done, we need a way to overwrite bootstrap variables.

This code should be injected before bootstrap itself and should live in its own folder inside “src”. For that, we create a new folder called “global” inside “src”, and a sass file called “variables.scss” where we are going to put the bootstrap variables that we want to overwrite along with their new values.

$ mkdir src/global
$ touch src/global/variables.scss

Bootstrap defines a variable called $font-size-base that has a default value of 14 px. Many other bootstrap variables depend on this variable, son changing it have a huge impact on bootstrap visualization.

$font-size-base: 20px;

Because we need to inject the “global” sass files before bootstrap, we need to define a new placeholder in “main.scss”.

// inject:global
// endinject

// bower:scss
// endbower

// inject:app
// endinject

We are now ready to update the gulp file to inject the sass files inside the “global” folder. The process is similar to what we did before when we injected the sass files inside our “styles” folder as can be seen below.

var gulp = require('gulp');
var sass = require('gulp-sass');
var inject = require('gulp-inject');
var wiredep = require('wiredep').stream;

gulp.task('styles', function(){
  var injectAppFiles = gulp.src('src/styles/*.scss', {read: false});
  var injectGlobalFiles = gulp.src('src/global/*.scss', {read: false});

  function transformFilepath(filepath) {
    return '@import "' + filepath + '";';
  }

  var injectAppOptions = {
    transform: transformFilepath,
    starttag: '// inject:app',
    endtag: '// endinject',
    addRootSlash: false
  };

  var injectGlobalOptions = {
    transform: transformFilepath,
    starttag: '// inject:global',
    endtag: '// endinject',
    addRootSlash: false
  };

  return gulp.src('src/main.scss')
    .pipe(wiredep())
    .pipe(inject(injectGlobalFiles, injectGlobalOptions))
    .pipe(inject(injectAppFiles, injectAppOptions))
    .pipe(sass())
    .pipe(gulp.dest('dist/styles'));
});

gulp.task('html', ['styles'], function(){
  var injectFiles = gulp.src(['dist/styles/main.css']);

  var injectOptions = {
    addRootSlash: false,
    ignorePath: ['src', 'dist']
  };

  return gulp.src('src/index.html')
    .pipe(inject(injectFiles, injectOptions))
    .pipe(gulp.dest('dist'));
});

To verify that it’s working, we run gulp html and inspect the resulting css file.

body {
  font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
  font-size: 20px;
  line-height: 1.42857;
  color: #333333;
  background-color: #fff; 
}

We can now see that we managed to control bootstrap internals overwriting just one variable.

To wrap up, we can define a new task that deletes the “dist” folder before we start compiling and moving files around. To do that we are going to need a new npm package called “del”.

$ npm install --save-dev del

And we need to modify our gulp file once more. In addition tu using the “del” package, we are going to simplify the command needed to run the task using the “default” task keyword.

var gulp = require('gulp');
var sass = require('gulp-sass');
var inject = require('gulp-inject');
var wiredep = require('wiredep').stream;
var del = require('del');

gulp.task('clean', function(cb){
  del(['dist'], cb);
});

gulp.task('styles', function(){
  var injectAppFiles = gulp.src('src/styles/*.scss', {read: false});
  var injectGlobalFiles = gulp.src('src/global/*.scss', {read: false});

  function transformFilepath(filepath) {
    return '@import "' + filepath + '";';
  }

  var injectAppOptions = {
    transform: transformFilepath,
    starttag: '// inject:app',
    endtag: '// endinject',
    addRootSlash: false
  };

  var injectGlobalOptions = {
    transform: transformFilepath,
    starttag: '// inject:global',
    endtag: '// endinject',
    addRootSlash: false
  };

  return gulp.src('src/main.scss')
    .pipe(wiredep())
    .pipe(inject(injectGlobalFiles, injectGlobalOptions))
    .pipe(inject(injectAppFiles, injectAppOptions))
    .pipe(sass())
    .pipe(gulp.dest('dist/styles'));
});

gulp.task('default', ['clean', 'styles'], function(){
  var injectFiles = gulp.src(['dist/styles/main.css']);

  var injectOptions = {
    addRootSlash: false,
    ignorePath: ['src', 'dist']
  };

  return gulp.src('src/index.html')
    .pipe(inject(injectFiles, injectOptions))
    .pipe(gulp.dest('dist'));
});

Finally, bootstrap is completely integrated into our workflow and we can check it running just gulp with no extra parameter.

Adding Other CSS Dependencies

Most libraries out there are not written or available in sass, how do we manage those libraries in our workflow?

Because those libraries doesn’t need to be compiled we are going to concatenate them into a single file called “vendors.css”. To do that, we are going to need three new npm packages: “gulp-concat”, “main-bower-files” and “gulp-filter”.

$ npm install --save-dev main-bower-files gulp-concat gulp-filter

Next we create a new task called “vendors” in our gulp file and make sure is invoked in the “default” task.

var gulp = require('gulp');
var sass = require('gulp-sass');
var inject = require('gulp-inject');
var wiredep = require('wiredep').stream;
var del = require('del');
var mainBowerFiles = require('main-bower-files');
var filter = require('gulp-filter');
var concat = require('gulp-concat');

gulp.task('clean', function(cb){
  del(['dist'], cb);
});

gulp.task('styles', function(){
  var injectAppFiles = gulp.src('src/styles/*.scss', {read: false});
  var injectGlobalFiles = gulp.src('src/global/*.scss', {read: false});

  function transformFilepath(filepath) {
    return '@import "' + filepath + '";';
  }

  var injectAppOptions = {
    transform: transformFilepath,
    starttag: '// inject:app',
    endtag: '// endinject',
    addRootSlash: false
  };

  var injectGlobalOptions = {
    transform: transformFilepath,
    starttag: '// inject:global',
    endtag: '// endinject',
    addRootSlash: false
  };

  return gulp.src('src/main.scss')
    .pipe(wiredep())
    .pipe(inject(injectGlobalFiles, injectGlobalOptions))
    .pipe(inject(injectAppFiles, injectAppOptions))
    .pipe(sass())
    .pipe(gulp.dest('dist/styles'));
});

gulp.task('vendors', function(){
  return gulp.src(mainBowerFiles())
    .pipe(filter('*.css'))
    .pipe(concat('vendors.css'))
    .pipe(gulp.dest('dist/styles'));
});

gulp.task('default', ['clean', 'styles', 'vendors'], function(){
  var injectFiles = gulp.src(['dist/styles/main.css', 'dist/styles/vendors.css']);

  var injectOptions = {
    addRootSlash: false,
    ignorePath: ['src', 'dist']
  };

  return gulp.src('src/index.html')
    .pipe(inject(injectFiles, injectOptions))
    .pipe(gulp.dest('dist'));
});

The new “vendors” task, grabs the main files of every bower library installed (js, css, sass, etc.) as defined in the bower.json file of every library (line 45), then filters only the css files (line 46), concatenates them in a file called “vendor.css” (line 47) that is then stored inside “dist/styles” (line 48).

Because we need to make sure that this new file gets referenced from “index.html”, we add that file to the injected files in the default task (line 52). Take note that the order of which the files are defined matters and because many libraries relies on bootstrap, we have to include the “vendors.css” file after our “main.css” files were bootstrap lives.

To try it out, we need to download some bower library that has a css file. For this tutorial we are going to use the library “bootstrap-material-desing”.

$ bower install --save bootstrap-material-design

We can test if everything is working Ok running the command gulp. As a result we get a new file called “vendor.css” in our “dist/styles” folder that is referenced in “index.html”.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Gulp Test</title>
  <!-- inject:css -->
  <link rel="stylesheet" href="styles/main.css">
  <link rel="stylesheet" href="styles/vendors.css">
  <!-- endinject -->
</head>
<body>  
</body>
</html>

Both css files are very long and could take a while to download when in production, so the next logical step is to use a minifier tool to reduce the total size of the generated css files. For that purpose we are going to use the npm package “gulp-csso”.

$ npm install --save-dev gulp-csso

Then we modify our gulp file to include the minifier in the process.

var gulp = require('gulp');
var sass = require('gulp-sass');
var inject = require('gulp-inject');
var wiredep = require('wiredep').stream;
var del = require('del');
var mainBowerFiles = require('main-bower-files');
var filter = require('gulp-filter');
var concat = require('gulp-concat');
var csso = require('gulp-csso');

gulp.task('clean', function(cb){
  del(['dist'], cb);
});

gulp.task('styles', function(){
  var injectAppFiles = gulp.src('src/styles/*.scss', {read: false});
  var injectGlobalFiles = gulp.src('src/global/*.scss', {read: false});

  function transformFilepath(filepath) {
    return '@import "' + filepath + '";';
  }

  var injectAppOptions = {
    transform: transformFilepath,
    starttag: '// inject:app',
    endtag: '// endinject',
    addRootSlash: false
  };

  var injectGlobalOptions = {
    transform: transformFilepath,
    starttag: '// inject:global',
    endtag: '// endinject',
    addRootSlash: false
  };

  return gulp.src('src/main.scss')
    .pipe(wiredep())
    .pipe(inject(injectGlobalFiles, injectGlobalOptions))
    .pipe(inject(injectAppFiles, injectAppOptions))
    .pipe(sass())
    .pipe(csso())
    .pipe(gulp.dest('dist/styles'));
});

gulp.task('vendors', function(){
  return gulp.src(mainBowerFiles())
    .pipe(filter('*.css'))
    .pipe(concat('vendors.css'))
    .pipe(csso())
    .pipe(gulp.dest('dist/styles'));
});

gulp.task('default', ['clean', 'vendors', 'styles'], function(){
  var injectFiles = gulp.src(['dist/styles/main.css', 'dist/styles/vendors.css']);

  var injectOptions = {
    addRootSlash: false,
    ignorePath: ['src', 'dist']
  };

  return gulp.src('src/index.html')
    .pipe(inject(injectFiles, injectOptions))
    .pipe(gulp.dest('dist'));
});

Now when we run the gulp command, we end up with two minified css files in our “dist/styles” folder.

This was a very long post, but there is a long to say about gulp. All files are available on github.

45 thoughts on “Working with Sass, Bootstrap and Gulp”

      1. thanks for this great tutorial! Is it please possible to explain that after creating the layout.scss file and the panel.scss file I move the following contents to main.scss `$primary-color: red;
        body {
        background-color: $primary-color;
        }` and replace this code in the main.scss with the placeholders? I only ask in this section above you mention not having any code in the main.scss but in the example it shows this code in the main.scss, the code for panel.scss and then the placeholders within the same main.scss file.

        When I am running gulp html the only the body styles are there and it is not compiling the styles from panel.scss

        I would really appreciate some help with this as I am struggling to get to grips with this section. Thanks again

        1. ya your’s and mine are in the same condition ? have you got the solution for this problem ??
          and i think the one with the code in the state is the layout.scss which he created at this state but he mistakely written main.scss. I tried his initial code of main.scss moving to the layout.scss and the same css in the panel.scss and just put the // inject:app
          // endinject in the main.scss and run grunt html and even grunt styles but nothing happend. The compile is not working 🙁 🙁

          Any Solution anyone

    1. sincerelly i prefer use bindep. It is very simple to config and very powerfull. wiredep dont solve all the real cases.
      It happened a lot of times i find bower i cant inject , resources missing and bower components in which you can t load optional parts or a specific version for mobile after preprocessing. Bindep is more professional. npm bindep if you want to try

    1. That would be nice but unfortunately I don’t have the time right now to improve the gulp building process. In the meantime check the gulp-angular yeoman generator to get some ideas of how to implement the “watch” task

    2. I dont understand why the index.html file is not being copied to the dist folder, but i change the file name to index2.html it is copied. It does not make any sense to me.

  1. Hey ! I’m New in sass i’m attached bootstrap .scss file in my main style.scss & it working as normal Is it the way ? to use bootstrap with sass b/c some user attached two time bootstrap file like with html before close head or also in main.scss file i’m confuse which is the right way ?

  2. Thank you very much for your smart and so progressive/patient article.
    But… I have a problem with the injection of scss wich, (hélas…) doesn’t work.
    Should I whrite some @import somewhere ?

    So I tried with your zip folder (thanks again for the share !) : but when I change something in the scss file (the color of the variable for example), it doesn’t have impact the main.css.
    Any idea ?…
    Thanks one more time !

  3. Thank for your post.

    I tried to learning step by step , It work for me.

    But I have some problem about your cb (call back) from gulp-del plugin that you implement

    ————————————————-
    Ex…

    gulp.task(‘clean’, function(cb){
    del([‘dist’], cb);
    });

    after run command line = $gulp

    It will delete index.html on dest folder

    ————————————————–

    Now we think we will delete task clean until you or me or team can fix it

    Refer gulpfile.js on line —> gulp.task(‘default’, [‘clean’, ‘styles’, ‘vendors’], function(){

    we fix my problem by used only —> gulp.task(‘default’, [‘styles’], function(){

    because of task clean still not work for me
    and task vendor still have problem to learning because of bootstrap-material-design fail after $ bower bootstrap-material-design

    I don’t know why ??? that I can not test task vendor

    Thank again for your post.
    Sorry I ‘m very poor english wording , I came from Thailand

    รักนะ จุ๊บๆ

  4. Sorry my friend.

    Refer My post before 1 hr.

    i have problem 2 thing

    1) task clean
    2) vendor css

    Now i can fix vendor problem by my self because I do it wrong something by my self when I tried to do with your post step by step.

    When I back to download your code on GIT and run CMD $ npm install. and $ bower install it no have any problem.

    But task clean still wait action.

  5. Great post, thank you!

    According to this workflow, how would you go about properly name spacing? Reference: http://stackoverflow.com/questions/13966259/how-to-namespace-twitter-bootstrap-so-styles-dont-conflict

    In the main.scss file, I tried the following:

    .namespace {

    // inject:global
    // endinject

    // bower:scss
    // endbower

    // inject:app
    // endinject

    }

    Though, the output renders a double name space in the main.css file:

    .namespace {

    .namespace html {

    }

    }

    Ideas how to do this properly?

  6. Again my friend.

    Sorry

    I found some thing about gunt plugin wiredep

    “wiredep”: “^4.0.0” ——-> current version ($ npm install –save-dev wiredep) not work it will not create index.html file at folder dist

    but your original project is “wiredep”: “^2.2.2” no have any problem sir

    Thank

    1. Final problem from today ….. Good night

      We found version of plugin del [$ npm install –save-dev del] current version is “del”: “^2.2.0”
      The new version I think that effect to delete file of index.html on dist folder.

      If we change back to “del”: “^1.2.0” (Refer your GIT) it no have problem.

      Don’t sure but if we go back from “del”: “^2.2.0” to “del”: “^1.2.0” it no have problem.

  7. remove variable cb from the clean callback with del v2.20 and the “clean” function will create index.html

    gulp.task(‘clean’, function(){
    del([‘dist’]);
    });

    1. OMG thank you so much!! I was wondering why my index.html was not injecting my css file and not generating into my public folder. Your solution worked for me!

  8. Hi David,

    It’s absolutely the best tutorial about using gulp, sass and bootstrap together. Thank you!

  9. Thank you very much for this tutorial. I had a hard time figuring out how all these tools are stitched together, and finally it makes some sense. And all this just to write some CSS… it boggles the mind! I guess it’s not 1997 anymore :-).

  10. Thanks David your tutorial is really great. Looks like Browserify improved on this workflow and removed the need for Bower and now it looks like webpack is even yet another improvement on the Browserify workflow.

    I don’t understand how those others work yet but you helped me understand this process a lot better. Now I’m going to try and use Bootstrap 4 which now is built with Sass but uses the Grunt build tool. Not sure how to navigate through this jungle just yet, but taking the knowledge you shared in this tutorial, I feel confident on taking my next step.

    @ Casey – thanks for your tip, it helped me get back my html file.

    1. You’re welcome. I wrote that post a while ago, now I’m using webpack as well without bower, it’s much cleaner and simple. I haven’t used Bootstrap 4 yet but it’s nice to know that this version is build with Sass instead of Less.

  11. I ran into an issue with “npm ERR! Refusing to install gulp as a dependency of itself” it looks like naming the project “gulp” (mkdir gulp) was causing the circular dependency so I renamed it to gulp-tutorial and it looked to fix it

  12. Hi

    I really enjoyed your tutorial.
    But it seems that I got stucked in a error after trying to add vendors css.
    Every time that I call gulp I receive this output:

    “events.js:160
    throw er; // Unhandled ‘error’ event
    ^
    Error: bower_components\bootstrap-material-design\scss\_variables.scss
    Error: File to import not found or unreadable: bootstrap/scss/variables.
    Parent style sheet: C:/Users//WebstormProjects/sass-bootstrap-gulp/bower_components/bootstrap-material-design/scss/_variables.scss
    on line 42 of bower_components/bootstrap-material-design/scss/_variables.scss
    >> @import “bootstrap/scss/variables”; // from bootstrap node_module
    ^

    at options.error (C:\Users\\WebstormProjects\sass-bootstrap-gulp\node_modules\node-sass\lib\index.js:291:26)”

    Could give me a hand on it? I searched at Google but so far I didn’t find a solution that fixed the problem.

    Thanks

  13. Hi,
    after $ npm install –save-dev gulp-inject I am getting “Error : Cannot find any-promise implementation nor global.promise.”, is there anything I need to do before gulp inject ?

  14. /* Original part — Begin */
    gulp.task(‘vendors’, function(){
    return gulp.src(mainBowerFiles())
    .pipe(filter(‘*.css’))
    .pipe(concat(‘vendors.css’))
    .pipe(csso())
    .pipe(gulp.dest(‘dist/styles’));
    });
    /* Original part — End */

    change it to:

    gulp.task(‘vendors’, function(){
    return gulp.src(mainBowerFiles())
    .pipe(filter(‘**/*.css’))
    .pipe(concat(‘vendors.css’))
    .pipe(csso())
    .pipe(gulp.dest(‘dist/styles’));
    });

    Then run everything… this should work….

  15. Dave, great tutorial. I wanted to point out a minor correction when you moved the content css files into `/src/styles`. The source code for /src/styles/layout.scss is incorrectly labeled as /src/styles/main.scss. In reality there is no such file (and thank you for the git repo on top of this, really going the distance!)

    The error I mention is directly below the text ** The file “main.scss” will become the master sass file where the other files will be injected and ideally ** Thanks again!

  16. Another thing to point out that I discovered, is that the .scss files are not actually “interoperable” in the sense that the files aren’t aggregated and then share things like $primary-color: red after. So, if both file a.scss and b.scss use $primary-color, it needs to be declared in the first file (probably a.scss) or you’ll get a compile error for SASS.

  17. Jeez, nice tut. Man, it took me the whole day to go through your tut to integrate it in my workflow(theme boilerplate: gulp; bower; git; automatic compiling on changing, adding or deleting files; browsersync; concat; jshint; etc, etc).

  18. Thank you, I must say that this article is Legendary.
    It is one of the finest examples of a written tutorial that I have ever come across.
    It’s more challenging and very well written with no patronising simplistic terminology and most importantly easy to understand for newbie’s like me.
    I only visited this page looking for gulp plugins to use with bootstrap.
    If you don’t mind I follow all your teachings in future, though I maybe a little late to this party being 2022.
    Thanks again, Fraser.

    1. Comments like yours gives me the motivation to write more articles and tutorials for other people. Thank you

So, what do you think?

This site uses Akismet to reduce spam. Learn how your comment data is processed.