On Github bengie / modern-front-end-workflows
Techorama 2014, by Jochen Vandendriessche and Gregory Van Looy
open source - cross platformFreelance front-end developer - JS junkie
Freelance HTML & CSS architect - sucks @ JS
1yr @ DPP -> new workflow for in-house team#kthxbye
boss - happy | you - not happy/bored | new challenge = upgrade your workflow | not the ultimate workflow |/* no classes here, only element selectors */ body {} p {} a {} ul {} h1 {} h2 {} h3 {}
/* grid */ .col {} .col--2 {} .col--primary {} /* layout specific: .layout-- prefix */ .layout--homepage .container {}
.layout--XXXX classes are added to the html tag
Biggest chunk of your CSS file
/* skip the prefix, it's too verbose */ .module-carousel {} /* that's the way, uhu uhu */ .carousel {}
/* .is- prefix */ .is-active {} .is-hidden {} .is-collapsed {} .is-expanded {}
for JavaScript = easier to debug
/* .js-is- prefix */ .js-is-active {} .js-is-hidden {} .js-is-collapsed {} .js-is-expanded {}
Will you need this one?
.theme--culture .tag {} .theme--x-mas blockquote:before { content: 'hohoho'; }
.theme-- classes are added to the html tag
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 ...
An interchangeable single-part of a larger system that can easily be re-used
(function(){ // self executing anonymous function }());
there's no privacy
'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 }; }());
Encapsules how disparate modules interact with each other by acting as an intermediary
Allows modules to broadcast or listen to notifications without worrying about the system
'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 }; }());
| - js |- config config.js |- 3rdparty jquery.min.js ... |- modules toggle.js ... |- vendor modernizr.js webfont.js ... main.js main.toggle.js
Block - Element - Modifier
.block {} .block__element {} /* note: double underscore */ .block--modifier {} /* note: double hyphen */
<article class="article"> <header class="article__header">...</header> <div class="article__body">...</div> <footer class="article__footer">...</footer> </article>
<!-- 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>
/* Bad */ .pageHeader {} .button--blue {} /* Good */ .page-header {} .button--primary {}
/* This is OK! */ .article__header--no-border {} .teaser--large--primary {}
“BEM, so ugly it actually works!”
it's ugly but it works - easier to debug & scalable/* old skool */ article > header {} /* BEM */ .article__header {} /* old skool + Smacss theming */ .theme--x-mas article > header {} /* BEM + SMACSS theming */ .theme--x-mas .article__header {}
Minified and gZipped
$('.my #awesome selector');
document.querySelectorAll('.my #awesome selector');
$(el).find(selector);
el.querySelectorAll(selector);
$(el).addClass(className);
el.className += ' ' + className;
if (el.classList) { el.classList.add(className); }else{ el.className += ' ' + className; }
$(el).outerHeight()
el.offsetHeight
$(el).parent();
el.parentNode
$(el).remove();
el.parentNode.removeChild(el);
Like Qwery, Bonzo, Bean, Reqwest, etc...
$primary-color: #f90; h1 { color: $primary-color; } /* output */ h1 { color: #f90; }
/* 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; }
_forms.scss
/* main.scss */ @import 'forms';the core of modular CSS
@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; }
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; }
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%; }
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
.block { &__element {} &--modifier {} } /* output */ .block {} .block__element {} .block--modifier {}
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.
Grunt and Grunt plugins are installed and managed via npm, the Node.js package manager.
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']
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' }
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' }
// 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 } }
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