BEM & SCSS
STYLE GUIDE
What is BEM?
What is BEM?
-
Front-end development methodology
-
User Interface design pattern
-
Ensures maintainability and longevity
-
Ability to efficiently organize people’s work in a team
Block

Block.
Block.
-
A logically and functionally independent page component
-
Equivalent of a component in Web Components
-
Encapsulates behavior (JavaScript), templates & styles
-
Independent, contained, reusable and can be nested

Block.
Element

Element.
Element.
-
A constituent part of a block that can't be used outside of it
-
Child elements only relevant to the parent block
-
Never used outside of the context of a parent block
-
Remember, blocks can be nested

Element.
Modifier

Modifier.
Modifier.
-
Modify the appearance and behavior of a block or an element
-
Similar in essence to HTML attributes
-
Theme switch, active state, size variations
-
The use of modifiers is optional

Modifier.
IRL Example
Template
<header class="header">
<nav class="nav">
<ul class="nav__list">
<li class="nav__item">Home</li>
<li class="nav__item--active">About</li>
<li class="nav__item">Work</li>
<li class="nav__item">What We Do</li>
<li class="nav__item">Contact</li>
</ul>
</nav>
</nav>Styles
.nav {
background: dodgerblue;
display: table;
width: 100%;
}
.nav--fixed {
height: 55px;
position: fixed;
}
.nav__item {
background: dodgerblue;
display: table-cell;
height: 90px;
&:hover
&--active {
background: red;
color: white;
}
.nav--fixed & {
height: 55px;
}
}Crazy
IRL Example
Crazy IRL Example
%panel {
font-size: 2em;
height: 100%;
padding: 4em;
text-align: center;
width: 100%;
}
@mixin panel($bg) {
@extend %panel;
background: $bg;
}
.panel--red {
@include panel(#ff0000);
}
.panel--green {
@include panel(#00ff00);
}
.panel--blue {
@include panel(#0000ff);
}
Crazy IRL Example
$panels = (
(
name: 'red',
bg: '#ff0000'
),
(
name: 'green',
bg: '#00ff00'
),
(
name: 'blue',
bg: '#0000ff'
)
);
@each $panel in $panels {
.#{map-get($panel, name)} {
@include panel(#{map-get($panel, bg));
}
}JavaScript
JavaScript.
-
Use JavaScript to add behaviour to a component (block)
-
Data attributes to communicate between the two
-
Selection, initialization and modification
-
Template / style agnostic, configurable modules
Data Attributes.
<header class="header">
<nav class="nav" data-nav>
<ul>
<li class="nav__item">
<a href="#" data-nav-item>
Home
</a>
</li>
<li class="nav__item">
<a href="#" data-nav-item>
About
</a>
</li>
<li class="nav__item">
<a href="#" data-nav-item>
Contact
</a>
</li>
</ul>
</nav>
</header>JavaScript.
module.exports = (function() {
'use strict';
function nav($element) {
this.$el = $element;
this.$children = {};
this.modifier = '';
}
// set nav item selector and hook up event listener
nav.prototype.setChildren = function(selector) {
this.$children = this.$el.find(selector);
this.$children.on('click', function(e) {
e.preventDefault();
this.activate($(this));
});
};
// set modifier class to use in activating
nav.prototype.setChildModifier = function(modifier) {
this.modifier = modifier;
};
// active/deactivate nav item
nav.prototype.activate($child) {
$child.parent().toggleClass(this.modifier);
};
return nav;
})();JavaScript.
(function() {
'use strict';
var // load nav module
Nav = require('./components/nav'),
// instantiate new nav instance
nav = new Nav($('[data-nav]'));
// set nav children
nav.setChildren('[data-nav-item]');
// set class to activate with
nav.setChildModifier('.nav__item--active');
})();Data Attributes.
<header class="header">
<nav class="nav" data-nav='{"scroll": true, "offset": 20}'>
<ul>
<li class="nav__item">
<a href="#" data-nav-item>
Home
</a>
</li>
<li class="nav__item">
<a href="#" data-nav-item>
About
</a>
</li>
<li class="nav__item">
<a href="#" data-nav-item>
Contact
</a>
</li>
</ul>
</nav>
</header>JavaScript.
(function() {
'use strict';
var // load nav module
Nav = require('./components/nav'),
$nav = $('[data-nav]'),
// instantiate new nav instance
nav = new Nav($nav, JSON.parse($nav.attr('[data-nav]')));
// set nav children
nav.setChildren('[data-nav-item]');
// set class to activate with
nav.setChildModifier('.nav__item--active');
})();JavaScript.
module.exports = (function() {
'use strict';
function nav($element, opts) {
this.$el = $element;
this.$children = {};
this.modifier = '';
this.offset = opts.offset || 0;
// if scroll options set then initialize
if(opts.scroll) {
this.initScroll();
}
}
// initialize scroll handler
nav.prototype.initScroll = function() {
};
return nav;
})();Help not hinder
SCSS Style Guide
Why a style guide?
-
Help us build more modular and maintainable UI components
-
Enforcing simple rules to ensure code follows standards
-
Help us to move more quickly and work as a team
-
It should not slow us down or hinder expression
-
DS Boilerplate has worked wonders for us so lets improve
Linting Sass.
-
Automate the code review process
-
Parse scss, log potential issues and style guide violations
-
Ultimate goal is to have all our codebases look like it was authored by the same developer
Style Rules
-
Hyphenated BEM
-
4 space indentation
-
Single quote strings
-
Maximum 3 levels nesting (& / media queries)
-
No elements in selectors
-
Alphabetical property lists
-
...and more?
Configuration
Configuration
var gulp = require('gulp');
var scssLint = require('gulp-scss-lint');
var config = require('../config');
module.exports = function() {
gulp.src([config.paths.sassSourceRoot + '/**/*.scss'])
.pipe(scssLint({
config: '.scss-lint.yml'
}))
.on('error', function() {})
};
Configuration
exclude:
- 'public_html/src/scss/base/**'
- 'public_html/src/scss/bourbon/**'
- 'public_html/src/scss/neat/**'
- 'public_html/src/scss/components/_icons.scss'
- 'public_html/src/scss/templates/_icons.scss'
linters:
Indentation: # Line should be indented 4 spaces, but was indented 2 spaces
severity: warning
width: 4
MergeableSelector:
enabled: false
SelectorFormat:
enabled: false
StringQuotes: # Prefer single quoted strings
enabled: true
SelectorDepth:
max_depth: 3
SelectorFormat:
convention: hyphenated_BEMDEMOS!
BEM / SCSS Style Guide
By Adam Chambers
BEM / SCSS Style Guide
- 932