Less: Extend a Previously Defined Nested Selector

LESS: Extend a previously defined nested selector

LESS currently does not support extending a "concatenated name" selectors (basically, .top &-first &-item is interpreted as three distinct selector elements and never found by extend looking for a single selector).

A workaround for your particular case:

.top {
&-first {
background: black;
}

&-second {
background: green;
}

&-first, &-second {
&-item {
color: white;
}
}
}

LESS: Extend a previously defined nested selector

LESS currently does not support extending a "concatenated name" selectors (basically, .top &-first &-item is interpreted as three distinct selector elements and never found by extend looking for a single selector).

A workaround for your particular case:

.top {
&-first {
background: black;
}

&-second {
background: green;
}

&-first, &-second {
&-item {
color: white;
}
}
}

Extend function doesn't work with nested selectors

Reason:

Quoting Less Website: emphasis is mine

Extend by default looks for exact match between selectors. It does matter whether selector uses leading star or not. It does not matter that two nth-expressions have the same meaning, they need to have to same form in order to be matched. The only exception are quotes in attribute selector, less knows they have the same meaning and matches them.

The following would work because the parameter to the extend function exactly matches another selector that is already defined. (Note: I have removed some properties to keep it simple.)

.gradientBg { background: rgb(200, 200, 200); }
.gr-box, .btn-1 { &:extend(.gradientBg); }

But the below would not work because the compiled selector path of the nested selector would be .parent .gradientBg and the parameter provided to the extend function is not an exact match.

.parent{
.gradientBg { background: rgb(200, 200, 200); }
}
.gr-box, .btn-1 { &:extend(.gradientBg); }

Less compiler would not even throw an error in the above scenario because it fails silently when there is no match.



Solution:

When using extend feature to extend nested selectors, an exactly matching selector (full selector path) should be provided (or) the all keyword should be used.

The below would work as the full matching selector path is provided to the extend function.

.parent{
.gradientBg { background: rgb(200, 200, 200); }
}
.gr-box, .btn-1 { &:extend(.parent .gradientBg); }

Or, depending on your needs even this would work because the all keyword is used.

.parent{
.gradientBg { background: rgb(200, 200, 200); }
}
.gr-box, .btn-1 { &:extend(.gradientBg all); }

But note how the output selector has the structure .parent .gr-box and .parent .btn-1. This is because Less replaces only the matched part of the selector with the new selector. In .parent .gradientBg (original selector), the matched part (provided as parameter to extend) is only .gradientBg and hence the output selector after extend would be .parent .gr-box.

Here is what the Less website says about extend "all": emphasis is mine

When you specify the all keyword last in an extend argument it tells Less to match that selector as part of another selector. The selector will be copied and the matched part of the selector only will then be replaced with the extend, making a new selector.

Another thing that should also be noted when using all keyword is that, Less would extend properties of all selectors that have a matching part. So, for example if we consider the below Less code.

.parent{
.gradientBg { background: rgb(200, 200, 200); }
}
.parent2{
.gradientBg{ color: red; }
}
.gr-box, .btn-1 { &:extend(.gradientBg all); }

the output would be as follows because .gradientBg selector is used both within .parent and .parent2.

.parent .gradientBg, .parent .gr-box, .parent .btn-1 {
background: #c8c8c8;
}
.parent2 .gradientBg, .parent2 .gr-box, .parent2 .btn-1 {
color: red;
}

extend parent and children nested selectors in less

The issue with parentChildProperty not being applied to the ul element can be fixed by also extending .parent-class .child-class in the ul styles

This will not resolve all the issues. It will still leave many unnecessary selectors in the generated CSS, but it will generate the required CSS to allow the page to display correctly.

Updated less:

.parent-class{
parentProperty: value;
.child-class { parentChildProperty: value; }
}

.child-class {
childProperty:value;
& > li{ childLiProperty:value; }
}

nav{
&:extend(.parent-class all);
ul{
&:extend(.child-class all);
&:extend(.parent-class .child-class);
}
}

Does LESS have an extend feature?

Yes, Less.js introduced extend in v1.4.0.

:extend()

Rather than implementing the at-rule (@extend) syntax used by SASS and Stylus, LESS implemented the pseudo-class syntax, which gives LESS's implementation the flexibility to be applied either directly to a selector itself, or inside a statement. So both of these will work:

.sidenav:extend(.nav) {...}

or

.sidenav {
&:extend(.nav);
...
}

Additionally, you can use the all directive to extend "nested" classes as well:

.sidenav:extend(.nav all){};

And you can add a comma-separated list of classes you wish to extend:

.global-nav {
&:extend(.navbar, .nav all, .navbar-fixed-top all, .navbar-inverse);
height: 70px;
}

When extending nested selectors you should notice the differences:

nested selectors .selector1 and selector2:

.selector1 {
property1: a;
.selector2 {
property2: b;
}
}

Now .selector3:extend(.selector1 .selector2){}; outputs:

.selector1 {
property1: a;
}
.selector1 .selector2,
.selector3 {
property2: b;
}

, .selector3:extend(.selector1 all){}; outputs:

.selector1,
.selector3 {
property1: a;
}
.selector1 .selector2,
.selector3 .selector2 {
property2: b;
}

,.selector3:extend(.selector2){}; outputs

.selector1 {
property1: a;
}
.selector1 .selector2 {
property2: b;
}

and finally .selector3:extend(.selector2 all){};:

.selector1 {
property1: a;
}
.selector1 .selector2,
.selector1 .selector3 {
property2: b;
}

Extending nested selectors in LESS

With the requirements you listed you will want to do something like this:

.table tr {
&:hover {
color: red;
.c1, .c2, .c3 {
color: green;
}
}
}

Think of & as anything you want to be at the same level as the parent level, whether it be a class or a pseudo class.

When you nest items usually it matches the HTML structure/hierarchy so deciding how and where to nest is fairly straightforward. You want to share as much as possible without going overboard in getting to "nested hell".

LESS: How to fully extend two classes when relationships between both of them exist

The all option will add the new selector to all the hierarchical relationships / nested selectors that already exist.

So, in the nested selector .parent .child when extending .parent with .newParent it will extend the nested selector with .newParent .child, because this relationship with .child has been defined. When extending .child with .newChild, the nested selector gets extended by .parent .newChild, because the relationship between .child and .parent exists. And you end up with this CSS:

.parent .child,
.newParent .child,
.parent .newChild {
color: white;
}

Note: This does not create selectors based on nested selectors created with the extension. Prior to the extension there is neither a definition of .newParent .child nor is there one of .parent .newChild, which could result in .newParent .newChild being created after the rule extension.

I am not completely sure how exacty you want your CSS output to look like, but you can always extend the "relationship"/combined/nested selector as well and avoid the all option, so that you don't generate all the hybrid selectors (like .parent .newChild and .newParent child):

.newParent {
&:extend(.parent);
}
.newChild {
&:extend(.child);
}
.newParent .newChild {
&:extend(.parent .child);
}

or in a nested form:

.newChild {
&:extend(.child);
}
.newParent {
&:extend(.parent);
.newChild {
&:extend(.parent .child);
}
}

and the CSS output will be:

.parent,
.newParent {
color: blue;
}
.child,
.newChild {
color: red;
}
.parent .child,
.newParent .newChild {
color: white;
}

If desired, you can now simply add all the nested selector combinationsh by adding the all option. This will get you all the permutations of the selectors, and acchieve this CSS:

.parent,
.newParent {
color: blue;
}
.child,
.newChild {
color: red;
}
.parent .child,
.parent .newChild,
.newParent .child,
.newParent .newChild {
color: white;
}

Less CSS :extend() doesn't work when an extra selector is attached to parent selector

There is a primary difference between the two extend modes that are given in the question and that is why one works while the other doesn't.

The first method (extending using parent selector (&)) is generally used by placing it within another selector block like:

.test{
a:a
}
#dummy{
&:extend(.test all);
}

This is called as Extend inside Ruleset and this generally does not require the curly braces {} at the end of the line. In fact, for this aproach, an error would be thrown only when the braces are provided.


The second method is called as Extend attached to a selector because here a new selector is being created by attaching an extra class to the parent selector and for this approach the curly braces are required mandatorily at the end. Compilation error would be thrown otherwise.

Including the curly braces like in the below snippet would cause the code to compile properly:

.test{
a:a
}

#dummy{
&.a_class:extend(.test all){};
}

In my opinion, curly braces are required for the 2nd method and not the 1st because without braces, the 2nd one is generally not how we write the rules in CSS - where every selector must have a body ({}) to contain its rules. The first selector already has its body because the extend itself is placed within it.



Related Topics



Leave a reply



Submit