Less.js: Parent Selectors should have targets

Created on 13 Dec 2012  ·  118Comments  ·  Source: less/less.js

When creating a Parent Selector rule, everything before the & gets prepended to the outermost scope.

The feature could be greatly improved by allowing for "parent targeting", where you could apply the parent selector to a specific parent (in the case where you have deeply nested selectors).

The syntax for this feature is probably the biggest hindrance, so I would love to start a discussion on the possibilities.

feature request medium priority needs decision

Most helpful comment

I'm going to re-open this. We've seen variations on this idea (such as #1154), and I feel like there's a possibility of a solution.

That is, there are times when people have a logical stack of elements, but don't necessarily want to inherit the entire stack in their output.

Also, there are times when people want to inherit SOME of the stack, but not all of it.

I think we can all agree that we don't want messy syntax, but I'd like to see a variety of ideas. As we've explored here, targeting by name seems problematic. But we could also target by level:

.main-content {
    .button {
        .button-icon {
            background-image: @button-icon;
            &{2}:hover { background-image: @button-icon-hover; }
            &{2}.focus { background-image: @button-icon-focus; }
            .ie8 &{3} { background-image: @button-icon-ie8; }  // In this case, &{3} is the same as & because it goes to top
        }
    }
}

Basically, step "up" the inheritance tree to the level we want to inherit / append to the current selectors.

Or, something like, maybe "breaking" the inheritance and starting over, without having to move the class outside of the block. I dunno, like:

.grandparent {
  .parent {
    /.child {   // Ordered logically, but outputs cleaner CSS
      background: white;
    }
  }
  background: blue;
}

or, borrowing from the top example:

.grandparent {
  .parent {
    &{0}.child {   // inherit NONE of the parents
      background: white;
    }
  }
  background: blue;
}

Both outputting:

.grandparent {
  background: blue;
}
.child {
  background: white;
}

Thoughts?

All 118 comments

Example usage:

.main-content {
    .button {
        .button-icon {
            background-image: @button-icon;
            (.button):hover { background-image: @button-icon-hover; }
            (.button).focus { background-image: @button-icon-focus; }
            .ie8 (.main-content) { background-image: @button-icon-ie8; }
        }
    }
}

This may be a contrived example, but I have several real-world scenarios where such a syntax would be a life saver.

There's a variety of solutions in LESS to handle this. You can assign variables and use those as part of selector chains (new in 1.3.1 I think). The & is not really a parent selector in the CSS4 sense of the term. It literally just appends the inherited selectors to wherever you place them.

I usually just end up writing these code blocks like this:

.main-content {
    .ie8 & { background-image: @button-icon-ie8; }
    .button {
        &:hover { background-image: @button-icon-hover; }
        &.focus { background-image: @button-icon-focus; }
        .button-icon {
            background-image: @button-icon;
        }
    }
}

I do the same thing -- I duplicate my nested blocks with the overrides. In a real-world scenario, this results in an unmanageable amount of duplication.
FYI, in your example, the background-image needs to be applied to the .button-icon, so you'd have way more duplication.

Can you explain "assign variables and use those as part of selector chains"?

I'm probably wrong about that quote, so probably shouldn't try to explain it. That is, I haven't tried it personally.

So, it looks like what you want to do is modify a selector in the inherited chain. How would this work in this case?

.button {
.main-content {
    .button {
        .main-content {
            .button-icon {
                background-image: @button-icon;
                (.button):hover { background-image: @button-icon-hover; }
                (.button).focus { background-image: @button-icon-focus; }
                .ie8 (.main-content) { background-image: @button-icon-ie8; }
            }
        }
    }
}
}

It's messy CSS, but it's perfectly valid CSS.

That's a good example ... if the "parent target" matches multiple items, we'd want to choose just one. Choosing the "closest" one is my first reaction. But I think this would be an edge case.

So far, I've scratched down several attempts to invent a syntax that I like ... and the parenthesis so far is my favorite. It seems readable, and definitely catches your attention to highlight that this is no normal selector.

The only problem -- does this interfere with any existing CSS selector rules? Looking at the less parser, I see that a selector element can match /\([^()@]\)/ which means it's already being parsed correctly. I don't know if these parenthesis are valid.

I just realized that the parenthesis syntax looks almost exactly like a mixin definition ... with the exception of the class dot .. So maybe it's not a good idea. Feedback?

Your use case sounds like what is discussed in #965 .. what do you think?

Bearing in mind & is quite powerful already and we are bringing in :extend() in 1.4.0, it seems to me if the only thing missing is being able to call a mixin that adds selectors to your selector chain, then if we decide to add that functionality it should be as simple as possible.. the more we add to selectors, the more complicate less gets to learn and understand.

Yeah, I feel like, unfortunately, the logic gets a little messy, and like @agatronic, there may be other ways to fill in what you want in the future.

I agree. This over-complication is a result of a very complicated LESS structure, and time would probably be better spent in simplifying the LESS code instead of introducing a very complicated syntax.

I'm going to re-open this. We've seen variations on this idea (such as #1154), and I feel like there's a possibility of a solution.

That is, there are times when people have a logical stack of elements, but don't necessarily want to inherit the entire stack in their output.

Also, there are times when people want to inherit SOME of the stack, but not all of it.

I think we can all agree that we don't want messy syntax, but I'd like to see a variety of ideas. As we've explored here, targeting by name seems problematic. But we could also target by level:

.main-content {
    .button {
        .button-icon {
            background-image: @button-icon;
            &{2}:hover { background-image: @button-icon-hover; }
            &{2}.focus { background-image: @button-icon-focus; }
            .ie8 &{3} { background-image: @button-icon-ie8; }  // In this case, &{3} is the same as & because it goes to top
        }
    }
}

Basically, step "up" the inheritance tree to the level we want to inherit / append to the current selectors.

Or, something like, maybe "breaking" the inheritance and starting over, without having to move the class outside of the block. I dunno, like:

.grandparent {
  .parent {
    /.child {   // Ordered logically, but outputs cleaner CSS
      background: white;
    }
  }
  background: blue;
}

or, borrowing from the top example:

.grandparent {
  .parent {
    &{0}.child {   // inherit NONE of the parents
      background: white;
    }
  }
  background: blue;
}

Both outputting:

.grandparent {
  background: blue;
}
.child {
  background: white;
}

Thoughts?

I like it. I prefer my suggestion of &1 or @Soviut &(1). I think probably that is my current winner.

One thought.. Matthews solution doesn't allow you to have a mixin (with unknown inheritance) and just work within your current selectors (as in #1158). Is it too complicated to have &(2..n) .var &(1) ? or could you do & .var &(1) and the first & would be all the selectors not chosen already. alternatively should it default to putting the selector parts you don't use at the begining unless you use \ ?

One question: why &(1) and not &{1}? The latter is similar to inline variable syntax, whereas parentheses feels kind of like a math operation. Or a mixin, and it's not similar to either. It's more like a reference.

How would it break mixins? Not understanding that.

in a selector you have lots of pseudo classes using brackets and then you have variable substitution which borrows its syntax from the more verbose ~"@{var}" so for me normal brackets work better (especially if we should allow &(2..n)).. but having said that I don't have a strong preference between ( and {.. I'm more interested in the above.. how to step up 1 inheritance level verus breaking out of the current selector.

I'm surprised no one has mentioned how to target specific (immediate) parents when more than one are present. Here's some CSS which I'd like to arrive at:

/* regular css */
.foo, .bar {
    margin: 0 auto;
    line-height: 1.2;
}
.bar {
    line-height: 2;  /* override */
}

Unfortunately, I actually have to write it like that in LESS syntax as well. It'd be nice to target .bar within the first block. Perhaps something like:

/* LESS css */
.foo, .bar {
    margin: 0 auto;
    line-height: 1.2;

    &(2) {
        line-height: 2;
    }
}

Then the ancestors could be accessed with additional & combinators (I like the "targeting by level" idea):

/* LESS css */
.mommy {
    .foo, .bar {
        margin: 0 auto;
        line-height: 1.2;

        &(2) {
            line-height: 2;
        }

        /* same results for &&(1) */
        &&.mommyClass {
            color: #000;
        }
    }
}

@Smolations that came up in a different issue.

We thought that this might be done using guards on css rulsets combined with being able to test parent selectors in the guard condition. but good to bring it up.

Ah, I see what you mean. I just didn't see that issue thread because it's subject didn't catch my eye (you wouldn't happen to know where I could find that discussion, would you? =] ).

I think that any time you can marry various functionality to a unified syntax, you contribute to the intuitiveness of the final product. Even if I _hacked_ that functionality by using guards, and assuming this thread's idea is implemented in any of the suggested forms, achieving the desired result in my example would require using two separate syntax's. My suggestion tries to use the already-familiar syntax of the ampersand.

If my request (the ability to access a specific parent selector) isn't implemented in the future, I'd still like to see a solution to this thread's problem that uses the ampersand because it's already meaningful when accessing the LESS selector family tree. =]

here https://github.com/cloudhead/less.js/issues/1174

for every feature we like to make sure it has real value-add.. if it makes it easier to not refactor your css to a better structure then it doesn't have value add. if it makes your less harder to understand, thats not good and if it makes less harder to learn, that isn't good either. So, I mostly support the simple functionality in this post (but not enough to make it high priority - unfortunately there are things more important), but I am really wary about how complex it should become. If we combine 2 existing features that have been asked for and as a side product allow you to do what you want to do, without making selectors more complicated, I think thats better unless your use-cases are general enough that special functionality should be added because they have so much value-add.

lets see if we can re-write your last example...

.mommy {
   .foo, .bar {
       margin: 0 auto;
       line-height: 1.2;
   }
   .bar {
       line-height: 2;
   }
   .mommyClass {
       color: #000;
   }
}

and lets say we want to not have to write the .bar selector twice..

.mommy {
   .foo {
       margin: 0 auto;
       line-height: 1.2;
   }
   .bar:extend(.mommy .foo) {
       line-height: 2;
   }
   .mommyClass {
       color: #000;
   }
}

With these examples, its quite easy to understand what the result css will be.. with yours, it isn't, without learning some more..

What we need to convince us of the more complicated parts of this proposal are real use cases that can't be done any other way.

note - to implement the extend syntax options we will need to add a rule that the basics of the original request.. e.g. to escape the current nesting. We don't have to expose it, but we may as well. Do we really need specific parent targets?

For "escaping" I was thinking >| or something like that.. but yes, if we do need specific parent selectors &(0) to escape and the rest to target a specific nesting level seem to make some sense.

Initially, we considered a lot of syntactic approaches for this feature in Sass but ultimately decided to do something much simpler because the number of use cases was very large:

We are exposing & to SassScript as a comma separated list of space separated values. Then all of the manipulation of the selectors can be done in "user space" by simply manipulating selectors with Sass functions.

For example:

.foo, .bar > a { first: nth(&, 1); second-combinator: nth(nth(&,2),2) }

would generate:

.foo, .bar > a { first: ".foo"; second-combinator: ">"}

We will have this feature in Sass 3.3. I suspect we'll also ship with a few standard selector manipulation functions and then wait to see what the community develops before standardizing any more.

@chriseppstein, nice, I really like the flexibility of that approach. thank you

@jonschlinkert :) & is a feature that Less adopted from Sass; I think the dev community is best served if they both continue to work similarly.

@chriseppstein That's pretty cool, I like how & acts like a variable, on which you can perform funcitons. Makes this concept much easier to understand and read.

so presumably you assign a selector to a variable and then if you need that in a selector, you use the variable.. I like that it doesn't further complicate the selector logic

@a:~".c";
@{a}.b {
   & + & {
      .d(nth(&, 1));
   } 
}

take the above example.. We do this

  1. call .d
  2. evaluate @{a}.b
  3. work out & + &

This was partly justified because it used to be the case that calling mixins could pullute the current scope (something we no longer do).

Stages 1 and 2 are done in one step and 3 in another.

This is also done because until all the mixins have been called you cannot work out all the rulesets selectors.

I think to implement this you would have to have a concept of whether a ruleset was in a mixing-in state or not and at the point it leaves that state (or upon start of evaluation if its never been mixed in) we work out the selectors. I think that would work. Then we could get the paths at evaluation time.

The problem with that approach is extends.. These would have to be applied as you go along - again at the moment we process the AST for extends.

Another solution would be to continue to move towards the visitor model but either allow the visitors to run multiple times or else be cross dependent or something. hrmm.

I will have to think about this.

How would it interact with "return mixins" https://github.com/cloudhead/less.js/issues/73 feature?

What would be result of this operation:

.some-class {
  #unlock-this {
    .mixin() {
      first: nth(&, 1); 
    }
  }

  .foo, .bar > a { 
    #unlock-this();
    .mixin();
  }
}

It could be either this:

.some-class .foo,
.some-class .bar > a {
  first: #unlock-this;
}

or this:

.some-class .foo,
.some-class .bar > a {
  first: .some-class .foo;
}

I would expect for it to be useful, for & to equal the final selector, as
it does when used as part of selectors.

It looks like there's some powerful stuff here for referencing the inherited selector. Interesting.

I still also like the idea of a simple escape character, to just turn off block inheritance without much fanfare. CSS Crush uses a caret ^. Seems simple. How about it?

.this {
  .is {
    .deeply {
      .nested {
        ^.related-but-simpler-output {
          property: awesome;
        }
      }
    }
  }
}

// Outputs

.related-but-simpler-output {
  property: awesome;
}

The nth() stuff is cool too (for "choosing" the inherited selector), which I don't think would conflict with supporting this.

+1 for a way to reference/target a particular parent selector using some kind of dict syntax, like &2 or &{2} or &^^ or even &~2 (these last two suggestions are inspired by git).

+1 for a way to turn off block inheritance (as suggested just above by @matthewdl). This request is a dreamed one, as it allows to keep the output shorter, while at the same time taking advantage of scoped blocks (and variables) during the pre-processing.

I believe these two features (target parent selector & escape block inheritance) would be really helpful for those who are trying to write CSS following the BEM methodology.

We are exposing & to SassScript as a comma separated list of space separated values.

I don't agree that this is needed or even intuitive, and grabbing the combinator from a selector doesn't make sense as having any real-world value. In addition, sometimes the values are not space separated, so I'm not sure it solves this problem.

This is straightforward but doesn't have space separation.

.one {
  &.two {       & inherited value is == ".one"  
    &.three {   & inherited value is == ".one.two".
       &1:hover { }  // Outputs .one.two.three:hover { } &1 always == & 
       &2:active { }  // Outputs .one.two:active { }
    }
  }
}

Most people here have talked about referencing a particular block level, which seems quicker to decipher than space separated-ness and nested lists.

.parent1, .parent2 > .immediate-child {
  &.child1, .child2 {
    .box, .getting-complicated {
      // &2 is easy to understand here if it refers to "&.child1, .child2", but...
     nth(nth(nth(&,4),1),2) { } // or whatever it would be to breakdown lists of lists of lists
    }
  }
}

I get that it's powerful. But is it really easier? Or needed?

nth(nth(nth(&,4),1),2) { } // or whatever it would be to breakdown lists of lists of lists

Lol I get your point, but couldn't we show code examples to make this same point with virtually every feature of Less.js? Too much abstraction is a hindrance regardless which feature you're referring to. More to the point, in reality who would actually do that?

I get that it's powerful. But is it really easier? Or needed?

Perception is reality ;-) Valid point though. it would be good to hear from more developers like @maniqui

Why would it be lists of lists of lists? & represents the current context. So at most it's a list of lists. The first list represents the comma delimited selectors, the second list is just a sequence of simple selectors and combinators. (at least that's how it's going to work in Sass).

@chriseppstein Admittedly, I don't quite understand how you're using & with nth().

How would you refactor this:

.one {
  &.two, &.four {      
    &.three {  
       &1:hover { }  
       &2:active { }  // I want to return .one.two:active, .one.four:active { }
    }
  }
}

My larger point is that while nth() may be a great feature and I understand why it's in SASS, I'm not sure it directly addresses the issue raised in this thread, which is to reference a particular node of the inheritance chain. Instead, it sounds like it allows you to select a portion of the inherited value. ... Which MAY solve the problem and I may just not understand the simple usage of nth().

That looks like some brittle code you're making there.

Okay, so we're between brittle and complicated, although I'm not sure how it's brittle. You're just jumping up the blocks. Sure, you could move things out of your blocks, but you could also do so in a way that would break & itself.

It's possible that the syntax required in a solution is more complicated than refactoring to meet your needs, which is why @lukeapage and @scottrippey closed this in the first place.. The easiest-to-understand usage seems to be escaping inheritance, as in:

.one {
  .two, .four {      
    ^.three { }  // just output .three { }
  }
}

&1, &2 still seems pretty easy to conceptualize.

Okay, here's what I've wished I could do with the &:

  • & behaves like it does now
  • &^1 just moves out one level.
  • If you have lists of selectors, maybe reference separately? Like &^1[2] or something.

So, like this

.class {
   .child1, .child2 {
      .grandparent & { }
      .after-parent-insert &^1 { }
      .child-modifier&^1 { }
      .special-child-modifier&^1[2] { }
   }
}

Which would output this (if there was stuff at each level)

.class { }

.class .child1,
.class .child2 { }

.grandparent .class .child1,
.grandparent .class .child2 { }

.class .after-parent-insert .child1,
.class .after-parent-insert .child2 { }

.class .child-modifier.child1,
.class .child-modifier.child2 { }

.class .special-child-modifier.child2 { }

This is what I've wanted, at least. Idk if it covers all that has been discussed here, and maybe I'm wildly off the mark, but I thought I'd add my 2¢.

As for escaping, I like the simplicity of ^, if that really seems like it's important, although I don't really feel like it's necessary. I write my less to show me relationships. Nesting, then escaping something seems like it'd break that for me. If I need a variable in multiple scopes, I think I'd just wrap everything in & { } and declare variables there.

I agree about changing the default behaviour so that it only goes up one level but there would need to be a way to get all the way to the root in one step then. Perhaps negative values could start at the root and work their way down the hierarchy:

&^-1

Also, what about the edge case where you want to use & as a prefix? Should the syntax be a follows?

.grandparent {
    .parent {
        .child {
            & &^1 {        // <-- trying to refer to .parent
               color: blue;
            }
        }
    }
}

In that situation, where does the index start from? Originally, I was tempted to write &^2 but technically the first & indicates that I'm already dealing with the .child element so I only need to go up by 1.

I write my less to show me relationships. Nesting, then escaping something seems like it'd break that for me.

If I get you right, I definitely agree that nesting helps for showing relationships, most of the time helping to keep code short and concise while, hopefully, still readable [1]. On the other hand, "flat is better than nested" [2].

Although humans are not compilers, in the context of CSS compiling, it's easy to see that, when nesting too deep (more than 2 levels), (ab)using & compiles to longer selectors, thus, generating longer outputs.
Longer selectors means, usually, higher ( _unnecessary_ higher) specificity, which is, most of the times, undesirable. An unnecessary high specificity can easily fireback
Longer output means, of course, larger files. Again, we humans are not compilers, but we know our craftsmanship and we know the benefits of having code that compiles to shorter files.

We write our CSS in LESS to, well, write _less_ and maybe, to compile to _less_ amounts of code.
I'm guilty of, sometimes, trying to be too clever writing my LESS code. My future me usually hates me, as I usually end up shooting my future self in the foot.

Bottom line: I've found situations (particularly, while writing CSS following BEM/SUIT methodologies) where the & didn't help at all, and having a "immediate parent selector" (something like the proposed ^) would have really been helpful. Of course, I missed to document those situations and now I can't come up with a real-world example.

[1] Should code be short / concise?: http://stackoverflow.com/questions/952194/should-code-be-short-concise
[2] Zen of Python: http://www.python.org/dev/peps/pep-0020/

(To be honest, the whole thing looks to me like a sort of overengineering... Or maybe I just don't clearly understand possible use-cases).
Either way, isn't "numeric parent targeting" a little bit fragile? You see, you do some modifications in outer selectors (e.g. insert or remove a selector) and... boom! you have to correct (or at least you have to check if you have to correct) all those indices in the nested blocks. Just a thought...

+1 for this issue.

1575 is a duplicate that I created because github doesn't let you search for literal &'s, and I didn't know at the time about the "parent selector" and "appender" terminology. In fact, I'm going to throw the term 'ampersand' in here just in case someone else goes searching for it.

There is a fairly distilled use case / explanation of expectations there, if you want to read it.

I don't have strong opinions on the syntax for this feature. My expectation is that the current enclosing scope is the one that will most commonly be referenced, so the syntax for that should be terse. Options like &^ and &_n_ seem great to me.

Just want to add we don't want to change default behavior.. & was invented
primarily to add :hover etc. Without a space.

W.r.t. bem I work on a large and complex web application and we have a rule
to never use more than 2 selectors. We use mixins, extends and variables
heavily. I've never seen a need for a feature like this.. when I have
considered it, ive found it often makes more sense to refactor.

I'm interested in usecases that aren't just ... Arggg I don't want to
refactor this selector block..

I'm not against this feature in principle, but it does risk adding a
feature that makes people's less harder to follow and maintain.

As someone who has voted and argued in favor of having a "parent selector", I must admit that I'm struggling hard with finding a real-world, not-contrived use case. I may have jumped in the "would-be-nice-to-have feature" wagon.

Maybe a good exercise would be to begin with some output I would like to achieve and see what are the possible _existing_ Less inputs that would generate it. Then, evaluate stuff like "readability", "conciseness", "shortness", "flat vs nested", etc.

/* a block (.block) */
.myblock {
  prop: value;
}

/* an element (.block__element) */
.myblock__date {
  prop: value;
}

/* an element (.block__elementB) nested inside an element (.block__elementA)
.myblock__date .myblock__year {
  prop: value;
}

/* an element (.block__elementC) nested inside an element (.block__elementB) nested inside an element (.block__elementA) 
.myblock__date .myblock__year .myblock__digit {
  prop: value;
}

/* an element (.block__elementB) present in a particular modified context (.block__elementA--modified) */
.myblock__date--end .myblock__year {
  prop: value;
}

/* idem */
.myblock__date--end .myblock__digit {
  prop: value;
}

/* an element modified (.block__elementA--modifier) present in a particular context (.block__elementD) */
.myblock__header .myblock__date--end {
  prop: value;
}

/* an element (.block__elementC) nested in a particular modified context (.block__elementA--modifier) nested in a particular context (.block__elementD)  */
.myblock__header .myblock__date--end .myblock__year {
  prop: value;
}

I'll experiment. Maybe someone else would like to share how would they solve this?

@maniqui

Is there any special reason you want to prefix each class with ".myblock__"? If those elements are descendants of the root .myblock element then wouldn't just something like .myblock .date .year .digit be more consistent? And if they are not descendants then it's really not quite easy to understand what they have to do with ".myblock" class at all (and why they are named like this)...
Well, if you really need to prefix everything with the same prefix then:

.my-elements(~'.myblock__');

.my-elements(@-) {

    @{-}date {
        prop: value;

        @{-}year {
            prop: value;
            @{-}digit {
                prop: value;
            }
        }

        &--end { 
            @{-}year {
                prop: value;
            }

            @{-}digit {
                prop: value;
            }

            @{-}header & {
                prop: value;

                @{-}year {
                    prop: value;
                }
            }
        }
    }
}

Yes, this code looks at least strange, but this is only because the desired CSS is strange itself (so I actually wonder what the corresponding HTML would be).
But the main problem is that this example is more about "string processing" and not too much about "parent targeting"... And if it goes to "parent targeting" then.. mmm.. of course if all those element classes are nested within HTML in almost random order then you need some special mechanics (like "parent targeting") to convert that "random" structure into "not-so-random" LESS code... but is this what the LESS is for?


Update: did some minor changes in the code (collapsed --end). now it's even more weird, doh!

@seven-phases-max

That way of writing CSS is based on the naming conventions of methodologies like BEM and SUIT.

Regarding the way you wrote it, I use a similar approach.

& { // This creates a scope for variables.
  @block: myblock;

  .@{block} {
   prop: value;
  }

  .@{block}__date {
    prop: value;    
  }

  .@{block}__date .@{block}__year  {
    prop: value;
  }

  .@{block}__date .@{block}__year .@{block}__digit  {
    prop: value;
  }

  .@{block}__header .@{block}__date {
    prop: value;
  }

  .@{block}__header .@{block}__date .@{block}__year {
    prop: value;
  }  
}

(by the way, this code doesn't output the CSS I asked on my previous post, this is just an example to show another way of doing variable scoping in Less).
Agreed about this not being about having a parent selector.

Ah, I see now... So aren't all those BEM things meant for an automatic CSS generation from some templates (using their dedicated tools and scripts)? Well, never mind though, your example is enough for me to never ever even consider to write CSS (with LESS or without) by hands using these BEM conventions :)

@maniqui Continuing to play with your example... I think it's still an interesting exercise (with bloatprefix removed). In context of possible readability improvements I suspect that the key there will be just to try to collect the properties of similiar elements together. E.g. if .year, .digit and .date elements have similiar properties I would finish with something like this:

.date {
            &             {prop: value}
    .header &--end        {prop: value}

                   .year  {prop: value}
            &--end .year  {prop: value}
    .header &--end .year  {prop: value}

             .year .digit {prop: value}
            &--end .digit {prop: value}
}

(Using "compact" form just to save time for formatting, of course it won't be so "clean" with multiple properties per element). And this code seems to be very similiar to your last snippet. I.e. looks like nesting is definitely a foe in a situations like this - it may make code to be more compact and help to remove duplicates, but in the same time it's likely to kill readability unless the corressponding HTML nesting is very strict and structured.

Just going to throw a use case in here.

I have a list of items which can be expanded. Certain (nested) child selectors need different styling when the item is expanded.

E.g. When expanded a child selector needs to be displayed with additional content, etc.

I like to keep properties together where they differ between their expanded and default states for maintainability.

The problem I have is that I need to scope everything under .items to prevent it leaking to other parts and also if I want to do a tree structure.

The line .is-expanded& causes the selector items.is-expanded > .item > .item-secondary, when what I really want is .items > .item.is-expanded > .item-secondary.

.items {

  .item {

    // Margin/transition.
    & {
      transition: 100ms margin;
      .is-expanded& { .mvs; }
    }

    // Border radius.
    &:last-child { border-radius: 0 0 2px 2px; }
    &.is-expanded & { border-radius: 2px !important; }

    // Display.
    & > .item-secondary {
      display: none;
      .is-expanded& { display: block !important; }
    }

  }

}

For me the simplest Less syntax proposal was &1, &2, etc. with 1 being the first parent selector. Very natural, easy to grok.

So for this case I would simply use .is-expanded&2.

When you want something like .grandparent.black .parent .child
I was thinking something like this but I like having a number like &(3)
but then it becomes confusing because does it then append the rest of the current selector to that selector? or would it have to be like &(3).black & which would then apply the current selector we're in? or would it automatically inherit? Would there be a case where you wouldn't want it to automatically inherit? If we didn't want it to inherit then it wouldn't be written into the .parent .child selector, so it should inherit.

.grandparent {
  .parent {
    .child {
      background: white;

      &&&.black {
        background: black;
      }
    }
  }
}

&:1 is my favorite (:1 here behaving like an pseudo selector). The :extend() syntax follows standard CSS defined pseudo-selector syntax already, so I feel like a pseudo-like-syntax would fit very nicely. & is a thing, &:1 is a subset of that thing, just like a and a:hover. Why reinvent the wheel, y'know?

&(1) makes & seem like a function rather than a selector string, and I wouldn't want just &1 because if I have, for example, column styles that I choose to define as below, they'd have to be largely rewritten (probably requiring changes to my markup as well).

.col {
  &1 { /* stuff */ } // .col1
  &2 { /* stuff */ } // .col2
  &3 { /* stuff */ } // .col3
  ...
}

&:1 poses a much less significant threat to current stylesheets out there (I can't think of any reason :1 would be in a stylesheet, as it's not valid CSS and it can't be used as a JS hook or anything either) and will likely never interfere with any CSS pseudo selectors or elements, current or future. And, like I mentioned before, it's conceptually consistent with other features of Less.


By the way, &[1] (attribute selector syntax) would make sense too. And it kinda looks like JS array syntax, which is cool and fits the idea pretty nicely I think. Numeric attributes don't really work in HTML, so all the benefits of &:1 apply to &[1] also.

UPDATE: as @SomMeri mentioned, the ^ and ^^ conflict with shadow DOM selectors.

I kept exploring this more after this comment, and I ended up with something closer to what @scottrippey proposed, but I'm leaving this piece here as part of the conversation.


I think a lot of the feedback on brittleness and over-complication with numeric targeting has a lot of validity. We may, at most, need only two "modifiers" for inherited selectors, without having to number steps in the tree. Maybe the following is more intuitive?

  • < - extending the concept from CSS child selection, a parent modifier. When used with &, omits the immediate parent
  • ^ - "escapes" the current context. When used with &, inherits only the immediate parent

Every modifier alters the next & and next one only. Best demonstrated by example.

Example 1 - @vjpr's example

.items {

  .item {

    & > .item-secondary {
      display: none;
      < &.is-expanded ^ & { display: block !important; } 
    }

  }

}

// results in

.items .item > .item-secondary {
  display: none;
}
.items .item.is-expanded > .item-secondary {
  display: block !important;
}

_Note: in the above example, < &.is-expanded could be written <.is-expanded._

Example 2 - escaping the tree

Often people want to "structure" Less classes to match markup (to make their styles self-documenting), but don't want to output more nested CSS.

.books {
  float: left;
  ^ .book {
    display: inline-block;
  }
}
// results in
.books {
  float: left;
}
.book {
  display: inline-block;
}

Example 3 - Advanced Usage

.grandparent {
  .parent {
    .child {
      background: blue;
      < < &:hover < ^ & {  
        background: red;
      }
    }
  }
}

// results in
.grandparent .parent .child {
  background: blue;
}
.grandparent:hover .parent .child {

}

In the previous example, < < selects .grandparent { }, and < selects .parent { } while ^ escapes it from .grandparent { }. While this is a more advanced example and is maybe strange syntax at first, I feel that the use case itself is very "real world". As I'm writing styles, I'm describing the presentation of .child. Based on the hover state of the grandparent container, I want to alter this current .child class. It can be refactored into the following alternative, but can actually be harder to maintain because we're now describing .child in two different places, and duplicating classes:

.grandparent {
  .parent {
    .child {
      background: blue;
    }
  }
  &:hover {
    .parent {
      .child {
        background: red;
      }
    }
  }
}

So, while refactoring is _possible_, having targeting actually is a better DRY approach. ( @lukeapage - that may be the kind of example you're looking for, where targeting minimizes code more than refactoring can do.)

Of course, if a user wants to specify just which element exactly we're monitoring the hover state for, they can minimize brittleness with an escape and describe it exactly, for the same output:

.grandparent {
  .parent {
    .child {
      background: blue;
      ^ .grandparent:hover .parent .child {  // or you could write it ^ .grandparent:hover .parent ^ & if you wanted
        background: red;
      }
    }
  }
}

So, with 2 modifiers we could cover I think most if not all permutations of use cases without needing precise numerical targeting. Thoughts?


_EDIT: I found what I believe to be a better solution. See below._

I realized a simpler way to write the above might be this:

.grandparent {
  .parent {
    .child {
      background: blue;
      << &:hover ^^ & {  
        background: red;
      }
    }
  }
}

Basically, given the concatenation (&) chain [.grandfather][.parent][.child], and used with &, < removes the last one in the chain, and ^ "includes" elements in the chain starting from the end.

So:
<< & = [.grandfather][.parent][.child]
^^ & = [.grandfather][.parent][.child]

^ by itself would remove all: [.grandfather][.parent][.child]

Of course, it occurred to me that ^ is used as "begins with" as an attribute selector. So it may make sense to reserve that for consuming the chain from the front. As in ^ is the root of the current tree and ^^ could be the first element of the tree.

What's intuitive for other people?


_EDIT: I found what I believe to be a better solution. See below._

@matthew-dean The ^ and ^^ would conflict with with cat (^^) and hat (^) ShadowDOM selectors which are already supported by less.js #1801 .

@SomMeri Right, crap, forgot about that. What do you think of < & to refer to the "parent context for &"?


_EDIT: You don't have to answer that. See below._

I've been playing around more with syntax, selectors, and various examples, and I found myself back at a syntax that was close to what @scottrippey had to begin with. I had started writing up different examples with $ (CSS attribute "ends with" selector) instead of ^, but it just started to read like an ugly regex.

Eventually, with all the different selectors, I found that probably the easiest would be to not try to "navigate" up the tree and select it by number (which is not clear), or by symbol (which can still be hard to identify which parent you're talking about), but to actually specify which selector in the selector chain of & you really mean.

.grandparent {
  .parent {
    .child {
      background: blue;
      &(.grandparent):hover {  
        background: red;
      }
    }
  }
}

So, as has been suggested, & becomes like a function that can contain zero arguments to concatenate the whole list, or can take arguments to modify output of the list.

So, back to @vjpr's example:

.items {
  .item {
    & > .item-secondary {
      display: none;
      &(.item).is-expanded { display: block !important; } 
    }
  }
}

That approach has the benefit of reading very cleanly, certainly reads more cleanly than my previous examples with << and ^^. I'm including the entire & context but modifying one of the selectors in my selector chain. It's not at all ambiguous which parent element I'm referring to, or what class I want to attach to it. It's right there, written plainly.

The more I look at it, this is the most readable and least ambiguous and least fragile solution to various tokens and numerical referencing. It reads more like an :extend but for &.

I think @scottrippey had it right to begin with, although I prefer &(.selector) over just (.selector) to indicate a special case of & rather than something that looks like an anonymous function.

I like it. Has an intuitive feeling about it.

Sent from my iPhone

On Jul 15, 2014, at 5:14 PM, Matthew Dean [email protected] wrote:

I've been playing around more with syntax, selectors, and various examples, and I found myself back at a syntax that was close to what @scottrippey had to begin with. I had started writing up different examples with $ (CSS attribute "ends with" selector) instead of ^, but it just started to read like an ugly regex.

Eventually, with all the different selectors, I found that probably the easiest would be to not try to "navigate" up the tree and select it by number (which is not clear), or by symbol (which can still be hard to identify which parent you're talking about), but to actually specify which selector in the selector chain of & you really mean.

.grandparent {
.parent {
.child {
background: blue;
&(.grandparent):hover {
background: red;
}
}
}
}
So, as has been suggested, & becomes like a function that can contain zero arguments to concatenate the whole list, or can take arguments to modify output of the list.

So, back to @vjpr's example:

.items {
.item {
& > .item-secondary {
display: none;
&(.item).is-expanded { display: block !important; }
}
}
}
That approach has the benefit of reading very cleanly, certainly reads more cleanly than my previous examples with << and ^^. I'm including the entire&` context but modifying one of the selectors in my selector chain. It's not at all ambiguous which parent element I'm referring to, or what class I want to attach to it. It's right there, written plainly.

The more I look at it, this is the most readable and least ambiguous and least fragile solution to various tokens and numerical referencing. It reads more like an :extend but for &.

I think @scottrippey had it right to begin with, although I prefer &(.selector) over just (.selector) to indicate a special case of & rather than something that looks like an anonymous function.


Reply to this email directly or view it on GitHub.

Yeah, and apologies to @scottrippey for leading away from his syntax. I didn't even get what he meant about the unnecessary amount of duplication until I went through some other examples and later had my own experiences trying to solve the same thing. It really seems the least problematic way to target a parent for child styling in a DRY way.

Actually there's nothing wrong with using ^ and ^^ since they are no longer used for ShadowDOM selectors (replaced by /deep/ et all, see #2023 - so we'll have to remove #1801 stuff). There's always a concern about using any generic symbol within selector definition (and even & is not an exception :) since they _may_ choose whatever symbols they want for CSS eventually (though somewhere at the mailing-lists there were rumors to promiss to never do this anymore).

I mean we still can keep ^ and ^^ syntax in mind (unless someone actually is about to implement the feature right tomorrow).


I like the &(selector) idea too. The only problem I can see is (well, quite artificial example though):

ul {
    li {
        ul {
            li {
                &(ul):hover a {color: red} // which one?
            }
        }
    }
}

Ah, and we had such question already in https://github.com/less/less.js/issues/1075#issuecomment-11350357.

Thinking of the double ul example more I guess it's actually fine to not care about it too much. Since this is a syntactic sugar feature in general it's OK for it to not cover all possible cases.
So:

  • &(ul) above should point to the top level ul
  • and &(ul li ul) to point to the nested ul (or use ordinal CSS syntax, i.e. love to write messy CSS chains? - get your messy Less too :).

And another problem, currently the following code is valid and compiles as expected:

p:nth-child {
    &(2) {
        color: potato;
    }
}

Won't this be broken? (Notice that the value in parens also can be @{var} so we can't distinguish between two features by simple number/not-number condition).

Actually there's nothing wrong with using ^ and ^^ since they are actually no longer used for ShadowDOM selectors

That's good because that's still a use case that I'd still like to see supported: "escaping" the current context, which is different from parent targeting.

For the &(ul) example, I think it's ok that it refers to both. At that point, we don't need to save the developer from themselves and it keeps it straightforward. i.e. At some point, suggesting a refactor is ok. It's basically finding matches in the inheritance list. OR we treat it like extend, similar to your second example &(ul li ul), and you're forced to write out all the preceding selectors that match that inner selector: e.g. &(.grandparent .parent) to refer to .parent { } in .grandparent { .parent { } } instead of just &(.parent). It's more verbose, so I'm not sure I like it as much, but... it is technically less ambiguous.

In the nth-child example... it seems fairly made-up? Like, I get that it's technically possible today, but why would someone not specify which nth-child in the selector itself? Are there other examples with parentheses that might actually be used?

If there is some ambiguity and worry about breaking changes, a non-match of a selector in the inheritance chain could result in text output as written. I feel like there's some kind of behavior like that in Less already but can't think of what.

Thanks for feedback.

In the nth-child example... it seems fairly made-up?

Not quite, see this codepen (and it is actually a simplified extract from a real project I have under development right now). So we may consider this example as relying on something "undocumented" of course but we can't deny it is a compilable code for a while now and we're better to not break it.
(The code in the codepen is a bit more complicated than it could be and uses additional dirty hacks just to work #1973 around).

a non-match of a selector in the inheritance chain could result in text output as written.

Good spot.

Hmm.... This morning I also thought of:

.book:not {
  &(.harry-potter) { }
}

However, with an output fall-back, that would work. BUT the ambiguity _could_ make the code harder to immediately understand by a co-maintainer.

Instead, we might want something like [], as in:

.book {
  &.harry-potter {
    .title {
      result: awesome;
      &[.harry-potter]:hover { result: not-awesome; }
    }
  }
  &:not {
    &(.harry-potter) { foo: bar; }
  }
}

// results in

.book.harry-potter .title {
  result: awesome;
}
.book.harry-potter:hover .title {
  result: not-awesome;
}
book:not(.harry-potter) {
  foo: bar;
}

If () is potentially conflicting, and {} is likely confusing, that's the next best bracket. Of course, your selector can have brackets, but in this case, I can't think of a selector that would be passed to [] in CSS. Attribute selectors could refer to classes, but they would drop the period.

Or there's probably other ways to target that someone might find more readable, such as:

.title {
    result: awesome;
    &&(.harry-potter):hover { 
      result: not-awesome; 
    }
}

While && exists in code, the likelihood of &&(.selector) conflicting with code starts shrinking dramatically. Just another option.

_As a side note, I also notice from this example that parent targeting offers an output ordering benefit. The "override" can immediately follow the child declaration, which refactoring would not necessarily provide. Another +1 for this feature._

[...] would be confused with selector attributes same way, currently valid code (probably even more widely used than n-th or not):

a {
    &[title] { // is it selector attr or just custom parent tag?
        color: banana;
    }
}

@seven-phases-max Suggestions, then? () feels like the right syntax for both CSS and Less for sending a selector to a function, so maybe &&() is better? Or &+another symbol / wrapper?

Why not just treat & as an array and access it PHP array_slice() style?

&[_offset_]:

  • Gets the item at _offset_.
  • Same as &[_offset_, 1].

&[_offset_, _length_]:

  • Gets _length_ number of items starting from _offset_.
  • Negative _offset_ translates to "Whole Length - |_offset_|"
  • Negative _length_ translates to "Whole Length - |_length_| - |_offset_|" (I might be confusing this, but you got me.)
  • Zero value _offset_ means the beginning of the array.
  • Zero value _length_ means the rest of the array.
  • Note that "&[_offset_]" is not the same with "&[_offset_,]".

Or if you want to use functions it can become slice(&, offset[, length]), but it adds too much complexity IMO.

--- o ---

Take this as an example and mark the position:

#Frame {
    & .boxHolder1,
    & .boxHolder2 {
        & > div {
            & > .title > span {
                // Stuff
            }

            html.no-js & > .title > span {
                // [Position 1]
            }
        }
    }
}

Now for Position 1, we are at:

html.no-js #Frame .boxHolder1 > div, html.no-js #Frame .boxHolder2 > div

Which makes "&" an array like this:

    0               1           2                               3           4               5
[   "html.no-js",   "#Frame",   [".boxHolder1", ".boxHolder2"], "> div",    "> .title",     "> span"];

What we can do are these (Why am I giving you examples? It's just array slicing!):

&[1]        =>  #Frame
&[1, 1]     =>  #Frame
&[1, 2]     =>  #Frame .boxHolder1, #Frame .boxHolder2
&[1, 3]     =>  #Frame .boxHolder1 > div, #Frame .boxHolder2 > div
&[1,]       =>  #Frame .boxHolder1 > div > .title > span, #Frame .boxHolder2 > div > .title > span
&[, 1]      =>  .html.no-js #Frame
&[-2]       =>  > .title
&[-2,]      =>  > .title > span
&[-2, 1]    =>  > .title
&[, -3]     =>  html.no-js #Frame .boxHolder1, html.no-js #Frame .boxHolder2

Also, with the addition of a special value "&", we can get the offset and length of previous value of "&" in current path:

Note that we came to Position 1 by making this selection:

html.no-js & > .title > span

so when we were making that selection, "&" was referring to:

#Frame .boxHolder1 > div, #Frame .boxHolder2 > div

so when "&" is used as _offset_ or _length_, it translates to the position of that "&" in current path array.

// Find the value of "&" when we were selecting into Position 1
&[&, &] =>  &[1, 3] =>  #Frame .boxHolder1 > div, #Frame .boxHolder2 > div

// Find the value of "&" and what we prepended to it when we were selecting into Position 1 (This can be empty)
&[, &]  =>  &[, 4]  =>  html.no-js #Frame .boxHolder1 > div, html.no-js #Frame .boxHolder2 > div

// Find the value of "&" and what we appended to it when we were selecting into Position 1 (This can also be empty)
&[&,]   =>  &[1]    =>  #Frame .boxHolder1 > div > .title > span, #Frame .boxHolder2 > div > .title > span

Note how the offset and length can be swapped and modified for "&" when it becomes negative for more use cases:

// Find what we have prepended to "&" when we were selecting into Position 1
&[, -&] =>  &[, 1]  =>  html.no-js

// Find what we have appended to "&" when we were selecting into Position 1
&[-&,]  =>  &[-2,]  =>  > .title > span // I am dying to be able to select this!

Why I was dying to select that:

#Frame {
    & .boxHolder {
        & > div {
            & + & {
                //This selects "#Frame .boxHolder > div + #Frame .boxHolder > div" which is useless.

                margin-top: 10px;
            }

            & + &[-&,] {
                //This selects "#Frame .boxHolder > div + div" which is what we want.

                margin-top: 10px;
            }
        }
    }
}

--- o ---

Notes:

  • Usage of [] might get this confused with the attribute selector. Can attributes start with "&", "-" or a number? AFAIK, they can not, so the parser won't have any problems with it, but developers might. Usage of () might be more appropriate.
  • Some people wanted to have a way to select a single member of a union. For example selecting "h1" in this example:
.myBox {
    & h1,
    & h2 {
        & > span {
            /*
                To only select ".myBox h1 > span" here, you have these "tools":

                &[, &-1]    =>  .myBox
                &[-&,]      =>  > span

                So you can:
                &[, &-1] h1 &[-&,] {
                    //Selected ".myBox h1 > span"
                }

                Or just this for the sake of simplicity by sacrificing flexibility:
                &[, &-1] h1 > span {
                    //Selected ".myBox h1 > span"
                }
            */
        }
    }
}

I find no reason to break a union. Selectors should not be unioned unless they will share all properties. If they are to share some properties and still have differences, they should be defined like this:

.myBox {
    & h1,
    & h2 {
        //Shared properties
    }

    & h1 {
        //Properties unique to "& h1"
    }

    & h2 {
        //Properties unique to "& h2"
    }
}

--- o ---

This proposal both keeps the variable-like standing of "&" and extends it in a way that every web developer should expect it to behave. Not many languages support slicing/substringing like this (I guess Python does and Ruby has something like this), but this whole array[offset, length] syntax will be implemented for both PHP and JavaScript in later versions.

Introducing this type of array accessing/slicing might also come in handy in other cases:

@color: rgba(12, 12, 12, 0.5);

@red: red(@color);
@red: @color["red"];

@hue: hue(@color);
@hue: @color["hue"];

@color: rgba(red(@color), green(@color)*0.5, blue(@color)*0.2, alpha(@color));
@color["green"] *: 0.5;
@color["blue"] *: 0.2;

But of course, this is a discussion for another issue.

@matthew-dean

() feels like the right syntax for both CSS and Less for sending a selector to a function

Yes, indeed, parens look like more natural for CSS in this context. My +1 for this.

@AlicanC

The problems of the numeric indexing of parent selectors were discussed above. It is more powerful in theory but is it really useful in practice? (Reading and maintaining deep nested structures where you need to (literally!) count nesting levels does not seem to be something easy).

Either way this does not mean we (eventually) can't implement both models (e.g. "numeric" and "by identifier"). In that case I think the idea of &(offset, length) is nice.


@red: @color["red"];

Well, Less tries to keep CSS syntax and operators, so [] is definitely an alien operator for a list/array operations in Less. For this particular feature (i.e. "a generic way to get color channel value") a function like color-channel(@var, channel-id) would be more respectful to Less traditions (even if more verbose, after all I doubt anybody uses such color manipulations _that_ often).

@color["green"] *: 0.5;

Less is not imperative language so this feature is simply impossible in that "variable redifinition" form (for more info see for example links I collected there).

Array indexing with () is a little bit weird, but people with some Visual Basic background will agree that it's not THAT big of an annoyance. Since variables start with @ and functions with ., there will be less confusion than there would be in JavaScript if it used () for indexing.

Implementing arrays and array slicing just for this might be an overkill, but they could really help improve the language in other ways. Being able to properly count, index and slice function arguments would eliminate the need of having millions of overloads or colors, shadows, etc. being able to be indexed could eliminate the need of functions like color-channel(), red(), green(), etc.

So instead of implementing weird syntax and cluttering the language to solve this issue, these array functionalities can be implemented and then extended for this issue.

I guess we're quickly going out of topic here, so just a few brief remarks (and if you want to continue discussion further please create a new issue/feature request and/or see links below):

Array indexing with () is a little bit weird

The CSS syntax and semantics as a whole _are_ weird for anyone who is familiar with only C-like stuff (JavaScript, PHP etc.).

functions with .

There're no functions in Less (except those built-in and they do not start with .).

Being able to properly count, index and slice function arguments

You can count and index mixin arguments with List Functions (and for other array related features see corresponding threads, e.g. #1857, #2078, #1694, #1421 etc. etc.)

being able to be indexed could eliminate the need of functions like color-channel() , red() , green() , etc.

Yet again it's just a matter of "do we need this _so_ often?" question - the opposite argumentation also works: "a function like color-channel could eliminate the need for dedicated specialized syntax". ;)
The trick here is that built-in functions are super-easy to add to the Less compiler while some special operators are quite painful and bloating in implementation, so a reasonable Less development approach is:
"if some feature is uncertain to be really something must-have in the language core it's better to go as a built-in or even a plugin function".

And for other color functions discussion also see #1853.

@AlicanC I always tell people when talking about Less (or differentiating from SASS) is that CSS does not presume the author is a programmer and familiar with programming structures, and neither does Less. Array slicing and referencing by indexing (or numbering) is perhaps arguably more powerful, but it's also arguably more complex than referencing by selector. And, while I was originally advocating for it, I think like @seven-phases-max said, indexed referencing is probably overkill, and not needed in the majority of use cases.

That said, I also agree that we could probably support sending the & to the list functions we already support (or something like it), like extract(&, 3) for a more precise tool for those who would know how to use it.

Most of the time, we try to find the simplest method to apply to the majority of use cases, not the most powerful method to apply to all use cases, including edge cases or theoretical ones. IMHO, if you want a programming-like approach to styling with a CSS-like syntax, that's available in SASS. If you want a straightforward CSS-like approach that "extends" CSS (a kind of CSS+), that's Less, but that's a whole other topic.

@seven-phases-max
I will just stop replying to your comments about arrayly-stuff to not go further out of topic. I will create a separate issue (or comment on related issues) about those later.

The not-out-of-topic part is that if we really agree to implement selector indexing, I think it should be done in a way that we are familiar with and in a way that all parts of the language can benefit from it.

If we are to decide on something like &(1) to get the last child from a selector, can't we:

  1. Not do it in a way that makes us say "&(n) in Less just works like &[&.length-n] in JavaScript." but make it work like how indexing works in JavaScript,
  2. Just consider & as a special variable like @currentSelector and provide this whole indexing awesome to all variables,
  3. If found necessary, add:

    • Python/Ruby/PHP 6/ES 6 style @var(offset[, length]) slicing,

    • the &(&, &) special case,

    • named key indexing like @myBackground("image")

(Please comment on number 3 when I post them where they are relevant.)

I find number 2 relevant for this issue since if it is decided to be implemented, it becomes a prerequisite for this issue.

@matthew-dean
I understand. If project leaders think this way, then number 2 and 3 above are never to be implemented. Even number 1 is complicated if you think that way. It's not like I will try to challenge the whole philosophy of a language. What's decided is decided.

As a person who gets more excitement from slicing operator announcements then graduating from university, I will seek other languages.

Thanks for paying attention to my big chunks of text :)

So, I did not understand after this long conversation if we can hope to an extension of behaviour of '&' or not.......

In case this is useful, here is a real-world example:

.presentation > div {
  s {
    color: black;
    .dialogue & {
      color: white;
    }
  }
}

My expectation was for the innermost thing to expand to .presentation > div .dialogue s; however, it expanded to .dialogue .presentation > div s. I have a few more examples like this, and I think my only option is to drop them out of the nice nesting hierarchy I established (which is, as you can imagine, much larger than the snippet above), and I think it will make the LESS file harder to maintain.

Having not read half of that page, what would help me would be just a way to go “up” in hierarchy. I am not sure if there’s an alternative way to do this in LESS.

@mwichary

Well, there're a lot of examples like this above (in a general case when asking for "real-world" stuff it's being more about "some real-world" _use-cases_ that cannot be achieved _without_ the feature, not just some snippet where one would expect/prefer to use the proposed feature for just DRY and/or syntactic sugar purposes (not denying the importance of DRY/synsugar stuff - hence feature is considered to be useful and marked with "medium priority").

For your specific example it's too many methods to achieve the same w/o #1075 so it's barely adds something new to the topic (so to also answer to @ldetomi: In summary since it looks like this now has some agreement on the syntax, the feature is just waiting for its volunteer to implement it - no volunteer -> no feature).

---

As for your particular snippet the same is just too easy to achieve with mixins (and that's just one of several possible methods):

.presentation > div {
    .s(black);
    .dialogue  {.s(gray)}
    .monologue {.s(white)}

    .s(@color) {
        s {color: @color}
    }
}

Or even like this (to reduce amount of brackets):

.presentation > div {
    .-(           black);
    .-(dialogue,  white);
    .-(monologue, gray);
    .-(prologue,  red);
    .-(catalogue, blue);
    .-(ideologue, green);

    .-(@color) {s {color: @color}}
    .-(@prefix, @color) {
        .@{prefix} {.-(@color)}
    }
}

.As you can see the only difference there is just using .s or .- instead of & (and mixin name is independent of actual s element name).

@seven-phases-max, thanks. Those examples look pretty harrowing, though – painful to understand and maintain, especially considering that I kept color and one class only for simplicity (there are many more properties and classes there, and they don’t map 1:1).

Those examples look pretty harrowing

Do they? Or is it only because one does not use to use mixins ;) ? (I'm afraid same argument would apply to .dialogue &(1) when someone sees it for the first time...).

there are many more properties and classes there, and they don’t map 1:1

In that case _nesting_ is evil at all and some people (me for example) consider "flat" selectors more readable (after all the whole idea of putting parent element selector into its child is generally flawed, we don't have such elements teleportation in the HTML, do we?), e.g.:

.presentation > div {

    s {
        color:       black;
        font-weight: bold;
    }

    .dialogue s {
        color:      white;
        font-style: italic;
    }

    .monologue s {
        color:      gray;
        background: yellow;
    }
}

Yes, s is repeated there, but the readability is worth it (here once I wrote a brief on this for very similar snippet).

+1 for this feature. I almost started implement it until I find this issue. So lucky..
There are two points for the syntax:

  1. Find the target parent selector.
  2. How to insert before, add to, or append after the target.

My thoughts:

.table .row .cell {
    .editing &(.row) {}
    .editing &(^.row) {}
    .editing &(.row^) {}
}

complied to:

.table .row.editing .cell {}
.table .editing .row .cell {}
.table .row .editing .cell {}

This could be further developed if multiple match.

@supnate Please see this comment from @matthew-dean above (this post briefly defines the syntax that looks like the one we finally less or more agreed upon as being the least problematic).

My thoughts:

That looks like a move in wrong direction, the order of resulting selectors should be set by proper placing of the & itself (just like it works with plain & now) and not by positioning of yet another special symbol. E.g. (using the syntax of the comment) the resulting CSS in your example would be achieved with something like:

.table .row .cell {
    &(.row).editing  {} // -> .table .row.editing .cell  {}
    .editing &(.row) {} // -> .table .editing .row .cell {}
    &(.row) .editing {} // -> .table .row .editing .cell {}
}

@seven-phases-max Thanks for the reply, sorry that I didn't go through the previous discussion. However I think there's only little difference between mine and @matthew-dean 's. From my side, it's important to be intuitive that what makes difference (.editing), then where it should be.

Btw. thinking of the recent example (more about possible implementation details for edge-case situations), here's another point of problems, consider the following code:

.a .b .c .d .e {
    &(.d) .x &(.b) {}
}

Now the question is where .c and .e land in the resulting selector? Honestly I can't come with any reasonable placement (any result would seem to be pretty artificial). Though since the example is also artificial itself it does not look like a real problem as we can always end up with "in that case the result is undefined/unspecified" (e.g. "no multiple & referring to different elements" or something like that).

P.S. Ah, thinking of it more it looks like it's OK to result in some garbage like:

.a .b .c .d .a .x .b .c .d .e .e {} // or
.a .a .b .c .d .x .b .c .d .e .e {}

just like

.a .b .c .d .e {
    & .x & {}
}

compiles to

.a .b .c .d .e .x .a .b .c .d .e {}

See issue #2433 on a related topic about a "tree reference" syntax that could apply to local properties and properties of parent selectors, at which point it may be worth considering as a reference to parent selectors.

Example syntax:

.table .row .cell {
    ${.row}.editing  {}  // .table .row.editing .cell {}
}

Just another possibility.

I like this discussion!

I tried this but it doesn't work:

.feature {
  .feature-header:hover {
    background: red;
    @saved-context: &;
    .anotherTheme {
      .special-case-one,
      .special-case-two {
        & @{saved-context} {
          background: green;
        }
      }
    }
  }
}

/* expected output */
.feature .feature-header:hover {
  background: red;
}
.anotherTheme .special-case-one .feature .feature-header:hover,
.anotherTheme .special-case-two .feature .feature-header:hover {
  background: green;
}

/* actual output */
.feature .feature-header:hover {
  background: red;
}
.feature .feature-header:hover .anotherTheme .special-case-one &,
.feature .feature-header:hover .anotherTheme .special-case-two & {
  background: green;
}

To make this work you'll have to write:

.feature {
  .feature-header:hover {
    background: red;
    .anotherTheme .special-case-one &,
    .anotherTheme .special-case-two & {
      background: green;
    }
  }
}

Just my two cents.

wow wow wow such awesome so preprocessing

Guys, please see this dicussion on stackoverflow for some other thougths, especially the comment section:

http://stackoverflow.com/questions/32777856/less-css-parent-selector-walking-up-only-two-three-x-steps?noredirect=1#comment53532878_32777856

In it is suggested that this pattern might be desired:

    // The &(1) would be all of & minus the last one.
    // The $(0, 1) would be all of & minus all of &(1)
    &(1) > .bottom > &(0, 1) {
            bottom: 0;
    }

Personally I love simplicity of ctsstc proposal:

.grandparent {
  .parent {
    .child {
      background: white;

      &&&.black {
        background: black;
      }
    }
  }
}

&&&.black

It's just incompatible with current & behaviour. Additionally it's the same "manual (re)counting problem" mentioned earlier.

I've implemented something similar for SASS in inStyle, currently working on possible LESS/Stylus ports.

I've faced the same problem in PostCSS ecosystem, ending up writing a custom nested selectors getter plugin.

The syntax I've chosen to to climb up the nested selectors hierarchy is &, ^&, ^^& (...) instead of &, &&, &&&. It works nice and doesn't clash with any known selector/pseudo-selector.

Since this issue is still open...

So far, trying to "select and insert" from parent selectors ends of being terribly complex. The "where" becomes quickly ambiguous.

I think the only workable solution if this feature were to happen would need to be something similar to "capturing" and "replacing" that's used with regular expressions, where you define insertion points (in this example marked with ?) that you use to manipulate output later.

.grandparent? {
  .parent? {
    .child {
      background: blue;
      &(1, :hover) {     // all selectors, but replace 1st insertion point with ":hover"
        background: red;
      }
      &(2, .active) {     // all selectors, but replace 2nd insertion point with ".active"
        background: white;
      }
    }
  }
}

resulting in:

.grandparent .parent .child {
  background: blue;
}
.grandparent:hover .parent .child {
  background: red;
}
.grandparent .parent.active .child {
  background: white;
}

I'm not suggesting at all that that's the right syntax, but any kind of "targeting" that's been tried in this thread, either by index, or "scope level", or named selector, just isn't going to work without a precise target.

With insertion points, they have a left-to-right, top-to-bottom indexing order that is unambiguous. You can see where your thing will go, and can specify exactly what that thing is.

So any workable solution I believe will need to be some variation of this.

Whats going on which this one ? Anyprogress?

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@seven-phases-max FYI I changed stalebot to not mark an issue as stale if the issue is prioritized in some way.

I've already implemented this functionality locally.

Having worked with Sass since this issue was opened, I think this issue could possibly be combined with this Less proposal: https://github.com/less/less-meta/issues/16

In short, the proposal as it is at the moment is to make simple selectors (mixins) assignable to variables. I'm wondering if this shouldn't become valid (what Sass does):

.grandparent {
  @grandparent: &;
  .parent {
    .child {
      background: blue;
      @{grandparent}:hover & //the problem here is that `&` contains .grandparent also, ideas?
        background: red;
      }
    }
  }
}

@matthew-dean Are you saying this already exists in Sass? Stylus?

Like I said, I've basically implemented in a very similar functionality as the one you are proposing, only as a method call:

.grandparent {
       .something {
              closest('.grandparent', { &:hover { color:red: } })

      }
}

Spent over a week on it. Only problem now is tthat I would prefer to move to Stylus and dump Less altogether but now I would have to re-implement this functionality which is a hassle.

If you wonder why I don't submit the solution see here.

@matthew-dean Are you saying this already exists in Sass? Stylus?

This exists in Sass. I used it today:

.selector {
  $sel: &;
  @at-root :global(.platform-web) {
    @include responsive-margins($sel)
  }
}

@mixin responsive-margin($sel: '&') {
  #{$sel} {
    margin: $gap-s auto;

    + #{$sel} {
      padding-top: $gap-m;
    }

    @media (min-width: $size-phablet) {
      margin: $gap-l auto;
    }
  }
}

I needed to do this because my mixin had & + &, but that would have repeated my global CSS modules selector. So I had to "save" it, and then start over at the root with the global class and passing in the saved selector.

@matthew-dean are you saying you walk up to the root node (possible with less) and then use a saved selector to ? Your example is not complete as it appears to be taken from a real code source. Not sure what it is supposed to do and generate.

In my example, it could target any ancestor selector and then apply a rule from there, so in my example this

.grandparent {
       .something {
              closest('.grandparent', { &:hover { color:red: } })
      }
}

is the same as

.grandparent { &:hover { .something { color:red; } } }

@momomo

As far as figuring out the code-base, I'd be perfectly willing to tell you what I know / have learned about the code base if you want to take a look. Feel free to DM me on Twitter and I'll see what I can do to help - https://twitter.com/matthewdeaners

@momomo

Your example is not complete

I've updated my example.

the question was considered stupid, censored and closed down

Interesting. I'd be curious if someone used the word "stupid". Otherwise, it may be that someone couldn't see the value, and it can be difficult to advocate for your own use cases, especially for a general-purpose library.

To be fair, this whole thread was entirely theoretical until I needed this today and there would have been no other way to implement this in an abstracted way.

Also, to this:

Even the Sass guys said it was impossible.

Note that my use-case doesn't address "inserting" something like a :hover within a parent (inherited) selector. So your use-case may have very well been impossible in Sass, as it is in Less. So my example was just posted to illustrate that SOME use cases could be solved, but doesn't solve all, including some earlier ones I posted.

"So your use-case may have very well been impossible in Sass, as it is in Less"

No it wasn't impossible functionality wise. Sure, i had to add an extension method but it was considered impossible because Less or Sass couldn't look up because it's compiled and doesn't know about it's surroundings (html). It was considered "useless", not beneficial in any way. Well, your coding standards are not good enough I guess. I wasn't talking about walking up the dom tree, but the declared css/less/sass tree.

It's really not that complex to understand and my example is simple enough. Do you think it's difficult to understand?

Mine basically solves most, if not all of these issues.

The idea is that if your styling something, and need to walk up to some ancestor, say for hover or class add, or attribute, or whatever, that code should live in the same location, not have to be declared separately at the ancestor level and repeated all the way down to the same element that needs to be styled. That causes code fragmentation and in the end you have code to style the same elements in several locations.

I was advocating a for solution that allowed you to walk up by targeting a certain selector, or walk up N parents.

For instance

closest(3, { :hover { walks up three parents } }

also works

closest(1, { :hover { walk up one parent } } which is the same as &{ :hover { walk up one parent }}

closest('.someAncestorVeryHighUp', { :hover { walks up all the way up to that class } })

closest('[attribute=valueAlsoWorks]', { :hover { walks up all the way up to that attribute val match } })

If my code is to be made public, I want an apology first, because I am still frustrated, mostly because I kept hitting a wall like most here and the lack of this feature has been a major irritant for years because it always led to code getting more complex than it needed as the less/css code in a project grew.

Also, you just reopened a closed ticket. Why was it closed? Who the ** decided to close it? I hate Reddit, censorship and people who think they know better or have all of the answers.

So for making a question that originated six years ago, if not longer (other previous issues) that was closed down, finally addressed and solved by me without any help to understand that shit code, I want an apology. (<- "Keep dreaming. It's free." - spm).

Yes, sound just like Trump. I am fully aware, but then again, this is a rant.

@matthew-dean

Speaking of your example at https://github.com/less/less.js/issues/1075#issuecomment-396367364.
We even have a working plugin prototype for this: https://github.com/less/less.js/issues/2619#issuecomment-115006426
(also at https://github.com/seven-phases-max/less-plugin-reflections).
The only problem is that (even if the plugin works as you expect) there's some theoretical trickery from the language point of view: because of lazy-evaluation the value of @grandparent is not supposed to be "frozen" at the @grandparent: &; line (i.e. unless there's some specific syntax and/or some specific function to force its evaluation, the LE-principle dictates @{grandparent}:hover & to be expanded to the same &:hover & thing). I.e. we can't really save that value just like this: @grandparent: &; - more research needed.

And it's beatiful code:

https://imgur.com/a/RWCEYtB

All offtopic is cleared (for those interested I moved all the accusations with my comments here).

No it wasn't impossible functionality wise. Sure, i had to add an extension method but it was considered impossible because less couldn't look up because it's compiled and doesn't know about it's surroundings (html).

Then that's just someone completely misunderstanding your example. No, your example is simple and I understand what you're going for. It's a similar request to the "capture" and "replace" examples throughout this thread, you're just demonstrating using different syntax.

I wonder if the easiest isn't just:

  1. Allowing assigning & to a variable, or as a param to a function.
  2. Using the replace function.
  3. Adding an "at-root" type selector (^?)

Then you could just do:

.grandparent {
  .something {
    @grandparent-hover: replace(&, '.grandparent', '.grandparent:hover');
    ^ @{grandparent-hover} {
      color: red;
    }
  }
}

It's a few features in one, which is why this thread's discussion has been so complex, because when people raise examples, they're talking about several features for one use case.

Oops, I had this tab open on my browser and didn't see the vitriolic comments that happened in the interim.

Or build in replace? ...
🤔

.grandparent {
  .something {
    // & currently = .grandparent .something
    &:visited {
      color: yellow;
    }
    &(.grandparent; .grandparent:hover) {
      // & now = .grandparent:hover .something
      color: red;
      &:visited {
        color: blue;
      }
    }
  }
}

// output CSS
.grandparent .something:visited {
  color: yellow;
}
.grandparent:hover .something {
  color: red;
}
.grandparent:hover .something:visited {
  color: blue;
}

Spitballin'... seems more robust and clear than numerical index referencing, which can be ambiguous. Just selector search / replace built into a &() function.... one downside is we may want other "& alteration" extensions in the future?

This still requires the feature additions of:

  1. Passing selectors to functions.
  2. & as a function

Or build in replace? ...

Or explicitly declaring a placeholder within the selector of interest to not make potentially independent code to guess, know or hack specific element name or position (where not necessary).

For my taste the Sassish @at-root thing has typical hardcoded backdoor problem (of being easily broken):

.foo {
   @at-root .bar {color: red}
}

then in a far away galaxy:

.baz {
    @import "foo.less"; // oops
}

Or explicitly declaring a placeholder within the selector of interest to not make potentially independent code to guess, know or hack specific element name or position (where not necessary).

That example looks a bit confusing to me, although I think that's essentially "capture" and "replace"? I couldn't tell. Like this but only with explicit names? If that's the case, that's also reasonable to me.

For my taste the Sassish @at-root thing has typical hardcoded backdoor problem (of being easily broken):

I don't disagree, that's why I wrote a second example that doesn't require it. I don't think it's completely unnecessary, though. I just think it shouldn't be built into a potential solution here. I think "in-place alteration of & inheritance" is probably better (than something using an @at-root-like solution).

But, to be clear, as I mentioned above, capture/replace also seems appropriate.

@seven-phases-max One thing that might help this thread from just spinning its wheels forever and ever. Of these solutions: a) in-place alteration of &, b) capture/replace, c) other - Do you have a preference? Because part of the wheel-spinning is literally approach, not just syntax. If there was some consensus on approach, syntax might eventually gel here.

What's wrong with:

css  = '' +
'.body {  ' +

        '.main { ' +

            '.section {' +

                '.article {' +
                        // --- Call made here! ---

                        // Climb up to .main, and then add the selectors inside the body below
                        'closest(".main", {' +

                            '&:hover {' +
                                'background-color:red;' +   // Meaning that when you hover .main then .article will get background-color:red;
                            '}' +

                            '&.someclass {' +
                                'background-color:blue;' +   // Meaning that when .someclass is added to .main then .article will get background-color:blue;
                            '}' +

                        '})' +

                        // Do note that purpose of climbing up is to style article, nothing else.

                    '}' +

                '}' +
        '}' +
'}';

less.render( css ,function (error, output) {
        console.log('xxx');  console.log(output.css);
});

You can do a bunch of other stuff two.

@momomo

What's wrong with:

There's nothing particularly "wrong" with it. In fact, for a while now, Less has allowed functions to be called at practically any node. So you could likely do this as a Less @plugin. I wonder if that's not the best way to resolve this.

But, as far as syntax, IMO it's not a good fit in the Less language, because there are a few oddities / outliers in that construction, such as wrapping selectors in quotes, and calling a function at the root. Which you CAN do and define functions for that purpose. So if you want to solve this as a plugin, you can do it today, without any Less syntax changes whatsoever. It would be called literally with:

@plugin "closest";
.body {
  .main {
    .section {
      .article {
        // This will parse, as there's nothing invalid about this syntax
        closest(".main", {
          &:hover {
            background-color: red;
          }
          &.someclass {
            background-color: blue;
          }
        });
      }
    }
  }
}

The only tricky part: whatever selector is returned by closest() will still have all the same merging rules applied. I still think that a plugin could manage to give the desired behavior.

@matthew-dean

Exactly. Regarding the quotes not being ideal, I agree with you, however considering the complexity and time it has taken to resolve this issue using other means and the perceived downside of using quotes, I think it is basically a non issue, especially if we want to speed up the adding of such a functionality.

Surely it can and should ideally be done prior to the plugin method kicking in (performance being one reason), but this could showcase the desired functionality and pave the way as it is not even clear right now, what the desired functionality should do. There's also a lot of other issues, syntax wise with Less that are far worse.

Please contact me on opensource ATTT momomo DOTTT com and we can take it from there. I do not use Twitter unfortunately.

Per comment here, in discussion about how to "consume" a captured & selector, there was discussion about replacing & in-place, which then lead back to this issue. Since this is more in line, I thought I'd continue that here of what was being discussed, which was similar to what I mentioned at https://github.com/less/less.js/issues/1075#issuecomment-397697714

  • &() - return in-place &
  • &(arg) - replace & with arg and return
  • &(arg1, arg2) - replace arg1 in & with arg2 and return as new &

The tricky thing about that is: is it a modification of selector inheritance for all further children? Or is it just a modification _for that particular selector_? As in:

.component {
  &(.foo) .child { // selector is now .foo .child ?
    & .grandchild { // is & now .foo .child? Have we altered it permanently? I would presume yes
    }
  }
}

Also, this would prevent things like:

.something:not {
  &(.selector) {}
}

I'm not sure anyone would ever have reason to write that, but it's worth pointing out.

What about this approach:

^ - back one selector
^(x) - back x selectors
(sign: "^" is inspired from: http://docs.emmet.io/ )

#wrapper {
  .row {

   .text-side {
      h2{
        color : #000;
        }// h2
      .text-description{
          p{
            color : #333;
            mark{
              background-color: green;
              } // mark
            } // p
        } // .text-description  
    }

    ^ .image-side {
   // avoid 1 selectors: .row
      .image-block {
        ^(2) img{
        // avoid 2 selectors: .image-block .image-side
          width: 100%;
          } // img
        } // .image-block
      } // .image-side

  }// . row
} // #wrapper

Source: https://github.com/sass/sass/issues/2193

Was this page helpful?
0 / 5 - 0 ratings