-
Notifications
You must be signed in to change notification settings - Fork 3.4k
Description
This is something I've wanted since first using LESS, and I think I've come up with a solution that could be pretty awesome if we get some feedback and tweak it to get the kinks out.
Proposal, Part 1: The @options
directive
I propose that we use the @options
directive to allow setting processing options directly inside less files themselves, like this:
@options {
// options
}
As with media queries, the @options
directive wraps a declaration block which contains the options/flags for how you want your less to compile. When this directive is found, less.js reads in the options and processes all less files accordingly. If multiple @options
directives are identified, less.js either throws an error or it uses the options from the last directive (I'll leave that decision to others). If the latter is chosen and multiple @options
directives can exist simultaneously, my suggestion would be to process it like this:
If multiple @options
directives could co-exist:
First directive identified...
@options {
paths: "";
rootpath: "";
relativeUrls: "false";
strictImports: "false";
compress: "false";
yuicompress: "true";
optimization: "1";
silent: "false";
lint: "false";
color: "true";
}
Second directive identified
@options {
compress: "true"; // less.js uses `compress: "true"`, but leaves the other options in tact
}
Another suggestion would be to use !important
to designate which options should not be overridden.
Why would multiple @options
directives exist? A good use case is that when importing external libraries, such as Twitter Bootstrap, you may wish to override the options from that library.
Regardless of how it's actually implemented, what I find so compelling about this concept is that it paves the way for keeping the syntax and language cleaner, it keeps options closer to the less files, and forces a stronger separation of concerns between processing/compiling features and language features. To that end, I often see feature requests or Issues where the OP is asking for something to be added to the language, when really all that is needed is a new processing/compiling option. LESS is a language, but less.js is a compiler! This makes that distinction much clearer, and it allows for the user to store different options across different projects, either directly inside a single less file, in several less files, or even organized in options.less
files that can be reused across projects. Whichever you prefer, it's a better approach than putting those options in a json
file or javascript.
see: #850
Proposal, Part 2: Setting "Contexts" with the @options
directive
With this proposal, "context" is created in a kind of similar way to setting context in "logic-less" templating languages, such as mustache. So, for instance, rather than hard-coding processing options in @import
or @include
directives, we would instead use "contexts" so that the choice is made by the developer, and they have the choice of setting the options in the less files themselves or on-the-fly at compiling time.
@options("default") {
paths: "";
rootpath: "";
relativeUrls: "false";
strictImports: "false";
compress: "false";
yuicompress: "true";
optimization: "1";
silent: "false";
lint: "false";
color: "true";
}
(and how awesome would it be to be able to use variables in paths
and rootpaths
now that we are storing those properties in our less files!)
A common use case is to create contexts for environments, like development and production, so that less.js (env
) processes the options according to how they are defined in the less files themselves:
@options("dev") {
compress: "false";
}
and...
@options("prod") {
compress: "true";
}
Applying contextual options in your less files
Media queries are a great starting point for inspiration, for example:
@media (min-width: 768px) and (max-width: 979px) {
.row {
margin-left: -20px;
zoom: 1;
}
}
And CSS syntax already allows us flexibility with media queries with @import and @media, like:
@import url(example.css) screen and (color), projection and (color) { // declarations };
// and
@import url("phone.css") only screen and (max-width:400px) { // declarations };
// and
@media screen and (color), projection and (color) { // declarations }
And written in HTML, XHTML, XML, @import and @media:
<link rel="stylesheet" media="screen and (color), projection and (color)" rel="stylesheet" href="example.css">
So following convention established by @imports
and media queries, here are some ideas for how we can take the contexts we created using the @options("context")
directive and apply them throughout our less stylesheets:
@context("prod") {
.some-experimental-class {
display: none;
}
.sidebar {
width: 250px;
}
...
}
and...
@context("dev") {
.some-experimental-class {
display: block;
}
.sidebar {
width: 210px;
}
...
}
This gives us the choice to extend the directive in the future:
@context ("experimental") and (some-rule: true) { // declarations }
// and
@context all and (some-rule: true) { // declarations }
As with media queries, these would be equivalent:
@context all { // declarations }
@context { // declarations }
Or, for example, we could set the "contexts" from our @options
on other directives like this:
@import:dev "forms.less"
// or
@import("dev") "forms.less"
// or
@import:context("dev") "forms.less"
// or
@import:options("dev") "forms.less"
Example of other Issues that could be resolved with this:
- support for default variables: Added support for default variables #1104
- Added support for default variables #1104
- Importing - Both CSS and Less SomMeri/less4j#19
@include
: @include .css file contents #560
I would solve the @include
issue like this:
@options("dev") {
css: "passthrough"; // or some more elegant way of describing it
}
and...
@options("prod") {
css: "append";
}
or maybe just...
@options("prod") {
concatenate: "true"; // which would apply to both css and less.
}
Last, it's understood that @options
could collide with custom variables, so either 1) bake it in as a reserved word and force people to not use that as a variable name, 2) allow @options: variable
and @options {}
to co-exist peacefully, or 3) we just use a different namespacey term than @options
. I like #2 the most, alternatively #1.