Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dynamic variables and mixins #857

Closed
jslegers opened this issue Jul 18, 2013 · 19 comments
Closed

Dynamic variables and mixins #857

jslegers opened this issue Jul 18, 2013 · 19 comments

Comments

@jslegers
Copy link

Dynamic extends work fine. The following works as expected :

%#{$component} {
     // do something
}

Dynamic mixins and variables don't seem to work, though. The following all give an error :

@mixin #{$component} {
     // do something
}
$var1 : parametername;
$$var1 : 5;
$var1 : parametername;
$#{$var1} : 5

Dynamic extends, mixins and variables increase the meta-programming capabilities of the language significantly. For that reason, I'm most definitely looking forward to the addition of dynamic mixins and/or variables to use them in the meta-framework I'm building.

Are these features planned for a future release? If not, is there a technical reason for that? Or is there maybe a technique you can recommend to achieve the same effect?

@Snugug
Copy link

Snugug commented Jul 18, 2013

This is a duplicate of #626

@nex3 nex3 closed this as completed Jul 19, 2013
@jslegers jslegers mentioned this issue Jul 19, 2013
7 tasks
@chriseppstein
Copy link

Not a duplicate.

@chriseppstein chriseppstein reopened this Jul 20, 2013
@Snugug
Copy link

Snugug commented Jul 20, 2013

Sorry, I read dynamic mixin as dynamic include of a mixin.

@jslegers
Copy link
Author

I'm currently doing this :

// Definitions
@mixin setComponent($placeholder) {
  // Do stuff
  %#{$placeholder} {
      @content;
  }
}

@mixin getComponent($placeholder) {
  // Do stuff
  @extend %#{$placeholder};
}
// Example use
@include setComponent(table-cell) {
    padding:4px;
    margin:4px;
}

td {
    @include getComponent(table-cell);
}

th {
    @include getComponent(table-cell);
}
// Output

td, th {
  padding: 4px;
  margin: 4px;
}

Basicly, I'm looking for ways to achieve the following :

  • support mixins as an alternative implementation for 'setComponent' and 'getComponent' dynamicly, based on the value of a global variable $extendComponents
  • test whether or not a certain component has already been defined, to avoid duplication. This should work for mixins in Sass 3.3, but I don't think an implementation exists for placeholders yet.

Both this and #336 seem essential to allow these features and other features I have in mind for the meta-framework I'm building.

@chriseppstein
Copy link

John,

The abilities you asking for aren't going to happen any time soon, but as I
noted at some point, the archetype compass plugin will give you the ability
you want, right now. It does this by writing ruby extensions instead of
relying on pure sass capabilities

On Sunday, July 21, 2013, John Slegers wrote:

What I'm trying to create, is a toolset that dynamicly CREATES mixins and
placeholders, to allow advanced configurable automation. It already works
for placeholders, but not for mixins.

Both this and #336 #336 are
essential features for the meta-framework I'm building.


Reply to this email directly or view it on GitHubhttps://github.com//issues/857#issuecomment-21310599
.

@jslegers
Copy link
Author

Are there any technical reasons these features aren't going to be implemented?

Sass has a lot of meta-programming potential, but access to global variables from mixins as well as the dynamic creation of variables and mixins are pretty essential to allow any such advanced usage of Sass.

Dependence on Compass and Compass plugins is not an option for me, as I want my meta-framework to be compatible with implementations of Sass in other languages besides Ruby (like PHPSass) and have as little dependencies as possible.

The lack of these very essential features and bizarre unpredictable behavior like #856 are more and more forcing me to consider writing my own pre-processor language to leverage my needs, which I'd rather not do when there's already LESS, Sass and PHP-Crush.

@chriseppstein
Copy link

@jslegers Metaprogramming is one way to accomplish what you're thinking, but there are others. We are already adding a bunch of meta programming abilities from the calling side, but the definition side seems less useful from all the use cases I've seen so far. I'd rather approach this problem from use cases and end-user APIs and capabilities. I'd like to understand why defining a mixin dynamically provides some ability that defining it statically does not and then I'd need to weigh that complexity against the understandability and learnability for new users. For instance, dynamic definitions will almost certainly require the introduction of closures and possibly anonymous functions and mixins. I think these are very advanced concepts that make the language harder to comprehend.

@chriseppstein
Copy link

But if you feel the need to write your own preprocessor, feel free. You may also want to check out Rework; it's a good foundation for writing new css preprocessors and exploring new concepts. We tend to be rather conservative in our language design philosophy.

@jslegers
Copy link
Author

What I wanted to achieve, is a way to easily maintain and optimise Cascade Framework.

The codebase would consist of four levels :

  • Registering : here, all styles are defined with a simple "register" mixin, which generates placeholders in the background. This will determine the sequence and grouping of your different styling rules.
  • Meta : here, the meta-framework's codebased will come, which will take care of the more fancy features
  • Components : here, a hierarchic component model is defined, with each component extending the registered styles in the first layer
  • Selectors : here, the actual selectors inherit the components defined in the layer underneath

Major advantages :

  • SCSS code is optimised for readability and maintenance, while CSS is optimised for performance
  • You're not stuck with pre-defined classes. By abstracting away everything in the component layer, you're free to choose your classes by inheriting everything from them.
  • Want to change a value for any specific rule or component? You change it at exactly one spot and your CSS is automaticly optimised
  • ...

There are several ways to implement this, but for every implementation I have in mind I'm missing one or two features in SCSS. The ability to modifiy global variables from within a mixin is the key feature I'm missing. Adding that alone would allow me to implement 95% of the meta-framework's features in a very short time.

I'm currently experimenting with writing my own preprocessor language in PHP, although I still consider moving back to SCSS if one or more of the missing features are implemented. Thanks for suggesting Rework, though. It might inspire me :-)

@Snugug
Copy link

Snugug commented Jul 22, 2013

You keep saying that you can't modify global variables from within mixins, and you keep being incorrect about that. If you would simply try it, you'll see you can, in fact, change global variable values from within mixins.

$foo: 'Hello World';

@mixin change-global-variable-foo($var: $foo) {
  // If nothing is passed in, $foo remains $foo
  $foo: $var;
  content: $foo;
}

.bar {
  @include change-global-variable-foo; // content: 'Hello World';
  @include change-global-variable-foo('Goodbye World'); // content: 'Goodbye World';
  @include change-global-variable-foo; // content: 'Goodbye World'; Global variable $foo has been changed
}

I'm willing to bet you have your argument variables in your mixin named the same as your global variables. Assuming that is the case, you will not see the global variable change as you are within a local variable namespace. The issue you'd be having there is a misunderstanding about how scoping works.

The way I deal with scoping is to have my global namespaced variables have unique names related to the mixins they are for and to have the same names w/o namespacing in the mixin.

@chriseppstein
Copy link

@Snugug Ya, I feel like I'm talking to a wall too, so I'm going to stop for now. I have more productive things to do.

@jslegers
Copy link
Author

@Snugug @chriseppstein

Nevermind. I must have made a mistake experimenting with it the first time and wrongly interpreted issue #336. I apologise for wasting your time with that.

I guess I don't see any blocking issues with Sass at the moment, although issue #324 still makes me cringe...

@jslegers
Copy link
Author

@Snugug & @chriseppstein :

Even though I've been able to improve my syntax significantly due to various improvements in Sass 3.3, the lack of dynamic mixins remains a problem for my implementations. No matter how much I improve the architecture of the meta-framework I'm building, I keep stumbling on the lack of dynamic mixins as a blocking issue.

Just today, I found two use cases for interpolated mixins that I struggle to implement without interpolated mixins.

This is the first one :

Input:

// Polyfill for "opacity"
@mixin opacity($value) {
  opacity: $value;
  filter: alpha(opacity=$value * 100);
}

// Polyfill for "transform"
@mixin transform($value) {
  -ms-transform: $value;
  -webkit-transform: $value;
  transform: $value;
}

// This mixin will use a polyfill or custom rule definition when available
@mixin rules($styles) {
  @each $key, $value in $styles {
    if mixin-exists($key) {
      @include #{$key}($value);
    } @else {
      #{$key}: $value;
    }
  }
}

// The user can use standard CSS3 without caring about browser
// support. For browsers that don't support certain features, 
// polyfills will automatically be used when available.
// Polyfills can be modified or deleted at any time without any
// impact on user defined code.
.exampleclass {
  @include rules(
    width : 100%,
    opacity : 0.5,
    transform : rotate(7deg)
  );
}

Output:

.exampleclass {
   width : 100%;
   opacity : 0.5;
   filter: alpha(opacity=50);
   -ms-transform: rotate(7deg);
   -webkit-transform: rotate(7deg);
   transform: rotate(7deg);
}

This is the second one :

Input:

// Custom "grid" property
@mixin grid-cell($values) {
  @extend %clearfix;
  $float : nth($values, 1);
  $width : nth($values, 2);
  float: $float;
  width: $width;
}

// This mixin will use a polyfill or custom rule definition when available
@mixin rules($styles) {
  @each $key, $value in $styles {
    if mixin-exists($key) {
      @include #{$key}($value);
    } @else {
      #{$key}: $value;
    }
  }
}
.exampleclass1 {
  @include rules(
    grid-cell : left 30%,
    color : #000
  );
}
.exampleclass2 {
  @include rules(
    grid-cell : right 40%,
    color : #fff
  );
}

Output:

.exampleclass1, .exampleclass2 {
  display: block;
}

.exampleclass1:before, .exampleclass1:after,
.exampleclass2:before, .exampleclass2:after {
  content: "";
  display: table;
}

.exampleclass1:after, .exampleclass2:after {
  clear: both;
}

.exampleclass1 {
  float: left;
  width: 30%;
  color : #000;
}

.exampleclass2 {
  float: right;
  width: 40%;
  color : #fff;
}

See also #626 .

@Undistraction
Copy link

I too keep hitting this same stumbling block. I know everything is now frozen until LibSass catches up, but this would be a really powerful addition.

@laras126
Copy link

I think I ran into this as well. I'm customizing the Bootstrap grid mixins, and wanted to print out the classes for prototyping:

$grid-columns:  12; 
$sizes: ( xs, sm, md, lg );
$actions: ( push, pull, offset );

@for $i from 1 to $grid-columns {
  @each $size in $sizes {

    .col-#{$size}-#{$i} { @include make-#{$size}-column($i); }

    @each $action in $actions {
      .col-#{$size}-#{$i}-#{$action} { 
        @include make-#{$size}-column-#{$action}($i); 
      }
    } // each $actions

  }
}

Saw @jslegers prompt here. Hopefully that's helpful!

@lhguerra
Copy link

I may be a little late, but just ran into another case of the same issue:

// Translate susy columns to content columns.
@for $i from 1 through 24
  $translate: 24 / $i // Needs parse logic.
  =columns($i) // Or (#{$i}), not sure since none of this will work.
    +span($translate)

I began with this, and wanted to create a dinamic mixin to add the gridsystem's columns in a content-oriented way, so I would say "this box will have as many susy columns as it takes to make a 3 columns diagram".

.foo
  +container()
  .bar
    +columns(3) // This would apply the span(8) susy mixin, in my case.

I stopped here because it does not work, but I wanted to improve it to use inside media queries with just one declaration, so you see why classes and placeholders will not work for me.

@mattpilott
Copy link

I have now run into needing this, any updates in the last 3 years?

@ArmorDarks
Copy link

See #626 and #2057

@fuwaraido
Copy link

I think modifying SASS syntax is not appropriate solution for these kind of things...now I'm using jinja template engine to generate SASS codes and it works so so fine. If SASS needs meta-programming abilities, implementing a good macro language on top of current syntax is the way to go!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

10 participants