Targeting BEM-formatted CSS selectors with Sass - css

I'm trying to slightly modify the styles of a block formatting plugin in Wordpress by overriding them in my own theme stylesheet. I use Sass but I'm new to it.
Pasting all of the selectors right out of Developer Tools works, but I know that's not the elegant/modular way to do it:
.an-accordion.an-accordion--v2.an-accordion.an-accordion--design-basic .an-accordion__heading {
color: gold
}
What's the right way to do this in Sass? I've tried something like this:
.an-accordion {
&--v2 {
&--design-basic {
&__heading {
color: gold;
}
}
}
}
but it doesn't work. I can tell I'm missing something about the way .an-accordion repeats.

You can use the power of local scoped string variables $something:... combined with the power of string interpolation #{...} and combine it with the current selector string & to create a compound selector for any combination of block, element, and modifier. Which I think is quite nice and readable:
.an-accordion {
$modifier-v2: #{&}--v2;
$modifier-design-basic: #{&}--design-basic;
$element-heading: #{&}__heading;
&#{$modifier-v2}#{$modifier-design-basic} {
#{$element-heading} {
color: gold;
}
}
}
which will result in:
.an-accordion.an-accordion--v2.an-accordion--design-basic .an-accordion__heading {
color: gold;
}
I tried it out on sassmeister.com
Note that I omitted the duplicated .an-accordion class in the selector; if this is important for you to increase the specifity you can insert it with #{&}.

BEM is about blocks, elements, and modifiers. Block scope is the biggest one, the element is some part inside the block and the modifier is optional and represents the status of your block-element. In Sass you can nest elements if they are parent and children and you don't need to repeat the parent element, in your stlesheet, if the beginning of your property is the same for both parent and child, but if the beginning is different you must repeat.
In a html like this:
<div class=" an-accordion an-accordion--v2 .an-accordion--design-basic .an-accordion__heading"></div>
You could have some scss code like this:
.an-accordion{
color: #000;
&__heading{
background-color: tomato;
}
&--v2{
font-weight: bold;
}
&--design-basic{
border: none;
}
}

Related

how to ignore class prefix in css file

I see some e.g. div/button style in Chrome console like this:
/* Chrome browser styles tab */
.ItemClass1-0-3-171.ItemClass2-0-3-173: {
background-color: "red"
}
How do I define a new style in CSS ignoring that class numbers? because it can be a different number for other div/button on the page..
/* CSS file */
.ItemClass1.ItemClass2 {
background-color: "blue"
}
You can use two attribute contains selectors for this.
[class*="ItemClass1"][class*="ItemClass2"] {
background-color: red;
}
<p class="ItemClass1-0-3-171 ItemClass2-0-3-173">foo</p>
But keep in mind that this will also select elements with the class fooItemClass2.
You can use an attribute selector with a starts-with value to pick up anything that starts with ItemClass.
Note: This solution assumes ItemClass is the first classname and doesn't account for whether the element has both classes. For these reasons Sven's answer might better suit your needs.
[class^='ItemClass'] {
background-color: blue;
padding: 4rem;
}
<div class="ItemClass1-0-3-171.ItemClass2-0-3-173"></div>

Should CSS utility classes have the "!important" keyword?

Let’s say I have a few utility classes:
.primary-text {
color: blue;
}
.danger-text {
color: red;
}
.display-400 {
width: 400px;
}
.max-width-100 {
max-width: 100%;
}
Do classes like this require the !important keyword?
If you have some other CSS files that are loaded before this file, you have three ways to force your CSS content to load:
add !important
add your CSS file link tag at the end of another link tag
find a more accurate selector for your tag like this:
span.primary-text {
color: blue;
}
This code has higher priority.
But if you don't use any other CSS file that contains these selectors with the same properties, you don’t need to use !important.

Sass: Using Ampersand '&' for body tag which has a specific class

Demo: https://codepen.io/moradxd/pen/WJpPyQ
Assume i have this HTML code:
<body class="boxed">
<div class="text-white">
Button
</div>
</dody>
I'm using this sass code as following:
.boxed {
// error with using "Ampersand"
body& {
}
}
But it results a compiling error which says:
Although the result i want is as following:
// This the result i want
body.boxed {
}
I know that i can use it like this, and it will result what i'm looking for:
// I know i can use this
body {
&.boxed {
}
}
But i want to separate the .boxed class code from inside the body css code for orgnization purpose.
So why this is not allowed although the similar code for element and it's parent is working for the following:
// Although this similar code for element and
// it's parent is working
.btn-featured {
.text-white & {
font-size: 30px;
}
}
In fact i hope to know why this not allowed!
Hello morad you need to use #at-root
.boxed {
#at-root body#{&} {
color: red;
}
}
codepen
You need to swap your selectors around for it to work like you've said.
body {
&.boxed {
background: red;
}
}
The issue is that the ampersand connects the previous selector to the current selector. So when you do something like this:
.boxed {
body & {
background: red;
}
}
It's trying to add 'nothing' to the body tag inside an element with the boxed class on it. The best way is to do it how you've already stated.
More info on referencing parent selectors.

CSS Modules: selecting child class through selectors

So,
I have appended a home class to body like so:
document.body.classList.add("home")
I want to select appContainer a child element of body class by doing
html body.home #appContainer { ..... }
This works without CSS Modules but was wondering how I can do it with CSS modules. Thanks
You need to use wrap the class that you want to be global into :global(). If your selector uses an element you must write it directly after the element with no space in between, like element:global(.class) which translates into element.class.
Therefore, in your case html body:global(.home) #appContainer is the answer.
For anyone else that comes across this issue, I am using postcss-preset-env and I had to do this:
Worked ✅
.toolTipTest :global .rc-tooltip-arrow {
color: blue;
}
This did not work ❌
.toolTipTest:global(.rc-tooltip-arrow) {
color: blue;
}
And neither did this ❌
.toolTipTest:global(.rc-tooltip-arrow) {
color: blue;
}
// Neither Did this
.toolTipTest {
&:global(.rc-tooltip-arrow) {
color: blue;
}
}

Namespacing class using a single element in LESS

The title is worded poorly, I think an example will better show what I'm trying to do.
I have a LESS file which is actually a CSS file that I grabbed from another site. I want to use these classes, but only when another class is also attached to the element.
An example of what I want to do, using valid LESS:
.external-style {
&.foo {
color: red;
}
&.bar{
color: blue;
}
}
But because there's probably over ten thousand rules I don't want to apply this to each rule individually.
What I'd like to be able to do is something like this:
.external-style {
& {
.foo {
color: red;
}
.bar {
color: blue;
}
}
}
Is this possible in LESS?
The advantage for me is that I can then do this:
<div class="external-style foo"></div>
instead of this:
<div class="external-style"><div class="foo"></div></div>
Which is important when I'm using the display property.

Resources