Bootstrap 4 Tutorial: Working with npm, Grunt.js and Sass (Part 2)

• 4 minutes READ

Firstly read the Bootstrap 4 Tutorial: Working with npm, Grunt.js and Sass (Part 1).

Bootstrap 4 comes with an official Sass port for the framework. If you are not familiar with Sass, implementing it via Bootstrap can be a little tricky.

Startup Bootstrap Builder

In the previous article, we installed, configured and customized npm and Grunt.js setups for faster development. In this article, were going to deal with Compass and Sass as well as implement a watch task to watch for changes and run the tasks it needs to. At the end of this tutorial, were going to come up with Bootstrap 4 theme that is built with Sass.

Looking for a Bootstrap online builder?

Resources you need for this tutorial:

HTML

Before we work with the Sass and Grunt.js tasks, we’ll need to set up our markup along with respective JavaScript code. You can copy the code on your index.html file.

No-Code Email Template Builder

With Postcards Email Builder you can create and edit email templates online without any coding skills! Includes more than 100 components to help you create custom emails templates faster than ever before.

Free Email BuilderFree Email Templates

Note: All of the JavaScript code will be placed in the JS folder.

<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<title>Bootstrap 4 Sass</title>
	<meta content="Bootstrap 4 with Sass" name="description">
	<meta content="width=device-width, initial-scale=1.0" name="viewport">
	<link href="css/style.css" rel="stylesheet">
</head>
<body>
	<!-- Banner -->
	<div class="header-overlay">
		<div class="overlay morp"></div>
	</div>
	<section id="header-content">
		<div class="content">
			<h1>Bootstrap 4</h1>
			<h2 class="text-intro opacity-0">The most popular HTML, CSS, and JS
			framework</h2>
			<p class="text-intro opacity-0">Bootstrap makes front-end web development
			faster and easier. It's made for folks of all skill levels, devices of all
			shapes, and projects of all sizes.</p><a class=
			"text-intro opacity-0 btn btn-primary" data-target="main-content" href="#"
			id="open-more-info">READ MORE &rarr;</a>
		</div>
	</section><!-- Banner --><!-- Main Content -->
	<section class="main-hide" id="main-content">
		<div class="content">
			<h1>Designed for everyone, everywhere.</h1><br>
			<p>Bootstrap makes front-end web development faster and easier. It's made
			for folks of all skill levels, devices of all shapes, and projects of all
			sizes.</p><br>
			<p>Quickly include Bootstrap's CSS and JS into any project. Use your
			favorite package manager, download the source, or use the Bootstrap CDN.</p>
			<hr>
			<div class="col-xs-12 col-md-6 col-xl-6">
				<div class="card">
					<img alt="Card image cap" class="card-img-top" src="img/card-img.jpg">
					<div class="card-block">
						<h4 class="card-title">Custom</h4>
						<p class="card-text">A card is a flexible and extensible content
						container.</p><a class="btn btn-primary" href="#">More &rarr;</a>
					</div>
				</div>
			</div>
			<div class="col-xs-12 col-md-6 col-xl-6">
				<div class="card">
					<img alt="Card image cap" class="card-img-top" src="img/card-img.jpg">
					<div class="card-block">
						<h4 class="card-title">Great</h4>
						<p class="card-text">A card is a flexible and extensible content
						container.</p><a class="btn btn-primary" href="#">More &rarr;</a>
					</div>
				</div>
			</div>
			<div class="col-xs-12 col-md-6 col-xl-6">
				<div class="card">
					<img alt="Card image cap" class="card-img-top" src="img/card-img.jpg">
					<div class="card-block">
						<h4 class="card-title">Flexible</h4>
						<p class="card-text">A card is a flexible and extensible content
						container.</p><a class="btn btn-primary" href="#">More &rarr;</a>
					</div>
				</div>
			</div>
			<div class="col-xs-12 col-md-6 col-xl-6">
				<div class="card">
					<img alt="Card image cap" class="card-img-top" src="img/card-img.jpg">
					<div class="card-block">
						<h4 class="card-title">Easy</h4>
						<p class="card-text">A card is a flexible and extensible content
						container.</p><a class="btn btn-primary" href="#">More &rarr;</a>
					</div>
				</div>
			</div>
			<hr>
			<br>
			<br>
			<h3>Connect</h3>
			<p>Quickly get a project started with any of our examples ranging from using
			parts of the framework to custom components and layouts.</p>
			<form id="contact-form" name="contact-form">
				<div class="row">
					<div class="col-xs-12 col-sm-6 col-lg-6">
						<fieldset class="form-group">
							<input class="form form-control" data-name="Name" id="name" name="name"
							placeholder="Your Name.." required="" type="text">
						</fieldset>
					</div>
					<div class="col-xs-12 col-sm-6 col-lg-6">
						<fieldset class="form-group">
							<input class="form form-control" data-name="Email Address" id="email"
							name="email-address" placeholder="Your Email.." required="" type=
							"email">
						</fieldset>
					</div>
					<div class="col-xs-12 col-sm-12 col-lg-12 no-padding">
						<fieldset class="form-group">
							<textarea class="form textarea form-control" data-name="Text Area" id=
							"text-area" name="message" placeholder="Your Message.." required=
							""></textarea>
						</fieldset>
					</div>
				</div><button class="btn btn-block btn-primary" type=
				"submit">Submit</button>
			</form>
		</div><!-- Main Content -->
		<!-- Footer -->
		<footer>
			<p>Awesomeness - Geeky</p>
		</footer>
	</section><!-- Close Button-->
	<button class="hide-close" id="close-content"><i class=
	"fa fa-times"></i></button> 
	<script src="js/jquery.min.js">
	</script> 
	<script src="js/jquery.easings.min.js">
	</script> 
	<script src="js/bootstrap.min.js">
	</script> 
	<script src="js/velocity.min.js">
	</script> 
	<script src="js/vegas.js">
	</script> 
	<script src="js/jquery.mousewheel.js">
	</script> 
	<script src="js/jquery.mCustomScrollbar.js">
	</script> 
	<script src="js/custom.js">
	</script>
</body>
</html>

Install Bootstrap 4 via npm

Download Bootstrap 4 alpha using the git branch v4-dev. Open your terminal or command prompt and inside the project directory type npm install twbs/bootstrap#v4-dev and the package is downloaded directly from GitHub to npm_modules folder.

Installing Bootstrap 4 via npm

Bootstrap Folder

Bootstrap Folder

Working with Sass

Now that we have the Bootstrap 4 package, let’s move the scss folder inside the Bootstrap package to our sass folder so that we can have access to all of the main core files of Bootstrap Sass. If you want to simply import the compiled bootstrap CSS file –bootstrap.min.css or bootstrap.css to the custom .scss files that would also work.

Working with Sass

Next, start working on custom .scss files. Open the base folder and inside the _import.scss we will import the necessary CSS files that we need. Copy and paste the following code:

Low-Code Website Builders

With Startup App and Slides App you can build unlimited websites using the online website editor which includes ready-made designed and coded elements, templates and themes.

Try Startup App Try Slides AppOther Products
/* IMPORTS */
@import url(font-awesome.min.css);
@import url(animate.css);
@import url(jquery.mCustomScrollbar.css);
@import url(vegas.css);

Let’s also import the Google Fonts links that we need inside the _typography.scss file.

/* TYPOGRAPHY */
/* Google Fonts */
@import url("https://fonts.googleapis.com/css?family=Lato:400,300,300italic,400italic,700,700italic,900,900italic");
@import url("https://fonts.googleapis.com/css?family=Raleway:400,300,500,600,700,800,900");

Work on the variables for our Sass. Inside the _variables.scss file, copy the code below.

/* VARIABLES */

// colors
$color-white: #FFFFFF;
$color-turquoise: #00af94;

// backgrounds
$bg-white: #FFFFFF;
$bg-turquoise: #00c8aa;
$bg-black: #000000;

// font families
$font-lato: 'Lato', Arial, Verdana, sans-serif;
$font-raleway: 'Raleway', Arial, Verdana, sans-serif;

Then for mixins, let’s add the following code inside the _mixins.scss file (which is inside the helper folder).

/* MIXINS */
@mixin transition-cubic-bezier ($time) {
	-webkit-transition: all $time cubic-bezier(0.42, 0, 0.58, 1);
	-moz-transition: all $time cubic-bezier(0.42, 0, 0.58, 1);
	-ms-transition: all $time cubic-bezier(0.42, 0, 0.58, 1);
	-o-transition: all $time cubic-bezier(0.42, 0, 0.58, 1);
	transition: all $time cubic-bezier(0.42, 0, 0.58, 1);
}

Finally, we’ll work on our main file which is the style.scss. We will start by importing our support .scss files along with the regular CSS via Sass variables.

/* 
Theme Name: Bootstrap 4 Sass
Description: Bootstrap 4 with Sass
*/
//bootstrap
@import "scss/bootstrap";
// typography
@import "base/typography";

// compass
@import "compass";

// import files
@import "base/import";

// mixins
@import "helpers/mixins";

// variables
@import "helpers/variables";


/* GENERAL STYLES
-------------------------------------------------*/

body {
  background: $bg-white;
  font-family: $font-lato;
  font-weight: 400;
  font-size: 100%;
  line-height: 1.5em;
  position: absolute;
  height: 100%;
  color: $color-white;
  -webkit-font-smoothing: antialiased;
}

body, html {
  margin: 0;
  padding: 0;
  -webkit-tap-highlight-color: transparent;
  width: 100%;
}

body, input, select, textarea {
	@include transition(all, 0.2s,cubic-bezier(0.42, 0, 0.58, 1) );
}


.scroll-touch {
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}

a {
  font-family: $font-lato;
    color: $color-white;
    cursor: pointer;
  text-decoration: none;
	@include transition(all, 0.2s,cubic-bezier(0.42, 0, 0.58, 1));
  &:hover {
    color: $color-turquoise;
    text-decoration: none !important;
    outline: none !important;
  }
  &:active, &:focus {
    outline: none !important;
    text-decoration: none !important;
    color: $color-white;
  }
}

.btn{
  margin-top: 27px;
}

strong, b {
  font-weight: 700;
}

em, i {
  font-style: italic;
}

p {
  font-family: $font-lato;
  margin: 0;
  font-size: 1em;
  line-height: 1.6em;
  color: $color-white;
  font-weight: 400;
}

h1, h2, h3, h4, h5, h6 {
  color: $color-white;
  font-weight: 400;
  line-height: 1.3em;
}

h1 {
  font-size: 44px;
  font-family: $font-raleway;
  font-weight: 700;
}

h2 {
  font-size: 22px;
  margin-top: 15px;
  margin-bottom: 30px;
}

h3 {
  font-size: 30px;
  margin-bottom: 25px;
}

h4 {
  font-size: 25px;
  margin-bottom: 25px;
}

h5 {
  font-size: 20px;
  margin-bottom: 25px;
}

h6 {
  font-size: 15px;
  margin-bottom: 25px;
}


.clear {
  clear: both;
}


/* ALIGNMENT
-------------------------------------------------*/

.align-left {
  text-align: left;
}

.align-center {
  text-align: center;
}

.align-right {
  text-align: right;
}

.no-margin-bottom {
  margin-bottom: 0;
}

.opacity-0 {
  opacity: 0 !important;
  visibility: hidden !important;
}


.opacity-1 {
  opacity: 1 !important;
  visibility: visible !important;
}

.index-999 {
  z-index: -999 !important;
}


/* CONTENT
-------------------------------------------------*/

.header-overlay {
  position: fixed;
  width: 100%;
  top: 0;
  left: -100vw;
  height: 100%;
  overflow: visible;
  opacity: 0;
}

.overlay {
  position: fixed;
  overflow: hidden;
  top: 0;
  left: -50%;
  background: rgba(7, 7, 7, 0.8);
  width: 100%;
  height: 100%;
  @include transition(all, 0.5s,cubic-bezier(0.7, 0, 0.3, 1));
  border-right: 1px solid #15171e;
  &.morp {
    -webkit-transform: skew(-25deg, 0deg);
    -moz-transform: skew(-25deg, 0deg);
    -ms-transform: skew(-25deg, 0deg);
    -o-transform: skew(-25deg, 0deg);
    transform: skew(-25deg, 0deg);
  }
}

#header-content {
  position: fixed;
  left: 0;
  top: 0;
  width: 50%;
  height: 100%;
  opacity: 0;
  @include transition(all,0.3s,cubic-bezier(0, 0, 0.58, 1));
  .content {
    position: absolute;
    z-index: 0;
    left: 0;
    padding: 0;
    top: 50vh;
    width: 100%;
    padding: 0 10%;
    @include transition(all, 0.3s, cubic-bezier(0, 0, 0.58, 1));
    @include translateY(-50%);
    text-align: left;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    opacity: 1;
    visibility: visible;
  }
}


#main-content {
  background:$bg-white;
  position: relative;
  overflow: auto;
  @include translate3d(100%, 0, 0);
  top: 0;
  width: 50%;
  @include transition(all, 0.5s, cubic-bezier(0.7, 0, 0.3, 1));
  &.main-hide {
  	@include translate3d(200%, 0, 0);
  }
  .content {
    width: 100%;
    padding: 8% 10% 0;
    text-align: left;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    opacity: 1;
    visibility: visible;
    h1, h2, h3, h4, h5, h6 {
      color: #757A86;
    }
    h3 {
      font-family: $font-raleway;
      text-transform: uppercase;
      font-weight: 700;
    }

    p {
      color: #747c83;
      font-family: $font-lato;
    }
    
      img {
        overflow: hidden;
        @include transition(all, 0.3s, cubic-bezier(0.42, 0, 0.58, 1));
        -webkit-backface-visibility: hidden;
        backface-visibility: hidden;
        @include scale(1);
        @include transition(all, 0.6s, cubic-bezier(0.215, 0.61, 0.355, 1));
      }
 
    }
  }


#close-content {
  position: fixed;
  background: transparent;
  border: none;
  top: 15px;
  right: 15px;
  color: #757A86;
  font-size: 2em;
  &:hover {
  	@include scale(1.2);;
  }
  &.hide-close {
    right: -40px;
  }
}

.mCSB_scrollTools {
  position: absolute;
  width: 5px;
  height: auto;
  left: auto;
  top: 0;
  right: -5px;
  bottom: 0;
  z-index: 1;
  @include transition(all, 0.5s, cubic-bezier(0.7, 0, 0.3, 1));
  .mCSB_draggerRail {
    width: 5px;
    height: 100%;
    margin: 0;
    @include border-radius(0);
    background-color: #757a86;
    filter: "alpha(opacity=40)";
    -ms-filter: "alpha(opacity=40)";
  }
  .mCSB_dragger .mCSB_dragger_bar {
    /* the dragger element */
    position: relative;
    width: 5px;
    height: 100%;
    margin: 0 auto;
    @include border-radius(0);
    text-align: center;
    background-color: #025aa5;
  }
}

.mCSB_scrollTools-left {
  right: 50% !important;
}

#contact-form {
  margin-top: 40px;
  .form-control {
    background: $color-white;
    border: 1px solid rgba(0, 0, 0, 0.1);
    @include border-radius(0);
    box-shadow: none;
    font-weight: 400;
    outline: medium none;
    height: 40px;
    width: 100%;
    color: $bg-black;
    @include transition(all, 0.3s, cubic-bezier(0, 0, 0.58, 1));
    text-align: left;
    &:hover, &:focus {
      box-shadow: none;
    }
    &::-webkit-input-placeholder, &::-moz-placeholder, &:-moz-placeholder, &:-ms-input-placeholder {
      color: #747c83 !important;
    }
  }
  textarea.form-control {
    min-height: 150px;
  }
  button#valid-form {
    font-weight: 400;
    padding: 1em 2em;
    font-size: 1em;
    display: block;
    margin: 0 auto 0;
    background: $bg-turquoise;
    color: $color-white;
    @include border-radius(0);
    font-weight: 700;
    width: 100%;
    line-height: 10px;
    &:hover {
      background: $color-turquoise;
      color: $color-white;
    }
  }
}

/* FOOTER
-------------------------------------------------*/

footer {
  padding: 10px 0;
  background: transparent;
  text-align: center;
  p {
    font-size: 11px;
    font-weight: 100;
    letter-spacing: 2px;
    text-transform: uppercase;
    color: #757A86;
  }
}


/* MEDIA QUERIES
-------------------------------------------------*/

@media only screen and (max-width: 1024px) {
  .overlay {
    width: 100%;
    height: 100%;
    left: 0;
    &.morp {
    	@include skew(0deg, 0deg); ;
    }
  }
  #header-content {
    position: relative;
    width: 100%;
    height: auto;
    padding: 100px 0 150px;
    .content {
      position: relative;
      left: 50%;
      @include translateX(-50%); ;
      top: auto;
      text-align: center;
    }
  }
  #main-content {
    position: relative;
    @include translate3d(0, 0, 0);;
    top: 0;
    width: 100%;
    &.main-hide {
    	@include translate3d(0, 0, 0); ;
    }
  }
  #close-content {
    display: none;
  }
  .mCSB_scrollTools {
    right: 0;
  }
  .mCSB_scrollTools-left {
    right: 0 !important;
  }
}

/* Small Devices, Tablets */
@media only screen and (max-width: 768px) {}

/* Extra Small Devices, Phones */
@media only screen and (max-width: 480px) {
  #header-content {
    padding: 50px 0 100px;
    .content {
      padding: 0 3%;
    }
    &.minimal-phone {
      height: 100vh;
    }
  }
  h1.text-intro {
    font-size: 30px;
  }
  h2.text-intro {
    font-size: 18px;
  }
}

@media screen and (max-device-width: 667px) and (orientation: landscape) {
  #header-content {
    padding: 50px 0 100px;
  }
  h1.text-intro {
    font-size: 30px;
  }
  h2.text-intro {
    font-size: 18px;
  }
}

Compiling Sass files via Grunt.js

Going back to our Gruntfile.js, we set up Compass and Sass using the following code below. These code will set the path linking to our style.scss file and style.css file and we enable Compass by setting up compass: true on the options of grunt sass.

compass: {      
    dist: {        
      options: {      
        sassDir: 'sass',
        cssDir: 'css',
        environment: 'production'
      }
    },
    dev: {              
      options: {
        sassDir: 'sass',
        cssDir: 'css'
      }
    }
  },

sass: {
    	dist: {
            options: {                 
                compass: true,
            },
    		files: {
    			'css/style.css' : 'sass/style.scss'
    		}
    	}
    },

The final thing we need to do is to create and set up a config.rb file for Compass inside the project directory. This file defines the relationship between files and their assets, how and where the CSS should be generated, and any dependencies for a project via Compass. Copy the code below inside the config.rb file.

require 'compass/import-once/activate'
cache = true
cache_path = 'C:/temp/sass/'
# Require any additional compass plugins here.

# Set this to the root of your project when deployed:
http_path = "/"
css_dir = "/"
sass_dir = "sass"
images_dir = "images"
javascripts_dir = "javascripts"

# You can select your preferred output style here (can be overridden via the command line):
# output_style = :expanded or :nested or :compact or :compressed

# To enable relative paths to assets via compass helper functions. Uncomment:
# relative_assets = true

# To disable debugging comments that display the original location of your selectors. Uncomment:
line_comments = false


# If you prefer the indented syntax, you might want to regenerate this
# project again passing --syntax sass, or you can uncomment this:
# preferred_syntax = :sass
# and then run:
# sass-convert -R --from scss --to sass sass scss && rm -rf sass && mv scss sass

Finally, compile .scss files to one CSS file, go to your terminal or command prompt and inside your project directory type grunt sass.

npm sass compile

We can also ask Grunt to watch for changes and run the tasks it needs. The watch task will run every time a file is changed and saved. Since the configuration was already set up on our Sass files we just need to simply type grunt watch on command prompt or terminal.

grunt watch

Working with custom.js

As a final touch, let’s add up the following JavaScript codes inside our custom.js file to enable our animation for our Bootstrap theme. You can also create a minified version of this file by just typing grunt uglify.

/* JAVASCRIPT CODES */

$(window).load(function() {
  setTimeout(function() {
    $('.header-overlay').velocity({
      translateX: "100%",
      opacity: "1",
    }, {
      duration: 850,
      easing: [0.6, 0, 0.3, 1],
    })
  }, 850);


  setTimeout(function() {
    $('#header-content').velocity({
      opacity: "1",
      complete: function() {
        setTimeout(function() {
          $('.text-intro').each(function(i) {
            (function(self) {
              setTimeout(function() {
                $(self).addClass('animated-middle fadeInUp').removeClass('opacity-0');
              }, (i * 150) + 150);
            })(this);
          });
        }, 0);
      }
    }, {
      duration: 1000,
      easing: [0.7, 0, 0.3, 1],
    })
  }, 1600);
})


$(document).ready(function() {
  $('a#open-more-info').on("click", function() {
    $(".overlay").toggleClass("morp");
    $("#main-content").toggleClass("main-hide");
    $("#close-content").toggleClass("hide-close");
    $('.mCSB_scrollTools').toggleClass('mCSB_scrollTools-left');
    setTimeout(function() {
      $("#mcs_container").mCustomScrollbar("scrollTo", "#main-content", {
        scrollInertia: 500,
        callbacks: false
      });
    }, 350);
  });
  $('button#close-content').on("click", function() {
    $(".overlay").addClass("morp");
    $("#main-content").addClass("main-hide");
    $("#close-content").addClass("hide-close");
    $('.mCSB_scrollTools').removeClass('mCSB_scrollTools-left');
    setTimeout(function() {
      $("#mcs_container").mCustomScrollbar("scrollTo", "#main-content", {
        scrollInertia: 500,
        callbacks: false
      });
    }, 350);
  });


  $(function() {
    $('body').bind('mousewheel', function(event) {
      event.preventDefault();
      var scrollTop = this.scrollTop;
      this.scrollTop = (scrollTop + ((event.deltaY * event.deltaFactor) * -1));
    });
  });
  
  var ifTouchDevices = navigator.userAgent.match(/(iPhone|iPod|iPad|Android|BlackBerry|Windows Phone)/);

  function scrollbar() {
    if (ifTouchDevices) {
      $('body').addClass('scroll-touch');
      $('a#open-more-info').on("click", function() {
        event.preventDefault();
        var target = "#" + this.getAttribute('data-target');
        $('body').animate({
          scrollTop: $(target).offset().top
        }, 500);
      });
    } else {
      $('body').mCustomScrollbar({
        scrollInertia: 150,
        axis: "y"
      });
    }
  }
  scrollbar();
});

You can download the demo files here.

Wrapping Up

Bootstrap is a great CSS framework that you can use to build a website, and using the SASS version lets you do almost anything you want.

Thankfully, task runner like Grunt.js makes the work much easier if you take time to learn the simple commands.

Hopefully this tutorial has helped you understand npm, Grunt.js and Sass and how it can speed up your Bootstrap development. If you have any questions or suggestions, drop a line in the comments.

Sam Norton

Sam is an expert Full Stack Developer who loves making digital solutions that are meaningful and new. Sam is an expert in web design and development. He uses his knowledge of HTML/CSS, JavaScript, jQuery, Ruby, Ruby on Rails, WordPress, Node.js, React, Express.js, Gatsby.js, GraphQL, and Strapi.js to make custom websites that reflect clients' unique brands and serve their business niches. Committed to staying ahead of the curve, Sam harnesses the power of the latest technologies, CMS, and platforms to build cutting-edge websites that outperform competitors.

Posts by Sam Norton