BEM: block, element, modifier
Last modified:
Use <abbr title="block, element, modifier">BEM</abbr> as a naming schema for classes.
BEM example
Consider:
.person {
}
.person__hand {
}
.person--female {
}
.person--female__hand {
}
.person__hand--left {
}
Nested child blocks of a block are defined using a double underscore.
Variations on a block use hyphens. For example, .button
would be the base
style, with .button--error
and .button--success
as modifiers.
The block should hold all the necessary styling for that block, whereas the modifier selector should only add or override its necessary styles, ideally in explicit/long-form.
Note: nested blocks are flattened --- we don’t go overboard.
Don’t do this:
.parent-block__child-block__element {
}
Do this:
.parent-block__element {
}
This is still verbose enough to denote that the class expects to be nested
inside .parent-block
).
Further reading
Specificity and Sass nesting
Last modified:
Selectors should be written as flat as possible, avoiding any Sass nesting unless explicitly needed to increase specificity.
Nesting --- while powerful, and a main feature of Sass --- can produce very specific, long output CSS selectors.
When avoided unless necessary, and followed alongside a consistent class naming
scheme, the CSS’ cascading becomes predictable, and in turn lowers the need for
!important
throughout the codebase.
Example of flatter Sass nesting using BEM
Do this:
// .bc-body comes from Foundations > Body.
.bc-body {
// styles…
a {
text-decoration: underline;
}
}
// Your partial:
.block {}
.block__element {}
.block__element--modifier {
// styles…
.bc-body a & {
text-decoration: none;
}
}
Don’t do this:
// .bc-body comes from Foundations > Body.
.bc-body {
// styles…
a {
text-decoration: underline;
}
}
// Your partial:
.block {
// styles…
.block__element {
// styles…
.block__element--modifier {
// styles…
a {
text-decoration: none !important;
}
}
}
}
Writing clear selectors
Last modified:
Write class-based selectors.
The design system uses classes, sometimes with nested selectors for specific HTML elements.
Do not use IDs in selectors, even when required for some specific purpose (eg linking). There are legitimate use cases for giving an element an ID, eg <main>
and the primary <nav>
often have IDs so that we can link to them.
In such cases add a class to the element that required the ID, and use it in selector --- even if it shares the same as the ID.
Example of class-based selectors
Do this:
.main-content { }
.main-navigation { }
Don’t do this:
#main-content { }
#main-navigation { }
IDs raise specificity unnecessarily and their usage scales poorly in medium+ sized codebases.
Short & long-form CSS notation
Last modified:
Short-form notation is fine, but prefer explicit, long-form property notation for overrides.
Short-form notation is A-OK when setting a number of things, but be careful.
Long-form is more explicit/declarative, and should be favoured to avoid unintentionally overriding a style, or adding something that will later cascade undesirably to a child.
Example of long-form over short-form notation
Imagine we’ve got styling for .foo
:
.foo {
background:
url(pattern.jpg) /* -image */
red; /* -color */
margin: 0 auto;
}
…and then a bit later we want to add a BEM modifier for the .foo
block, say .foo--variant
where we want to change the background image, its background color, and the top margin:
Don’t do this:
.foo--variant {
background-color: blue:
background: url(texture.jpg);
margin: 1em auto 0 auto
}
Do this:
.foo--variant {
background-color: blue:
background-image: url(texture.jpg);
margin-top: 1em;
}
If you use the first --variant
code block, what background color is .foo
? Transparent.
Why? Because the short-form overrides the more explicit background-color
property. This might not be a problem for us, and if it is, one solution could be to simply put background-color
after the background
property, but even cleaner would be to agree that all overrides are explicitly written in long form.
Of course, if all four margins are being reset, then using the margin
short-form is entirely fine.
Avoid custom vendor prefixes
Last modified:
Avoid hand-writing vendor prefixes.
There are experimental [vendor] prefixes, and experimental non-standard prefixes.
Hand-writing and updating vendor prefixes quickly becomes unmaintainable.
An autoprefixer handles automatic addition/substitution at Sass compilation of the properties that have support in current browsers.
Don’t do this:
.foo {
-moz-appearance: none;
-webkit-appearance: none;
appearance: none;
}
Do this:
.foo {
appearance: none;
}
Let the autoprefixer do the work for us.
Exceptions
Where rare exceptions are necessary, comment why the vendor prefix is added, eg:
.foo {
-ms-text-size-adjust: 100%;
-webkit-text-size-adjust: 100%;
// text-size-adjust is non-standard, but implemented,
// and improves readability.
a {
text-decoration: underline;
webkit-text-decoration-skip: objects;
// text-decoration-skip is non-standard, but implemented,
// and improves legibility of descender glyphs with
// text-decoration: underline.
}
}
Being careful with `@extend`
Last modified:
Use `@extend` wisely; opt for mixins instead.
Use mixins (mixins without arguments are legit) for reusable code snippets. Avoid the use of @extend
unless a relationship needs to be made explicit.
Example of the problem with @extend
The issue is that @extend
will indiscriminately extend every instance of a matching selector that comes across. For example:
Don’t do this:
.foo {
color: red;
}
.footer .foo {
font-weight: bold;
}
.bar {
@extend .foo;
}
You might have been expecting that to compile into:
.foo,
.bar {
color: red;
}
.footer .foo {
font-weight: bold;
}
But because @extend
establishes relationships you will find you instead get:
.foo,
.bar {
color: red;
}
.footer .foo,
.footer .bar {
font-weight: bold;
}
This can result in absolutely enormous selector strings (aka ‘selector explosion’).
Instead use selector placeholders or ‘silent classes’ (%foo
) like in the example below…
.foo,
%foo {
color: red;
}
.footer .foo {
font-weight: bold;
}
.bar {
@extend %foo;
}
…or better yet, just use mixins.
For more information pop over to the Sass Guidelines Project entry on @extend
.
Code commenting and spacing
Last modified:
Document (almost) all the things, using the [SassDoc format](http://sassdoc.com/) commenting format.
Quoting from the Sass Guidelines:
CSS is a tricky language, full of hacks and oddities. Because of this, it should be heavily commented, especially if you or someone else intend to read and update the code 6 months or 1 year from now. Don’t let you or anybody else be in the position of I-didn’t-write-this-oh-my-god-why.
Generally:
-
Title and briefly describe any non-trivial declaration blocks with C-style comments, eg:
** * Block, function, mixin, ... title */
-
Document functions and mixins using the global SassDoc format using inline comments with an extra slash (
///
), eg:/// Replace a string with a string /// http://codepen.io/jakob-e/pen/doMoML /// /// @author @eriksen_dk <https://twitter.com/eriksen_dk> /// /// @param {string} $string - The haystack string to be manipulated /// @param {string} $search - The needle to be replace /// @param {string} $replace - The replacement /// /// @return {string} - The manipulated string with replaced values
-
Space declaration blocks (unless nested) with two blank lines.
-
When documenting specific properties or values, use stock-standard inline comments (
//
) on the same line if your comment fits within 80 characters for that line (including the code), or on the following line.
Writing responsive selectors
Last modified:
Write *mobile-first* responsive styles, with media queries *inside* selectors.
Mobile-first
A webpage is ‘mobile-first’ if the page loads well for mobile viewports when all media queries are disabled or removed; the remaining selectors provide styling for small viewports first.
Where possible write mobile-first styles, and then layer tablet and desktop styles ‘on top’ through media queries.
This approach is superior to its opposite, where media query rules contain tablet and mobile styling, effectively undoing the (default) desktop styles, since the latter is usually more complex than mobile styles.
Nest responsive styles inside what it modifies
When adding media queries, nest them inside the selector that is being modified. This keeps related styles together.
Example of mobile-first styles
Do this:
.bc-body--guide {
margin: 3em 1.5em; // mobile (default)
// ≥768px: let’s pad sides a bit more
@include bc-media(sm) {
margin-left: 4em;
margin-right: 4em;
}
// ≥992px: now center the page design
@include bc-media(md) {
max-width: 52em;
margin-left: auto;
margin-right: auto;
}
}
Don’t do this:
.bc-body--guide {
max-width: 52em;
margin: 3em auto 1.5em auto;
}
@include bc-media(sm) {
.bc-body--guide {
margin-left: 4em;
margin-right: 4em;
max-width: none; // needs resetting…
}
}
@include bc-media(xs) {
.bc-body--guide {
margin-left: 1.5em;
margin-right: 1.5em;
}
}