WordPress Theme Development: Getting Started with Bones and Sass

Bones is a starter theme for WordPress. If you are developing a WordPress theme, Bones will be a good starting point for you since you do not have to start from scratch. Sure, there are a lot of themes that you can modify and expand on. So why Bones, specifically?

  1. It is mobile first and responsive – creating a responsive theme becomes easier as you do not need to create the CSS from scratch. Responsive designs are a necessity because we want to cater to those who love browsing on their mobile devices (like me).
  2. Very detailed – the CSS and the SCSS files are fully documented, making it easier for you to customize it however you want.
  3. Comes with Sass and Less – Sass and Less are CSS preprocessors that allows you to use variables, mixins, and functions when developing your CSS files. This allows you to define properties just once and you can reuse these properties whenever you want to; it speeds up your development workflow as you do need need to repeat yourself (DRY).

What about learning curve? This should not be your concern about working with Bones as installing and working with Sass is quite easy. Besides, developers should not let learning curves get in their way of learning new tools and resources.

What do you need?

Prior to downloading a copy of Bones starter theme, you need to install several things on your machine. These are:

  1. RVM and Ruby (Mac OS X) or Ruby and Devkit (windows)
  2. The Compass gem – it comes with Sass

Compass and Sass are not similar; Sass is a CSS3 extension that allows you to use cool functionalities like variables, mixins, nested rules, etc. Compass is more of a Sass library or a framework.

I am not going to get into details about installing these on your machine as it is easy to find links on how to install Ruby. Installing the gem is even easier as it is described in detail on Compass’ official documentation.

Bones Overview

The PHP files in the main folder are self-explanatory. These are the files that are essential for every WordPress theme. Based on your needs, you may need to edit some of these files if you want certain elements to be structured differently. Altering the grid system can be one example.

When working with CSS and Sass, you will be working more on the scss folder situated inside the library folder. I suggests you to remove the Less folder since we will not be needing it.

Which SCSS files contain which element?

A starter theme like Bones can be useful since all the elements are already created before hand; it is like a boilerplate. However, it is also easy to get lost when you’re using it for the first time. Thankfully, Sass provides detailed comments on the compiled CSS file.

Chrome developer tools makes it easier for you to figure out where an element is located at. Right-click > Inspect element on a particular section. Click on the link that is surrounded by a red border as shown below. It will show you the line number of the element and which .scss file it belongs to.

Chrome DevTools
Chrome DevTools

I think it is important for me to address this since most of the widget styling isn’t located in _base.scss. Developers that are new to Bones may get confused and wonder where the widget styling is located at. Finding it in this particular manner definitely saves time.

The styling in _base.scss will be available in all devices regardless of screen size. If you need to style an element based on the screen size, just edit the relevant .scss file – the names of the scss files are pretty self-explanatory and it is heavily commented. It makes it easier for you to develop your stylings based on specific screen sizes.

Coding your first theme with Bones

This is the theme created for this article. It is similar to my current theme but it is a good starting point. You can use it for learning purpose.

demo-bones
Demo for this tutorial. Download it here

Note that I am not going to teach you how to code the theme from scratch; I assume that you are familiar with WordPress theme development since you are reading this tutorial. The theme designed for this is pretty basic and it is easy to emulate. I am going to introduce several Sass functionality in this article and I am going to provide the full theme file for download should you need to refer to it.

Using Variables

Lets start with the most basic concept – variables. We are going to create the blue border on top of the header. Before doing that, fire up your terminal and navigate to the scss folder. You need to run compass watch in order for sass to pick up on the changes when you edit and save any .scss file.

$ cd bones-theme-tutorial/library/scss
$ compass watch

Open up the _mixin.scss file and add a variable. _mixin.scss file is basically where you dump in your variables and mixins; we will discuss the latter shortly after this.

$header-border: #009fd9;

Then, you can easily call this variable in the _base.scss file under the header class:

.header {
    border-top: 6px solid $header-border;
}

The beauty of declaring colors under a variable is that you can reuse the colors easily without wasting time looking up for previous declaration of the color code or memorizing it. Often times, we tend to use the same color scheme on a lot of elements in our design. Therefore, declaring it in a variable will definitely speed up our workflow. Of course, you can pretty much declare any types of information and it is not just limited to colors. Use it to declare font-family, font-size or any css properties that you plan on reusing.

Mixins

If Variable declaration allows you to reuse a single CSS property, Mixin allows you to reuse a group of CSS property. You can also pass parameters in mixins to make it more flexible. Think of it as methods or functions in CSS!

In order to demonstrate how mixins work, lets try and recreate the post information located under each of the post title. I used Genericons font for each of the icons.

Add the font folder in the library folder. Then, declare the font face and font properties in the _mixins.scss file.

@font-face {
	font-family: 'Genericons';
	src: url('../fonts/genericons-regular-webfont.eot');
	src: url('../fonts/bistroscript-webfont.eot?#iefix') format('embedded-opentype'),
		 url('../fonts/genericons-regular-webfont.woff') format('woff'),
		 url('../fonts/genericons-regular-webfont.ttf') format('truetype'),
	     url('../fonts/genericons-regular-webfont.svg#genericonsregular') format('svg');
	font-weight: normal;
	font-style: normal;
}

[class*="genericon"] {
	display: inline-block;
	width: 16px;
	height: 16px;
	-webkit-font-smoothing: antialiased;
	font-size: 16px;
	line-height: 1;
	font-family: 'Genericons';
	text-decoration: inherit;
	font-weight: normal;
	font-style: normal;
	vertical-align: top;
}

/* IE7 */
[class*="genericon"] {
	*overflow: auto;
	*zoom: 1;
	*display: inline;
}

Declare a mixin for the general properties of the Genericons.

@mixin genericons() {
    display: inline-block;
    vertical-align: text-bottom;
    font: normal 16px/1 Genericons;
    color: #a0a3a7;
    padding-right: 4px;
}

Once you have declared the mixin, you can call it under each classes that are meant to display the icons. This shall be in the _base.scss file.

.the-author a:before, 
.the-comment a:before, 
.the-time a:before,
.the-category a:before {
	@include genericons();
}

Of course, each of this span element will display a single icon. Lets create a mixin that accept a parameter.

@mixin icon( $icon ) {
	content: $icon;
}

Once again, in the _base.scss file, you can call the mixin on each of the element like so:

.the-author a:before {
	@include icon('\f304');
}

.the-time a:before {
	@include icon('\f307');
}

.the-comment a:before {
	@include icon('\f300');
}

CSS3 Features

Often times, we will want to use CSS3 features in order to spice up the design of our themes. This is pretty annoying because of vendor prefixes and we often have to repeat ourselves. Adding a simple border radius will normally look like this:

-webkit-border-radius: 12px;
-moz-border-radius: 12px; 
-o-border-radius: 12px;
border-radius: 12px;

Well, Compass makes working with CSS3 easier in Sass since it provides modules that contains cross-browser mixins. It helps you include CSS3 features easily in your .scss file without declaring each vendor prefixes. For example, lets add a border-radius to an element.

You need to import the CSS3 modules in your style.scss file.

@import "compass/css3";

Then, in your _base.scss file, just include border-radius like so,

.my-class {
    @include border-radius( 5px );
}

This will generate a CSS file that contains border-radius declarations will all the vendor prefixes!

A list of CSS3 properties that are supported by the Compass CSS3 modules can be access here.

I just introduced a few Sass functionality in this article. If you need to look at a full list of features and how to use them, check out this Sass guide. Compass has other modules that can be use with Sass. You can check out the Compass docs for more information.

Keeping CSS file size low

Be careful with mixins

Mixins certainly makes your life easier; it allows you to declare a group of CSS properties and reuse it wherever you want. However, it is easy to overdo it. Mixins can also bloat your CSS file if you are not careful. Remember that your CSS file should be small since you want your webpage to load faster. Speed is definitely important especially for users that are browsing your page on a mobile device.

Lets assume that you are adding a nice hover effect to an element. You’ll add mixins for the keyframe and animation:

@mixin animation( $animations ) {
	animation: $animations;
	-webkit-animation: $animations;
	-moz-animation: $animations;
	-o-animation: $animations;
}

@mixin keyframes( $animation ) {
	@-webkit-keyframes $animation { 
		@content; 
	}

	@-moz-keyframes $animation { 
		@content; 
	}

	@-o-keyframes $animation { 
		@content; 
	}

	@-keyframes $animation {
		@content
	}
}

Then, you may be tempted to include your mixins like so:

@include keyframes( cuteness ) {
	25% {
		@include transform( translateX( 20px ) );
	}

	50% {
		@include transform( translateX( 40px ) );
	}

	75% {
		@include transform( translateX( 60px ) );
	}

	100% {
		@include transform( translateX( 80px ) );
	}
}

.icon1 a:hover:before {
	color: #000;
	@include animation( cuteness .4s ease-out );
	@include transition( color .2s ease-in-out );
}

.icon2 a:hover:before {
	color: #2daae2;
	@include animation( cuteness .4s ease-out );
	@include transition( color .2s ease-in-out );
}

.icon3 a:hover:before {
	color: #007bb6;
	@include animation( cuteness .4s ease-out );
	@include transition( color .2s ease-in-out );
}

.icon4 a:hover:before {
	color: #333;
	@include animation( cuteness .4s ease-out );
	@include transition( color .2s ease-in-out );
}

.icon5 a:hover:before {
	color: #fe9900;
	@include animation( cuteness .4s ease-out );
	@include transition( color .2s ease-in-out );
}

Notice that you are repeating the mixin more than once. This is definitely not a good way of using mixin since it will generate a bloated CSS file that looks like this:

@-webkit-keyframes cuteness {
    /* line 23, ../scss/_481up.scss */
    25% {
      -webkit-transform: translateX(20px);
      -moz-transform: translateX(20px);
      -ms-transform: translateX(20px);
      -o-transform: translateX(20px);
      transform: translateX(20px);
    }

    /* line 27, ../scss/_481up.scss */
    50% {
      -webkit-transform: translateX(40px);
      -moz-transform: translateX(40px);
      -ms-transform: translateX(40px);
      -o-transform: translateX(40px);
      transform: translateX(40px);
    }

    /* line 31, ../scss/_481up.scss */
    75% {
      -webkit-transform: translateX(60px);
      -moz-transform: translateX(60px);
      -ms-transform: translateX(60px);
      -o-transform: translateX(60px);
      transform: translateX(60px);
    }

    /* line 35, ../scss/_481up.scss */
    100% {
      -webkit-transform: translateX(80px);
      -moz-transform: translateX(80px);
      -ms-transform: translateX(80px);
      -o-transform: translateX(80px);
      transform: translateX(80px);
    }
}

  @-moz-keyframes cuteness {
    /* line 23, ../scss/_481up.scss */
    25% {
      -webkit-transform: translateX(20px);
      -moz-transform: translateX(20px);
      -ms-transform: translateX(20px);
      -o-transform: translateX(20px);
      transform: translateX(20px);
    }

    /* line 27, ../scss/_481up.scss */
    50% {
      -webkit-transform: translateX(40px);
      -moz-transform: translateX(40px);
      -ms-transform: translateX(40px);
      -o-transform: translateX(40px);
      transform: translateX(40px);
    }

    /* line 31, ../scss/_481up.scss */
    75% {
      -webkit-transform: translateX(60px);
      -moz-transform: translateX(60px);
      -ms-transform: translateX(60px);
      -o-transform: translateX(60px);
      transform: translateX(60px);
    }

    /* line 35, ../scss/_481up.scss */
    100% {
      -webkit-transform: translateX(80px);
      -moz-transform: translateX(80px);
      -ms-transform: translateX(80px);
      -o-transform: translateX(80px);
      transform: translateX(80px);
    }
}

  @-o-keyframes cuteness {
    /* line 23, ../scss/_481up.scss */
    25% {
      -webkit-transform: translateX(20px);
      -moz-transform: translateX(20px);
      -ms-transform: translateX(20px);
      -o-transform: translateX(20px);
      transform: translateX(20px);
    }

    /* line 27, ../scss/_481up.scss */
    50% {
      -webkit-transform: translateX(40px);
      -moz-transform: translateX(40px);
      -ms-transform: translateX(40px);
      -o-transform: translateX(40px);
      transform: translateX(40px);
    }

    /* line 31, ../scss/_481up.scss */
    75% {
      -webkit-transform: translateX(60px);
      -moz-transform: translateX(60px);
      -ms-transform: translateX(60px);
      -o-transform: translateX(60px);
      transform: translateX(60px);
    }

    /* line 35, ../scss/_481up.scss */
    100% {
      -webkit-transform: translateX(80px);
      -moz-transform: translateX(80px);
      -ms-transform: translateX(80px);
      -o-transform: translateX(80px);
      transform: translateX(80px);
    }
}

  @-keyframes cuteness {
    /* line 23, ../scss/_481up.scss */
    25% {
      -webkit-transform: translateX(20px);
      -moz-transform: translateX(20px);
      -ms-transform: translateX(20px);
      -o-transform: translateX(20px);
      transform: translateX(20px);
    }

    /* line 27, ../scss/_481up.scss */
    50% {
      -webkit-transform: translateX(40px);
      -moz-transform: translateX(40px);
      -ms-transform: translateX(40px);
      -o-transform: translateX(40px);
      transform: translateX(40px);
    }

    /* line 31, ../scss/_481up.scss */
    75% {
      -webkit-transform: translateX(60px);
      -moz-transform: translateX(60px);
      -ms-transform: translateX(60px);
      -o-transform: translateX(60px);
      transform: translateX(60px);
    }

    /* line 35, ../scss/_481up.scss */
    100% {
      -webkit-transform: translateX(80px);
      -moz-transform: translateX(80px);
      -ms-transform: translateX(80px);
      -o-transform: translateX(80px);
      transform: translateX(80px);
    }
}

  /* line 40, ../scss/_481up.scss */
  .icon1 a:hover:before {
    color: #000;
    animation: cuteness 0.4s ease-out;
    -webkit-animation: cuteness 0.4s ease-out;
    -moz-animation: cuteness 0.4s ease-out;
    -o-animation: cuteness 0.4s ease-out;
    -webkit-transition: color 0.2s ease-in-out;
    -transition: color 0.2s ease-in-out;
    transition: color 0.2s ease-in-out;
  }

  /* line 46, ../scss/_481up.scss */
  .icon2 a:hover:before {
    color: #2daae2;
    animation: cuteness 0.4s ease-out;
    -webkit-animation: cuteness 0.4s ease-out;
    -moz-animation: cuteness 0.4s ease-out;
    -o-animation: cuteness 0.4s ease-out;
    -webkit-transition: color 0.2s ease-in-out;
    -transition: color 0.2s ease-in-out;
    transition: color 0.2s ease-in-out;
  }

  /* line 52, ../scss/_481up.scss */
  .icon3 a:hover:before {
    color: #007bb6;
    animation: cuteness 0.4s ease-out;
    -webkit-animation: cuteness 0.4s ease-out;
    -moz-animation: cuteness 0.4s ease-out;
    -o-animation: cuteness 0.4s ease-out;
    -webkit-transition: color 0.2s ease-in-out;
    -transition: color 0.2s ease-in-out;
    transition: color 0.2s ease-in-out;
  }

  /* line 58, ../scss/_481up.scss */
  .icon4 a:hover:before {
    color: #333;
    animation: cuteness 0.4s ease-out;
    -webkit-animation: cuteness 0.4s ease-out;
    -moz-animation: cuteness 0.4s ease-out;
    -o-animation: cuteness 0.4s ease-out;
    -webkit-transition: color 0.2s ease-in-out;
    -transition: color 0.2s ease-in-out;
    transition: color 0.2s ease-in-out;
  }

  /* line 64, ../scss/_481up.scss */
  .icon5 a:hover:before {
    color: #fe9900;
    animation: cuteness 0.4s ease-out;
    -webkit-animation: cuteness 0.4s ease-out;
    -moz-animation: cuteness 0.4s ease-out;
    -o-animation: cuteness 0.4s ease-out;
    -webkit-transition: color 0.2s ease-in-out;
    -transition: color 0.2s ease-in-out;
    transition: color 0.2s ease-in-out;
  }

Make sure not to repeat a mixin more than once. If there are many elements that share a common mixin, group them together and call the mixin as shown below:

.icon1 a:hover:before,
.icon2 a:hover:before,
.icon3 a:hover:before,
.icon4 a:hover:before,
.icon5 a:hover:before {
	@include animation( cuteness .4s ease-out );
	@include transition( color .2s ease-in-out );
}

Mixins are powerful but it is easy to overdo it. Therefore, use it wisely!

Remove unused stylings

In _481up.scss and _768up.scss, you will notice that the navigation stylings are available in here as well. This is useful if you want the navbar to appear differently in specific screen sizes. However, depending on your design, you may not need these additional navigation stylings. My current theme do not have a navigation bar; I removed the navigation stylings from the three scss files: _base.scss, _481up.scss, and _768up.scss. You may also want to remove certain widget stylings that you do not need or combine them together but this really depends on your design.

It is good to remove all these unused stylings as you do not want a bloated CSS file. Once you come up with your theme, do some extra cleaning at the end by pruning all those unused stylings.

Minify CSS for production

Once your theme is ready for deployment, do not forget to minify your CSS in order to improve the performance of your site. You can minify it automatically with Grunt.js or with performance related plugins like W3 Total Cache.

Personally, I prefer to minify my CSS using Grunt.js. In my experience, the minifying engine of W3 Total Cache can be quite buggy with certain plugins like Jetpack.

Final Thoughts

Bones is definitely a great starter theme that you can use as a starting point for your design. It makes developing WordPress theme faster as you do not need to start from scratch. Furthermore, it follows the mobile-first approach, which ensures that your site works nicely on different devices.

It may be intimidating at first, but once you get started and play with it, you will notice that it is quite intuitive to work with. So give the starter theme a try. If you have never work with Sass or Compass before, this is definitely a good starting point. You can download the theme designed for this article for your reference. Happy developing!

  • How are you planning for updates to the Bones files? It’s not possible to keep all edits/additions in a separate CSS file, so are we expected to diff every single file when Bones gets updated?

    • Updating it through ‘git pull’ should work but you may need to resolve any conflicts that you may have.

  • Great review of this “framework” that i use for every project !

    • Bones is not a framework but a template

      • I know, that’s why i said “…”
        Now I’m using Roots.
        Powerfull.

  • When using just Bones as a parent and creating a website with custom design in it, without a child theme. How do I update it without my changes disappearing?

  • Thank you so much! I’m building on Bones from now on and every tutorial is out of date (painfully so), so I’m really glad you put this together.