-
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
Add support for "default" variables (similar to !default in SASS) #1706
Comments
See The Documentation.Update: Removed my previous comment of this post to not confuse future visitors (here I was trying to answer "how to define a variable if it's not already defined" question and missed the point it's an "XY Problem" and the correct answer for a |
If you define the same variable twice, the last declaration wins and is valid in the whole scope. So, another option would be to declare variables normally and ask user to include his variables after your library. library.less
user.less:
compiles into:
|
Thanks for the feedback on our PR. These solutions would be workable if we had a single thing or a few small things to consume. So let me elaborate on why these solutions are not workable in our case. We make a large framework of components implemented as a class hierarchy. We declare the variables for each component in its own file... in our base theme. We then "derive" themes from here that want to modify this variables. Users can further do so to tune a theme to their needs. Further, variables often "derive" from other variables. This most obvious is base-color. We have spent a good deal of time discussing alternatives to the problems of large class hierarchies of components using aggressive variable value propagation combined with themes and their own inheritance behavior. By far the best solution to these problems is to always define variables as "!default" (in Sass terms). This allows users to simply get in first and set their values before these values are used to calculate further values. The timing is then quite simple to manage for our users. Since this is always the case for every variable in all of our themes, syntax struggles like those suggested above would be quite a burden and also error prone. We'd love to contribute other features to Less as we continue. I think our (unusually?) large scale use case could be a helpful validation of what is needed to scale up the language / feature set. I hope you will consider merging this or provide some alternatives that are more declarative in nature. Exploiting scope tricks really won't work for us. Thanks again for your time and consideration! |
It seems you might be better off defining mixins instead of straight classes. Then, variables can be overridden late in your main stylesheet or another derived theme. That's how I'm able to override things like my gutter widths in my grids after I've imported them. |
Just to clarify we are talking about variables here only. And we don't have a main stylesheet - we have one per class per theme. :) Maybe I am not following your suggestion but as an example, one of our theme variable files looks like this:
This file sets up the various Panel values for a theme that has a chain of base themes 4 deep. Those base themes have their own variable values for Panel but these are loaded first so override them. Unless a user theme derives from this theme and provides values of their own. This pattern is repeated a lot :) |
Okay, well why not just override
Now anywhere that's used in the theme will be overridden. If nobody overrides this down your import chain, then whatever it was originally set to will be the default. |
We don't have a "main less sheet" :) I appreciate your help and suggestions, but we have had many discussions around this internally and they tend to go on for a bit. Suffice it to say that we believe we need default variable setting as we've proposed here. This feature exists in Sass and we found it very helpful in reducing complexity and providing users flexibility in configuring our themes. I am curious if all this means that this pull request or some similar derivative would ever be accepted? We'd be happy to tweak the syntax if that is objectionable. |
Simply replace "your main less sheet" in answers above with "any of user's less sheets". So far it seems that SASS |
To put it another way, imagine if you weren't importing and instead had all your variables in the same sheet. That's effectively what importing does. So in that situation, if you had two variable declarations with the same name, the last one in the sheet would win.
Because base colour is declared again at the end, the base-color used in the div will be red. It will compile to:
|
This came up before, in fact i believe it was even implemented and we had a pull request from guys who wanted it in bootstrap and they agreed after some discussion that it was a pointless feature in less.. The only thing it allows you to do is for users to define variables before importing. If users define overrides after then it works as if it had a default on it.. that's because even in the imported file it will take the last definition in the same way as css, even if defined after usage. Its not that much of a burden to users of a library to say they override variables after imports (or import a variables file after imports) vs adding more syntax to less.. we think that is one of the adanvantages of less, that you can do the same amount as sass but most of the time with simpler syntax. This is counter what a JavaScript programmer might think, but the idea behind it is that its closer to css. Please can you elaborate on why asking consumers to consider order is not possible or desirable? We will accept features with strong usecases but we have to be rigorous to avoid language and complexity bloat. |
And just to clarify, we're not saying "no" by any stretch. We're trying to show his this feature may already exist. |
I added some info to less-docs |
Closing as already available (in "Less way") feature (http://lesscss.org/features/#variables-feature-default-variables). |
I think this confusion often comes up because while there is overlapping syntax, SASS "executes" from top-to-bottom but LESS does not, and instead operates more like CSS. LESS is like CSS+ (CSS with additional features), and SASS is like "PHP with CSS syntax". I wonder if there's a way (if necessary) we could make that distinction. |
Because we teach library users (and consumers) to never edit library codes themselves. The missing
The sass approach means that a library only has to provide a single file, that the user will then be able to customize. my-color: red;
@import "./my-library.less"; Instead of: @import "./my-library-variables.less";
my-color: red;
@import "./my-library-rules.less"; I think that you should reconsider this issue. |
@arcanis Actually I can't see why you think that:
In Less it's absolutely the same library design:
I guess you're simply missing the "Lazy-Loading" thing (and exactly the same example is used in the docs to say |
@seven-phases-max WTF, you're right. Dang it, I should probably delete my entire reply (and just did). You're telling me you can import bootstrap, and just override specific vars, and it will just work? I didn't believe you, and did my own tests to verify it. How did I miss this?? I think it's because every article I've seen on Bootstrap, which included customizing, recommended that you copy the variables.less file and set your own values, which of course causes issues when more variables are added to the library. And I think I had the impression that selectors were being output at the immediate point of import. Did variables always "lazy load" in this way? This has to be the single most important commonly missed feature in Less. I've never seen a blog post about Less or a Less library that mentions this feature. Even with everything in the thread here, and the documentation, it wasn't immediately obvious what that meant with real-world libraries. I thought everyone was simply saying you could override variables declared earlier by setting them later, and I never got that it would affect the evaluation of variables from earlier imported documents. I can't believe I never got this until now. This changes basically everything about how I structure my less documents, and it has a tiny mention in the docs that doesn't demonstrate output. Maybe the reason we get so many requests for a At the very least, thanks for explaining it again @seven-phases-max! |
Woops. Well, you're right. I misunderstood the docs too, my bad! |
@Soviut you are a gosh-darn genius! I had no idea variable declarations work like that in SASS/LESS. Thank you! |
I'm still slapping my forehead that this wasn't obvious behavior with everything else I've written in Less. I think my problem was that I'd seen poor example articles written by people who didn't understand how to do it. Also, note: @spikesagal as far as your statement: "I had no idea variable declarations work like that in SASS/LESS" -- To the best of my knowledge, the behavior of variables is not the same between the two languages, since SASS and LESS evaluate things very differently. |
And they are killing this feature in BS4 because of moving to Sass... 👎 Still pretty sad about that move. So it begins, the fight for more !default variables: [twbs/bootstrap#17418] |
Well, they cannot kill Less feature by changing SCSS sources in whatever way. (Technically it's not even a "feature" (like some sort of "synthesized behaviour") but a fundamental property/corollary of lazy-evaluation). As long as there's Less version of BS (that is just a matter of count of people willing to contribute to it), and there's at least one global variable - you will always be able to override that variable. |
Well. The official v4-alpha of bootstrap moved to Sass. That, at least in my understanding, is killing less in their official documentation - and that means also killing the support for this feature because Sass has no such thing as lazy-loading variables. They only support !default, which, in a way, means: "Oh yeah, we allow you to override our variables just once from the outside, and only if we permit to do so. So, if we forget to give you access to a variable by simply omitting the !default, you're pretty much screwed and have to override the whole darn selector in your files. Deal with it." |
Well, this only means "they kill it in Bootstrap" (which is obviously something out of this thread scope - as long as there's no Less version of BS, we should not care of whatever Bootstrap features in this repository, should we?). |
Since this discussion was pretty much about overriding variables in bootstrap... |
We're sticking with less 😄 |
…ad + class options. - `handleMenuItemClick`: Even when an item is *disabled*, we still consume the *click* user input. It's just that we don't do anything with it... + `hideMenu()` is now an extra API. - `options.monitorBodyEvents` option: (default: `true`) monitor body mouse click events: when the user clicks outside SlickGrid the menu will be destroyed. Note: more complex applications for which this basic functionality is unsuitable can handle this themselves by calling the `hideMenu()` API when the application logic decides the header menu should go away. - `destroy()` method should not forget to kill the possibly already created menu attached to the grid container: `hideMenu()` -- otherwise that one would be lingering and load the DOM for no gain at all. - documentation in the headermenu LESS file - theming the headermenu LESS file: ## Theming Themable entities; see also less/less.js#1706 why we do it this way. The user can override these settings by specifying the same name, with their choice of value, in the user-defined theme LESS file which should follow this LESS file. See also: http://stackoverflow.com/questions/18792255/how-to-override-mixins-in-less-css-1-4
I have a use case for the originally stated dilemma. Consider the following simplified example:
Item 3 is not currently possible, so far as I can see. |
It does not look like you've read the thread: @import "path/to/file.less";
@import "here.less"; // if it's not defined <elsewhere>, then use the value I define <here>
@import "elsewhere.less"; which of course can be reduced to: @import "path/to/file.less"; // <- define default value there
@font-size-h1: foo; // if it's not defined here then use the value defined above which is basically the same example as http://lesscss.org/features/#variables-feature-default-variables |
Yeah I read the thread. Looks like you don't understand what I'm asking, because your example has my question backwards.
This is the opposite of what I need. This will overwrite the value of IOW the local value of In any case, I found the solution last night, which is to assign the local value first, then put the Thanks anyway. |
I'd rather suggest you to start with some Less variable basics to refresh your view: (because it seems you're just trying to think of it all in an imperative C/PHP-like manner, while in Less/CSS it's totally "declarative up-side-down").
Then it's simply the opposite: @font-size-h1: foo;
@import "path/to/file.less"; tada! |
...which is where I landed, as I said. |
I was going to suggest the article I wrote and then saw @seven-phases-max had linked to it. 😄 Trust us, what you're asking for already exists! But you have to understand Less's variable evaluation to understand how/why it exists. |
I have a component - which is a data grid. This component should have a default styling - defined by the component package. But if a certain variable from outside is already defined that one should have priority. app.less As the grid is a component, forget about adding anything here - or adding any code after the grid.less file. |
Less evaluates like CSS. .css {
--color: blue;
color: var(--color); // --color will be red
--color: red;
border-color: var(--color); // --color will still be red, red is the scope's final value
}
.less {
@color: blue;
color: @color; // @color will be red
@color: red;
border-color: @color; // @color will still be red, red is the scope's final value
} Scss, instead, doesn't mimic CSS evaluation and instead evaluates more like, say, PHP. .scss {
$color: blue;
color: $color; // $color will be blue
$color: red;
border-color: $color; // $color will be red
} So, in Sass/SCSS, in order to override a root variable's value, you are forced to do two things:
As in: // main.scss
@import "overrides.scss";
@import "library.scss";
// overrides.scss
$color: red;
// library.scss
$color: blue !default;
.scss {
color: $color;
} In Less, theming is much easier. You (typically) don't need to change anything about your library, you just need to put your overrides after. You only need to do one thing. // main.less
@import "library.less";
@import "overrides.less";
// overrides.less
@color: red;
// library.less
@color: blue;
.less {
color: @color;
} Therefore, you do not need Think of Less evaluation like the CSS's cascade. It just works. The final declaration wins. |
We are considering making the move from SASS to LESS, but the main obstacle for us is lack of a "default variable" feature (see http://sass-lang.com/documentation/file.SASS_REFERENCE.html#variable_defaults_)
This feature is incredibly useful when distributing your SASS code as a library where the variables serve as a sort of "API" for your users. In such a use case all the user has to do is make sure his variables are included up front to override the defaults that he wants to change, and voila! no fiddling with ordering of the (possibly) many files that may contain the variables that need overriding.
I've started working on an implementation, haven't written any tests yet but, hopefully we can use this pull request as a starting point for discussion: #1705
I chose the following syntax:
as opposed to the SASS way:
for 2 reasons - it's more concise, and the !default syntax may present potential problems with expression parsing on the right hand side in the future (but I could be wrong about that).
The implementation I came up with was surprisingly simple - hopefully I haven't missed anything important. I'd really appreciate any feedback you may have.
Cheers,
Phil
The text was updated successfully, but these errors were encountered: