Let us say I have the following setup,
.block
.block__header
.block__content
.block__footer
Now I want to show an active state of this block. Let us say the block itself gets a green background and element 2 and 3 should get bold text. As I understand the philosophy of BEM, one should not use child selectors in order to keep the specificity as low as possible.
So is this really the way to do it?
.block.block--active
.block__header
.block__content.block__content--active
.block__footer.block__footer--active
Update: and how would I write that solution in SASS (very new to it)? This my setup so far... if I can use nested selectors, what is best practice here?
.block {
&--active {
}
&__header {
}
&__content {
// active modifier of content
&--active {
font-weight: bold;
}
// would be the same as
.block--active & {
font-weight: bold;
}
// but can i reference the active block somehow else in sass?
// & is a parent selector but i would need the parent of the parent here...
}
&__footer {
&--active {
}
}
}
The philosophy of BEM is about to keep blocks context free. The low specificity is just a good practice, not a golden rule. I give three valid solutions below.
If you're sure the block cannot be recursively included in itself, a simple cascade can be used:
.block--active {
background-color: green;
}
.block--active .block__element-2,
.block--active .block__element-3 {
font-weight: bold;
}
If the elements are directly located in the block, the children selector is valid:
.block--active {
background-color: green;
}
.block--active > .block__element-2,
.block--active > .block__element-3 {
font-weight: bold;
}
Or the flat solution (but not DRY):
.block--active {
background-color: green;
}
.block__element-2--active,
.block__element-3--active {
font-weight: bold;
}
With SCSS, there are several ways to write the first solution. Here is the one I use:
.block {
&--active {
background-color: green;
}
&--active &__element-2,
&--active &__element-3 {
font-weight: bold;
}
}
See another solution here.
Related
Assuming that I have the following HTML:
<div class="navigation__item">
<span class="navigation__item__icon"></span>
</div>
I want to apply some rules to an icon, when hovering an item, which can be described with the following CSS:
.navigation__item__icon {
color: black;
}
.navigation__item:hover .navigation__item__icon {
color: white;
}
I can achieve this using the following SCSS:
.navigation__item {
&:hover {
.navigation__item__icon { <-- here
color: white;
}
}
&__icon {
color: black;
}
}
Here, is there any way to avoid writing navigation__item? Something like "parent rule \ element".
I like Sass for logical structure so that if I want to rename the whole navigation block with elements, I can simply change navigation class name in the root, and everything is renamed. This case breaks this advantage.
Update: Actually, I have found a way to do this without using {} braces. & can be repeated more than once:
.navigation__item {
&:hover &__icon {
color: white;
}
&__icon {
color: black;
}
}
It is great, but it doesn't make much sense if I have many rules and rules for &:hover itself. The question is still open - is this possible to access sibling element definition from within the {} block.
In Stylus there is a Partial reference but I don't know anything similar in SASS. One solution could be using a variable for the parent selector:
.navigation__item {
$selector: &;
&:hover {
#{$selector}__icon {
color: white;
}
}
&__icon {
color: black;
}
}
Is usefull is you change navigation__item class for another.
EDIT: I had used a wrong example, it's OK now.
I recently started using BEM methodology and I'm confused about class inheritance, or rather - when we talk about BEM - some use cases of modifiers.
Let's look at this example, I have a simple element with few children
.b-content { width: 100%; }
.b-content__image { display: block; }
.b-content__date { font-size: 14px; }
.b-content__title { font-size: 30px; }
.b-content__text { font-size: 16px; }
Now I want to reuse my .b-content block with slightly different styles, so I use modifier .m-compact and now I'm not sure what approach is the right one (in BEM).
Whether I should append modifier class to all elements (which I find more valid according to documentation):
.b-content.m-compact { width: 50%; }
.b-content__image.m-compact { display: none; }
.b-content__date.m-compact { font-size: 12px; }
.b-content__title.m-compact { font-size: 24px; }
.b-content__text.m-compact { font-size: 14px; }
or should I append modifier only to the parent element:
.b-content.m-compact { width: 50%; }
.b-content.m-compact .b-content__image { display: none; }
.b-content.m-compact .b-content__date { font-size: 12px; }
.b-content.m-compact .b-content__title { font-size: 24px; }
.b-content.m-compact .b-content__text { font-size: 14px; }
I find this second method more logical, you know, since I'm writing cascading styles and in real world if I want to write e-mail to 10 people, I would write one and just add more recipients, but on the other hand I realize BEM is practically non-cascading approach.
So what should I use and why?
As you point out in the last lines of your question, when doing BEM you should avoid cascading so, as a corollary to this, you don't have to repeat the modifier where it isn't needed.
For your Modifier I'd write something like this:
.b-content--m-compact {
width: 50%;
}
In your example the Block and the Modifier set only the width, so this is a limited use case. In general it comes handy to use some kind of CSS preprocess to ease the code writing, e.g. in SASS:
.my-block
width: 100%
color: red
&--modifier
#extend .my-block
border: 1px solid red
which will results in:
.my-block, .my-block--modifier {
width: 100%;
color: red;
}
.my-block--modifier {
border: 1px solid red;
}
Modifier in BEM looks like this: .block_modName_modValue
You can add additional class - but it's not BEM. And also modifiers have a name and value.
Block in BEM set namespace
So you set default styles for blocks and all unique(that can be changed) place in css with modifiers. This way your styles don't messed up.
To do this you need:
Place common styles in block styles(.portfolio)
Place unique style(with modifiers) like this.(portfolio_theme_list)
In css you don't need to separate this(preprocessor will be needed).
.portfolio {
/* common styles */
&_theme_list {
/* modifiers style */
}
}
In BEM project-stub(template engine) it would look like this:
If you add modifier to block. Then compile(bemjson) to html.
{
block : 'portfolio',
mods : { theme : 'list' },
}
You will see this code
<div class="portfolio portfolio_theme_list">
</div>
You write elements correctly and understand that they need to be separated(without inheritence).
So now you need just define styles for your block with modifier(portfolio_theme_list).
You have 2 options:
1) If you have 2 different blocks - you need separate common and
unique styles. Unique styles place in styles with modified blocks.
2) If you have only 1 different block & you already have styles on
this blocks. Then you can override and don't separate common
styles(but it can cause pain if you add another modifier/instance)
Ok so I have a placeholder with a nested selector:
%block {
.title {
font-size:12px;
}
}
I want to extend it and ADD to the .title class:
.superblock {
#extend %block;
.title {
font-weight:bold;
}
}
The answer I WANT is this:
.superblock .title {
font-size: 12px;
font-weight: bold; }
However, the answer I get is this:
.superblock .title {
font-size: 12px; }
.superblock .title {
font-weight: bold; }
Am I doing something wrong or is this just how it works? To clarify I want to merge it. If I add something directly inside the .superblock and add like another .superblock2 which also extends %block they merge without any problems.
Sass has no functionality for "merging" duplicate selectors. You'll need to find another utility to do this after the CSS has been compiled.
The #extend directive isn't just a way to use classes in place of mixins (similar to LESS style mixin calls). Why #extend works the way it does becomes clear when you're extending normal classes instead of extend classes:
.block {
font-size:12px;
}
.foo {
#extend .block;
font-weight: bold;
}
Output:
.block, .foo {
font-size: 12px;
}
.foo {
font-weight: bold;
}
Using an extend class just suppresses the emission of the original class name.
Now that you've seen why #extend works the way it does, do you still want what #extend offers? If the answer is no, then you need to use a mixin:
#mixin block {
// styles
.title {
font-size: 12px;
#content;
}
}
.superblock {
#include block {
font-weight: bold;
}
}
Output:
.superblock .title {
font-size: 12px;
font-weight: bold;
}
This is pretty much it. SASS produces extended declarations separately.
And it has no functionality of grouping declarations by selector, it's not that smart.
But you need not worry that much about CSS cleanness. Modern web servers serve CSS gzipped, all duplication will be nicely compressed.
LESS can do that. However you would write:
.superblock {
.title {
.block .title;
}
}
Not sure if it works with #extend too.
You can use a tools, I used it to clean the css
https://github.com/addyosmani/grunt-uncss
"A grunt task for removing unused CSS from your projects with UnCSS."
Ok so I have a placeholder with a nested selector:
%block {
.title {
font-size:12px;
}
}
I want to extend it and ADD to the .title class:
.superblock {
#extend %block;
.title {
font-weight:bold;
}
}
The answer I WANT is this:
.superblock .title {
font-size: 12px;
font-weight: bold; }
However, the answer I get is this:
.superblock .title {
font-size: 12px; }
.superblock .title {
font-weight: bold; }
Am I doing something wrong or is this just how it works? To clarify I want to merge it. If I add something directly inside the .superblock and add like another .superblock2 which also extends %block they merge without any problems.
Sass has no functionality for "merging" duplicate selectors. You'll need to find another utility to do this after the CSS has been compiled.
The #extend directive isn't just a way to use classes in place of mixins (similar to LESS style mixin calls). Why #extend works the way it does becomes clear when you're extending normal classes instead of extend classes:
.block {
font-size:12px;
}
.foo {
#extend .block;
font-weight: bold;
}
Output:
.block, .foo {
font-size: 12px;
}
.foo {
font-weight: bold;
}
Using an extend class just suppresses the emission of the original class name.
Now that you've seen why #extend works the way it does, do you still want what #extend offers? If the answer is no, then you need to use a mixin:
#mixin block {
// styles
.title {
font-size: 12px;
#content;
}
}
.superblock {
#include block {
font-weight: bold;
}
}
Output:
.superblock .title {
font-size: 12px;
font-weight: bold;
}
This is pretty much it. SASS produces extended declarations separately.
And it has no functionality of grouping declarations by selector, it's not that smart.
But you need not worry that much about CSS cleanness. Modern web servers serve CSS gzipped, all duplication will be nicely compressed.
LESS can do that. However you would write:
.superblock {
.title {
.block .title;
}
}
Not sure if it works with #extend too.
You can use a tools, I used it to clean the css
https://github.com/addyosmani/grunt-uncss
"A grunt task for removing unused CSS from your projects with UnCSS."
Let say I have to repeat the color blue in my web page, what's most effective, time saving, and smart way of doing it?
Examples:
1. This example can mess up a little bit my css file.
#header, #content, #footer {
color: blue;
}
#header {
(other properties)
(other properties)
(other properties)
}
#content {
(other properties)
(other properties)
}
#footer {
(other properties)
(other properties)
(other properties)
}
2. With this example I'll end up modifying my html file more often.
css:
.default-color {
color: blue
}
#header {
(other properties)
(other properties)
(other properties)
}
#content {
(other properties)
(other properties)
}
#footer {
(other properties)
(other properties)
(other properties)
}
html:
<div id="header" class="default-color">
(content here)
</div>
<div id="content" class="default-color">
(content here)
</div>
<div id="footer" class="default-color">
(content here)
</div>
I'd prefer the first form. Adding a "default-color" class starts to move into the territory of adding style into your markup, and it's generally more flexible to keep them separate as much as possible. On the other hand, if you have a semantic class name you can add to all of those that makes sense, then that could work.
Otherwise, if you really do just want a "default" color, you can specify it on the html or div elements in your css, and just override it with more specific classes where you don't want elements to show up as the default color.
Consider authoring your stylesheets using SASS. This will allow you to manage duplication in a number of ways:
The simplest is to define a variable for your blue color and not worry about having to update multiple occurrences:
$color-corporate-base: #009
#header { color: $color-corporate-base; }
#content { color: $color-corporate-base; }
This will compile to regular CSS, putting the color values wherever they're referenced in your document:
#header { color: #009; }
#content { color: #009; }
You could use "mixins" to include rules into different selectors:
#mixin bold-color {
color: blue;
font-weight: bold;
}
#header {
#include bold-color;
background: black;
}
#content {
#include bold-color;
background: white;
}
This will compile to regular CSS, with the two included style rules in each selector. Of course, this creates duplication:
#header {
color: blue;
font-weight: bold;
background: black;
}
#content {
color: blue;
font-weight: bold;
background: white;
}
Even though that takes care of the duplication in your Sass stylesheet source making it easy to work with, the CSS output still has that duplication. (You could group the common styles with commas and put the different styles into their own selectors, but that's right back to your original question.)
There's a cool new feature of Sass that addresses this. It's called "selector inheritance". Check it out:
.bold-color {
color: blue;
font-weight: bold;
}
#header {
#extend .bold-color;
background: black;
}
#content {
#extend .bold-color;
background: white;
}
At a glance, this seems very similar to mixins, but look at the CSS output:
.bold-color, #header, #content {
color: blue;
font-weight: bold;
}
#header { background: black; }
#content { background: white; }
This lets you organize your selectors in your Sass stylesheet as you wish, and, you get the optimized output you want!
One way of doing it for standard compliant browsers would be to use !important.
Example:
div
{
color: blue !important;
}
I would prefer the first version, too. But remember that you can also use multiple classes within one element. So you could you something like:
.blue {
color: #00F;
}
.bold {
font-weight: bold;
}
<div class="blue bold">Text</div>