-
Notifications
You must be signed in to change notification settings - Fork 3.4k
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
Variables are actually variables?! #297
Comments
What if you do
What is the output? I was wondering about this little snippet from the docs as well... |
|
bump +1, I'm looking for clarification on this, too. I feel that it is important to be able to overwrite variables. Just as a simple example... if you have a set of Less documents defining the styles for a web app, and you import a skin.less that defines a bunch of color variables as part of that, I would want to be able to reskin the app by simply including an additional myskin.less that overwrites the particular Less variables that I need to overwrite for my skin. I don't want to have to edit the original skin.less. I don't want to have to create a new master Less document with custom import rules that imports my skin instead of the original. I don't want to have to copy and paste all of the variables from skin.less into myskin.less if I'm not going to be changing all of them. I guess the behavior I want and expect would be that if I have:
...then throughout all of the lines of Less, @color is effectively always blue. It seems like Less actually behaves exactly like I am describing (at least in 1.2.0) ... so perhaps this is just an issue that can be solved by making things more clear in the documentation. |
I'm also looking for clarification on this issue - my test using v1.3 results in variables being overridden, which I like. As zachstronaut comments, it's useful to override variables from thrid party sources (like Bootstrap). I intend to exploit this bug/feature for now and hope that nothing changes in future iterations of less.js. |
You're misunderstanding, I believe, and yes, I think that description comes from earlier inceptions of LESS. The reason they are not EXACTLY variable is that, outside of mixin parameters, variables have a set value in their context. Everything is evaluated sequentially, and values are essentially replaced. In other words, it's not like I don't know what @var will be, based on execution. @var will ALWAYS be exactly what it is, a value that has been assigned (a constant) in the process of evaluation. I don't know if that explanation is very good, but that's always been my interpretation of that statement. Variables NEVER change their value, because they always have a completely set code path. That does not mean they cannot be reassigned, but they are reassigned another constant value. (Which, by some definitions, makes them variable, but now we're getting into semantics.) |
the first declaration in the current scope is used rather than the last.. so to override you put override first. some projects even rely on this.. dotless uses variables mode (last declaration) and a flag to switch between less and dotless modes. |
Was this code changed at some point? In Less 1.3.0 the last variable declaration in the current scope is used... so to override you put override last. So the original poster's output CSS in Less 1.3.0 is:
CSS does this the same way. For example, the css below would make web browsers use the color:blue for the .test selector.
I think this is by design, and good. But, the terrible thing is that less.cs doesn't seem to provide any good control on scoping per file. So if I want to make one nice big .css file, and import bootstrap into it, then I have to be careful not to use a variable that bootstrap uses. |
By the way, the lessphp (a less compiler written in PHP) appears to generate CSS differently than less.js. The reason alixaxel's output was different than less.js is because the http://www.below.ch/lesso/parse/a7be1745/1 link he provided is using lessphp. I am guessing dotless works similarly to lessphp. I couldn't find an online dotless css generator to test. The dotless team thinks the way that less.js works might be a bug. It seems that it may, but I wonder what the dotless stance is on allowing mixins to contribute variables to the scope they are called. I personally have found it to be an annoyance because of the unintended consequences of bringing in someone else's less code as an import, however I wonder if has some usefulness for setting variables in a sub-scope (I couldn't think of any examples). @agatronic, did you ever hear from @cloudhead on whether he thinks this is a bug? The lesscss.org page says "Note that variables in LESS are actually ‘constants’ in that they can only be defined once.", but that seems like it would be even worse than the way less.js actually works now. My vote is to change less.js to work like dotless and lessphp |
dotless does not leak variables from a called mixin into the scope it is called from. I have not seen any change in the way less.js gets the variable to use, but testing it now, you seem to be right, |
I opened #905, and I agree with @bthule in this comment, that Less should behave more like dotless and lessphp. |
@MatthewDL @Synchro - being part of dotless I'm all for this.. what do you think, should we go ahead? my testing seems to show 1.3.0 gained this behaviour and no-one has complained so I think it would be a sane place to take less.js. WDYT? |
I think it's less confusing for it to act as suggested (last declatarion) - and I've often found that when I expected it to act like the docs say, it acted like a variable anyway! |
The problem with that though is that they're not actually acting as VARIABLES. Why would they be called variables if they behave as constants? |
I don't actually understand the problem. In the first two examples, it behaves exactly as I would expect, in that it follows the way properties are declared in CSS, which is what LESS is mimicking. It may be documented in a confusing way, but if I declare:
The value of @var should be red. Just like this DIRECTLY from the documentation:
Within the #page block, the documentation states that the color should be white. So, I don't understand the issue. The examples seem to be working as designed. |
Further to the point: this line in the documentation, "Note that variables in LESS are actually ‘constants’ in that they can only be defined once." is demonstrably untrue, as further along in the documentation, those variables are demonstrated to change their value within a mixin / block scope. (See "Scope") So, if that line were removed, it would help clear up confusion. Variables are variables, and you can and should be able to redefine them as much as you want, just as you would with any CSS property. Any case outside of THAT is a bug, but I see no bug here. |
The problem arises when you want the variable to be different values in different parts of your document. This comes into play especially when using grids. Lets say I want @column-width to be 60px initially. Then later in the document in a @media query I reset @column-width to be 40px. When Less compiles it, it sets @columns to be 40px across the entire document, as opposed to changing it as it traverses the document. Illustration:
It'll output 40px for both instead of 60px and 40px, respectively. |
Right. You can change it per scope, which is probably how it should be re-written. Just like multiple declarations of, say, a width property within a block results in the final one declared being the final value. As an alternative, you could define mixin blocks with a local value, or pass those column width values to the mixin to get the result you want. It certainly seems unclear in the documentation. But this still appears to work as designed. |
Yup, exactly. And I'd agree it works as designed. But I would like it to work this way ;) |
@MatthewDL.. because..
But yes I think, fix the scoping issues to be how you might expect and |
If it worked differently in the past, I think it might have been because of other scoping issues that @cloudhead addressed, but I couldn't say for sure offhand. Yes, there may still be scoping issues. I'm just pointing out how it appears to be designed, so that we can accurately frame what is and isn't a "bug". |
Another good example..
outputs...
|
Yes, that's the correct output. LESS is NOT a programming language. |
You shouldn't be reusing variable names in the same scope. |
@MatthewDL I well understand what DECLARATIVE means. It doesn't stop it being confusing.. Its easy to take a highly simplified view of things but do you not see how you are contradicting yourself "Variable values are evaluated per block, with the final one overriding previous values." - that implies sequential behaviour.. there are LOTs of inconsistencies in the way less handles variables and scope. Maybe it doesn't matter too much - the usecase for overriding variables is mostly themes and getting default values for theme variables that are not overridden and less does this at the moment. |
If the variables stay constants, what about outputting a warning to the console in case of overriding the variable within the scope? |
I don't disagree that it's potentially confusing for programmers who are |
Throwing an error would also keep behavior in line with the documentation. |
He said it isn't a sequential scripting language, meaning you can't do: @primary: red;
background: @primary;
@primary: blue;
foreground: @primary; LESS essentially flattens all the variables in a scope and uses the final result. |
@Soviut Correct. And since blocks inside inherit those other variables, you would get the same result even if |
For example, here's a "LESS" way of solving @nathanielks example:
You can have the same class outputting from the same variable, with two different values set in sequence. You just can't redefine the variable in that scope and have all subsequent code use the new value, and prior code use the old value. I think that's what @cloudhead intended to mean by the word "constants". |
ok, fine, I think your mis-interpretting me. I think the current behaviour is fine and I should have thought about that example a little more - it can be confusing, but is the expected behaviour. throwing an error when you define a variable for a second time would be a big step backwards - as I said - most of the use-cases for multiple definitions come from theming and having default variables which you want to keep in the less to aid development and have the variables close to the rulesets/mixins. The only way to do that is to define a variable and then over-write it later. It used to be people had to put the overrides first, now they have to put the overrides last.. thats fine and it makes sense. Throwing an error would solve no problems and just make using less in this context more difficult/impossible. So lets close this bug and leave things as they are.. There are other bugs that cover scoping issues and more complex questions. ?? |
@MatthewDL The best way to think about is the way a real CSS file works. If you define the body {
color: red;
color: blue;
} This is because CSS is declarative. There is no concept of "when" something was declared since there are no procedural "frames" or program flow. Your declarations will overwrite each-other in the same scope in CSS; The same is true for LESS. |
Correct. We've determined it works as designed (at least this particular behavior), so should be closed. @agatronic Wasn't intending to offend, just clarify for others. |
I'm glad I'm not the only one who's been confused about legality of redefining variables in LESS. Yes, the docs say they're constants... yet (at least as of less 1.3.0), redefining a given var multiple times works fine. For the record, there is now even explicit testing for this behavior in 1.3.0. It seems the lessc compiler does its parsing in one pass, and for any variable defined more than once, the last definition wins, so the last-defined value is used for all references to that variable. That final value is then "constant". But redefining variables in .less (as in the common case of theming, or overriding a vendor-provided set of less files a la twitter-bootstrap) doesn't seem to pose any problems. Note the test for this in the main project's test/less/variables.less:
which compiles to
... so it works, AND it is explicitly tested for in the official project's test suite. It seems like the only reason to consider variable redefinition illegal is the ambiguity in the docs' wording. Would LOVE confirmation from @cloudhead though! |
I think the term constant is generally correct in that they only ever have one value. However, like anything in CSS, they can be cascaded. I can't think of a better term for it than that. |
Right. Maybe the docs could simply say they have a value that can be overridden in that scope, like CSS? The "root-level" scope might need to be explained, since it's not wrapped in curly braces. |
I created less/old-lesscss.org#43 to cover this.. I think @MatthewDL's css analogy is a good way to explain it. |
Yeah, basically you could claim that values are like "CSS attributes", their values are constant, global to their scope, but can be overridden by cascading. |
Fixes less#297 Includes testcase
Integrate mml parsing test for issue less#297
Adding @var at end of documents yields results:
https://github.com/gruntjs/grunt-contrib-less/blob/master/tasks/less.js @ line ~144 |
Thanks, @matthew-dean Also allowed:
|
The LESS website says:
Note that variables in LESS are actually ‘constants’ in that they can only be defined once.
However, the following:
Generates:
Personally, I prefer variables to be variables, but I just can't get past the feeling that I might be missing something.
Are the docs wrong or outdated?
The text was updated successfully, but these errors were encountered: