A front-end workflow



A front-end workflow

0 0


modern-front-end-workflows

Slides for our presentation at Techorama 2014

On Github bengie / modern-front-end-workflows

A front-end workflow

Techorama 2014, by Jochen Vandendriessche and Gregory Van Looy

open source - cross platform

Jochen Vandendriessche

Freelance front-end developer - JS junkie

@joggink

Gregory Van Looy

Freelance HTML & CSS architect - sucks @ JS

@bengie

1yr @ DPP -> new workflow for in-house team

You already have a good workflow

#kthxbye

boss - happy | you - not happy/bored | new challenge = upgrade your workflow | not the ultimate workflow |
  • Scalable and Modular Architecture
    • SMACSS
    • JS
  • Coding guidelines
    • BEM
    • F*ck jQuery
  • Tools
    • Sass
    • Grunt
based on our workflow @ DPP - in conjunction with them -

Scalable and Modular Architecture

Scalable and Modular Architecture for CSS

SMACSS

Categorizing CSS rules

Base Layout Module State 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

<article class="article">
    <header class="article__header">...</header>
    <div class="article__body">...</div>
    <footer class="article__footer">...</footer>
</article>

... with modifier

<!-- note: class chaining -->
<article class="article article--primary">
    <header class="article__header">...</header>
    <div class="article__body">...</div>
    <footer class="article__footer">...</footer>
</article>
<div class="carousel">
    <header class="carousel__header">
        <h3 class="carousel__header__title">Carousel</h3>
    </header>
    <div class="carousel__clipper">
        <ul class="carousel__list">
            <li class="carousel__list__item">...</li>
            <li class="carousel__list__item">...</li>
            <li class="carousel__list__item">...</li>
            <li class="carousel__list__item">...</li>
        </ul>
    </div>
    <footer class="carousel__nav">
        <a href="#" class="carousel__nav__item carousel__nav__item--prev">...</a>
        <a href="#" class="carousel__nav__item carousel__nav__item--next">...</a>
    </footer>
</div>

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

it's ugly but it works - easier to debug & scalable

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
faster CSS = WIN!

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

Preprocessors define the future of CSS | Pick the one you like | I prefer Sass

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';
the core of modular CSS

Sass - Mixins

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

.alert {
    @include border-radius(4px);
}
DRY and maintainable code
/* 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 Multiple environment setup | prod - dev
// 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 {}
...
IE's 4096 selectors limit per CSS file

The end

Questions?