5

I'm confused about double ampersand behaviour in LESS compiler.

Look:

.heading {
    &&--type-small {
        font-size: 15px;
    }
}

Will be compiled to:

.heading.heading--type-small {
      font-size: 15px;
}

And thats good.

But.

.wrapper {
    .heading {
        &&--type-small {
            font-size: 15px;
        }
    }
}

Will produce:

.wrapper .heading.wrapper .heading--type-small {
    font-size: 15px;
}

It looks weird. No?

Is there any advice to make this code works like:

.wrapper .heading.heading--type-small {
    font-size: 15px;
}

Thanks =)

Qantas 94 Heavy
  • 15,750
  • 31
  • 68
  • 83
Sergey Kamardin
  • 1,640
  • 1
  • 18
  • 22
  • 1
    AFAIK, there is no way to insert only part of the outer selector. – SLaks Oct 07 '13 at 14:59
  • when was the `&&` feature added to LESS and where is it documented? I haven't heard of this one. – zzzzBov Oct 07 '13 at 15:16
  • 1
    @zzzzBov an ampersand that is not the first character in the nested rule name (as well as the second ampersand) is basically a placeholder for the whole nested rule path - see my answer for more details. I believe it was introduced in [LESS 1.3.1](https://github.com/less/less.js/blob/master/CHANGELOG.md#131) with some other functionality of the ampersand. Documented under ["Advanced Usage of &"](http://lesscss.org/#-advanced-usage-of-&). – Martin Turjak Oct 07 '13 at 16:35
  • @MartinTurjak, I was confused that there was something new going on with `&&`, I've known about using `&`, and didn't make the connection that it's duplicating the selector. – zzzzBov Oct 07 '13 at 16:41
  • @zzzzBov yeah, in deed - in `&&` the two have two different functions ... the first one does the "joining" the second one acts as a placeholder for the "path" (and I agree it makes it extra confusing as here it is inserted in front of the rest of the selector =). I phrased this so well now that I have to add it to my answer ;-) – Martin Turjak Oct 07 '13 at 16:46

1 Answers1

8

What happens when you use an ampersand in a nested rule is that the default nested structure gets ignored in the output selector and the ampersand acts as a placeholder for the complete list of outer selectors and will just insert all the parent rules all the way to the top of the hierarchy (the "path" for all nesting levels above) ... no way around that.

So using the first one - & will just join (concatenate) the nested selector to the whole list of outer selectors (appearing as if it just added it to the parent selector) and act as a combinator - see "Nested rules" at lescss.org. But then when you use the second ampersand - your selector will end up including all outer rules once again - the .wrapper and all rules in between will be added twice now. So, the behavior is not really strange. See also my answer to this question: "How to refer to two previous elements / tags / classes with LESS?" and for some more functionality of & see also seven-phases-max's comments below. Or find some examples of & being used as a "path" placeholder under "Advanced Usage of &" at lescss.org.

And to your concrete example:

I am not completely sure why you want to repeat the word "header" in the class name .header--type-small, if you are using it in addition to a class called .header ... I would just use additional classes such as .type-small, like so:

.wrapper {
    //style for the wrapper
    .heading{
        //general style for the heading
        &.type-small {
           //style for the heading with class .type-small
           font-size: 15px;
        }
        &.type-large {
           //style for the heading with class .type-large ... and so on
        }
    }
}

with output CSS:

.wrapper .heading.type-small {
  font-size: 15px;
}

but if you really really need the whole long string with the repeated names for some particular reason ... you could just do something like this:

.wrapper {
    //style for the wrapper
    .heading {
        //general style for the heading
        &.heading--type{
            &-small {
               //style for the heading with class .type-small
               font-size: 15px;
            }
        }
    }
}

with output CSS:

.wrapper .heading.heading--type-small {
    font-size: 15px;
}
Community
  • 1
  • 1
Martin Turjak
  • 20,896
  • 5
  • 56
  • 76
  • 1
    "the two & just have very different functionality" - That's not quite correct, in fact the ampersand is expanded identically no matter how much times you use it. The ampersand always refers to "the complete list of outer selectors" and never to "the nearest parent only" (see [Parent-Selectors](https://github.com/less/less.js/wiki/Parent-Selectors), [Appender](https://github.com/SomMeri/less4j/wiki/Less-language-Nesting#appender)). Try the following examples to get into `&` expanding mechanics: 1. `a { b { c & {-:-}}}` 2. `a, b { c, d { & + & {-:-}}}` – seven-phases-max Oct 07 '13 at 19:09
  • The typical point of confusion usually is: "it's not so obvious that use of `&` also cancels the default prepending of all the outer selectors", so for basic `a { b { & c {-:-}}}` case it may look like the `&` is just a synonym for the nearest parent - but it's not. – seven-phases-max Oct 07 '13 at 19:21
  • And I'm afraid we have to say 1) and 2) (and "use of the the & - in the rule name it does 2 things") when it goes to "explain in detail". Otherwise we just push things towards wrong direction and when @gobwas will face something like `a { b { c & {-:-}}}` he is shocked again (see for example https://github.com/less/less.js/issues/1575) – seven-phases-max Oct 07 '13 at 19:47
  • @seven-phases-max you are right ... I definitely did not want to add any extra confusion ... so I tried to simplify the answer in a different way now (but that it still works for the more complex cases without going in too much detail). And I explicitly added now that it never applies just to the parent selector. What do you think now? – Martin Turjak Oct 07 '13 at 20:25
  • @seven-phases-max no i will not :P But thanks, now its clear =) – Sergey Kamardin Oct 08 '13 at 06:30
  • @MartinTurjak thanks, now I know my mistake ) Was thinking that & its like just "this" keyword. And your question about "I am not completely sure why you want to repeat the word "header" in the class name..." - it is about some kind of mixing SMACSS and BEM methodology ) header--type-small fits module modifier naming convention: {name of module}--{modifier key}-{value} =) – Sergey Kamardin Oct 08 '13 at 06:32