less / less.js

Less. The dynamic stylesheet language.
http://lesscss.org
Apache License 2.0
17.03k stars 3.4k forks source link

Extend - around media queries #1165

Closed lukeapage closed 11 years ago

lukeapage commented 11 years ago
  1. Extend should match things inside media queries
  2. if the extend is inside a media query it should a) only match other things inside media queries? b) only match things inside its own media query c) match everything inside its own media query plus everything not inside another media query, but it should bring that extend inside itself..
  3. is uncontested

2 I am thinking should maybe be (b) in the short term till we figure out if it should match anything else.

Soviut commented 11 years ago

Well what would CSS proper do? How would it react? The closer we are to that, the better. Since media queries are theoretically at the root level in regular CSS, that might give some guidance.

DesignByOnyx commented 11 years ago

EDIT: I have since put a lot more time in thought into the :extend feature and do not really stand by this comment any more. I would like to leave it here for the purpose of conversation, but this should not be a guideline or a suggestion.


I have just run into scenario number 2, and I would like to voice my opinion about it. Let me provide some example code:

.shared-style { width: 10px; height: 10px; } 

@media screen and (min-width: 30em) {
    .shared-style { padding: 5px; } 
}
@media screen and (max-width: 30em) {
    .shared-style { margin: 8px; }
}
@media screen and (min-width: 48em) {
    .some-element:extend(.shared-style) { }
    .some-other-element:extend(.shared-style) { }
}
@media screen and (min-width: 60em) {
    .shared-style { background-color: red; }
}
@media screen and (max-width: 60em) {
    .shared-style { background-color: green; } 
}

After spending much time thinking about this, I don't know that there is really one single elegant solution. The most elegant solution (in my opinion) is for the compiler to copy styles from other media queries and build a brand new style declaration so that the above might end up as:

.shared-style { width: 10px; height: 10px; } 

@media screen and (min-width: 30em) {
    .shared-style { padding: 5px; } 
}
@media screen and (max-width: 30em) {
    .shared-style { margin: 8px; }
}
@media screen and (min-width: 48em) {
    .some-element,
    .some-other-element { 
        width: 10px;
        height: 10px;
        padding: 5px;
        background-color: green;
    }
}
@media screen and (min-width: 60em) {
    .shared-style { background-color: red; }
}
@media screen and (max-width: 60em) {
    .shared-style { background-color: green; } 
}

The only way this works is for the compiler to be smart enough to know which styles to copy and which styles not to copy. The most realistic solution is to disallow the use of :extend within a media query in favor of using classic mixins.

dmi3y commented 11 years ago

Definitely I like:

c) match everything inside its own media query plus everything not inside another media query, but it should bring that extend inside itself..

I could miss something right now but it sounds logical. You would not commonly put something shared on mixed styles into particular media query, and as well you would not usually need shared styles from another media query set other than you working with. I am obviously missing something :/

DesignByOnyx commented 11 years ago

@dmi3y - you kind of touch on a point that I don't [currently] endorse anymore. I agree that extend within a media query should:

  1. Match everything within its own media query - which is a very "normal" thing to expect and should be considered implied moving forward IMO (for the sake of typing).
  2. Match everything outside of media queries, but should copy those styles into itself

Even though I agree, the interesting thing about number 2 is we are no longer "extending" but rather duplicating styles in the fashion of a plain ol' mixin. This is very important IMO because the whole idea of :extend was to minimize duplication. But for the time being, let's assume that this is how extend should work within media queries.

By the same token of grabbing everything "outside" of media queries, should we not grab everything in other currently matching media queries? Best shown by example:

.b { }  <-- These get copied

@media only screen and (min-width: 30em) {
    .b { }  // <-- Why shouldn't this get copied?  It applies to anything greater than 30em
}
@media only screen and (min-width: 48em) {
    .a:extend( .b ) { }
}
@media only screen and (max-width: 60em) {
    .b { }  <-- This hurts my head (notice the MAX-width)
}
matthew-dean commented 11 years ago

This one you kind of have to wring your brain through. Think of :extend in general usage as a replacement for referencing mixins (so, copying the entire contents of the mixin within your selector).

So, I guess what I'm saying is that I would kind of expect us to do whatever has the same effect as referencing the mixin. Media queries create isolated sets, so I think extends should get walled off. As in:

.b { }

@media only screen and (min-width: 48em) {
    .a:extend( .b ) { }
}

// output
.b {
}
@media only screen and (min-width: 48em) {
    .a {  // EDITED: contains properties of .b, effectively same as using a class as mixin
    }
}

Yes, @DesignByOnyx, it would be a copy, but it's not necessarily the SAME copy because of the different ways you can extend (could contain base properties only).

Illustrated more clearly:

.b {
}
.c {
}
.d {
}
@media only screen and (min-width: 48em) {
    .a:extend( .b, .c ) { }
}
@media only screen and (max-width: 60em) {
    .a:extend( .c, .d ) { }
}

Hopefully, I've made obvious in the above example that the root classes should not be modified. The "result" of .a is local to that media query.

UPDATE: If it's copying (not modifying the original block) it doesn't really need to include the original.... so the output of the above would end up being:

.b {
  color: blue;
}
.c {
  border-width: 1px;
}
.d {
  background: none;
}
@media only screen and (min-width: 48em) {
    .a{ 
       color: blue;
       border-width: 1px;
     }  
}
@media only screen and (max-width: 60em) {
    .a{ 
       border-width: 1px;
       background: none;
     }  
}
dmi3y commented 11 years ago

ops, sorry, overlooked, correct myself

c) match everything inside its own media query plus everything not inside another media query, but it should bring that extend inside itself.. and leave it, with peace, where they are.

matthew-dean commented 11 years ago

So, I guess, like @Soviut said, classes within a media query have a local scope; therefore the result of :extend must also. It cannot "escape" and modify classes of a broader or different media scope, because that's not what CSS would do.

matthew-dean commented 11 years ago

So I think @DesignByOnyx, @dmi3y - we're all suggesting the same behavior?

dmi3y commented 11 years ago

I like how it tastes, but not yet sure

.b { }  <-- Extended with .a
.c { } <-- Extended with .d

@media only screen and (min-width: 30em) {
    .c {}
    .b {}
}
@media only screen and (min-width: 48em) {
    .c {}
    .a:extend( .b ) { }
}
@media only screen and (max-width: 60em) {
    .c {} <-- Extended with .d too
    .b {}
    .d:extend( .c ) {}
}

Results to:

.a, .b {}
.d .c {}

@media only screen and (min-width: 30em) {
    .c {}
    .b {}
}
@media only screen and (min-width: 48em) {
    .c {}
    .a{ }
}
@media only screen and (max-width: 60em) {
    .d, .c {}
    .b {}
    .d {}
}
DesignByOnyx commented 11 years ago

@matthewdl - I totally agree with you and @Soviut with extend not "escaping" the scope of the media query. However, there is this issue of copying from outside the media query.

With the example you provided, I would expect a different result. Let me add a property to show you:

.b { padding: 5px }
@media only screen and (min-width: 48em) {
    .a:extend( .b ) { }
}
/*
.b { padding: 5px }
@media only screen and (min-width: 48em) {
    .a { padding: 5px }
}
*/

That's what I meant by copying. I ran into this issue in the real world where I wanted an element to inherit my grid styles ONLY beginning at min-width: 48em. Here is some real world code I tried to use with alpha extend:

.grid-wrap {
    letter-spacing: -0.31em !important;
    word-spacing: -0.43em !important;

    > * {
        letter-spacing: normal;
        word-spacing: normal;
        display: inline-block;
        vertical-align: top;
        .box-sizing(border-box);
    }
}

@media only screen and (min-width: 48em) {
    .artist-list:extend( .grid-wrap ) {
        > li { width: 33.3% }
    }
}

I personally would expect the .grid-wrap styles to be copied into the media query, the same as writing the following (which is what I ended up with in my real world code):

@media only screen and (min-width: 48em) {
    .artist-list {
        .grid-wrap;
        > li { width: 33.3% }
    }
}

Does anybody else think this is the way :extend should work? We would get the benefits of using the modifiers (deep, shallow, etc) so that I would really write it like this today:

@media only screen and (min-width: 48em) {
    .artist-list:extend( .grid-wrap deep ) {
        > li { width: 33.3% }
    }
}
dmi3y commented 11 years ago

As my prospective, you just answered your question, why not just use mixins? Sorry if I missed it again.

lukeapage commented 11 years ago

It would basically work like a mixin call but with the options.. I was trying to think of usecases though and I'm stumped..

We use extend to introduce a new selector to an existing style block.. when will different media queries require new selectors not used outside the query?

Reading @matthewdl earlier comments persuaded me towards my option. b - no copying from outside the query

matthew-dean commented 11 years ago

@DesignByOnyx - You said you would expect a different result, yet your result looks exactly the same as what I ended up with. Of course, I edited it after I had originally posted it, so if you received via email, you might have seen my original draft. So, yes, I agree, in a media block :extend should essentially behave the same as importing a class, with the one caveat of joining multiple extend calls within the same media block.

matthew-dean commented 11 years ago

@dmi3y - To your above example, I don't agree. The extend is called within the context of that media query, so it is inherently declaring: "Extend this other class, but ONLY in the context of this media query."

As @DesignByOnyx said, the only way to serve that declaration is to copy the styles within the media query, and leave the outside block alone.

DesignByOnyx commented 11 years ago

@matthewdl - you did change your example while I was posting. Apologies and glad we are on the same page there. Also, do you consider my example above to be a poor use case (willing to shoot me over)? Aside from the :extend I think it's a perfectly normal scenario:

.b { padding: 5px } 

@media only screen and (min-width: 30em) {
    .b { color: red } 
}
@media only screen and (min-width: 48em) {
    .a:extend( .b ) { }
}
@media only screen and (max-width: 60em) {
    .b { background: green } 
}

I also think it's reasonable to expect that styles are copied from other "matching" media queries. I'm going on a limb here, but consider this output:

.b { padding: 5px } 

@media only screen and (min-width: 30em) {
    .b { color: red } 
}
@media only screen and (min-width: 48em) {
    .a { 
        padding: 5px;
        color: red;
    }
}
@media only screen and (min-width: 48em) and (max-width: 60em) {
    .a { background: green } 
}

Notice the brand new media query which was created. I don't want to take this discussion where it doesn't need to go, but I think the above scenario is easily solved and reasonable to expect, no?

EDIT: ... but forgot to mention the most important part. This requires the LESS parser to be able to understand media queries on a whole different level than it does now.

matthew-dean commented 11 years ago

@DesignByOnyx - Yeah, I think you're overthinking this. Your example presumes that the parser can know whether or not it's between two values. A small tweak shows the futility of this approach:

.b { padding: 5px } 

@media only screen and (min-width: 30em) and (orientation: portrait) {
    .b { color: red } 
}
@media only screen and (min-width: 48em) {
    .a:extend( .b ) { }
}
@media only screen and (max-width: 600px) {
    .b { background: green } 
}

The parser doesn't know what an em is or how big it is. It can't know. So, it can't be automatically combined with a max-pixel width. Only match / combine units? Well what about the next question:

Does the 48em apply to orientation: portrait up through the first 30ems? Or does it even apply at all, since the second query didn't mention orientation?

I would say, best to stop while you're ahead. I think what you're saying is on the one hand, clever, but trying to logic out different media query combinations is just likely to cause so many unexpected results that I don't think we even want to got there. The LESS parser doesn't understand the CSS it is parsing. It's a translator of syntax.

So, let's be pragmatic and simple and just limit :extend to importing those values within the current @media scope (and only from a root-level declaration, not from another @media block).

jonschlinkert commented 11 years ago

So, let's be pragmatic and simple and just limit :extend to importing those values within the current @media scope (and only from a root-level declaration, not from another @media block).

100% agree, there is no other option that makes sense. Extend should only work in current scope, values in other media blocks are in a different scope and are as arbitrary and unrelated as values that aren't in any media blocks at all.

DesignByOnyx commented 11 years ago

@jonschlinkert - I thought you were avoiding this one ;)

If that's the consensus, I totally agree as well and have not intention on creating a debate. For shits, I would like to take a stab at @matthewdl's last example, just in case it makes anybody say "hey, that's not bad". Please don't take this as me continuing the argument. A simple "nope" is a sufficient response to this.

.b { padding: 5px } 

@media only screen and (min-width: 30em) and (orientation: portrait) {
    .b { color: red } 
}
@media only screen and (min-width: 48em) {
    .a:extend( .b ) { }
}
@media only screen and (max-width: 600px) {
    .b { background: green } 
}

Output (a bit absurd, but predictable):

.b { padding: 5px } 

@media only screen and (min-width: 30em) and (orientation: portrait) {
    .b { color: red } 
}
@media only screen and (min-width: 48em) and (orientation: portrait) {
    .a { color: red } 
}
@media only screen and (min-width: 48em) {
    .a { padding: 5px } 
}
@media only screen and (max-width: 600px) {
    .b { background: green } 
}
@media only screen and (min-width: 48em) and (max-width: 600px) {
    .a { background: green } 
}

If 600px happens to be smaller than 48em, then the styles therein would never apply and would "gracefully fail" in the form of added bloat. This of course is an edge case and is easily avoided.

matthew-dean commented 11 years ago

o_O

Okay, now do this one:

.b { padding: 5px } 

@media only screen and (min-width: 30em) and (min-width: 10em), (width: 20em) and (max-width: 40em) {
    .b { color: red } 
}
@media not screen and (min-width: 20em), handheld and (width: 30em), only screen and (width: 20em) {
    .a:extend( .b ) { }
}
@media only screen and handheld and (max-width: 30em), (min-width: 31em) and (min-width: 32em) and (min-width: 33em), (width: 20em) and (width: 30em) {
    .b { background: green } 
}
.c:extend(.a) { }
jonschlinkert commented 11 years ago

I thought you were avoiding this one ;)

lol sometimes I can't control my own impulses...

DesignByOnyx commented 11 years ago

@matthewdl - your last comment sent me into a seizure and caused my mac to crash :skull:. A simple "nope" would have sufficed.

jonschlinkert commented 11 years ago

lol

lukeapage commented 11 years ago

So, let's be pragmatic and simple and just limit :extend to importing those values within the current @media scope (and only from a root-level declaration, not from another @media block).

to be clear...

.b { padding: 5px } 
@media only screen and (min-width: 30em) and (min-width: 10em), (width: 20em) and (max-width: 40em) {
    .b { color: red } 
}
@media not screen {
    .b { top: 1px; }
    @media (min-width: 20em) {
        .a:extend( .b ) { }
        @media tv {
            .b { bottom: 1px; }
        }
    }
}

.a will not appear in the output right - because it doesn't match anything?

dmi3y commented 11 years ago

@matthewdl you are absolutely right, that's less expectable behavior, but still I could think about cases when the out of media query extand possibility would be useful, here is raw, but suppose, real world example:

.b { color: red;}
.c { font-weight: 900; } // here is something important we need attract to
.d { display: none; }

@media only screen and (min-width: 100em) {
    .c { width: 300px; }
    .b { border: 1px solid red; }
}
@media only screen and (min-width: 60em) {
    .c { width: 200px; }
    .a:extend( .b ) { font-size: 11px; }
}
@media only screen and (max-width: 60em) {
    .c { width: 100px; }
    .b { display: none; }
    .d:extend( .c ) { display: block; } // hey I am only on narrow
}

Compiles to:

.a, .b { color: red; }
.d, .c { font-weight: 900; } 
.d { display: none; }

@media only screen and (min-width: 100em) {
    .c { width: 300px; }
    .b { border: 1px solid red; }
}
@media only screen and (min-width: 60em) {
    .c { width: 200px; }
    .a { font-size: 11px; }
}
@media only screen and (max-width: 60em) {
    .d, .c { width: 100px; }
    .b { display: none; }
    .d { display: block; }
}

@agatronic

.a will not appear in the output right - because it doesn't match anything?

I would expect it, talking about default behavior, though it would be nice to extend into upper level somehow;)

lukeapage commented 11 years ago

@dmi3y can you talk us through the situation where this css is required?

dmi3y commented 11 years ago

@agatronic Luke, could not think about something robust just instantly, but yet something I could picture, see that stupid example above

lukeapage commented 11 years ago

@dmi3y bit confused - looking at your example the output css is what I would expect - it is not getting any classes from outside itself?

dmi3y commented 11 years ago

okay, sorry for confusing, that's not what I aimed, but unfortunately in most cases it is what I do;)

here is the extend to out of the media query

.d:extend( .c ) { display: block; } // hey I am only on narrow

and its output outside it

.d, .c { font-weight: 900; }

again that's very raw sample, but I think with a bit of time I could build something more logical

lukeapage commented 11 years ago

ok, so the element with class "d" is only made visible in that media query so we conceptually only style it in that query.. but you want it to look like another element so you use extend? Of course the end result is that "d" is always styled like "c".. and I think that could lead to confusion as you might think that a selector in a media statement is not going to effect an html page with a non matching media set.

I think its a valid if niche example which you could get round with a mixin call..

And although it demonstrates a usage for selectors extending selectors outside of a media query, I think the end result could be confusing for people reading the css..

The alternatives are

  1. you use a mixin call - making it explicit that the styles are only used within that media query
  2. you move the extend out
    • disadvantage, your less has the same selector duplicated
    • advantage - your less is clearer and more readable IMO
dmi3y commented 11 years ago

agree, talking pragmatically that's the very last case when you will need think about optimization final stylesheet, so that's may be postponed for a while in favor mixins, but I am not rolling back yet;)

DesignByOnyx commented 11 years ago

OK, last scenario I can think of which id doesn't appear to be covered yet. Should all instances of .a get extended with .b? I think so.

.a { }
.b:extend(.a);

@media only screen and (min-width:30em) {
    .a { }
}
matthew-dean commented 11 years ago

@DesignByOnyx - You're lucky I didn't go full evil and change .b to .b:extend(.c). ;-)

@dmi3y - I still just don't agree. I get that you're only copying properties that should apply within those media queries. To take a piece of this:

.b { color: red;}
@media only screen and (min-width: 60em) {
    .a:extend( .b ) { font-size: 11px; }
}

Outputting this is inappropriate:

.a, .b { color: red;}

Nowhere did the code specify that the properties of .b should also apply to .a irrespective of the media query. You're specifying that the properties of .b should be extended only within that query. By modifying the external .b to include .a, you're changing the properties of .a within other media queries. So, it's creating leaky scope. In other words:

.b { color: red;}
@media only screen and (min-width: 60em) and (max-width: 69em) {
    .a:extend( .b ) { font-size: 11px; }
}
@media only screen and (min-width: 70em) {
    .a { font-size: 14px; }      // "I didn't ask to be red! If you do anything to make me red, so help me..."
}

The correct scoped output of the above is simply:

.b { color: red;}
@media only screen and (min-width: 60em) and (max-width: 69em) {
    .a { font-size: 11px; color: red; }
}
@media only screen and (min-width: 70em) {
    .a { font-size: 14px; }      // "Yay! I'm not red!"
}

You could say, why don't we just tell people to use mixins? Here's why.

.b { color: red;}
@media only screen and (min-width: 60em) and (max-width: 69em) {
    .a:extend( .b ) { font-size: 11px; }
    .c:extend( .b ) { border-color: black; }
}
@media only screen and (min-width: 70em) {
    .a { font-size: 14px; }  
}

/////// OUTPUT   ///////

.b { color: red;}
@media only screen and (min-width: 60em) and (max-width: 69em) {
    .a, .c { color: red }
    .a { font-size: 11px; }
    .c { border-color: black; }
}
@media only screen and (min-width: 70em) {
    .a { font-size: 14px; }  // "Yay! I'm still not red! LESS is awesome!"
}
lukeapage commented 11 years ago

@DesignByOnyx that is my point (1) in the original issue description. I agree with you and I think most people do.. (fingers crossed)

matthew-dean commented 11 years ago

@DesignByOnyx @agatronic Yep! Things not in media queries apply to all things within media queries (just like CSS). ^_^ So, yes, I would read your example the same way. If you extend outside of a media query block, it extends all the relevant things within media query blocks.

DesignByOnyx commented 11 years ago

@agatronic - I swept over the entire discussion, except your original post. My bad.

Then I am good on this topic. I too don't really see the value in @dmi3y's examples, and it only serves to cause more confusion than convenience. I understand where you are going and I appreciate the use case, but it feels like a far stretch for a very edge scenario.

Mind if I start that summary process? Please note that the modifiers any | exact and deep | shallow still apply.

  1. An :extend(.a) which takes place outside a media query will extend every .a within media queries.
  2. An :extend(.a) which takes place within a media query will:
    • Extend every .a within that same media query
    • Copy over all root-level .a styles which exist outside any media query (similar to a mixin)
dmi3y commented 11 years ago

all right, I am afraid make more tangling over here, but consider it like funny reading:

    .b { 
        color: red; // would you like it?

        border: 1px groove red;
        background: blue;
    }
    @media only screen and (min-width: 60em) and (max-width: 69em) {
        .a:extend( .b localize color ) { font-size: 11px; } // ? isolate, buildin, gimepls, grab ..?
        .b { font-size: 30px; }
        .c:extend(.b localize color) { border-radius: 20px; height: 100px; width: 100px; border: 200px solid red;} // I am a big brother ;)
    }
    @media only screen and (max-width: 59em) {
        .a { font-size: 14px; }      // "I didn't ask to be red! If you do anything to make me red, so help me..."
                                    // "but would't mind to inherit some default styles shared among the others @media-s"
        .b { font-size: 20px; } // "Hey I suppose to be red! here!"
    }

// output

    .b {
        color: red; // to whom who cares
    }
    .a, .b {
        border: 1px groove red;
        background: blue;
    }
    @media only screen and (min-width: 60em) and (max-width: 69em) {
        .a, .c {
            color: red; // you got it, here!
        }
        .a {    
            font-size: 11px;
        }

        .b { font-size: 30px; }
        .c { border-radius: 20px; height: 100px; width: 100px; border: 200px solid red;}
    }
    @media only screen and (max-width: 59em) {
        .a { font-size: 14px; }      // "okay, you are not, though you have some profits from the guy in previous media query, is'n that's look bit confusing..?"
        .b { font-size: 20px; } // "do not worry, you are!"
    }
lukeapage commented 11 years ago

@dmi3y Sorry, just too complicated. Lets see if we have a real life use-case for this after it goes live.

dmi3y commented 11 years ago

@agatronic totally agreed, just trying bring bit of destructive element, sometimes that could be good. Real time opinion, made it as simple as possible, so join @matthewdl and sorry another, abc sample just to make sure I got it right UPS: sorry edited

a. { ... }

@media ..case1.. {
    .b:extend(.a){ ... }
}

@media ..case2.. {
   .a { ... }
   .c:extend(.a){ ... }
}

result

.b, .a { ... }

@media ..case1.. { .a, .b { ... // + .a styles from above } }

@media ..case2.. { .c, .a { ... } // generic mediaquery .a styles .c { ... } }

matthew-dean commented 11 years ago

@DesignByOnyx - Yes, that summary seems correct. What is copied into the media query is dictated by the same :extend keywords. So, if I do a deep extend (called from within a media query), then all of the properties / nested selectors are imported into the media query within the selector I wish to extend. Pretty similar to calling a mixin, except for the collapsing behavior I illustrated if I do this multiple times / with multiple selectors (and, you make a good note, ALSO extending the selectors that already exist within that media block).

@dmi3y - Agree with @agatronic and @DesignByOnyx that your previous examples are overkill and I'm afraid your recent example is still slightly incorrect.

.a { ... }

@media ..case1.. {
    .b:extend(.a){ ... }
}

...creates:

.a { ... }

@media ..case1.. {
    .b { ... }  // contains properties from external .a and local .b
                 // not ".a, .b { }" because the properties / reference of .a are already inherited into this media block scope
}
dmi3y commented 11 years ago

@matthewdl thanks, corrected. Yes, now it fits better in my head, especially with your previous clarifications.

dmi3y commented 11 years ago

Alright, still something is not just agree inside of me with making extend works in a mixin-ish way, yes I know that's not the same, but. In that paticular case I would rather write more in less code in favor of making it logically clear, I might be wrong. But considering example with two extends for one mixin in the same mediaquery.

a. { ... }

@media ..case1.. {
    .b:extend(.a){ ... }
    .d:extend(.a){ ... }
}

@media ..case2.. {
   .a { ... }
   .c:extend(.a){ ... }
}

and having than

...
@media ..case1... {
   .b,.d { // copied styles from .a above }
   .b { ... }
   .d { ... }
}
...

would not be it something similar to this approach?

@media ..case1... {
   .a() { .a }
   .b :extend(a){ ... }
   .d :extend(a){ ... }
}
...
matthew-dean commented 11 years ago

@dmi3y - As stated, similar, but not the same. Extend has more options. For example:

#header .a { color: blue; }

@media only screen {
   .b:extend(.a any) { border-color: black; }
   .c:extend(.a any) { height: 1em; }
}

becomes:

#header .a { color: blue; }

@media only screen {
   #header .b, #header .c { color: blue; }
   .b { border-color: black; }
   .c { height: 1em; }
}

The reason is that without a media query, it would output like this:

#header .a, #header .b, #header .c { color: blue; }
.b { border-color: black; }
.c { height: 1em; }

However, the scope of #header .b and #header .c need to be limited to the @media declaration where they were defined.

So, in cases of @media queries, at times, the behavior is similar to mixins, but it is not the same. :extend collapses within its scope, and can optionally match partial classes or opt to not extend nested selectors, which mixins cannot do. Mixins just output their properties inline.

dmi3y commented 11 years ago

Finally I do understand it! And support in that point. Probably just not such satisfied with final syntax inside mediaqueries :/ Though it is far away another question. Just still something popping out in my mind about the mediaquery like variant.

.....
@media ..case1... {
   @extend (#header .a all) { // IMO more feels that something needs to be copied to this place than moved out
      .b{ ... }
      .d{ ... }
   }
}

and in some circumstances that could be sugared to just

....
@media:extend(#header .a all) ..case1... {
      .b{ ... }
      .d{ ... }
}

Does it make any sense? Or I am too wired today;)

matthew-dean commented 11 years ago

While your @extend syntax is compatible with my example, I feel like in most cases, it will be done at the declaration level. As in, on a case-by-case basis for individual selector declarations, and I don't think people necessarily want to move it around to include it in an @extend block. Because it's a different approach for declaring extend, I feel like it would be confusing to do both. Similar thoughts with the second example. It looks like you're extending the @media query itself, so to support that plus extending selectors feels confusing.

dmi3y commented 11 years ago

Yes, agree, that's another level of complication, which is not good.

lukeapage commented 11 years ago

Please feel free to look at the new test case extend-media.less and extend-media.css and suggest anything I've missed or anything you think disagrees with what is in this issue.

matthew-dean commented 10 years ago

Just revisiting this as I ran into an apparent bug today related to this. extend-media.less and extend-media.css appear to have incorrect output according to this thread. See @DesignByOnyx's summary:

An :extend(.a) which takes place within a media query will: a) Extend every .a within that same media query b) Copy over all root-level .a styles which exist outside any media query (similar to a mixin)

The second part of this is currently not occurring as of 1.7.5. Selectors outside of @media are being ignored.

seven-phases-max commented 10 years ago

Just revisiting this as I ran into an apparent bug today

No, not a really a bug (As I understand it the "copying" was intentionally postponed by @lukeapage for "better times" and that's why this behaviour is mentioned in the docs :)

matthew-dean commented 10 years ago

@seven-phases-max I tried to re-reread as much of the original related issues. I just couldn't find where this was mentioned, so I couldn't tell if it was accidental or intentional. Was that mentioned in the original commit?

One of the reasons I want to keep track is sometimes we implement part of a feature, then close the issue, and don't track the remainder of the feature. If we have a central way to track it, then maintainers may have something easier to point to when someone asks a related question, to the effect of: "Yes, we're planning on that, here's the feature spec, sub-features A,B,C have been implemented, sub-features X,Y,Z have yet to be implemented." Something like what W3C does with CSS3 specs. http://www.w3.org/Style/CSS/current-work

seven-phases-max commented 10 years ago

so I couldn't tell if it was accidental or intentional.

I'm assuming one could not "not to code" a relatively large piece of code by accident and then documenting this in very details :).