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

Creating Custom Functions #637

Closed
Deltachaos opened this issue Feb 16, 2012 · 28 comments
Closed

Creating Custom Functions #637

Deltachaos opened this issue Feb 16, 2012 · 28 comments

Comments

@Deltachaos
Copy link

Hi,

It would be nice to define Custome Functions like:

  darkfadein(@color, @value) {
    return fadein(darken(@color, @value));
  }

  .foo {
    color: darkfadein(#333, 10%);
  }

should be compiled to:

  .foo {
    color: #1a1a1a;
  }
@souldreamer
Copy link

Maybe to differentiate it from a mix-in and keep closer to CSS syntax, it could be:

@darkfadein(@color, @value): fadein(darken(@color, @value));

Kind of like a variable that depends on parameters (which if you think on is kind of a function =)

@alpha123
Copy link

+1 to @souldreamer's syntax.

@Deltachaos
Copy link
Author

But with @souldreamer's syntax it woul not be posible to write some
values into variables and use them again.

may useing this syntax:

  @darkfadein(@color, @value) {
     @foo: darken(@color, @value);
     return: fadein(@foo);
  }

may it would be possible in later versions to do something like this:

  @darkfadein(@color, @value) {
     @foo: darken(@color, @value);
     for(@i = 0; @i < 5; @i++): {
       @foo: darken(@foo, @i);
     };
     return: fadein(@foo);
  }

@tylertate
Copy link

I have a similar need to use a function of some sort to return a value. I think there are two possible solutions. One solution would be to extend the variable syntax as @Deltachaos outlined above, which is essentially:

@grid-width(@columns, @column-width) {
    @computedWidth = @columns*@column-width;
    return @computedWidth;
}
div {
    width: @grid-width(6,60);
}

A second approach would be to use mixins. Since the compiler treats mixins as JavaScript functions anyway, adding a return feature to mixins would likely be a simple remedy. Here's what it would look like:

.grid-width(@columns, @column-width) {
    @computedWidth = @columns*@column-width;
    return @computedWidth;
}
div {
    width: .grid-width(6,60);
}

SASS's "functional directives" provide a similar outcome, though I believe this mixin solution would be much more elegant.

Issues 508 and 609 are also related to this.

@Deltachaos
Copy link
Author

I think the second syntax of @tylertate would be the best. I seams to be the easiest because as you said less already parses mixins as JavaScript.

Would somethink like this be possible with mixins?

.grid-width(@columns, @column-width) {
    @computedWidth = @columns*@column-width;
    for (var i = 0; i <= 36; i++) {
      @computedWidth = darken(@computedWidth, i);
    }
    return @computedWidth;
}
div {
    width: .grid-width(6,60);
}

@matthew-dean
Copy link
Member

I'd rather see something like a defined LESS plugin syntax, and move programming-like logic to a JavaScript LESS plugin. These suggestions are inconsistent with current LESS syntax and design.

@lukeapage
Copy link
Member

Agree.. theres a bug about that marked documentation since its relatively simple to add functions when you know how.

@jackmoore
Copy link

So what was the resolution?

@lukeapage
Copy link
Member

We need to document it. see less/old-lesscss.org#54

and the linked issue from less.js shows how you can add a function to less in the browser

less = { functions: { rgbstr: function (color) {var str = color; return new(less.tree.Quoted)('"' + str + '"', str,true,1);}}};

at the moment there is no way to plugin functions into the node version, but there should be

@jackmoore
Copy link

Thanks, I appreciate the response and the position to get JavaScript out of LESS's syntax.

However, not being forced to associate a mixin's return value with a specific property seems like such a clear use case. Anyone that uses a grid is going to want to do what @Deltachaos was trying to do. It would be great to be able to achieve this without dropping down to the level of creating a plugin.

@lukeapage
Copy link
Member

Its a tricky thing - if you need to have loops or if's it needs to live in a plugin.

However if it is calling 3 less functions with specific values, I agree it makes sense to be able to extract that inside less in order to make the syntax DRY - it doesn't make sense to have to write a plugin to extract the fact that you want darken by 5% into one place.

@lukeapage lukeapage reopened this Feb 15, 2013
@lukeapage
Copy link
Member

I have re-opened but this might be duplicated somewhere else, we'll see.

at the moment variables from an mixins scope all get copied to the outer scope - which is kind of a way fo returning variables.. but its causing horrible problems and I want to remove it.

@lukeapage
Copy link
Member

duplicate of #538

@matthew-dean
Copy link
Member

"variables from an mixins scope all get copied to the outer scope"

Ugh, really? Yes, let's remove that behavior. I'd rather variables are marked for export, or some other thing than just automatic leaking. That's not an expected behavior to me. Variables should be block-scoped.

@lukeapage
Copy link
Member

yep, its like supporting functions via a back door bug

@fabienevain
Copy link

I find a hack : if you declare a global js function, you can use it later !

@fn: ~`fn = function(a) { return a; }`;

@arg: 8px;

p {
    font-size: ~`fn("@{arg}")`;
}

@neoascetic
Copy link

@fabienevain have found same hack just now :)

@ielgnaw
Copy link

ielgnaw commented Oct 13, 2015

@fabienevain It's work well, thanks~ 👍

@Ciantic
Copy link

Ciantic commented Jan 1, 2016

I found that you can create actual functions from the same eval jail by accessing process.mainModule... Only catch is that you may have to iterate over process.mainModule.children and match less.js if that order for some reason changes in future. I plan not to iterate just blindly trusting less is the third module.

Unfortunately you can't access the require, but you can access fs and other which are already required by less, which is plenty:

@anything: `(function() {
    // console.log(process.mainModule.children[0].exports); // node fs is here
    // console.log(process.mainModule.children[2].children) // children of less, more node modules!
    var less = process.mainModule.children[2].exports;

    less.functions.functionRegistry.add("firstfunc", function(a, context) {
        // console.log(a, context);
        return new less.tree.Color("00ff00");
    });

    less.functions.functionRegistry.add("secondfunc", function(a, context) {
        // console.log(a, context);
        return new less.tree.Color("ff0000");
    });
})()`;


test {
    background: firstfunc(white);
    color: secondfunc(black);
}

Neat thing about having real function is this context variable, which contains juicy details like the file being processed so you can e.g. create own svg data uri import with settings etc.

Edit I wonder why backticks are even introduced if JS is tried to be kept out. I like my LESS as copy & pasteable as possible so plugins are not good for me.

@seven-phases-max
Copy link
Member

I like my LESS as copy & pasteable as possible so plugins are not good for me.

So you assume your code is "copy&pasteable" even if you require node.js-based less.js compiler in your inline backtick hacks, but in the same time feel it goes wrong if you would use plugins? Doh!

@Ciantic
Copy link

Ciantic commented Jan 1, 2016

@seven-phases-max my tools are pretty messed up. If I could control the lessc command line arguments I would use plugins probably. (Or have one master plugin where I stuff everything) But no, I've screwed my environment, and I have ~100 WP themes in Eclipse workspace which I just can't get rid of because all the build commands etc are stuck in there.

@seven-phases-max
Copy link
Member

@Ciantic First of all you don't need any particular command line options to use a custom functions plugin - if necessary (#2479). Secondary I doubt whatever deeply screwed environment prohibits you to control compiler options (after all lessc "executable" is just an OS console script redirecting to actual node script - so one can easily inject anything there one way or another).

Either way my comment was just about "copy&pasteable vs. plugins" adv. while it turns out to be just a workaround for a broken build tools/chain.

@Ciantic
Copy link

Ciantic commented Jan 3, 2016

@seven-phases-max import plugin looks exactly the tool I need! Though I would like to define the functions inside my project, not in the global registry, this way I can edit the functions within the project and not worry about breaking billion less files if I make a change to global function.

@rjgotten
Copy link
Contributor

rjgotten commented Jan 4, 2016

@Ciantic

Though I would like to define the functions inside my project, not in the global registry, this way I can edit the functions within the project and not worry about breaking billion less files if I make a change to global function.

Read further in. The initial draft of my pull request went the easy route of global registration, but with some gained insight I later refined it to do scope-local registration. E.g.

.my-mixin() {
  @plugin "my-func.js";
  @value : my-func();
}

will not leak my-func outside of the mixin scope. Paths are ofcourse also relative to the file that contains the @plugin declaration, so everything is neatly bundled and shippable for consumption by third-parties; 100% transparantly.

That was my design goal for this feature. ^_^

@amarkalyane
Copy link

amarkalyane commented Aug 17, 2016

less.js is need for adding custom color combiantion
problem occur during commit the code
it is supporting but need of less.js

@hiyangguo
Copy link

I find a hack : if you declare a global js function, you can use it later !

@fn: ~`fn = function(a) { return a; }`;

@arg: 8px;

p {
    font-size: ~`fn("@{arg}")`;
}

@fabienevain How can I use less functions in @fn? Such as hsvsaturation unit and so on .Thx.

@rjgotten
Copy link
Contributor

@hiyangguo

You shouldn't use inline JS expressions, period.
Build and register custom functions the appropriate way.
Read the documentation. It's all there: http://lesscss.org/features/#plugin-atrules-feature

@hiyangguo
Copy link

@rjgotten OK, thank you very much.

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

No branches or pull requests

16 participants