Your Concept Of CSS Is Broken
Just because it's designed that way doesn't mean it's the right way
August 13, 2016
A few weeks ago, I talked to the team about BEM, a simple naming convention that brings sense and structure to CSS. It was brought up because CSS doesn't get the same attention like the other tech used in the company. Its impact to delivery is often seen as negligible, dismissing it as just another bug in need of a fix. But unknown to everyone, it's dragging everyone back. As always, I get mixed reactions from the developers. Some find it fascinating, some find it silly, and others be like "meh". Here were some questions that were thrown in.
Doesn't it defeat the purpose of the "cascade"?
Yes... and no.
The "cascade" is just a fancy term for resolution, and CSS has a lot of mechanisms to resolve styles. There's the order-based where, given the same selector weight, the styles of the latter override the styles of the former. There's the weight-based where styles of heavier selectors override styles of lighter selectors. There's inline styles that override any stylesheet-defined styles. Lastly there's !important
, the only way to override inline styles from a stylesheet. Without a convention, there's so many ways to write and break CSS.
BEM solves this by removing all but one mechanism of the cascade, the order-based mechanism. No inline styles, !important
and nesting of selectors. All selectors have the same weight, no weight calculation required. If something is messing with a specific style definition, it's either a valid definition that happened after the original definition or something isn't following the convention.
But what about generalizing within elements?
Generalizing and nesting should be avoided.
Documents are like trees. They start from a root, work their way into branches until it gets to the leaves. Whatever happens to a branch, everything under it gets affected. CSS behaves in the same top-down approach. Doing .my-paragraph a
styles all <a>
under an element with the class my-paragraph
. .my-paragraph .my-link
styles all elements with class my-link
under an element with class my-paragraph
.
Apps on the other hand are constructed differently. Apps are generally built using smaller components bottom-up. A large widget might be comprised of 3 smaller widgets but another large widget may share 2 of the 3 small widgets. .my-paragraph a
is not ideal since the element that has .my-paragraph
may contain widgets that also have an <a>
but not necessarily follow the styles defined by ``.my-paragraph a`. It's not ideal to generalize styles of elements under a piece of UI as it may contain pieces that may not agree with the generalized style.
BEM assumes nothing. It specifically targets an element and that element alone. It works perfectly with the bottom-up approach of building apps since it does not introduce side-effects to the descendants, save a few CSS properties that do (and should be guarded against). Scoping won't be required since BEM targets specific elements, and not doing a broad sweep.
How generic should the names be?
Stick to the business names.
BEM only enforces a naming structure. It doesn't enforce what the exact names of the pieces should be. However, the general guideline that seems to work is to use the business name of that UI piece. Project managers and designers think this way, where they have fancy names for sections of the site. Developers should follow suit. If a piece of the site is called a "call to action" that has a callout text, an action link all enclosed in an optionally bordered box, then by all means name them that way. This makes the markup self-documenting, immediately indicating what functionality some markup belongs to.
<div class="call-to-action call-to-action--bordered">
<p class="call-to-action__callout-text">...</p>
<a class="call-to-action__action-link">...</a>
</div>
But how can styles be shared?
Use CSS preprocessor functions like SASS's @extend
, @include
This was one of the anticipated follow-ups when naming was brought up. With BEM, the only class names present on an element is the either the block or element class and its modifiers. If that's the case, then is it even possible to compose styles ala Bootstrap, where classes are composed until the desired look is achieved?
<div class="some-section">
<button type="button" class="btn btn-primary btn-large text-center col-xs-12">I'm a huge button</button>
</div>
The answer is don't follow Bootstrap, seriously. In BEM, the names are there for identification (block or element class) and indicators of alternate states (modifier classes). Alignment, dimensions, position and all are all implementation detail, something that should be tucked away in CSS land. However, this doesn't mean Bootstrap becomes obsolete. Bootstrap is written in SCSS as of version 4 and ports have been done for versions 2 and 3. To achieve the same thing to get the huge button while still following the BEM convention, use preprocessor functions.
<div class="some-section">
<button type="button" class="some-section__my-huge-button">I'm a huge button</button>
</div>
.some-section__my-huge-button{
@extend .btn;
@extend .btn-primary;
@extend .btn-large;
@extend .text-center;
@extend .col-xs-12;
// Other stuff
}
Conclusion
It will take a while to sink in, and only when one gets into trouble does one start to consider structured CSS. So I highly suggest to NOT take CSS lightly. It can break an app silently, only showing symptoms halfway into development. And once everyone has started misusing it, just hope it's not too late to turn back.