While developing websites based on Symfony2 is an awesome experience due to useful console tasks, code reuse through bundles and other general best development practices, the front-end development in such a website is unfortunately mostly left out in the cold.

Although the front-end community is very active as well, we had little to no integration between the two communities. This was something we wanted to change and in the process of overhauling all our bundles, we also extended our integration of advanced front-end tooling in our Bundles.

We will be featuring a series of articles describing the entire front-end stack used in a default implementation of the Kunstmaan Bundles content management system.
First up, automating and optimizing the frontend workflow using Bower and Grunt.

Front-end package management through Bower

To avoid reinventing the wheel time and time again, we make use of a lot of external libraries and plugins for our front-end development (e.g. jQuery, Bourbon, ...). 

Before optimizing our front-end workflow, we used to download these third-party libraries from the web and then we manually included them in our projects. Working this way, when a library got updated we had to track this down and subsequently update the vendor code in our project. All of the vendor files were also added to our Git-repositories, which was just a waste of precious storage space.

This is exactly why we included Bower into our workflow. Bower is a front-end package manager by Twitter. For more info and documentation on Bower, make sure to visit their website

Installing Bower

Bower runs in a node.js environment and is easily installed through Node Package Manager (npm):

npm install -g bower

Bower in the bundles

First of all we have provided a default bower.json file with some default dependencies for our new workflow. 

  "name": "myProject",
  "version": "0.1.0",
  "dependencies": {
    "sass-bootstrap": "~2.3.2",
    "bourbon": "latest",
    "jquery": "<2.0.0",
    "cupcake": "latest",
    "socialite": "git://github.com/dbushell/Socialite.git",
    "flexslider": "latest",
    "fitvids": "latest",
    "svgeezy": "latest",
    "jquery-placeholder": "latest",
    "fancybox": "latest",
    "flat-ui": "latest"

We wil post an article about some of these dependencies (Bootstrap, Cupcake) in the next few weeks. If you want to include an extra vendor, just add it to the dependencies and run "bower install" or use the '--save' flag: "bower install packagename --save".

We have chosen to install the vendors in "web/vendor/". This can be adjusted by updating the .bowerrc file (hidden).

  "directory": "web/vendor/"

There is one important vendor which we use alot but do not download through Bower... and that would be Modernizr. Why? Read on and find out. 

Automating our workflow with Grunt

Having worked with the yeoman tool on some of our latest projects, we already had quite a bit of experience with automating our workflow and the advantages this brings. Yeoman uses Grunt to automate recurring tasks. Grunt is a task runner built in JavaScript, a language we, as front-end developers, are very familiar with. We were impressed by the power and possibilities of Grunt and decided to fully integrate it in our default project setup.

Installing Grunt

Grunt runs in a node.js environment and is easily installed through Node Package Manager (npm):

npm install -g grunt-cli
Grunt typically makes use of a 'Gruntfile'. This is a JavaScript file, placed in the root of your project, which is used to configure the task runner itself as well as the tasks of your choice. After having configured Grunt, it will execute the most mundane tasks for you with zero effort.
Grunt is also backed by an amazing community: there is a Grunt task for almost everything. If there isn't a task that fits your needs, the documentation is concise and comprehensive so writing your own custom task is quite easy.

Default tasks

A roundup of the tasks included by the default Bundles implementation:
  • Watch: The main task we use is the grunt-contrib-watch task. This task, as you might suspect, watches files or folders for changes and runs a certain task when a change happens. We basically have this one always running during our development process. It comes with LiveReload support and works flawlessly with our LiveReload Symfony bundle. This bundle injects a LiveReload snippet in each of our pages while in the dev environment. The watch task can hook into this and push changes to the page. This way we can see our website come together without ever having to refresh manually. We will go into detail about the LiveReload bundle in tomorrow's blog post.
  • Sass: This task is one we recently implemented. We leverage the power of grunt-sass to compile our SCSS files to standard CSS to be used in the browser. We used to compile our SCSS through Symfony's own Assetic. However, this gave us trouble with the LiveReload functionality: pages had to be reloaded in their entirety. Moving the compilation from Assetic to Grunt solved this problem: not only does the compilation happen very fast, LiveReload now manages to inject only the changed files. This makes our development process a lot faster and snappier.
  • JSHint: Another task we use is the grunt-contrib-jshint task. When we save JavaScript files, the watch task will call upon this task to run those files through JSHint. This way we can force coding standards and make sure our JavaScript files do not have blatant errors in them.
  • Imagemin: grunt-contrib-imagemin minifies JPEG and PNG files through OptiPNG and jpegtran. We originally used ImageOptim for this, however this was often forgotten. By using the imagemin task combined with the watch task, our images will always be optimised.
  • Svg2png: The SVG2PNG task is an interesting one: we often use SVG images to bring the best possible detail to modern browsers and retina screens. However, our good friends Internet Explorer (IE) 7 and 8 do not support SVG images. Since we still provide support for IE 7 and 8, we always have to convert our SVG images to a compatible format for these older browsers. Doing this by hand on a website with lots of icons or logos quickly becomes a drag. The SVG2PNG task does this for us: when we add a SVG file to our project, the watch task picks this up and calls the SVG2PNG task, which then converts the SVG to a PNG file suitable for older browsers.
  • modernizr: Last, but not at all least, is the grunt-modernizr task. We use Modernizr for HTML5 and CSS3 feature detection. Since we do not want to include the full Modernizr version with all feature tests in our production websites, we were looking for a way to easily create a custom Modernizr build suited to the exact needs of our different projects. One way would be to go to the Modernizr website and generate a custom build by only picking the tests we want. However, since we are all about automation now, we decided to use the grunt-modernizr task. This task scans our HTML, JavaScript and CSS files and determines which feature tests are referenced. It then outputs a lean, mean Modernizr machine. This way, we never have to deal with overhead from the Modernizr library.

Future plans

In the future, we will keep using Grunt as it has rapidly become a vital part of our workflow. To continue our move away from Assetic, we would also like to start using Grunt for concatenation and minification of our JavaScript and CSS files. This way, our entire front-end workflow will be Grunt-based. We are looking at new, interesting tasks daily and we would also like to expand our use of Grunt outside of the Bundles project: our HTML e-mail template would benefit tremendously from a custom Grunt task.

Grunt and Bower have had a huge impact on our front-end workflow. We can now focus on the development of our websites without having to deal with all the extra hassle of manually downloading third-party scripts, compiling SCSS, optimizing images and so on. These tools have brought massive improvements to our development process and we hope they will continue to do so in the future.