less / less.js

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

Mixins should accept LESS blocks #965

Closed rbu closed 10 years ago

rbu commented 12 years ago

It would be helpful if mixins had access to a content block passed to them, so one could encapsulate media queries or browser hacks in a central place and reference them symbolically.

This is basically the "Passing Content Blocks to a Mixin" feature of SASS: http://sass-lang.com/docs/yardoc/file.SASS_REFERENCE.html#mixin-content

In LESS this would mean something like:

.mobile() {
  @media all and (max-device-width: 480px) {
    @content
  }
}
.big-desktop-button {
  ...
  .mobile {
    display:none;
  }
}
dwt commented 12 years ago

Oh, I need this too!

caseyohara commented 11 years ago

+1

BruceClark commented 11 years ago

Absolutely. Please!

caseyohara commented 11 years ago

I can see some collisions by adding this functionality to the normal mixin syntax. Just by looking, it would impossible to tell if the call above would compile to

.big-desktop-button .mobile {
  display: none;  
}

or

@media all and (max-device-width: 480px) {
  .big-desktop-button {
    display: none;    
  }
}

So I think it would need new syntax. Here is my idea.

Allow blocks of LESS to be passed into parametric mixins, as a normal positional parameter surrounded by braces.

.mobile(@content) {
  @media all and (max-device-width: 480px) {
    @content
  }
}

.big-desktop-button {
  ...
  .mobile({
    display:none;
  });
}

Due to variable scoping and evaluation, I imagine this would require that mixins that accept LESS content blocks can only accept content blocks.

PiotrSkon commented 11 years ago

+1 This is a must.

Soviut commented 11 years ago

+1 SASS can do this.

cescalante commented 11 years ago

+1

matthew-dean commented 11 years ago

I don't understand these examples. What is @content defined as in the first example?

chimericdream commented 11 years ago

With SASS, you can pass a whole content block to a mixin independent of the parameters. So you have something like this:

.mobile() {
    @media your-query-here {
        @content
    }
}

.someclass {
    // some general styles here
    .mobile() {
        // your mobile styles here
    }
}

that would compile to:

.someclass {
    // some general styles here
}

@media your-query-here {
    .someclass {
        // your mobile styles here
    }
}

I'm adding my support for this. Real logic operators (if/else) and this feature are the biggest things keeping me from using LESS at this point.

davidrivers commented 11 years ago

+1!!!

jonschlinkert commented 11 years ago

I'm still confused by this. Some real world examples of this being used would really help a lot. I tried to read through the SASS docs, but I still struggled with it. It sounds like @content is really just passing a literal block of CSS to the bottom of the inheriting declaration block. Is that right?

davidrivers commented 11 years ago

@jonschlinkert You're right. It's extremely useful when you need a "wrapper" selector. For example, I have a project where we have to share the page with styles from a third-party, so we "pseudo-namespace" our styles in order to make them more specific than the third party styles. We do this by wrapping all of our styles with an arbitrary selector, and we can nest styles within that selector and leverage the fact that a complex selector such as .wrapper .module { /* styles here*/ } is more specific than most or all of our third-party's selectors (and therefor shouldn't be affected by them since we also embed our own "reset" styles on that wrapper before specifying styles that we want).

Another use for having an arbitrary selector to wrap styles is if you want to reuse Sass or Less "modules" between different projects or documents and for some reason you need to give them different wrapper hooks in your markup (or want the flexibility to do so).

My use cases are attempting to bridge the fact that the CSS cascade lacks a concept of a true namespace, but you can leverage a stylesheet preprocessor to get something practically close to one. "Media query bubbling" is a similar use case (since it allows "blocks" of styles to be passed around and wrapped with different selectors), although I don't know how Less supports this internally, but a cursory Google search suggests that Less does support this feature.

jonschlinkert commented 11 years ago

@DavidRivers thanks, the wrapper analogy helps, that's sort of how I saw it in my mind. Looking at this semantically, this is a better use case for the term @include than @content, which makes no sense to me here. (Sass had to pick a different term since they used @include for mixins.)

David if you find some time, would you mind adding some detailed code examples here so when @matthewdl and @lukeapage review again they can make a better evaluation of this? thanks!

matthew-dean commented 11 years ago

Yes, LESS supports media query bubbling. There's probably ways to do namespacing as you describe, but like @jonschlinkert, the examples are still a head-scratcher. That is, I can't see how it's not currently supported. You can segment code into, say, mobile-specific definitions using variables / mixin guards.

voodoom commented 11 years ago

+1!!

lukeapage commented 11 years ago

I wonder if extend and extend mixins (equivalent to sass placeholders) actually provide this functionality..

anyway, here is the example from the sass docs, converted into pseudo less.

.apply-to-ie6-only() {
  * html {
    @content;
  }
}
.apply-to-ie6-only() {
  #logo {
    background-image: url(/logo.gif);
  }
}

output

* html #logo { background-image: url(/logo.gif); }

usecase - stop repetition of selectors/media queries throughout the code and abstract them out to one place.

here is how I might do it with extend

.apply-to-ie6-only {
  #logo {
    background-image: url(/logo.gif);
  }
}

* html:extend(.apply-to-ie6-only all) {
}

disadvantages

here is how I might do it with mixins

.apply-ie6-only() {
  #logo {
    background-image: url(/logo.gif);
  }
}

*+html {
   .apply-ie6-only();
}

note this does work with multiple apply-to-ie6-only classes and because of media bubbling it does work applying media queries to multiple classes.

disadvantages

I can't think of a nice way of implementing a @content feature in less.. and I am also not sure what it brings to the table above my two work-arounds. What do you think @DavidRivers ?

Another proposed feature that might be useful for your specific case is "silent" imports. You could wrap everything in a class, import your library silently, then extend a particular namespace.. then the new namespaces selectors would be brought in and the old selectors kept quiet...

dwt commented 11 years ago

I'm not sure I completely understand your examples, but they seem to all be backwards in a way - sorry if I'm getting them wrong.

What I (and I the thread opener who is a collegue of mine) want to achieve is that I can group the media query specific styles with the other styles to a particular page element.

The idea here is that this solves the problem that using media queries always means you have to look in many files / places in the same file to find all the styles that pertain to one specific element.

The solution to this now should allow that you can write styles more along the lines of this:

.generic-class {
  .more-specific-class {
    // style declarations
    .something-that-makes-the-contained-styles-only-apply-on-mobile-devices {
      // mobile specific style declarations
    }
    .something-that-makes-the-contained-styles-only-apply-on-tablet-devices {
      // tablet specific style declarations
    }
    .something-that-makes-the-contained-styles-only-apply-on-desktop-devices {
      // desktop specific style declarations
    }
  }
}

That way the wrapping structure of less can be used to really group all styles for an element together and see the different responsive elements in one piece. No matter the solution - this is what it should allow to achieve to solve this bug.

SomMeri commented 11 years ago

@dwt I think that media bubbling is the feature you are looking for.

Documentation:

To make it work in your example, you have to replace:

Modified version of your example:

 .generic-class {
   .more-specific-class {
      content: "style declarations";
      @media mobile {
        content: "mobile specific style declarations";
      }
      @media tablet {
        content: "tablet specific style declarations";
      }
      @media desktop {
        content: "desktop specific style declarations";
      }
    }
 }

Less.js-1.3.3 compiled it into this:

 .generic-class .more-specific-class {
   content: "style declarations";
 }
 @media mobile {
   .generic-class .more-specific-class {
     content: "mobile specific style declarations";
   }
 }
 @media tablet {
   .generic-class .more-specific-class {
     content: "tablet specific style declarations";
   }
 }
 @media desktop {
   .generic-class .more-specific-class {
     content: "desktop specific style declarations";
   }
 }

Basically, @media nested inside ruleset acts as if or make exception for.

dwt commented 11 years ago

Maybe I'm misunderstanding you, but the whole point of this bug report is being able to abstract about the media rules and I think that the media query bubling is only providing the foundation of this feature.

I.e. we want to define the actual breakpoints of the media queries at exactly one point, and then invoke those rules at various places inside the styles so that we can change them consistently and easily at exactly one point.

Does that make sense?

SomMeri commented 11 years ago

@dwr I misunderstood you and what you want make sense now, sorry. You could currently use only media query interpolation, but that is somewhat limited compared to this feature.

@var: ~"tablet";
@media @var {  ...   }
dwt commented 11 years ago

Indeed I just found out that since 1.3 this is possible and a good start towards this goal. That being said, a way to abstract about blocks of less would still be very much apreciated.

DesignByOnyx commented 11 years ago

Here is an example that I think may help get the idea across. When compiling a stylesheet for legacy browsers, you could simply switch @isResponsive to false.

@isResponsive: true;
@handheld-up: ~"screen and (min-width: 30em)";
@handheld-down: ~"screen and (max-width: 48em)";
@handheld-only: ~"screen and (min-width: 30em) and (max-width: 48em)";
@tablet-up: ~"screen and (min-width: 48em)";
@tablet-down: ~"screen and (max-width: 60em)";
@tablet-only: ~"screen and (min-width: 48em) and (max-width: 60em)";

// @@content is a special variable with the "contents" of the style declaration
// The double @@ sign is just something I chose to distinguish it from a normal variable
.mq-style(@breakpoint) when (@isResponsive = true) {
    @media @breakpoint {
        @@content;
    }
}
// A litte duplication is necessary due to the inability to have complex guards
.mq-style(@breakpoint) when (@isResponsive = false) and (@breakpoint = @handheld-up) {
    @@content;
}
.mq-style(@breakpoint) when (@isResponsive = false) and (@breakpoint = @tablet-up) {
    @@content;
}

.some-element {
    .mq-style(@handheld-up) {
        /* This is the content be be used in place of the @@content variable */
        color: blue;
        background: red;
    }

    .mq-style(@tablet-only) { {
        border: 1px solid red;
    }
}
DesignByOnyx commented 11 years ago

And here is the output from my last example:

/* @isResponsive: true; */
@media screen and (min-width: 30em) {
    .some-element {
        color: blue;
        background: red;
    }
}
@media screen and (min-width: 48em) and (max-width: 60em) {
    .some-element {
        border: 1px solid red;
    }
}
/* @isResponsive: false; */
.some-element {
    color: blue;
    background: red;
}
leads commented 11 years ago

It would be great if LESS was capable of achieving this http://jakearchibald.github.io/sass-ie/

As far as I can see the only thing holding it back is the lack of support/alternative for @content.

mhulse commented 11 years ago

@leads yes! +1! :+1:

I was just in the process of converting sass-ie to LESS when I hit a brick wall with @content. I don't see how @lukeapage's examples would work in this case.

As an example of a feature I think we don’t need to implement, the sass @content feature allows you to abstract selectors or media queries into mixins, so they appear only in one place. In our debate on whether to include @content in less, I make the point that for most use-cases the functionality is already possible with existing less features. — Via Luke Page on Scott Logic: "Less vs Sass vs Stylus"

I think the sass-ie code is a prime example of @content's usefulness. I'm not sure if you could replicate that functionality with existing LESS features (no?).

SomMeri commented 11 years ago

I think I found a workaround for this. Since mixins see and can access their callers scope, you can place media and selectors into mixin and inject content by another mixin defined inside the caller:

/*
  Usage: the caller must have .content declared in its scope!
*/
.mobile() {
  @media all and (max-device-width: 480px) {
    .content();
  }
}

/* use the .mobile inside desktop button */
.big-desktop-button {
  .content() { //this will be injected into .mobile mixin
    display:none;
  }
  // the .mobile sees this scope and will use .content mixin
  .mobile();
}

/* use the .mobile inside mobile button */
.big-mobile-button {
  .content() { //this will be injected into .mobile mixin
    display:inline;
  }
  // the .mobile sees this scope and will use .content mixin
  .mobile();
}

compiles into:

@media all and (max-device-width: 480px) {
  .big-desktop-button {
    display: none;
  }
}
@media all and (max-device-width: 480px) {
  .big-mobile-button {
    display: inline;
  }
}
dwt commented 11 years ago

I would like to add another usecase to let us abstract about @media queries, ie compatibility.

Since IE 7 does not support @media queries, one possible workaround is to ensure that all styles that are in the desktop media query are also in an ie specific file and get included there. Since manual labour sucks and css is much better we have some css classes on body that let us target ie specificly from css where required with classes like .lt-ie8 or .lt-ie7. Now, if we could abstract about media queries, we could build a mixin which would put all the desktop rules into the desktop media query and into the scope of the .lt-ie8 class.

In pseudo-code I would assume this could look a bit like this

.desktop() {
 @media (something) {
  @content()
 }

 .lt-ie-9 {
  @content
 }
}

Pretty please, can we have something like this? I'd love to get rid of the special IE.css file we maintain by hand where we copy over styles once in a while and then forget to update them.

matthew-dean commented 11 years ago

As @SomMeri mentioned, you can define a content block as a mixin, and call that mixin in each of those media queries. It's a different pattern than SASS, but seems to accomplish the same goal, which is abstraction of a block of properties / values.

In addition, I've found this pattern powerful for defining a content block and including them in both a media query for responsive browsers and in the root for non-responsive (IE) browsers. I've done this by having a different "root" less file for both environments.

You can get full coverage on IE, mobile, and legacy mobile devices by having a <head> that looks like this:

<link rel="stylesheet" href="/css/mobile.css" media="screen and (max-width: 480px)">
<link rel="stylesheet" href="/css/main.css" media="(min-width:481px)">

<!--[if (lt IE 9) & (!IEMobile)]>
<link rel="stylesheet" href="/css/ie.css">
<![endif]-->

IE.css is essentially a compiled version of my less libs with responsiveness off. Mobile.css contains the root mobile styles without any media queries, and main.css contains all the media query declarations past mobile. I can't remember where I found this pattern, but it works really well.

mhulse commented 11 years ago

Hmmm, @SomMeri's suggestion looks like it might be a LESS way of doing sass-ie. I guess the only drawback I can see is that one would have to wrap .content() { ... } around everything?

dwt commented 11 years ago

Yeah, but it's still better than what we have currently. So we will start using it.

Here's what we use as the baseline as per @SomMeri's suggestion:

@mobile: ~"(max-width: 480px), (max-device-width: 480px)";
@desktop: ~"(min-device-width: 481px) and (min-width: 481px)";
// has desktop styles applying also
@netbook: ~"(min-device-width: 481px) and (min-width: 481px) and (max-height: 602px)";

.mobile() {
  @media @mobile {
    .content;
  }
}

.desktop() {
  .lt-ie9 & {
    // IE 8 and below gets targetted with this, IE 9 and up understand media queries
    .content;
  }
  @media @desktop {
    .content;
  }
}

.netbook() {
  @media @netbook {
    .content;
  }
}

/*
Example usage

.foo {
  color: red;

  & {
    .mobile;
    .content() {
      color: mobile;
    }
  }
  & {
  .desktop;
    .content() {
      color: desktop;

      .lt-ie8& {
        color: ie;
      }
    }
  }
}

.bar {
  .mobile;
  .content() {
    color: mobile;
  }
}
*/

Bear in mind that we also have this:


<!DOCTYPE html>
<!--[if lt IE 7]>      <html class=" lt-ie9 lt-ie8 lt-ie7"> <![endif]-->
<!--[if IE 7]>         <html class=" lt-ie9 lt-ie8"> <![endif]-->
<!--[if IE 8]>         <html class=" lt-ie9"> <![endif]-->
<!--[if gt IE 8]><!--> <html class=""> <!--<![endif]-->

This now allows us to have all styles in one place and write them down hierarchically. That is, all responsive breakpoints, ie 8 and below will see all the desktop styles (and only he does so) and we can also put specific ie hacks right where they belong in the hierarchical less without having to do anything special.

dwt commented 11 years ago

I would like to add that while this is a workaround for our problem, the resulting syntax is not pretty and litters the less code with meaningless & {} and .content {} blocks that are confusing has special cases like .lt-ie8&{} that doesn't allow a space between the class and the ampersand. This needs quite some explaining to uninitiated people. Thus there is still a big need for a more specific syntax that makes this concept easy to use.

mhulse commented 11 years ago

I would like to add that while this is a workaround for our problem, the resulting syntax is not pretty and litters the less code with meaningless & {} and .content {} blocks that are confusing has special cases like .lt-ie8&{} that doesn't allow a space between the class and the ampersand. This needs quite some explaining to uninitiated people. Thus there is still a big need for a more specific syntax that makes this concept easy to use.

:+1: +1

mhulse commented 11 years ago

Out of curiosity, has anyone written code to do @content in LESS? If someone were to write a patch, maybe we could get LESS updated via a pull request sooner than later. I'd give it a shot, but I could not guarantee I'd know where to start ... At least, it would take me a while to wrap my head around all the moving parts. Anyone here familiar with LESS's build process and/or know of a quick way to incorporate the @content functionality?

himedlooff commented 11 years ago

@mhulse you can kind fo replicate sass-ie in less by using string interpolation to change a media query into a media type (which is supported down to IE6). For example: @media only screen and (min-width:768px) { ... } to this... @media screen { ... }. I'm documenting the method here: https://github.com/himedlooff/media-query-to-type

mhulse commented 11 years ago

@himedlooff That's pretty cool! Thanks for sharing. As you can probably see, I've opened an issue with a few questions.

With that said, on top of wanting to support old IEs, I'm also looking to feed desktop styles to my printer.

Long story short, the printers I've used respect media queries ... For one of my latest projects, my goal is to provide "print what you see" for my users (by feeding the printer a desktop stylesheet) versus crafting a print-only style sheet, printing only mobile styles when using @media screen and ... or printing what will fit when using @media all and ....

In other words, I think what @content solves, at least in the context of sass-ie, is two-fold: 1) Desktop styles for old IEs 2) Desktop styles for print style sheets.

I'm sure everyone's needs, and philosophies, are different for when it comes to print, but my latest project uses the paid version of http://www.printfriendly.com/ for our story pages; our goal is to have the browser "print" command actually print "what people see" on the screen and PrintFriendly for a customizable printer-friendly article body (that way a user can get the best of both worlds).

As it stands currently, if you don't want have a separately crafted print stylesheet, then the mobile-first MQ setup isn't optimal for printers. Using something like @content (like sass-ie's setup) would kill two birds with one stone.

Ok, crawling back into my hole now. :+1:

himedlooff commented 11 years ago

@mhulse RE: printing with the media queries to type method you have the flexibility to tweak it to your needs. For example instead of converting a media query to @media screen { ... } you can change it to @media all { ... }. It's all up to you!

mhulse commented 11 years ago

@himedlooff Ah, thanks for the update! I'm looking forward to testing your workflow/code. I'll continue this convo via your issue tracker. Thanks!!!

matthew-dean commented 11 years ago

This syntax still looks strange to me. I think we should have define statements that establish a special class of mixin, so that the mixin would look normal "wrapped" around a block of selectors, much like a media query. Something like I posted to issue #1333.

@define media-mixin() {
  @media only screen and (min-device-pixel-ratio: 1.5) {
    @content;
  }
}
@media-mixin() {
  .a { color: red; }
  .b { color: blue; }
}
@media-mixin() {
  .c { color: yellow; }
}

// Outputs
  @media only screen and (min-device-pixel-ratio: 1.5) {
    .a { color: red; }
    .b { color: blue; }
    .c { color: yellow; }
  }

@blocks around selector blocks feels more CSS-y to me.

jonschlinkert commented 11 years ago

Small nitpick. To reiterate what I said here: https://github.com/cloudhead/less.js/issues/965#issuecomment-14417326, can we please get away from the using the term @content? We already have the content property: .class { content: "something" }, this idea does not leverages that concept or benefit from using similar nomenclature, and it has absolutely nothing to do with "content", so I think it's confusing people.

@include is a perfect term for this, because "includes" work this way. "includes" have been around for a while, and when you use the term include it implies that whatever you're "including" will be included as-is. So exactly what we're describing here. "content" says nothing. IMO if anything, it implies that it has something to do with the content property.

I'm aware that other preprocessors already use the terms "content" and "include" differently then I'm suggesting, that's okay, we don't need to live with their mistakes.

If you want this to happen, I think you'll get more traction with this concept using the @include term, and folks who are new to this discussion will understand what you're talking about with less explanation. That's just my 2c.

goto-bus-stop commented 11 years ago

Variables with LESS blocks + @include construct which simply puts its argument into current scope?

@block: {
  color: #0ff;
  background-color: #0f0;
}

.someMixin(@otherStyles) {
  border: 2px solid #f00;
  @include(@otherStyles);
}

.selector {
  .someMixin(@block);
  /* not exactly useful, but good for consistency: (blocks as values) */
  @include({
    font: bold 10pt 'Comic Sans';
  });
}

Would then become:

.selector {
  border: 2px solid #f00;
  color: #0ff;
  background-color: #0f0;
  font: bold 10pt 'Comic Sans';
}

This might be way too 'radical', but it certainly looks intuitive to me.

If you would now pass, for example, a string to .someMixin, it would throw like many of the built-in functions; something along the lines of '@include expects a LESS block'

Of course, you could also pass a block straight to .someMixin like so:

.selector {
  .someMixin({
    display: none;
  });
}

to obtain

.selector {
  border: 2px solid #f00;
  display: none;
}
DesignByOnyx commented 11 years ago

In addition to abandoning the use of @content, I would also like to see if we can get SASS out of our minds and not let it dictate how LESS solves this problem. The "problem" at hand is not that we need a way to pass content blocks to a mixin. The problem is that we need a way to easily compile two or more stylesheets for different classes of browsers (modern vs. old ie). If we were to solve this the SASS way, it would look something like this (I use "@content" merely for the sake of discussion):

.some-mixin() {
    // if ( some compile-time condition )
       @content
    // endif
}

header {
    .some-mixin() {
        color: red;
    }
}

I don't believe anybody has a problem with this way of doing things, especially with the oh-so-popular sass-ie article mentioned above. However this technique has 2 problems when talking about LESS.

  1. We must come up with a way to pass content blocks to a mixin. Based on the way LESS currently works with variable scope, using my example above it appears as though .some-mixin is being re-defined in the scope of header. So this syntax won't work... and I'll refrain from further suggestions for now (read on).
  2. We must introduce a new term to reference this content - which I don't think is going to be easy (I don't feel like @include or @block are a good fit, sorry)

If we focus on the problem at hand, I feel like @himedlooff as introduced a paradigm which not only makes sense but works in the current version of LESS and doesn't require any craziness. I suggest everybody go and read about this technique. Once you understand what he is suggesting, you will see that it is quite an elegant solution. And since LESS lets you use variables in selectors, the options are almost limitless... you just have to think differently than you would with SASS.

mhulse commented 11 years ago

If we focus on the problem at hand, I feel like @himedlooff as introduced a paradigm which not only makes sense but works in the current version of LESS and doesn't require any craziness. I suggest everybody go and read about this technique. Once you understand what he is suggesting, you will see that it is quite an elegant solution. And since LESS lets you use variables in selectors, the options are almost limitless... you just have to think differently than you would with SASS.

I totally agree.

Personally, I'm sold on @himedlooff's technique.

For those who are like me, and want to know why his solution works (from a CSS perspective), I've posted my ramblings here:

https://github.com/himedlooff/media-query-to-type/issues/1

Specifically the last post:

https://github.com/himedlooff/media-query-to-type/issues/1#issuecomment-18318359

The only drawback, and it's a small one, is that this technique leaves behind the at-rules whereas the @content of SASS (in the context of the sass-ie technique) completely removes any leftovers. For me, I personally don't care as this is for old IEs (and, in my particular case, print styles).

jonschlinkert commented 11 years ago

It sounds like this is a closed issue then? If the only thing left to resolve is "removing leftovers", then that is a very different issue than what was opened.

mhulse commented 11 years ago

@jonschlinkert I'm not sure if @himedlooff's solution solves everyone's needs.

I did not mean to imply in my last post that @himedlooff's code was the final solution (though, it might be).

I could be wrong, but it still seems like having something similar to SASS' @content might be a nice feature to have.

Maybe we could keep this issue open for a bit longer to get some sort of consensus from rest the group?

matthew-dean commented 11 years ago

The problem is that we need a way to easily compile two or more stylesheets for different classes of browsers (modern vs. old ie).

That seems solvable today by using different base stylesheets.

I started to write an expansion of my proposal, until I got to the place where I realized that we're all just talking about media queries, but the syntax is generic as if there's some other application beyond media queries. Is there?

It also feels like we can solve this with extending mixins (Issue #1177), without resorting to any additional special keywords or concepts.

.one {
  color: red;
  padding: 5px;
  &:extend(.media-print) {
    color: blue;
  }
  &:extend(.media-print) {
    padding: 10px;
  }
}
.two {
  border-color: blue;
  &:extend(.media-print) {
    border-color: black;
  }
}
@media print {
  .media-print() { };  
}

// outputs

.one {
  color: red;
  padding: 5px; 
}
.two {
  border-color: blue;
}
@media print {
  .one {
    color: blue;
    padding: 10px;
  }
  .two {
    border-color: black;
  }
}

You could also add mixin guards of course to the mixin definition, thereby switching the extend on and off.

...
@media print {
  .media-print() when (@isResponsive = true) { };  
}

So, yes, we're thinking about the problem differently, as @DesignByOnyx suggested, but it seems like we have a decent proposal on the table that CAN already solve this problem. Am I missing anything?

UPDATE: Oops, my bad, :extend doesn't work exactly that way currently. It doesn't add properties/values to the block it's extending, it adds the properties of the extend block to the current selector.

So, hmm, I am missing something. But it feels like there's a simple solution somewhere in there. Simpler than @include and @content. Something based on the simplicity of this pattern, but a tweak to syntax? Or a tweak to extend?

SomMeri commented 11 years ago

What about placing the block into a variable? (I think someone already sugested that.)

It does not require new keyword and would allow multiple blocks as mixin parameters. The @content keyword is restricted on one block.

.mixin(@block) {
  @block;
}

#use-mixin {
  .mixin(@variable);
  @variable: { size: 2; };
}
#short-expression-use-mixin {
  .mixin({ size: 2; });
}

Maybe even allow mixins to be placed into that variable too, making it even more flexible:

#use-mixin {
  .mixin(@variable);
  @variable: .block();
}
#short-expression-use-mixin {
  .mixin(.block());
}
.block {
  size: 2;
}
matthew-dean commented 11 years ago

Probably my least favorite form is the selector nested within parentheses. As in:

.mixin({ size: 2; });

Seems so much more like JS than CSS. But assigning to a mixin is an interesting idea.

The overarching question I still have: if the only remaining use case for this is media queries, then maybe we should just be looking at extending or grouping media queries, such as issues #950 or #1333.

Compiling different selector blocks for early-IE seems to be covered, so if media queries is the only thing that remains, we might want to close and shift to one of those issues. Just want to get any input of any other missed cases.

DesignByOnyx commented 11 years ago

The original use case which created this thread has been resolved. Can we provide another use case where passing content to mixin is necessary and vital? Let's assume we can get a block of content into a mixin - what is done with the content once it reaches the mixin?

mhulse commented 11 years ago

Not sure if this will be of any help, but I found this gist that shows some uses of the @content feature of SASS.

Also, here are the SASS docs for @content; there's a few examples there as well.

Do any of those examples present usefulness outside of media queries or to provide fallbacks for old IEs?

DesignByOnyx commented 11 years ago

keyframes is the only real use case I can see... (sad face). This now reminds me of two much simpler use cases where the same type of thing applies: ::selection and ::placeholder. Right now, my stylesheet looks like this:

.selection() {
    background-color : @highlightBgColor; 
    color : @highlightTextColor; 
    text-shadow : none; 
}
::-moz-selection { .selection(); }  
::selection { .selection(); }

.placeholder() {
    color: @lightGrey;
}
::-webkit-input-placeholder { .placeholder(); }
:-moz-placeholder { .placeholder(); } /* Firefox 18- */
::-moz-placeholder { .placeholder(); } /* Firefox 19+ */
:-ms-input-placeholder { .placeholder(); }

While the above works very well, it would be nice if I could do something such as:

.placeholder( @contentBlock ) {
    ::-webkit-input-placeholder { @contentBlock }
    :-moz-placeholder { @contentBlock } /* Firefox 18- */
    ::-moz-placeholder { @contentBlock } /* Firefox 19+ */
    :-ms-input-placeholder { @contentBlock }
}

Which would come in handy for things like keyframes:

.keyframes( @name, @contentBlock ) {
    @-webkit-keyframes @name {
        @contentBlock
    }
    @-moz-keyframes @name {
        @contentBlock
    }
    @keyframes @name {
        @contentBlock
    }
}

And this atrocity (sorry @matthewdl - just trying to make an example, not a formal proposal):

.keyframes("fadein", ~"
    from {
        opacity: 0;
    }
    to {
        opacity: 1;
    }
");