SASS and Specificity - css

I was refactoring code this afternoon and I realized something in peculiar that I was hoping someone could answer. I noticed the following...
&__main ul a {
....
};
equates too specificity (0, 0, 2) which makes sense. However....
&__main &__example a {
...
}
equates to specificity (0, 0, 1) which doesn't make sense bc doesn't the above snippet equate to...
.div__main .div__example a {
...
}
which should be specificty (0, 1, 1)? Becasuse I tried the snippet below and I get the specificity which I'm looking for...
&__main .div__example a {
....
}
Can someone please explain to me please because I'm confused as to why the compiler wouldn't take specificity into account when using an & to refer to a parent class? Because when you break it down to regular CSS it should, no? (edited)

Related

Add logic to CSS

I would like to use logic in my CSS. Styles need to be applied only if a product ID is higher than a specific number, e.g:
if (data-product-id > 25) {
padding: 50px;
}
Is this possible with CSS?
No, it isn't. Attribute selectors are based on simple string matching. There is no provision for less than / greater than numerical comparisons.
The closest you could get with CSS itself would be something like:
[data-product-id="25"],
[data-product-id="26"],
[data-product-id="27"],
/* etc */
This sort of thing is better handled with JS or server-side code which adds classes to elements.
You can apply some limited logic of the like you were asking about in CSS, but I advise against it. Nevertheless, the answer below is an answer, it's better to implement your logic in Javascript.
Assuming that you have a class called data-product for all your data products, you can create this rule:
.data-product {
padding: 50px;
}
.data-product[data-product-id="1"],
.data-product[data-product-id="2"],
.data-product[data-product-id="3"],
.data-product[data-product-id="4"],
.data-product[data-product-id="5"],
.data-product[data-product-id="6"],
.data-product[data-product-id="7"],
.data-product[data-product-id="8"],
.data-product[data-product-id="9"],
.data-product[data-product-id="10"],
.data-product[data-product-id="11"],
.data-product[data-product-id="12"],
.data-product[data-product-id="13"],
.data-product[data-product-id="14"],
.data-product[data-product-id="15"],
.data-product[data-product-id="16"],
.data-product[data-product-id="17"],
.data-product[data-product-id="18"],
.data-product[data-product-id="19"],
.data-product[data-product-id="20"],
.data-product[data-product-id="21"],
.data-product[data-product-id="22"],
.data-product[data-product-id="23"],
.data-product[data-product-id="24"],
.data-product[data-product-id="25"] {
padding: 25px;
}

Should I use a `&` when selecting direct children using `>`?

While writing less, I noticed that the following two snippets:
A.
.parent-el {
& > .direct-child { ... }
}
B.
.parent-el {
> .direct-child { ... }
}
will produce exactly the same css:
.parent-el > .direct-child {
...
}
I have several questions:
Are there any differences between A and B?
Is this intentional and by design?
Which one should I use, and why?
Are there any differences between A and B?
There will be no difference in the compiled CSS. The & in LESS is replaced with the outer selector in compiled CSS. So, A is really the same as doing:
.parent-el {
.parent-el > .direct-child { ... }
}
This, of course, is redundant and defeats the purpose of using LESS in the first place.
Is this intentional and by design?
The & really is not used as I believe it was intended in your example. A good example of using a & in LESS would be something like this:
.parent-el {
// define .parent-el styles
&__child {
// define .parent-el__child styles
}
}
In the above example, the & allows you to shorten the declaration of .parent-el__child.
Which one should I use, and why?
You should use B. In this case, using the & is redundant and unnecessary.
the use of the "&" is optional, when you insert the selector inside another becomes implicit that the intention is to start from your "parent".
Although I get less code when we do not use "&" I prefer to use it because the code is cleaner

My selector has maximum specificity but still isn't being applied. Why not?

I am trying to target an element with specific class inheritance. When using Chrome Dev tools, I see that the style is as specific as it gets and still it is crossed out and not being used.
I am using SASS and the following code:
.post-item, .post-type-project, .project, .type-project {
.post-text {
width: calc(35% - 15px);
}
}
I don't want to use the !important hack, because it breaks other CSS rules.
As a quick primer on CSS specificity in general, you can use the base-10 method to calculate the "weight" of a rule.
elements & pseudo-elements = 1
classes & pseudo-classes = 10
id's = 100
inline = 1000
So in this example, the first rule in your scenario is:
0 elements & pseudo-elements = 1 * 0 = 0
5 classes & pseudo-classes = 10 * 5 = 50
0 id's = 100 * 0 = 0
0 inline = 1000 * 0 = 0
TOTAL = 50
Calculating your rule, you've got a specificity of 30 (3 classes). In order to take precedence it needs to have a value greater than 50 (or exactly 50 and be declared after the other rule).
While you can add a single ID to the list, and it will bump up the specificity high enough to override the first rule, you are also limiting the rule to only one specific location. While you've avoided using the !important flag, you've introduced another issue.
Consider the you are trying to make sure a door is locked, but have this rule:
house door.exterior {
state: unlocked;
}
Alice isn't home, and you want to make sure Alice's door is locked, so you specify:
#alice door.exterior {
state: locked;
}
When Bob and Carol aren't home:
#carol door.exterior,
#bob door.exterior,
#alice door.exterior {
state: locked;
}
We've accomplished locking the doors, but we've both added a maintenance issue and obscured the reason for locking the doors. We'd need to add the rule for every person whose house we want to lock. We would be much better off by using a lower specificity rule that is semantically significant.
house.owner-away door.exterior {
state: locked;
}
CSS is often a balancing act between too specific and not specific enough. In this case the rule you're trying to override has 2 semantic classes that are overriding your rule, it specifies that it applies to posts in a post-list and only applied to post-text that follows a post-image. If these rules also apply to your rule, use them, if not, look around and see what other more general rules you can use.
Note While the base-10 method is an easy way of representing relative specificity, a single selector at each level will override any number of selectors at the next. For example, having 11 element selectors does not override 1 class, even though this method will calculate it at a higher value.
Try to verify that your code is placed lower in the compiled file. Otherwise try to strengthen the weight of your selector by an id (for example):
#parentContainer{
.post-item, .post-type-project, .project, .type-project {
.post-text {
width: calc(35% - 15px);
}
}
}
do it like this, increasing the nesting by adding one more item
body{
.post-item, .post-type-project, .project, .type-project {
.post-text {
width: calc(35% - 15px);
}
}
}
or
body .post-item, body .post-type-project, body .project, body .type-project {
.post-text {
width: calc(35% - 15px);
}
}
}

Transform properties/variables clashing in sass and overwriting eachother

I have the code at the bottom of the page in my sass. In my html I am targeting it as follows
class=" transform rotate-5 zoom-2 ......"
the problem is that the transform properties are clashing and aren't applying scale and rotate to the same div tag, I'm either getting one or the other.
I thought I could change this by using the #extend property and something like,
&.rotate-#{$i}
{#extend &.skew-#{$i}, &.zoom-#{$i}, &.zoom--#{$i};
transform: rotate($rotate#{deg});
}
but so far I've had no luck, if anyone can help it'd be greatly appreciated.
.transform {
overflow:hidden;
#for $i from 1 through 360 {
$rotate: $i;
$skew: $i;
$zoom: $i;
&.rotate-#{$i} {
transform: rotate($rotate#{deg});
}
&.skew-#{$i} {
transform: skew($skew#{deg});
}
&.zoom-#{$i} {
transform: scale($zoom,0);
}
&.zoom--#{$i} {
transform: scale(-$zoom,0);
}
}
}
I'm a big fan of utility classes that I thought easier to debug and to build with. But here it doesn't seem to be a good idea:
1) You are creating 360 * 4 selectors, yet you will probably only use 10-20 of them. It is a bit overkill.
2) As you describe it, the transform property values do not "combine" well, at least for what you are looking for. Even atomic CSS, which pushes very far the utility classes approach, specificies that combining different values of filter (for instance) needs creating a custom value / class.
I understand that it is not really a good answer, at least not the answer you were looking for, but if you want to keep your CSS only approach, I suggest you adopt a more "object-oriented" method for this issue and set your transform property directly in the css of your object, for instance with this mixin:
#mixin transform($rotate, $skew, $zoom) {
transform: rotate($rotate#{deg}) skew($skew#{deg}) scale($zoom,0) scale(-$zoom,0);
}

Can someone explain why the "nth-child"-selector has a higher priority than "hover"?

Yesterday I ran into this: One of my :hover-states stopped working. I found out that If I change this:
div.item {}
div.item:hover {}
div.item:nth-child(even) {}
to this:
div.item {}
div.item:nth-child(even) {}
div.item:hover {}
it works again.
I've created a demo on jsfiddle to show both cases.
Can somebody explain, why the :hover-state is overwritten by the selector?
The selectors have the same specificity, so the one that comes last takes priority.
To override the equal specificity you can chain the selectors
div.container_2 > div:nth-child(even):hover {
background: red;
}​

Resources