Do you and your team work with styleguides? Do you have documentation for your modules? If not, don’t panic. In this tutorial I’ll show you how it’s possible to improve the way you work with your team. We’ll explore concepts suggested by Brad Frost and Dave Olsen, using Pattern Lab to create styleguides, with Gulp to handle the assets. Let’s begin!
What is Gulp?
Gulp.js is a streaming build system and a task runner. The important concept is that you have streams where you define a lot of stuff for your files. It’s faster than managing things manually, saving your nerves and a lot of time. For example, in this tutorial we’ll put all our Sass-files in a pipe (a process):
- compiling Sass to CSS,
- concatenating the CSS files,
- minifying the output,
- and moving it all to another location
To learn more about the basics of Gulp, check out Kezz Bracey’s beginner guide The Command Line for Web Design: Automation with Gulp.
What is Pattern Lab?
Pattern Lab is a concept for creating atomic design systems; building modules instead of building pages directly. It defines several composite parts:
- atoms
- molecules
- organisms
- templates
- pages
You start with the smallest element, building out to larger parts, until you have full pages. This approach helps you save time, collaborate, and guarantees a solid structure.
Atoms
Atoms can’t be broken down into smaller pieces.

These are the simplest blocks, including fundamental tags like lists, colors, fonts, animations etc.
Molecules
Molecules are groups of different elements (atoms) which function together like a unit.

For example, a teaser with a headline, an image, paragraph and a link like “Read more”. Each of these is a single element, but together they become a molecule; part of a larger, more complex system.
Organisms
Organisms are groups of elements (atoms and molecules) and work like a section on your website.

Think, for example, of a website header. It’s a larger system, built from molecules such as a search form and navigation, both of which are in turn built of smaller atoms.

Check out the online UI demos and get a feeling for the whole system.
Let the Magic Happen!
Now it’s time to combine both these systems and create a workflow for your team. Pattern Lab will give us our HTML and deliver the simple UI, Gulp will handle all the assets we need.
Our Key-Features:
- Sass-Compiling (Libsass)
- Server (Browser-Sync)
- Livereload
- Minify (Javascript, CSS and images)
- Releasing / Deployment
- Bump up the version
- Tagging
- Push files and tags to endpoint
- Push files via rsync to a server
Introduction
To use Gulp you’ll first need to have node.js on your system. If you don’t, take a look at Kezz Bracey’s The Command Line for Web Design: Taming 3rd Party Packages for setup instructions.
Let’s start by installing Gulp.js globally. In the terminal, type:
npm install gulp -g
Now we need to clone the repository of Patternlab to give us a base to work from.
git clone git@github.com:pattern-lab/patternlab-php.git
Next we need a gulp file to setup our tasks. Create a gulpfile.js
in the root of your project folder. After that we need a config-file, where we define all the paths, so create a build.config.json
in your folder.
The following files are needed too:
.bowerrc
package.json
bower.json
After all these basic steps, we have the basic project structure. Now let’s start to build the tasks for our workflow.
Start With the Gulpfile
At the top of our gulpfile.js file, we require each dependency. If you install a plugin, you must “require” it and give it a name.
Start with gulp and our configfile for all paths and configuration.
var gulp = require('gulp'), config = require('./build.config.json');
During our development process we won’t need to be minifying our code (that’s a waste of time unless we’re ready to deploy). With the following production
variable we can toggle some tasks on and off. You will see this in action later on.
// Production Handling // Description: Use 'production' variable with 'gulpif' // Toggle minifing and optimization for assets var production;
With things setup, we can now start adding various tasks to help us in our development!
Task 1: Clean all Assets
// Install needed dependency npm install gulp-clean // Require dependency var clean = require('gulp-clean');
If you delete an image from the folder “source/”, you’ll find there’s a copy of the image in “public/” too. Because of this duplication we’ll perform a simple step to clean the image folder in “public/”.
// Task: Clean:before // Description: Removing assets files before running other tasks gulp.task('clean:before', function () { return gulp.src( config.assets.dest ) .pipe(clean({ force: true })) });
Task 2: Handle Scripts
// Install needed dependencies npm install gulp-concat gulp-uglify gulp-rename // Require dependencies var concat = require('gulp-concat'); var uglify = require('gulp-uglify'); var rename = require('gulp-rename');
For deployment purposes it’s important to have just one file with all scripts. To achieve this we’ll use the plugin gulp-concat and combine all our scripts to produce application.js
. If the variable production
is true, then application.js
will be uglified and get a new name: application.min.js
.
gulp.task('scripts', function () { return gulp.src(config.scripts.files) .pipe(concat( 'application.js' )) .pipe(gulpif(production, uglify())) .pipe(gulpif(production, rename({ suffix: '.min' }))) .pipe(gulp.dest( config.scripts.dest )) .pipe(browserSync.reload({stream:true})); });
Task 3: Fonts
This task will push all the fonts to the public folder. Nothing more.
// Task: Handle fonts gulp.task('fonts', function () { return gulp.src(config.fonts.files) .pipe(gulp.dest( config.fonts.dest )) .pipe(browserSync.reload({stream:true})); });
Task 4: Images
For this step we’ll install and require the plugin gulp-imagemin. Once we’ve done that we can use it to minify images. This will save memory and boost performance.
// Install imagemin npm install gulp-imagemin // Require dependencies var imagemin = require('gulp-imagemin');
If the variable production
is true, then all images will be minified. With that done, we push them to the destination folder.
// Task: Handle images gulp.task('images', function () { return gulp.src(config.images.files) .pipe(gulpif(production, imagemin())) .pipe(gulp.dest( config.images.dest )) .pipe(browserSync.reload({stream:true})); });
Task 5: Handle Sass
Let’s install and require the dependencies gulp-sass and gulp-cssmin.
// Install needed dependencies npm install gulp-sass gulp-cssmin // Require dependencies var sass = require('gulp-sass'); var cssmin = require('gulp-cssmin');
Now we’ll take all Sass files, use the Sass plugin to compile them to CSS, then if the variable production
is true, cssmin will do its thing.
// Task: Handle Sass and CSS gulp.task('sass', function () { return gulp.src(config.scss.files) .pipe(sass()) .pipe(gulpif(production, cssmin())) .pipe(gulpif(production, rename({ suffix: '.min' }))) .pipe(gulp.dest( config.scss.dest )) .pipe(browserSync.reload({stream:true})); });
Task 6: Pattern Lab Server
Pattern Lab has its own server, which you can start with a simple shell command. To execute this command we need the plugin gulp-shell.
// Install needed dependencies npm install gulp-shell // Require dependencies var shell = require('gulp-shell');
At this point we’ll copy the styleguide folder from core to public. At this point, you’ll be able to see a solid front-end in the browser.
// Task: patternlab // Description: Build static Pattern Lab files via PHP script gulp.task('patternlab', function () { return gulp.src('', {read: false}) .pipe(shell([ 'php core/builder.php -gpn' ])) .pipe(browserSync.reload({stream:true})); }); // Task: styleguide // Description: Copy Styleguide-Folder from core/ to public gulp.task('styleguide', function() { return gulp.src(config.patternlab.styleguide.files) .pipe(gulp.dest(config.patternlab.styleguide.dest)); });
Task 7: Start Server and Watch Files
Pattern Lab has a built-in server, but Browser-Sync handles the injection of CSS changes without page reload.
// Install needed dependencies npm install browser-sync gulp-watch // Require dependencies var browser-sync = require('browser-sync'); var watch = require('gulp-watch');
The watcher takes care of changes and triggers the specific tasks. After that, browser-sync updates our view in the browser.
// task: BrowserSync // Description: Run BrowserSync server with disabled ghost mode gulp.task('browser-sync', function() { browserSync({ server: { baseDir: config.root }, ghostMode: true, open: "external" }); });
We specify the files for the watcher and trigger the tasks which we need in the event of a change.
// Task: Watch files gulp.task('watch', function () { // Watch Pattern Lab files gulp.watch( config.patternlab.files, ['patternlab'] ); // Watch scripts gulp.watch( config.scripts.files, ['scripts'] ); // Watch images gulp.watch( config.images.files, ['images'] ); // Watch Sass gulp.watch( config.scss.files, ['sass'] ); // Watch fonts gulp.watch( config.fonts.files, ['fonts'] ); });
Task 8: Default-Task
Writing gulp
in the shell triggers the default task. But before Gulp starts this, it triggers the clean:before
task to clean all the public files.
// Task: Default // Description: Build all stuff of the project once gulp.task('default', ['clean:before'], function () { production = false; gulp.start( 'patternlab', 'styleguide', 'fonts', 'sass', 'images', 'scripts' ); });
Task 9: Start Process
Let’s create a task to develop at the styleguide, but without minifying assets. This triggers browser-sync, builds all assets and starts the watcher.
// Task: Start your production-process // Description: Typ 'gulp' in the terminal gulp.task('serve', function () { production = false; gulp.start( 'browser-sync', 'default', 'watch' ); });
Task 10: Releasing and Tagging
For this step we’re going to need a few new plugins.
- The plugin gulp-bump is to update the version number.
- gulp-filter will give us a specific file of the stream.
- gulp-git allows us to use git statements in gulp.
- And gulp-tag-version is for generating the tag.
// Install needed dependencies npm install gulp-bump gulp-filter gulp-git gulp-tag-version // Require dependencies var bump = require('gulp-bump'); var filter = require('gulp-filter'); var git = require('gulp-git'); var tagversion = require('gulp-tag-version');
Now you define the gulp-task release
, set the variable production to true
(now we need to minify) and open the stream. You must take all files with a version number, use the plugin bump and make it possible to define the type (patch, minor or major) via a parameter in the shell.
If you execute the release
-task without a type then gulp-bump
will take a patch - x.x.1. After this you push the files to the root and commit the changes. Now it’s time to generate a new tag for the project. The file package.json
is needed to get the current version number for the new tag.
Finally, we push all files and tags to the origin and in the branch which we want.
// Function: Releasing (Bump & Tagging) // Description: Bump npm versions, create Git tag and push to origin gulp.task('release', function () { production = true; return gulp.src(config.versioning.files) .pipe(bump({ type: gulp.env.type || 'patch' })) .pipe(gulp.dest('./')) .pipe(git.commit('Release a ' + gulp.env.type + '-update')) // read only one file to get version number .pipe(filter('package.json')) // Tag it .pipe(tagversion()) // Publish files and tags to endpoint .pipe(shell([ 'git push origin develop', 'git push origin --tags' ])); });
Task 11: Deployment
It’s possible to deploy all the files to a server via rsync; doing so is super fast and comfortable. Type gulp deploy
in your terminal and after a few seconds you have the local project at the server. You don’t need to drag and drop folders manually. Automate all the things. You define the host and the path of the folder, which you want to deploy in build.config.js
.
// Task: Deploy static content // Description: Deploy static content using rsync shell command gulp.task('deploy', function () { return gulp.src(config.deployment.local.path, {read: false}) .pipe(shell([ 'rsync '+ config.deployment.rsync.options +' '+ config.deployment.local.path +'/ '+ config.deployment.remote.host ])) });
Final Gulpfile
You’ve written so much code, and here is the final result! You may prefer to have a separate file for each task, in which case you’re welcome to split it up. In this case, for the sake of simplicity, we’ll display everything in a single Gulpfile:
var gulp = require('gulp'), bump = require('gulp-bump'), clean = require('gulp-clean'), concat = require('gulp-concat'), browserSync = require('browser-sync'), cssmin = require('gulp-cssmin'), filter = require('gulp-filter'), git = require('gulp-git'), gulpif = require('gulp-if'), imagemin = require('gulp-imagemin'), rename = require('gulp-rename'), sass = require('gulp-sass'), shell = require('gulp-shell'), tagversion = require('gulp-tag-version'), uglify = require('gulp-uglify'), config = require('./build.config.json'); // Trigger var production; // Task: Clean:before // Description: Removing assets files before running other tasks gulp.task('clean:before', function () { return gulp.src( config.assets.dest ) .pipe(clean({ force: true })) }); // Task: Handle scripts gulp.task('scripts', function () { return gulp.src(config.scripts.files) .pipe(concat( 'application.js' )) .pipe(gulpif(production, uglify())) .pipe(gulpif(production, rename({ suffix: '.min' }))) .pipe(gulp.dest( config.scripts.dest )) .pipe(browserSync.reload({stream:true})); }); // Task: Handle fonts gulp.task('fonts', function () { return gulp.src(config.fonts.files) .pipe(gulp.dest( config.fonts.dest )) .pipe(browserSync.reload({stream:true})); }); // Task: Handle images gulp.task('images', function () { return gulp.src(config.images.files) .pipe(gulpif(production, imagemin())) .pipe(gulp.dest( config.images.dest )) .pipe(browserSync.reload({stream:true})); }); // Task: Handle Sass and CSS gulp.task('sass', function () { return gulp.src(config.scss.files) .pipe(sass()) .pipe(gulpif(production, cssmin())) .pipe(gulpif(production, rename({ suffix: '.min' }))) .pipe(gulp.dest( config.scss.dest )) .pipe(browserSync.reload({stream:true})); }); // Task: patternlab // Description: Build static Pattern Lab files via PHP script gulp.task('patternlab', function () { return gulp.src('', {read: false}) .pipe(shell([ 'php core/builder.php -gpn' ])) .pipe(browserSync.reload({stream:true})); }); // Task: styleguide // Description: Copy Styleguide-Folder from core/ to public gulp.task('styleguide', function() { return gulp.src(config.patternlab.styleguide.files) .pipe(gulp.dest(config.patternlab.styleguide.dest)); }); // task: BrowserSync // Description: Run BrowserSync server with disabled ghost mode gulp.task('browser-sync', function() { browserSync({ server: { baseDir: config.root }, ghostMode: true, open: "external" }); }); // Task: Watch files gulp.task('watch', function () { // Watch Pattern Lab files gulp.watch( config.patternlab.files, ['patternlab'] ); // Watch scripts gulp.watch( config.scripts.files, ['scripts'] ); // Watch images gulp.watch( config.images.files, ['images'] ); // Watch Sass gulp.watch( config.scss.files, ['sass'] ); // Watch fonts gulp.watch( config.fonts.files, ['fonts'] ); }); // Task: Default // Description: Build all stuff of the project once gulp.task('default', ['clean:before'], function () { production = false; gulp.start( 'patternlab', 'styleguide', 'fonts', 'sass', 'images', 'scripts' ); }); // Task: Start your production-process // Description: Typ 'gulp' in the terminal gulp.task('serve', function () { production = false; gulp.start( 'browser-sync', 'default', 'watch' ); }); // Task: Deploy static content // Description: Deploy static content using rsync shell command gulp.task('deploy', function () { return gulp.src(config.deployment.local.path, {read: false}) .pipe(shell([ 'rsync '+ config.deployment.rsync.options +' '+ config.deployment.local.path +'/ '+ config.deployment.remote.host ])) }); // Function: Releasing (Bump & Tagging) // Description: Bump npm versions, create Git tag and push to origin gulp.task('release', function () { production = true; return gulp.src(config.versioning.files) .pipe(bump({ type: gulp.env.type || 'patch' })) .pipe(gulp.dest('./')) .pipe(git.commit('Release a ' + gulp.env.type + '-update')) // read only one file to get version number .pipe(filter('package.json')) // Tag it .pipe(tagversion()) // Publish files and tags to endpoint .pipe(shell([ 'git push origin develop', 'git push origin --tags' ])); });
Configfile
Now we need our configfile to set the various paths. This file is necessary to maintain the paths and configuration of the project:
{ "root": "./public", "deployment": { "local": { "path": "public" }, "remote": { "host": "YOUR HOST" }, "rsync": { "options": "-avzh --delete -e ssh" } }, "assets": { "base": "source/assets/", "dest": "public/assets/" }, "versioning": { "files": [ "./package.json", "./bower.json" ] }, "scripts": { "base" : "source/assets/javascripts/", "files" : [ "source/assets/javascripts/**/*.js" ], "dest" : "public/assets/javascripts/" }, "components": { "base": "source/assets/components/" }, "scss": { "base" : "source/assets/scss/", "files": [ "source/assets/scss/**/*.scss" ], "dest" : "public/assets/stylesheets/" }, "fonts": { "base" : "source/assets/fonts/", "files": [ "source/assets/fonts/**/*" ], "dest" : "public/assets/fonts/" }, "images": { "base" : "source/assets/images/", "files": [ "source/assets/images/**/*" ], "dest" : "public/assets/images/" }, "patternlab": { "styleguide": { "files": [ "core/styleguide/**" ], "dest": "public/styleguide/" }, "files": [ "source/_patterns/**/*.mustache", "source/_patterns/**/*.json", "source/_data/*.json" ] } }
Conclusion
I love working with a combination of Gulp and Pattern Lab. I’ve worked in different teams and set this foundation for each project. Feedback by each team member has always been positive because of its easy-to-follow process. Everybody have a solid surface, can pick the module and use it with ease. For questions or feedback please see the comments section below.