-
Notifications
You must be signed in to change notification settings - Fork 2.2k
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 mixin invocation #626
Comments
I do think it's incongruous from a user's perspective why they can interpolate a placeholder selector for extend but not for mixin & function definitions and calls. It seems like we can add interpolation for @include and @mixin fairly easily. However, function calls are trickier and I think it may be better to have a generic |
My primary use case is for mixins, so I'm happy with that solution. The generic call for function works for me too. Both would solve the issues I need solved. |
For another real world example I'm writing a mixin right now that has a keyword list, and needs to call a related function if an argument passed in contains a keyword. With function interpolation I could do (pseudocode) |
Any movement/further thoughts on this? We're running into quite a few places where this functionality would be immensely useful |
I'm reasonably open to this, although I doubt I'll have time to implement it in the near future. |
I'll take a swing at it. @nex3 do you prefer |
|
+1. Was looking for exactly this yesterday, in the same form as with #673. I was surprised to find that it didn't work. If I understand it correctly, one can do @media #{$breakpoint} {...} but not @include #{$mixin_name}; ? |
What about a mixin-including function? Something like: include(mixin_name, args...); ? |
@lunelson Interpolating a @media string (which gets printed straight out to CSS) and interpolating a mixin call (which then needs to dynamically call another piece of code) are very different processes, so it shouldn't be too surprising that it didn't work (especially considering you can't interpolate variables or functions either). As stated above by @chriseppstein, he thinks that they'll be able in do interpolation for mixins with the current |
Thanks @Snugug, I see your point and I missed @chriseppstein's mention concerning |
@chriseppstein how goes the work on the call/invoke methods? I ran into the need today and spent an hour trying to homebrew a solution in pure SASS before I realized it was impossible. I'd love to help, but unfortunately I have no ruby experience, and glancing over the apparently relevant files (interpolation.rb and functions.rb?), I realized this might not be the best project to jump in on headfirst. If there's anything I can do to help, please let me know. In the interim, is their a recommended solution around this? Should I just build a lookup table for the functions I plan to use and a behemoth if tree? Thanks! |
What is the state of this call/invoke function calling function feature? |
Low-priority. |
After thinking about this more, I don't really like the idea of adding interpolation support to mixin names. I dislike interpolation and only ever want to use it when there is no way of doing a more readable syntax. For example, I find this to be less readable than some of the options I demonstrate below: @include #{$some-mixin}($arg1, $arg2); Since, we have full control over what is a valid syntax for
Thoughts? |
I like the former much more than the later, but the more I think about this holistically, the more I'm not entirely thrilled by either especially when compared to interpolation. How, for instance, would either of the options proposed work with #366, assuming that's still on the roadmap? Can the answer be implied interpolation? What if a variable/expression is included for a mixin name (mixins currently strictly disallow for naming conventions that would look like a variable/expresion, and this would stay) that it would attempt to resolve the variable/expression? If it doesn't resolve to a string, throws an error. Once it's been resolved to a string, it uses that as its mixin name? From there it's just mixin as normal, probably passing through a check to see if the mixin exists (and if not, throwing an error). Something as simple as the following: $foo: 'bar';
@mixin baz {
content: 'baz';
}
.baz {
@include $foo;
} This would also work with the syntax in #366: .baz {
++$foo;
} |
Most of the desire to use interpolation on mixins/functions stems from the fact that they are not first-class entities (ie. they can't be passed as arguments to other mixins/functions). If mixins should become a first-class entity, we'll have to have some way of expressing that anyway, even if adding interpolation wasn't on the table. Snugug's proposed |
@Snugug @cimmanon The issue here is that using a variable is not forced. Any valid SassScript expression would be allowed, including no indirection at all. So any syntax we choose must allow for a bare identifier to be unambiguous. This is the case for |
@chriseppstein That's exactly what I'm implying. I would assume the same would be true for either of your proposed syntaxes, and that's how it would work with interpolation. For me, the most common use for this feature would be something like the following (and why I like interpolation): $mixins: 'foo', 'bar', 'baz';
@mixin api($mixin-type) {
@each $mixin in $mixins {
@if $mixin-type == $mixin {
@include namespace($mixin) {}
}
}
} Or, with interpolation, would look something like the following: $mixins: 'foo', 'bar', 'baz';
@mixin api($mixin-type) {
@each $mixin in $mixins {
@if $mixin-type == $mixin {
@include #{$mixin}-namespace {}
}
}
} |
@Snugug the problem is that we need to know what parts of that expression are the SassScript that means the mixin name and what parts are the argument list so that we can parse them appropriately. It's very hard to suss out the last parenthesis group using either a regexp or recursive descent parsing. As such, I'm pretty sure we need to have a way to demarcate the argument list expressions from the name expression. (correct me if I'm wrong, @nex3) |
Ahh, I understand your point now. In that case, this brings me back to one of two things that I think are the best options for this; either magical interpolation but only allowing for variables, or interpolation. I understand that you're not a fan of interpolation, but it does make that distinction for us that you're looking to have, it's an established (if not liked) pattern, and it doesn't add additional complexities to the mixin syntax, allowing for the mixin syntax to take on other forms. Additionally, when it comes to #366 for instance, one of the wants is mixins to look like properties using that syntax, and having interpolation brackets would bake it look identical to property interpolation that we can do now. |
Regarding #366, that syntax is simply sugar to support a common use case, but as I envision it, it does not express the full power of the Forcing the use of a variable is unnecessary and arbitrary -- the only places we do that are where the variable is an L-Value (being assigned to). And I don't agree that interpolation is a better option than the one I specified above. It's a jumble of curly braces and hash signs and it's hard to read. I don't see a need to escape our own syntax. CSS is a verbose language and we do not need to be terse here. I think the include syntax for dynamic mixin names can and should more closely resemble the |
Maybe where I'm getting caught up is the additions to the .foo {
@call baz [with (arglist)];
}
.bar {
@call qux [with (arglist)] {
content: 'Qux';
}
} I'd even be happy with |
I don't think a completely different directive is warranted to accomplish this. It's still just an include of a mixin. If I'm reading a file and I see |
I'm not entirely sure I agree with the later part of your statement. If I saw |
@Snugug right. As I stated originally "What i don't like about this is that it makes include seem like it could include things that aren't mixins". But I don't want to solve this with interpolation, nor a new directive. Some other keywords are fine though. |
I'd be happy to write up a formal proposal for the feature. I can get started on it tomorrow, unless there's something else blocking progress. For what its worth, I've made a working implementation of this in dart sass, with the API surface I described in #3439. Also, apologies for opening a duplicate, I tried a few different search terms, and didn't come up with anything. |
Feel free to start working on a proposal—there are instructions in CONTRIBUTING.md. |
The proposal has landed! I'll give it a month for public comment and then we can start working on implementation. |
Wow this is really cool. Definitely solves a problem and removes the need to do some very meta stuff like |
@stof The next steps here are implementing the embedded host wrapper and updating the website documentation. I can handle the latter if you do the former. |
I assume you intended to tag me(?) Happy to take a look at updating the embedded host wrapper if that's the case. (tangentially: the linked spec-test PR is sass/sass-spec#1942, but it should be sass/sass-spec#1933) |
Haha, yes, sorry about that! |
This issue has been closed and the new feature seems to have been merged in, but I cant see any official documentation or implementation details for the proposed solution, what is the status of this? EDIT: I found out, for anyone looking to do dynamic Mixin includes, the following works: @use "sass:meta";
$variable: "a";
@include meta.apply(meta.get-mixin("test-#{$variable}")); //Will include a mixin called "test-a". More info here: https://sass-lang.com/documentation/modules/meta/#mixins |
This is also documented in https://sass-lang.com/documentation/values/mixins/. |
One of the largest stumbling blocks that contributors face right now is being able to cleanly build extendable systems using Sass. While the zip/index method currently proposed as a best practice works OK for variables, it simply does not work for mixins or functions. While we could build a lookup function/mixin for dealing with this, if we are looking to have 3rd party add-ons to a system we've made, the writing of that lookup function/mixin needs to be passed off to the end user, creating a terrible end user experience especially for inexperienced users. As system contributors, we need a way to develop APIs for our systems in a way that is contained from within our extensions. The only way I can see this being accomplished is through mixin and function interpolation.
We are currently running into this problem with attempting to create an Output API for the next generation of Susy, one of the most widely used Compass extensions available (between the two versions, it's something like the 2nd most installed Sass/Compass gem that's not Sass or Compass itself; right behind Bootstrap). While we can create a lookup function/mixin for people to contribute output styles, it leaves the burden on the end user to do it if there are output styles created in contrib. We are thus left with the following user experience, which IMO is terrible:
Inside Extension
Inside non-core Output Style
Inside User's file
Would need to write to use non-core Output style, a bit too technical for most users
Read output styles from debug. Only really works from Command Line, not from GUI apps which many many people use.
What we'd much much rather prefer is to do the following.
Inside Extension
Inside non-core Output Style
Inside User's file
NOTHING! They'd just need to use the system like they'd expect to be able to without any setup! Everyone's happy!
The text was updated successfully, but these errors were encountered: