Using BEM (Block Element Modifier) And Emulated View Encapsulation In Angular 2.4.1
I am not a CSS (Cascading Stylesheets) expert. I know enough to get the job done, usually; but, when it comes to front-end styling the "right" way, I generally have more questions than I have answers. A little while ago, I learned about BEM - Block, Element, Modifier - from some fellow engineers at InVision App and immediately fell in love with it. At first glance, at least, it seems to bring a lot of clarity to the code and immediately solves many of the selector-collision issues I've battled with over the years. But, in Angular 2, our components can use "emulated view encapsulation," which simulates Shadow DOM and effectively scopes our CSS to our components. At first, I thought this removed the need for BEM; but, the more I think about it, the more I think that BEM still has a place in Angular 2.
I'm very new to the BEM (BlocK Element Modifier) philosophy; but, the way I understand it, BEM uses special naming conventions to group related CSS constructs. So, for example, if I had to create a "Name Badge" widget, I might have the following CSS rules:
/* Default view. */
.name-badge {}
.name-badge__name {}
.name-badge__avatar {}
.name-badge__description {}
/* Small rendering variation. */
.name-badge--small {}
.name-badge--small .name-badge__name {}
.name-badge--small .name-badge__avatar {}
.name-badge--small .name-badge__description {}
Here, the double-underscore (aka, the "double under") "__" scopes an Element to a particular Block (aka, a widget or component). And the double-dash "--" modifies a Block (or an Element, I believe). It's easy to look at this and see why the problem of selector-collisions becomes greatly minimized - everything is explicitly scoped. This has the added benefit of creating a very flat selector tree which I think has some performance benefits as well.
But, this post isn't about the mechanics or the possible benefits of BEM - it's about the potential role of BEM in an Angular 2 application using Emulated View Encapsulation. So, let's look a small Angular 2 component:
// Import the core angular services.
import { Component } from "@angular/core";
@Component({
selector: "bn-widget",
styles: [
`
:host {
display: block ;
font-size: 18px ;
margin: 20px 0px 20px 0px ;
}
span.header,
span.content,
/* NOTICE: There is no ".aside" in the list of selectors. */
span.footer {
display: block ;
margin: 5px 0px 5px 0px ;
}
`
],
template:
`
<span class="header">This is my Header.</span>
<span class="content">This is my Content.</span>
<span class="aside">This is my Aside.</span>
<span class="footer">This is my Footer.</span>
`
})
export class WidgetComponent {
// ...
}
When Angular 2 compiles and renders this component, it takes the supplied CSS and HTML and it injects component-specific identifiers. So, the HTML that you wrote as:
<span class="header"> ... </span>
... gets compiled and rendered as:
<span _ngcontent-wyq-2 class="header"> ... </span>
And, the CSS selector that you write as:
span.header { ... }
... gets compiled and injected into the HTML HEAD tag as:
span.header**[ _ngcontent-wyq-2 ]** { ... }
As you can see, Angular 2 has augmented our code with an HTML Attribute and a CSS Attribute selector in order to scope our component's CSS to our component. This prevents the component's CSS from leaking out and affecting any view elements outside of the component.
Given this scoping of the CSS, you may think that BEM no longer has any place in Angular 2. But, take another look at the code above - notice that there is no CSS selector defined for "span.aside". And yet, there is a span[class="aside"] in the HTML View. So, is this a bug? And if so, is the bug in the HTML or in the CSS? And, how can you be sure that "aside" is not some external CSS module (ie, another BEM "Block")?
These questions are exactly the kind of questions that BEM is good at answering. So, let's take a look at what this Angular 2 component might look like with BEM applied. In this case, since the emulated view encapsulation is scoping our CSS to the view, I have chosen to omit the "Block" portion of the CSS selectors - I didn't think the extra verbosity added much value:
// Import the core angular services.
import { Component } from "@angular/core";
@Component({
selector: "bn-widget-bem",
styles: [
`
:host {
display: block ;
font-size: 18px ;
margin: 20px 0px 20px 0px ;
}
span.__header,
span.__content,
/* NOTICE: There is no ".aside" in the list of selectors. */
span.__footer {
display: block ;
margin: 5px 0px 5px 0px ;
}
`
],
template:
`
<span class="__header">This is my Header.</span>
<span class="__content">This is my Content.</span>
<span class="aside">This is my Aside.</span>
<span class="__footer">This is my Footer.</span>
`
})
export class WidgetBemComponent {
// ...
}
In this version, with my "modified" BEM notation, it becomes much more obvious that the "aside" is different - unrelated to the other block-scoped elements inside the current component.
NOTE: In BEM, you can apply the double-under notation to nested blocks using a technique known as "mixes" (ex, <span class="__aside aside">). But, for clarity, I have omitted this technique from the demo.
Now, you might argue that "aside" - itself - should be an Angular 2 component, not a BEM block; and, that it should have its own encapsulated styles so that we're not relying on global styles "leaking" into the current component. That would make sense if you knew (as the developer) what Aside was supposed to be. But, maybe you're not sure yet - maybe you don't have enough evidence yet to point you in the direction of the proper abstraction. So, you leave "aside" as vanilla HTML and CSS until you can discover the patterns of use (at which point you may refactor it into an Angular 2 component with its own encapsulated styles).
In this post, I'm not prescribing a solution - I'm posing a question. Right now, my mindset is that BEM still solves real problems; even in the context of Angular 2 components; even with emulated view encapsulation in use. For me, BEM still clarifies ownership in a world where global styles can be consumed by custom elements.
What do you think?
As a final aside (no pun intended), I wanted to share a quote from a Eric Bidelman over on HTML5Rocks:
Do the ::shadow pseudo-element and /deep/ combinator defeat the purpose of style encapsulation? Out of the box, Shadow DOM prevents accidental styling from outsiders but it never promises to be a bullet proof vest. Developers should be allowed to intentionally style inner parts of your Shadow tree...if they know what they're doing. Having more control is also good for flexibility, theming, and the re-usability of your elements.
I thought this was an interesting perspective - that a cohesive application should be able to explicitly break into the Shadow DOM of a component for the purposes of theming. I tend to agree.
And, from what I read about styling web components over on Smashing Magazine, it seems like the "emulated" version of view encapsulation may provide the best of both worlds - allowing both style scoping and easy theming at the same time.
Want to use code from this post? Check out the license.
Reader Comments
Ew, BEM means that your html is definitely not going to be semantic and is probably going to be very rigid in terms of what layouts it supports. Am I in crazy land where people keep digging out anti-patterns and saying "here's this great new thing I thought of?"
Kind of like how Angular 2 has thrown the composition approach of Angular 1 out the window and rigidly couples everything together. We're so smart! you can find everything because it's all together! Face palm.
@Amy,
Can you elaborate? I am not sure how BEM actually relates to semantics? I think BEM is more about "scope" than it is about semantics; but, I am new to it. For example, if you have one widget that has a <Header> element and can also contain content, that may contain other types of widgets that each have a <Header> element, they are all semantic, yes? But, the use of BEM on top of that helps scope one header element to a particular widget.
I'm curious to understand what you mean more clearly.
So it's not semantic for the widget to know it's in the header, and in fact this is at cross purposes to the Angular CSS philosophy, which essentially says your widget should always have the same styling no matter *where* it is. Which makes no sense in terms of any development I've ever done--it is always better to be able to change the style based on context than to stubbornly insist on your own style.
And then another thing that's definitely not semantic from your example (but may not be specific to your example) is that the idea of a "small" rendering version would be represented in your HTML markup. Yes, I know that years of bootstrap grid systems have accustomed us to the idea that that "must be ok", but it's poor practice even if it does come from Google.
@Amy,
I'm not sure what you think semantic means, and don't see the connection to this article.
@Amy,
Re: the header, I think maybe I mispoke. I wasn't saying that the widget knew that it was *in* a header - I was saying that it *had* a header. Imagine a widget that had a Header and some sort of body that could contain arbitrary content. And, imagine that the arbitrary content contains other widgets that also have Headers within them. I was saying that scoping CSS to a widget would prevent the top-level header styles from accidentally being applied to the descendant header.
Perhaps what you are saying is just outside my understand of CSS technology. As far as I understand, the only insight CSS has about how it is being used is something like a media-query. Other than that, I don't think there are any other mechanisms that somehow tell CSS where or how it is being used.
I mean, unless you're talking about giving contextual overrides into the CSS selectors, like:
div.widget
aside > div.widget
footer > div.widget
body.print div.widget
... is that what you mean?
Also, I don't think any of this precludes being able to override things. In BEM, they have the "--" syntax to provide variations. And, in Angular, you can use the various ":host()" and ":host-context()" to be able to define variations.
But, like I said, I know enough CSS to get things done, generally -- but wouldn't consider myself an expert in CSS by any means.
Personally, I'd still try not to use styling in the `@Component` decorator. I prefer working with CSS pre-processors (such as Sass, or Less), and AFAIK it's impossible to make `styles` to be treated as anything else than plain CSS (correct me if I'm wrong). It also allows me to keep styles in a cleaner, separated manner outside of the Angular component. Imagine you have a block in BEM which uses more than one Angular component under the hood. In this case you'd have _hypothetically_ multiple places to manage all styling for just one block.
As a side-note on the BEM example you showed-in my three years of experience with BEM it's the first time I see an example of the modifier for a parent element the way you showed it (not saying it's the wrong approach, though). I can suggest trying something slightly different, which IMHO will improve readability of the CSS and the template's code: https://gist.github.com/lukaszklis/e24a539a34163aed928d86c47b655ec0
Hey Ben,
Great article (again :-) )
I've been wrestling with this question as while. I like the structure BEM provides and I love the encapsulation Angular (Shadow DOM) provides.
I playing with the combination of global styles (which you probably always have to prevent duplication) and the encapsulated component styles.
I trying a combination of ITCSS for global styles and BEM for the components.
For example:
// global style: object.media.scss
.o-icon--gender {
width: 3rem;
height: 3rem;
}
// name-badge component styles
host(){
display: block;
}
.__avatar{}
.__name{}
.__description{}
------------------------------------------
// component:
------------------------------------------
name-badge
avatar[ class="__avatar o-icon--gender" ]
male-icon
h3 [class="__name"]
name
p [class="__description]
description
-------------------------------------------
This way you can see immediately which are global styles (with the prefix) or component styles (starting with __ )
But on the other hand.. Why wouldn't we just style the elements itself like:
host():{
display: block;
}
avatar{}
h3{}
p{}
It would cleanup the HTML even more:
------------------------------------------
// component:
------------------------------------------
name-badge
avatar [class="o-icon--gender]
gender-icon
h3
name
p
description
-------------------------------------------
Why use BEM with Angular2 at all? Would it not be better (cleaner) to run something like modular css? Now this is a purely personal thought but BEM clutters the html with loads of classes... just get rid of it ;)
Let Angular handle the components css..
https://angular.io/docs/ts/latest/guide/component-styles.html#!#view-encapsulation
http://thesassway.com/advanced/modular-css-an-example
@Lukasz,
Ah, you are right - looking back now at what I put in as an override-example, it doesn't make any sense :D I would do it the way you outline in your gist. I have gone back and updated my code. I'm not sure what I was thinking about when I wrote that.
@Markus,
I am not familiar with Modular CSS. So, I can't speak to any pros/cons vs. something like BEM. And, as far as using BEM in Angular 2 - that's exactly the question I am examining in this blog post. For me, I think using BEM, or a sort of modified version of it, still serves a purpose for differentiating between a CSS class that is applied to the current component vs. one that is inherited from its ancestors.
@Arjen,
I'm afraid I'm not familiar with ITCSS - but a quick Google search shows me it is another CSS framework. I just don't have too much experience with various frameworks. I've done vanilla CSS and bit of OOCSS and SMACSS. BEM is really the first one that I connected with on an emotional / gut level. So, it just happens to be the one that I think about.
As far as why not just style the elements themselves, I believe BEM avoids that simply to make the markup more flexible. Meaning, you could change from a DIV to an ASIDE to a SPAN and the CSS wouldn't change. To be honest, I am not sure how valuable that is. But, I like the class approach because it make think about the role of things more, and less worry about the actual mark-up that is being used to implement those roles. But really, I'm fairly new to BEM, so I can't really speak with much authority on it.
@Ben,
But anything that is using encapsulation will not be used anywhere else right?.. I would rather see a separation of structure. component css should not "leak out". If there is common css then it should be put in a separate file :)
This is something I've been a little on the fence about. I initially liked how you could keep simple class names within the component and keep it "safe" to just their, but as I write more complex apps of many components, I fall back to preferring my more meaningful names of classes.
As you mentioned early, I love a flat of selection tree as possible. I nest when i need to or its warranted, because it is a powerful feature, but is unnecessary, and has that performance implication. People often overlook that selectors go right to left. That is one of my pet peeves about SASS, while I love it, it often leads to people nesting just be cause they can. Yes, it matches the structure of your page and looks pretty, but the CSS generated from that is going to match all links in all divs before it looks to then see if that div is in another div that has the one class name you're using.
@Markus,
Yeah, here's where the "encapsulation" bit is a little fuzzy. The "emulated view encapsulation" doesn't allow "local" styles from within the component of leaking out into other components thanks to the attribute-name-spacing that gets applied. But, there's nothing that stops global styles from "leaking", so to speak, into the component from elsewhere. And, of course, there is the encapsulation-piercing "deep" operator:
>>> ... or... /deep/
... which explicitly defines styles that are *intended* to leak into other components.
From what I have read, this "deep" operator was actually part of the Shadow DOM specification, but has since been deprecated.
One suggestion is to put all the reusable styles into a file and then include that file(s) into the components so that you don't have to have any leaking. But, from what I have read, that's not really well supported; and even in cases that it's kind of supported, it makes too many HTTP requests. Here's where I read a bit about this:
https://www.smashingmagazine.com/2016/12/styling-web-components-using-a-shared-style-sheet/
So, it seems that while we can leverage separate files philosophically, from a practical standpoint, the solution isn't quite there yet. At least, that's how I read it -- I could be misinterpreting.
@John,
Is there a particular CSS framework / methodology that you like? I'm fairly new to BEM, but I see people have been talking up "Modular CSS" - which I know nothing about.
I also often forget that CSS selectors are evaluated right-to-left. I think I first read that somewhere in the context of jQuery and the Sizzle library. I remember reading some article where John Resig argued that if you add a class-selector it can be faster even when it seems counter-intuitive. Meh, my CSS skills are getting old.
Interesting read. I've been taken off of CF (unfortunately), but dable in .net now as a part of my job. It's nice to have these articles about technologies that can be used with other server-side languages, so I can still use some of these techniques with my .net coding I am required to do for work. Great job on the article. I look forward to reading it in more depth after work and seeing if I can incorporate a test and some of the features into some of my code.
@Ben,
I don't really have a formal methodology that I follow. I probably could use a more formal style guide. From doing a quick look at Modular CSS, I feel like I'm somewhere between that and BEM.
I love using SASS, but I keep in mind my end CSS. I try to do meaningful names, so that you can tell between a more generic class vs one that will be specific to some purpose. I try to error on the side of flatter structure, but nest when it has a purpose: because I do explicitly want to style this element's child or this is the base style, but it will change when it is within an element of this class. I love leveraging the power of the selector to do cool things, which can give you some pretty complex selectors, but they're complex for a purpose.
@John,
"I try to do meaningful names" .... the one that gives me nightmares and PTSD is "label" from Twitter Bootstrap. The fact that they decided to give such a generic class name like "label" a global style is .... just ... beyond me. Think about how many widgets could have something that is called "label". Why on earth would they make that a global style?? <shakes fist>
The most complex structure I've seen recently is sibling selectors. Like, the state of a label NEXT TO a focused input. Something like:
input:focus + label { ... }
The problem that I have with getting too crazy is that it sometimes dictates the structure of the HTML. For example, this above selector only works if the label comes AFTER the input. If you try to move the label before the input, there is no CSS update you can make to get the same effect; so, I see people putting label after the input, even though, I think it makes for less "natural" HTML.
Of course, it might also just be my relatively old CSS mental model - maybe I'm just not comfortable enough with newer / more advanced CSS stuff.
@Alice,
Thanks for stopping by. I've heard good things about .net, and I know a lot of people in the Angular world use it. Hope you're enjoying it :D
@Ben,
Oh Bootstrap... it gives some great things, and then other times I scratch my head, or when I'm writing stuff the first thing I wonder is if I'm going to accidentally add one of their classes with a name I want to use (not a global .label mind you, but just other surprises).
I don't like CSS to dictate my HTML, but it can work in your favor for some styling when it's for your own specific needs, since it's your HTML structure. Sometimes, otherwise you have to do some tricky hacks just to keep it 'generic'. But then again, there's always that balancing act between generalizing things for reuse vs making for a single use case to make it easier.
Some of my more complex usually deals with children or direct children as opposed to adjacent, but it's all about the problem I'm trying to solve. I've found myself more often trying to see what I can accomplish in CSS alone, that I would otherwise normal default to using some extra HTML or some JS to accomplish.
@John,
Doing stuff in CSS alone, vs. doing stuff in the app logic is a funny line to walk, right? I often feel that when I run into things like ":hover". I can do a *lot* with a :hover pseudo-selector. But, then I start to wonder if I should be moving some of that into mouseover / mouseout logic that changes my component in some way.
Same with things like ::after / ::before for injecting arbitrary content into a container. CSS gets me pretty far; but, then, moving that content into actual component markup can take you even farther.
... constantly trying to figure out when to use what and trying to not fret too much over inconsistency (ie, which is X in the CSS, but Y is in the component). Probably never a "right" approach anyway ;P
@Ben,
Yeah, right now it's more in the realm of an "area I like to play in", testing what I *can* do to see what I *should* do in the future. Especially when working with components, there are ways that feel cleaner to do many of those things (at least at this time).
That division of X and Y always gets to me too. There will likely never be a "right" way, but there will always be loads of claims to the "right" way, as is the way of programming. ;-)
@Ben,
"For me, I think using BEM, or a sort of modified version of it, still serves a purpose for differentiating between a CSS class that is applied to the current component vs. one that is inherited from its ancestors."
But if you are struggling with inheritance you are doing Web Components wrong.
BEM stands for Block Element Modifier.
In Angular 2, if you implement Web Components correctly all "Blocks" and most "Elements" are in their own isolated scopes, so they wont be Inheriting anything anyway.
Most component SASS files will have a few (very few) HTML elements or web components, with styles. Then you may have some modifiers for each based on attribute binding or internal state.
Web Component encapsulated DOM already achieves everything BEM name spacing does out of the box.
@Eamonn,
I would only argue that not everything rises to the level of "component". And, not all "patterns" are necessarily obvious as components right away. One of the worst things you can do in programming is create the "wrong abstraction" as you pay a much higher cost in the future for maintenance. As such, it is often times better to see something duplicated several times in order to see the true pattern emerge. In that period of exploration and experimentation, I think it is perfectly natural to lean on shared styles before you jump immediately to encapsulation.
For example, imagine I wanted to make a special kind of Strong-tag styling:
```
<strong class="awesome">Woot
```
Sure, I could immediately try to break this out into its own "awesome-label" component. But, do I really have enough evidence yet to indicate that it should be broken out like that? It's hard to say one way or the other. So, I might as well leave it as it is, use it in a couple of places, see the patterns that emerge, and then refactor.
So, some things definitely merit being turned into components right away because we can clearly see the way they are going to be used. But, some things are not so clear cut.