Closed valllabh closed 8 years ago
go to http://www.lesscss.org/ and search for guards.
to avoid having to use mixins, looks at the feature request to allow guards on any css.
Yes I know that but if we are writing huge LESS we have to write and manage hundreds of .mixins I don't think mixin solves this issue. Even mixins will get lot of power with this feature. Less CSS almost have all the feature that some programming language has then why not this ?
.button-maker (@style: 'light') {
cursor: pointer;
display: inline-block;
padding: 5px 10px;
if(@style == 'light'){
/* adds light styling */
}
if(@style == 'dark'){
/* adds dark styling */
}
}
I would add a big +1 for this feature I'm really missing. Maintaining guards with a big codebase really is a nightmare. This would really make things a lot easier...
We can even add switch statement..
@lukeapage can you please reopen this issue. I think we should discuss this issue/feature
LESS is not a scripting language. The syntax / style resembles CSS.
LESS is meant to be declarative, meaning it shouldn't be capable complex situations with branching logic, loops, etc.
@pierresforge do you have any examples where guards are insufficient?
and where it wouldn't be solved by being able to have guards on any selector?
the discussion is open.. if you can persuade me (or another member of the core team) they are necessary then I'll re-open the issue.. but the conversation has been had before and alot of time has been spent on it.
The sentence from http://lesscss.org/ "LESS extends CSS with dynamic behavior such as variables, mixins, operations and functions."
Yes LESS is not scripting language, but still it has features like variables, functions & operations. What missing is conditional logic.
I think CSS Media Query is nothing but the conditional logic.
@media all and (max-width: 699px) and (min-width: 520px), (min-width: 1151px) {
body {
background: #ccc;
}
}
If CSS can have conditional logic like guards. so what is the difference. Guards just extending it to mixins. Guards only allows conditions outside CSS code block and not inside code block. Please see https://github.com/cloudhead/less.js/issues/1293#issuecomment-16929701 for an instance, guards cant help with such requirement..
Even HTML has conditional logic, yes and its a markup language not scripting language.
<!--[if gte IE 9]><!-->
<script src="" />
<!--<![endif]-->
@lukeapage thanks..
Guards are analogous to what media queries do. Notice that in ordinary CSS you can't nest media queries inside selectors; You can only put them at the root of the document.
Nope, I don't have any example where guards are not sufficient. However, there is plenty where guards are not really best for maintenability.
An example:
.button {
.inline-block();
.fix-ie-left-padding();
.shadow(0, 2px, rgba(0, 0, 0, @buttonShadowOpacity));
.border: 1px solid @buttonBorderColor;
if (green(@coreBackgroundColor) > 50%) {
background-color: darken(@coreBackgroundColor, 20%);
}
else if (red(@coreBackgroundColor) > 30%) {
background-color: darken(@coreBackgroundColor, 15%);
}
else if (blue(@coreBackgroundColor) > 25%) {
background-color: darken(@coreBackgroundColor, 8% );
}
else {
background-color: @coreBackgroundColor;
}
}
is in my sense more concise and self-explanatory (mostly in the cases where there's no point in making the code re-usable, as the algorithm depends on a lot of factors) than:
.buttonBackground(@color) when (green(@color) > 50%) {
background-color: darken(@coreBackgroundColor, 20%);
}
.buttonBackground(@color) when (red(@color) > 30%) {
background-color: darken(@coreBackgroundColor, 15%);
}
.buttonBackground(@color) when (red(@color) > 25%) {
background-color: darken(@coreBackgroundColor, 8%);
}
.buttonBackground(@color) {
background-color: @color;
}
.button {
.inline-block();
.fix-ie-left-padding();
.shadow(0, 2px, rgba(0, 0, 0, @buttonShadowOpacity));
.border: 1px solid @buttonBorderColor;
.buttonBackground(@coreBackgroundColor);
}
I agree this is subject to discussion, but this would be a pretty nice addition, if it does not involve a lot of core changes. It allows to wrap together most of the rules to apply to a single element without requiring the addition of mixins.
This kind of situation is why I wrote the contrast function - it acts as a kind of conditional operation that isn't otherwise available, and it's much neater than using guards, as you say. I guess we could implement some kind of generic function that acts as a kind of selector, rather than going as far as adding syntax for conditions.
@Soviut Devil's advocate - In LESS, you CAN actually place your media query inside of a selector, which is often quite useful.
I've been against ifs, because it starts to make the complexity of your code raise exponentially, and LESS is designed to be simple and straightforward. At this moment in time, there's some push to stabilize the language over adding new features.
However.... devil's advocate again, based on the logic that media queries now appear within selectors, guards could theoretically follow the same principle.
.button {
.inline-block();
.fix-ie-left-padding();
.shadow(0, 2px, rgba(0, 0, 0, @buttonShadowOpacity));
.border: 1px solid @buttonBorderColor;
background-color: @coreBackgroundColor;
@when (green(@coreBackgroundColor) > 50%) {
background-color: darken(@coreBackgroundColor, 20%);
}
@when (red(@coreBackgroundColor) > 30%) {
background-color: darken(@coreBackgroundColor, 15%);
}
@when (blue(@coreBackgroundColor) > 25%) {
background-color: darken(@coreBackgroundColor, 8% );
}
}
or
.button {
.inline-block();
.fix-ie-left-padding();
.shadow(0, 2px, rgba(0, 0, 0, @buttonShadowOpacity));
.border: 1px solid @buttonBorderColor;
background-color: @coreBackgroundColor;
background-color: darken(@coreBackgroundColor, 20%) when (green(@coreBackgroundColor) > 50%) ;
background-color: darken(@coreBackgroundColor, 15%) when (red(@coreBackgroundColor) > 30%);
background-color: darken(@coreBackgroundColor, 8% ) when (blue(@coreBackgroundColor) > 25%);
}
More declarative than ifs, closer syntax to existing LESS, and evaluates each statement independently (like LESS / CSS is defined).
Alternatively (or in addition to), similarly, attaching the evaluation of a mixin to the point of evaluation:
.button {
.inline-block();
.fix-ie-left-padding();
.shadow(0, 2px, rgba(0, 0, 0, @buttonShadowOpacity));
.border: 1px solid @buttonBorderColor;
background-color: @coreBackgroundColor;
.backgroundMixin(@coreBackgroundColor, 20%) when (green(@coreBackgroundColor) > 50%) ;
.backgroundMixin(@coreBackgroundColor, 15%) when (red(@coreBackgroundColor) > 30%);
.backgroundMixin(@coreBackgroundColor, 8% ) when (blue(@coreBackgroundColor) > 25%);
}
Arguably, statement-level guards are no more/less complex or different logic than mixin-level guards IMO. Probably examples 2 and 3 are less expansive to the language. Just thought I'd throw that in there for thought / discussion.
I guess no one had much to say on my declaration guards proposal. ;-)
In fact, no, not much to say. That would just be a great thing, IMO :)
Yeah, if they're already on mixins, and media queries can be inside elements, seems like the logic for extending them is reasonable. Curious what @lukeapage and @jonschlinkert and others think.
@matthewdl :+1:
Think I already supported them on the issue they are discussed on.... :)
@lukeapage Are you talking about issue #748? If so, I've added a little to the syntax. ;-)
I'd like to chime in expressing my desire for some form of if
or @when
.
I don't agree with the sentiment that guards are universally better and make LESS code less complex than using conditionals. I'm finding practical situations where guards in fact make my LESS files uglier and harder to read but an if
/@when
would actually be exceptionally readable. Guards and if
/@when
conditionals are inherently just tools, both of them can be used to make things beautiful or ugly and are better suited to different situations. Hell, I can create a ~300b .less file that only uses LESS' most basic selector expansion feature to eat up over 1GB of RAM and leave the CPU churning for minutes.
In a recent project I was working on with the intent of heavy support for both desktop and mobile I eventually decided that doing this with inline media queries wasn't good enough. Serving extra css to either mode that it wouldn't even use didn't sound nice, plus the whole trouble with trying to cascade background-images double loading and having to work around that while still being unable to embed a data URI without wasting time serving an entire image that a mode wouldn't use. And dealing with this by trying to cascade two stylesheets would just make my less a bigger mess since now there would be two places declaring the styles for one class.
My decision was to take a page from how MediaWiki handles RTL, by serving two stylesheets from one. MediaWiki does this by writing stylesheets in LTR and then flipping the stylesheet to create a second RTL stylesheet. There is no need to try and override everything specified for LTR to handle RTL. Likewise I decided I'd write my primary styles in a main.less (and potentially files referenced from it to separate the styles), that file would not be compiled directly. Instead I would have two stylesheets a desktop.less and a mobile.less, both of these would set stuff like @mobile: true/false;
, @desktop: true/false;
, and also override some less variables like the sizes of things. This way my main.less ends up turned into both a desktop.css and mobile.css, and I can handle the media query/test determining which one to load elsewhere (if I REALLY wanted to I could actually determine which one to load even on a device that doesn't even support media queries).
The one issue however is conditional blocks. There are portions of the code I decide "mobile doesn't need these styles" or "desktop doesn't need these styles". Currently the only way I can do this in less is with: ((Ok, technically these styles are hypothetical since I'm not using an accordion navigation but they hold true to what I am doing))
.navigation {
color: @text-color;
// [...]
// Navigation is an accordion on the desktop so include our accordion styles
.dummy() when (@desktop = true) {
.accordion-styles();
}
.dummy();
}
In this situation it would be much much more understandable to use something like @when
.
.navigation {
color: @text-color;
// [...]
// Navigation is an accordion on the desktop so include our accordion styles
@when (@desktop = true) {
.accordion-styles();
}
}
I actually contemplated pre-processing my less files to turn something like a @when
syntax into a hack where a dynamically generated mixin+guard is created and then included directly after it.
Needless to say, if the idea of pre-processing a pre-processor sounds like a good idea that will make things easier to understand... something is wrong with the current pre-processor.
And the truth is that this doesn't only apply to my desktop/mobile technique. This could equally apply to themes schemes. Write one main less file with your theme. Then have a bunch of less files you simply include that less file into and then set some less variables to control things like the colour scheme. Colour tweaks will work fine, but you'll immediately run off a cliff once you find you could really use something like @use_horizontal_nav
.
@dantman as of 1.5.0 and guards on css styles which is linked you can do this
.navigation {
& when (@desktop = true) {
color:red;
}
}
which is sugar for your example.
The only downside is that at the moment it thinks that it is a new selector so it doesn't merge it with the previous selector. However I think this is unlikely to cause you issues and will be something I/We will fix in future versions.
Ok thanks, can we have that documented as a feature?
To be honest I'm also not sure it's very understandable compared to @when
, I'm not sure that if I had previously seen & when(...)
in a .less file I would've understood what it did at a glance. @when
as sugar for & when()
could be nice.
I agree with @matthew-dean adding this kind of conditional structures will likely make the LESS core files harder to maintain and test. And the .less files harder to debug and so forth.
What we got right now is a piece of art, people can understand and learn the basics of LESS in literally less than 10 minutes (im talking about nesting and mixing), adding conditional statements just makes things harder to digest to other kind of people like designers and enthusiasts. trying to use guards to mimic or simulate IF statements is just trying to exacerbate a problem that does not exist and does not need a solution.
If a developer need more complexity and control over their LESS files before compiling them, there is nothing to stop them to use languages like PHP to pre-process some less files in order to achieve greater control, flexibility and complexity, there is many ways to do this is just a matter of use the imagination to do this in the most correct or convenient way.
For example lest say I have a file named "myfile.less.php" that have crazy code with conditional, switches, arrays and all the fancy stuff you can see on PHP and then from that ".less.php" file spit another file called "myfile.less" that have the final LESS code, this way i can query all the server vars with PHP, decide what @imports my new .less file will include or exclude, no more need to pass large amount of silly vars to the LESS compiler and no need to write ugly, hard to read silly guards to mimic of simulate IF or Switch structures.
A basic workflow could be like this
1- check if cache expired 1- call script to find and pre-process all the files ".less.php" files 2- call script to compile all the ".less" files 3- update cache
@enav I fervantly disagree. as well as developing less I use it in enterprise solutions based on top of node, where we do not want to have to use 2 languages to solve a problem.
@dantman there are quite a few things not documented and yes, we need to document that. there is an effort going on at less-docs to expand the documentation and make the website alot better. I'll add an issue there.
@lukeapage, yeah I noticed that recently. I skimmed through the changelog and noticed stuff like svg-gradient and property merging with +.
Strictly speaking & when ...
is already documented since it's just a simple combo of two documented features &
and CSS Guards. But a dedicated example will not harm of course.
@seven-phases-max, I wasn't aware of that documentation. From the looks of it, that's an incomplete new set of documentation to replace the old site, going by the "This will be moved to the lesscss.org repo soon! We recommend that you avoid linking to this site until everything is live." comment. Which is nice to know that something like that is in the works.
I was referring to the current documentation on lesscss.org which were the only docs I was aware of, and currently only documents guards as a mixin feature, not suggesting that it's available anywhere else.
Guards are not as good as this. :(
This is a required feature and also present in scass.
+1 for this
Just since this comment today was raised a few years after this issue was opened, one thing I will point out that comes to mind at the moment.
Some of the resistance to @when
in the original discussion is the additional at-rule. However, thinking about it now, it shouldn't be needed.
Considering that these two blocks are equal:
.a {
& .b { color: blue; }
}
.a {
.b { color: blue; }
}
Then these two statements should also be accepted by the parser as equal.
.a {
& when (1=1) { color: blue; }
}
.a {
when (1=1) { color: blue; }
}
If for no other reason than & when
is syntactically really awkward. Omitting &
to still imply a selector join with the parent selector has existed all along, so omitting &
to imply a guard on the parent selector doesn't seem like a leap. Also, block-root functions are now accepted by the parser for use with plugins, so it no longer looks as strange as it might have in 2013.
I don't see any reason to reopen this. After all we already have #2072
when (1=1) { color: blue; }
Opening a great confusion box just for the sake of two characters economy?
Guards are not as good as this.
I thought it would not even be worth to comment a random unreasonable comment. Any position should have something behind it (Otherwise I'd say any conditional code is lame and if
s are silly just because I know how to write anything without even a single condition in almost every language).
I don't see any reason to reopen this. After all we already have
when (1=1) { color: blue; }
Errr... no we don't?
But it's reasonable that we should. We could just allow it and be done with it.
Wait, until I'll find the feature request it's supposed to link to (sorry, I accidentally clicked enter before finishing it)
So merging this to #2072.
@seven-phases-max
That issue is called "Allow variable redefinition inside guarded self running blocks" and the tangential discussion at the bottom to that issue contained analogous use cases to a select / case and a vbscript iif([ifcond], [then], [else]) function more than just the naked when, although it's perhaps a more logical pickup for the discussion. Buuut... probably it should just be raised as a new issue with just the when ()
vs & when ()
question.
with just the when () vs & when () question.
You can't be serious on this, can you? Because of what exactly? when
won't not become if
(see #2072 for all the reasons) if you remove &
. So leave it as is and be done with it.
Hey, can you tone it down a notch? If you're upset about it, take a moment and come back later to look at the reasoning I posted in the new issue. A few things have evolved since these original discussions.
I did not mean to offend or something. My:
So leave it as is and be done with it.
was just a reply to:
We could just allow it and be done with it.
@seven-phases-max Okay, tone gets lost in text. In any case, I wasn't terribly attached to this idea, and overall, the existing @when
proposal is better / more versatile anyway.
I think 'sass' is better than less. It already has if-else. It is really hard and many lines of coding the less. When works but it doesn't full fill the need. Additionally we can make loops in less but language should have it's own feature.
@aukgit Use the right tool for the job. If SCSS suits you better, it's better to use that. Sass and Less are very different projects.
@matthew-dean after learning less for months and writing tons less if 'less' community doesn't want to be better then it's loss for both of us. I think that is why bootstrap moved to 'sass' (https://github.com/twbs/bootstrap/tree/v4-dev) from less (initial reason was faster compilation). Whereas there initial source was in 'less'. Now they write their original source in 'sass' and then convert it to less (whereas it was the other way around).
Another thing :
"Right tool for the right job"
shouldn't apply here
Less | Scss | Stylus similar projects . Started with one which was popular at the time , now it seems like that 'less' community doesn't want to improve themselves.
The idea behind the 'less' is to write less and do more. If we are writing same mixn over and over again then there is no point of naming it 'less' , better if we name it 'more and less'. Somewhere it does reduction but somewhere it requires more writing.
Thank you for your comments.
Fanboyish outdated propaganda. It would be a waste of time to argue all of these biased or simply false statements.
E.g.:
There's no way to make really compilacted conditional code.
Yeah, "I need a tool to write my spaghetti code" - just like this. If you write a code like this, indeed Less is definitely not your tool.
I think "less" does 'x' something very different than sass and stylus. All other does similar things whereas 'less' claims it is not a similar kind. :)
Now, I get it. Thank you. Truly, it is really the most misunderstood language ever. Even javascript is not that misunderstood as Douglas Crockford says.
People make mistakes in js but there are lot of awareness. For 'Less' it should be in the first page( less.org) describing what less is and what not.
Thank you.
Wrote a mixen :
Less:
.if-value-present(@attribute; @value: '') {
.x (@value) when not (@value = '') {
@{attribute}: @value;
}
.x(@value);
}
.if-value-present-multi(@attributes; @values) {
.iter(@i) when (@i > 0) {
.iter(@i - 1);
// loop
@attr: extract(@attributes, @i);
@value: extract(@values, @i);
.if-value-present(@attr, @value);
}
@len: length(@values);
.iter(@len);
}
.x {
@var: c1,c2, c3;
@val: v1, '', 'wddww';
.if-value-present-multi(@var;@val);
}
.x{
.if-value-present(content, 'new val');
}
CSS Output:
.x {
c1: v1;
c3: 'wddww';
}
.x {
content: 'new val';
}
if 'less' community doesn't want to be better then it's loss for both of us
I think it's a mistake to assume that "X feature I want isn't supported" == "Less community doesn't want to be better". That doesn't get us anywhere.
There are several discussions on Github to work on improving control flows. In fact, this thread wasn't rejected, it's been merged to #2072. You commented on a closed 3-year-old thread. I had some other ideas that I thought might have made it a bit different from #2072, but was essentially convinced otherwise.
now it seems likes doesn't have better features
Less doesn't have more features, this is true. This is by design. This is so you can learn Less in a day, and Less's features cover 99% of typical use cases. Sass has a steeper learning curve. Sass had so much feature bloat that they had to build a low-level C-based compiler just to be able to continue to compile it. Which is a great achievement. But not a great testament for something that, at the end of the day, is just making a style sheet.
Now they write their original source in 'sass' and then convert it to less (whereas it was the other way around).
True, but that's based on multiple factors, and you can ask them what those would be. I personally feel that abandoning Less users is a mistake, since there are a lot of people using Bootstrap in Less today.
At the end of the day, your complaints about the hoops needed to write conditionals are not wrong. The examples offered here are simply a bad fit for the language. I suggest you read over the thread in #2072.
(this is actually a bit offtopic, but I'm just wondering)
@var: c1, c2, c3; @val: v1, '', 'wddww';
And what would be a use of these variables beside calling .if-value-present-multi
?
(And if these variables are used for something else) Would not it make sense, to use a just property/value pairs instead, e.g.:
@rules:
c1 v1,
c3 'wddww';
// no need for c2 since it has no effect
More over, what could be a purpose of writing .x {.if-value-present(content, 'new val')}
if you need just .x {content: 'new val'}
? And writing .x {.if-value-present(content)}
if it does nothing?
(Though honesty I did look at [1] - well, not commenting on anything except just one thing: (just to save your time) consider throwing away ancient libs like elements
and lesshat
- and use autoprefixer
instead. We don't need vendor-prefixing libraries for either CSS preprocessor for a while now).
This is usually how most of "feature requests" come in: "XY Problem". (Other good examples of similar fails in the related area: #1400 and #1894, it's not that the features would be totally useless (hence both are kept open), but when one starts to ask for their actual use cases, it usually reveals it was just wrong Y in attempt to solve X).
How about having something like conditional css code depending upon variable