A front-end workflow

Techorama 2014, by Jochen Vandendriessche and Gregory Van Looy

Jochen Vandendriessche

Freelance front-end developer - JS junkie

@joggink


Gregory Van Looy

Freelance HTML & CSS architect - sucks @ JS

@bengie

You already have a good workflow

#kthxbye

  • Scalable and Modular Architecture
    • SMACSS
    • JS
  • Coding guidelines
    • BEM
    • F*ck jQuery
  • Tools
    • Sass
    • Grunt

Scalable and Modular Architecture

Scalable and Modular Architecture for CSS

SMACSS

Categorizing CSS rules

  1. Base
  2. Layout
  3. Module
  4. State
  5. Theme

1. Base rules


/* no classes here, only element selectors */
body {}
p {}
a {}
ul {}
h1 {}
h2 {}
h3 {}
                    

2. Layout rules


/* grid  */
.col {}
.col--2 {}
.col--primary {}

/* layout specific: .layout-- prefix */
.layout--homepage .container {}
                    

.layout--XXXX classes are added to the html tag

3. Module rules

Biggest chunk of your CSS file


/* skip the prefix, it's too verbose */
.module-carousel {}

/* that's the way, uhu uhu */
.carousel {}
                    

4a. Static State rules


/* .is- prefix */
.is-active {}
.is-hidden {}
.is-collapsed {}
.is-expanded {}
                    

4b. Dynamic State rules

for JavaScript = easier to debug


/* .js-is- prefix */
.js-is-active {}
.js-is-hidden {}
.js-is-collapsed {}
.js-is-expanded {}
                    

5. Theme rules

Will you need this one?

YES!!!!


.theme--culture .tag {}
.theme--x-mas blockquote:before {
    content: 'hohoho';
}
                        

.theme-- classes are added to the html tag

Folder structure

in combination with Sass


| - sass
   main.scss
   _site-settings.scss
   _mixins.scss
  |- base
     _base.scss
     _normalize.scss
     _defaults.scss
     _webfonts.scss
    ...
  |- layout
     _layout.scss
     _grid.scss
     _sections.scss
    ...
  |- modules
    _modules.scss
    _carousel.scss
    _pagination.scss
    ...
  |- state
    _state.scss
    ...
  |- theme
    _theme.scss
    _x-mas.scss
    ...

                    

JS modular setup

Module Pattern

An interchangeable single-part of a larger system that can easily be re-used

Example


(function(){
  // self executing anonymous function
}());
					

This is great but...

there's no privacy

Example


'use strict';
ToggleModule = (function() {

  // private function
  function _init() {
    // collect all toggle objects and make them behave
  }

  // private function
  function _show(args) {
    // show stuff
  }

  // private function
  function _hide(args) {
    // hide stuff
  }

  // private _init is public available as init
  return {
    init: _init
  };

}());
					

Mediator pattern

Encapsules how disparate modules interact with each other by acting as an intermediary

Possible implementation: pub/sub

Allows modules to broadcast or listen to notifications without worrying about the system

Example


'use strict';
ToggleModule = (function() {

  // private function
  function _init() {
    // let's the mediator know we have to functions that listen
    Mediator.subscribe('/toggle/show', _show);
    Mediator.subscribe('/toggle/hide', _hide);
    // collect all toggle objects and make them behave
  }

  // private function
  function _show(args) {
    // show stuff
  }

  // private function
  function _hide(args) {
    // hide stuff
  }

// private _init is public available as init
  return {
    init: _init
  };

}());
					

Advantages

  • Solves modules inter-dependency issues
  • Notifications can be handled by any number of modules at once

Folder structure


| - js
  |- config
     config.js
  |- 3rdparty
     jquery.min.js
    ...
  |- modules
     toggle.js
    ...
  |- vendor
    modernizr.js
    webfont.js
    ...
  main.js
  main.toggle.js
                    

Coding guidelines

BEM

Block - Element - Modifier


.block {}
.block__element {} /* note: double underscore */
.block--modifier {} /* note: double hyphen */
                    

More on BEM: here, here, here and here

Example


...
...
...

... with modifier



...
...
...


                    

Naming conventions


/* Bad */
.pageHeader {}
.button--blue {}

/* Good */
.page-header {}
.button--primary {}
                    

/* This is OK! */
.article__header--no-border {}
.teaser--large--primary {}
                    

FUGLY!!!

“BEM, so ugly it actually works!”

@jdesramaults

Advantages

  • It's clear what each selector's purpose is
  • Less chance of colliding with CSS of third party plugins
  • Lesser nesting of selectors
    
    /* old skool */
    article > header {}
    /* BEM */
    .article__header {}
    
    /* old skool + Smacss theming */
    .theme--x-mas article > header {}
    /* BEM + SMACSS theming */
    .theme--x-mas .article__header {}
                                
  • … thus, your CSS rendering is faster

F*ck jQuery

Why people use jQuery

  • Easy development
  • Fixes cross browser issues

jQuery is huge!

  • A shitload of plugins
  • Almost everybody knows / uses jQuery

No really, jQuery is huge!

238.66kb normal file size

Ok, that's not fair

28.58kb

Minified and gZipped

That's not so bad?

Do you really need jquery?

Common use of jQuery

Selectors

$('.my #awesome selector');
document.querySelectorAll('.my #awesome selector');

Finding children

$(el).find(selector);
el.querySelectorAll(selector);

querySelectorAll?

http://caniuse.com/#search=querySelectorAll

Adding classnames

$(el).addClass(className);
el.className += ' ' + className;

if (el.classList) {
    el.classList.add(className);
}else{
    el.className += ' ' + className;
}
                    

Width / height

$(el).outerHeight()
el.offsetHeight

Parent

$(el).parent();
el.parentNode

Dom manipulation

$(el).remove();
el.parentNode.removeChild(el);

So back to basic?

Micro libraries

Like Qwery, Bonzo, Bean, Reqwest, etc...

In our initial setup we used jQuery

We switched to micro frameworks

Tools

CSS preprocessors

Sass - Less

Sass

  • Variables
  • Nesting
  • Partials
  • Mixins
  • Extends
  • Operators
  • Frameworks

Sass - variables


$primary-color: #f90;

h1 {
    color: $primary-color;
}

/* output */
h1 {
    color: #f90;
}
                    

Sass - Nesting


/* Sass */
nav {
    ul {
        list-style: none;
        margin: 0;
        padding: 0;
        overflow: hidden; /* clearfixer */
    }
    li {
        float: left;
    }
    a {
        display: block;
    }
}
                    

/* output */
nav ul {
    list-style: none;
    margin: 0;
    padding: 0;
    overflow: hidden; /* clearfixer */
}
nav li {
    float: left;
}
nav a {
    display: block;
}
                    

Sass - Partials

_forms.scss


/* main.scss */
@import 'forms';
                    

Sass - Mixins


@mixin border-radius($radius) {
    -webkit-border-radius: $radius;
    -moz-border-radius: $radius;
    border-radius: $radius;
}

.alert {
    @include border-radius(4px);
}
                    

/* Output */
.alert {
    -webkit-border-radius: 4px;
    -moz-border-radius: 4px;
    border-radius: 4px;
}
                    

Sass - Extends


nav {
    ul {
        list-style: none;
        margin: 0;
        padding: 0;
    }
}
                    

/* extended version */
.unstyled-list {
    list-style: none;
    margin: 0;
    padding: 0;
}

nav {
    ul {
        @extend .unstyled-list;
    }
}
                    

/* output */
.unstyled-list,
nav ul {
    list-style: none;
    margin: 0;
    padding: 0;
}
                    

Sass - Operators

standard operators : +, -, *, / and %


h1 {
    margin-bottom: $base-spacing-unit*3;
}
.col--2 {
    width: 360px / 960px * 100%;  /* = 37.5% */
}
                    

@for $i from 1 through 11 {
    .col--#{$i} {
        width: ((100%/12) * $i);
    }
}
/* output */
.col--1 {
    width: 8.3333333%;
}
.col--2 {
    width: 16.6666667%;
}
...
.col--11 {
    width: 91.6666667%;
}
                    

Sass - Functions


a:hover {
    color: lighten($link-color, 10%); /* more on color functions :  http://jackiebalzer.com/color */
}
.col--2 {
    width: floor(360px / 960px * 100%);
}
.overlay {
    background-color: rgba(255,0,0,0.75) + rgba(0,255,0,0.75); /* output = rgba(255,255,0,0.75) */
}
                    

More on Sass functions

Sass - Frameworks

Compass - Bourbon - Susy - Bootstrap - …

Sass 3.3 + BEM

Even Easier BEM-ing with Sass 3.3

Writing modular CSS (BEM/OOCSS) selectors with Sass 3.3


.block {
    &__element {}

    &--modifier {}
}
/* output */
.block {}
.block__element {}
.block--modifier {}
                    

Automate your workflow with Grunt

gruntjs.com

What is grunt?

In one word: automation. The less work you have to do when performing repetitive tasks like minification, compilation, unit testing, linting, etc, the easier your job becomes.

How does it work?

Grunt and Grunt plugins are installed and managed via npm, the Node.js package manager.

Grunt & JS

grunt-contrib-jshint

options: {
  curly: true,
  browser: true,
  node: true,
  camelcase: true,
  eqeqeq: true,
  eqnull: true,
  indent: 2,
  latedef: true,
  newcap: true,
  quotmark: 'single',
  trailing: true,
  undef: true,
  unused: true,
  strict: true,
  globals: {
    'bean': true,
    'bonzo': true,
    'qwery': true,
    'Arbiter': true,
    'moment': true,
    'reqwest': true,
    'App': false,
    'FB': true,
  },
  reporter: require('jshint-stylish')
},
all: ['<%= package.jssrc %>/modules/*.js', '<%= package.jssrc %>/*.js', '<%= package.jssrc %>/test/spec/*.js']
grunt-contrib-concat

options: {
  compress: false,
  separator: ''
},
dist: {
  src: [
    '<%= package.jssrc %>/3rdparty/*.js',
    '<%= package.jssrc %>/main.js',
    '<%= package.jssrc %>/modules/*.js',
    '<%= package.jssrc %>/*.js'
  ],
  dest: '<%= package.js %>/main.js'
}
grunt-contrib-uglify

options: {
  mangle: true,
  compress: true,
  beautify: false ,
  banner: '/*! <%= package.title || package.name %> - v<%= package.version %> - ' +
          '<%= grunt.template.today("yyyy-mm-dd") %>\n' +
          '<%= package.homepage ? "* " + package.homepage + "\\n" : "" %>' +
          ' * Copyright (c) <%= grunt.template.today("yyyy") %> <%= package.author.name %>;' +
          ' Licensed <%= _.pluck(package.licenses, "type").join(", ") %> */\n'
},
dist: {
  src: '<%= package.js %>/main.js',
  dest: '<%= package.js %>/main.min.js'
}

Grunt & CSS

grunt-contrib-sass

// for production
dist: {
    files: {
        '<%= package.csssrc %>/main.min.css': '<%= package.sass %>/main.scss'
    },
    options: {
        style : 'compressed'
    }
},
// for development
dev: {
    files: {
        '<%= package.csssrc %>/main.css': '<%= package.sass %>/main.scss'
    },
    options: {
        style : 'expanded',
        lineNumbers: true,
        trace: false
    }
}
                    

Bless

main.css


@import url('main-blessed1.css?z=1400852303781');

/* selector rule #4097 continues here */
.something {}
.something-else {}
...
                    

The end

Questions?