Jump to content

Requests for comment/LESS

From mediawiki.org
Request for comment (RFC)
LESS
Component Frontend
Creation date
Author(s) Ori.livneh, Jon Robson, Steven Walling
Document status implemented
Accepted and merged in 1.22alpha [see Gerrit change 78669 and Gerrit change 84101 ]. --brion (talk) 20:10, 23 September 2013 (UTC)[reply]
See Phabricator.

This document proposes adopting LESS as a stylesheet language in core.

What is LESS?

[edit]

LESS is a stylesheet language that compiles into CSS. LESS extends CSS with variables, mixins, operations, and functions. These abstractions provide a means for writing concise and well-factored stylesheets that derive the styling of individual interface components from a core set of definitions.

Code samples

[edit]

As an example, let's use LESS to refactor some actual CSS code in core:

/* resources/mediawiki.special/mediawiki.special.changeemail.css@14b3f87 */

#mw-emailaddress-validity.valid {
	border: 1px solid #80FF80;
	background-color: #C0FFC0;
	color: black;
}
#mw-emailaddress-validity.invalid {
	border: 1px solid #FF8080;
	background-color: #FFC0C0;
	color: black;
}

The first thing we'll do is use LESS's ability to concisely express inheritance and cascade by nesting selectors:

#mw-emailaddress-validity {
    color: black;

    /* '&' concatenates a nested selector with its parent: */
    &.valid {
        border: 1px solid #80FF80;
        background-color: #C0FFC0;
    }

    &.invalid {
        border: 1px solid #FF8080;
        background-color: #FFC0C0;
    }
}

If you look closely at the rules for &.valid and &.invalid, you'll notice that they share a common pattern of setting a background color on an input element and declaring a one-pixel border on that element that is painted a slightly darker shade of that color (12.5% darker, to be exact). LESS allows us to express this pattern as a mixin. At their most basic, mixins are simply a shorthand for a group of CSS rules that go together. More sophisticated mixins can take parameters:

/* form-mixins.less */
.input-background(@color) {
    background-color: @color;
    border: 1px solid darken(@color, 12.5%);
}

You may also notice that the colors used to style the valid & invalid input boxes form two-thirds of a triadic color scheme. (Triadic color schemes consist of three colors that are equidistant from each other on the color wheel.) LESS allows us to assign these colors human-readable names and gives us functions we can use to derive harmonious color themes from a base color:

/* form-mixins.less (cont'd) */
@invalidInput: #ffc0c0;
@validInput: spin(@invalidInput, 120);

Because we'd like to build up a library of generic style components, we defined our mixin and variables in form-mixins.less. To use these definitions in mediawiki.special.changeemail.css, we use the @import directive. The final result looks like this:

@import "form-mixins.less";

#mw-emailaddress-validity {
    color: black;
    &.valid   { .input-background( @validInput ); }
    &.invalid { .input-background( @invalidInput ); }
}

Why use a CSS preprocessor?

[edit]

Using any CSS preprocessor makes your code easier to write, reuse and maintain by adding features such as...

  • mixins, which allows you to embed a rule into another rule, and then extend it with further arguments;
  • variables, which let you define and then reuse values throughout a stylesheet, leading to more consistent design;
  • and nesting, which can clarify how rules inherit from each other and helps avoid unnecessary repetition. Having more concise stylesheets and clearer relationships between styles means that refactoring is easier and diffs are cleaner.

In addition to making stylesheets less of a pain for you to create, the above features can make an open source project easier to customize and extend. For a great example of this in action, check out the very popular Bootstrap project's use of LESS. By writing the core variables that define Bootstrap styles in LESS, as well as making these variables respond to each other in terms of their derived color balance, contrast, spacing, and size, the project makes it exceedingly easy to change the styles overall. Wikia, which uses Sass/SCSS, takes this strategy and runs with it. Their ThemeCustomizer GUI references and updates Sass variables to allow end users to pick text color, links, background color and few other elements of the page.

Why LESS? Why not SCSS / Sass?

[edit]

LESS is one of the most popular and stable CSS preprocessors. Sass is another excellent CSS preprocessor. The E3 team made extensive use of Sass in our work to update the login and account creation interfaces and found it to be an excellent tool. Comparisons of LESS and Sass often give undue emphasis to minor differences in syntax, but it's important not to lose sight of the broad overlap that exists between these two tools. They are very similar. Our preference for LESS stems from a preference for its PHP implementation: we like its documentation and its large suite of unit tests, and we see the change log and the history of activity on its issue tracker as indicators of a healthy open-source project.

If this RFC is implemented, we will convert the existing SCSS code in core to LESS, and remove the special make process for that.

Case study: LESS in MobileFrontend

[edit]

Moving to LESS from CSS was a very easy transition for the Wikimedia mobile web team working on the MobileFrontend extension — it was a simple rename: Gerrit change 27480

Amongst various other reasons using LESS has helped the MobileFrontend extension in the following ways:

Dead code elimination
In this particular example there was no element with class .overlay meaning it was easy to identify and remove all the CSS that was being wasted by using the hierarchy: https://gerrit.wikimedia.org/r/#/c/74519/1/less/specials/userlogin.less.
Nicer diffs
In this particular example it was much easier to rename an element and its corresponding CSS due to the way the CSS rules were structured in LESS: https://gerrit.wikimedia.org/r/#/c/76336/1/less/common/notifications.less. Had these been written into CSS without nesting there would have been many many more instances to change.
Consistent colors / margins
Use of a variables file helps us be consistent with our colour scheme. For instance in this commit rather than introducing a new variant of the colour red we used an existing one @redBase: https://gerrit.wikimedia.org/r/#/c/73195/2/less/common/pagelist.less
Managing vendor prefixes and other things via mixins
In this patch we removed support for the vendor prefix -o after realising that it didn't exist. This was a one-line LESS change but as you can see from the commit it changed a lot of CSS! Gerrit change 72214. Another great example was dropping -moz-background-size support (Gerrit change 72216). Also see Gerrit change 72223 as another example of where this has been useful.

LESS in MediaWiki core

[edit]

Adding support for LESS to MediaWiki means that generating CSS from LESS files would not require a separate build step, but instead happen on the fly. ResourceLoader makes this easy to implement because it already implements a static asset pipeline. It does not generally serve JS and CSS files as they are written on disk; instead it applies a series of transformations to the raw source code that optimize it and adapt it for the client's environment, and caches the result of its transformations to avoid repeating work. It already performs minification and RTL flipping of CSS. ResourceLoader has worked extremely well for MediaWiki and we think that by extending it judiciously, LESS support could be implemented in a manner that does not degrade performance. Gerrit #Id052a0 contains a draft implementation.

Why not implement this as an extension?

[edit]

MediaWiki core has over 10,000 lines of CSS code, some of it quite ancient, and we think that a tool like LESS can help us a lot with its upkeep. This will help us define and enforce a set of consistent design guidelines for MediaWiki's interface, and it's fun to write. Concentrating these generic definitions in core and providing a uniform interface for working with LESS will facilitate standardization and code reuse.

Further reading

[edit]

Comments

[edit]

Userscript and gadget support?

[edit]

Will this also support User:Yuvipanda/something.less files natively? Yuvipanda (talk) 09:21, 19 August 2013 (UTC)[reply]

Same question goes for gadgets I guess? Steven Walling (WMF) • talk 19:41, 19 August 2013 (UTC)[reply]
Gerrit change 78669 patch #3 doesn't make .less files a known content type, so editing them on-wiki doesn't work very well.
  • Would you want MediaWiki to automatically load 'User:Yuvipanda/common.less' ? What if you already had a common.css? It seems too tricky.
  • @import( 'something.less' ) in user CSS files seems left as is, so we'd have to reconfigure the server to transform the request.
  • Adding importStylesheet( 'User:Spage/common.less' ); to common.js doesn't work, it embeds a <link rel="stylesheet" href="...wiki/index.php?action=raw&ctype=text/css&title=User:Spage/common.less"></link> and the file is not processed. This one seems like a feature that should work.
-- S Page (WMF) (talk) 20:42, 19 August 2013 (UTC)[reply]
Having chatted with Ori, I think the answer is that theoretically this is totally possible, but it won't happen as a result of the current patchset and RFC. I'd say that the best thing to recommend as a first step for gadget and userscript writers is to use one of the many GUI or CLI tools available for locally writing LESS and compiling to CSS (http://crunchapp.net/, http://wearekiss.com/simpless, http://incident57.com/less/). This will give interested people an opportunity to actually try writing their script/gadget stylesheets in LESS, and then plop the resulting CSS in the MediaWiki namespace until support is added. Steven Walling (WMF) • talk 21:16, 19 August 2013 (UTC)[reply]
Hmm, I think calling it out of scope for this RFC/patch is totally okay as long as it is explicitly mentioned as something that should be done by 'someone' as future work :) Yuvipanda (talk) 21:40, 19 August 2013 (UTC)[reply]
Mark it future, and note that security should be taken into account for @import etc. :) --brion (talk) 17:39, 13 September 2013 (UTC)[reply]

Well done

[edit]

This is a nice RFC to read. :-) --MZMcBride (talk) 00:48, 20 August 2013 (UTC)[reply]

Thanks! --Ori.livneh (talk) 16:42, 20 August 2013 (UTC)[reply]

Learning curve

[edit]

So I guess the problem is: there are many, many people who understand CSS but who have never encountered LESS before. As you convert core CSS to LESS, you make it more difficult for them to contribute. Is that a fair criticism? Jarry1250 (talk) 19:58, 22 August 2013 (UTC)[reply]

It's a concern but I'm not sure it's a big problem. None of the mobile team when we began using LESS had used it before. It's very quick to pick up - mostly it looks like CSS (a CSS file is a valid less file) with a few extra bits. Most of the confusion in our project has come from people editing the script generated css files instead of the less files which this patch will solve. Jdlrobson (talk) 21:00, 22 August 2013 (UTC)[reply]
But you were being paid to make the jump :) Have many volunteers been put in the same situation? Jarry1250 (talk) 21:05, 22 August 2013 (UTC)[reply]
Regardless it was not a big jump. If you can write CSS you can write LESS :) Jdlrobson (talk) 21:27, 22 August 2013 (UTC)[reply]
Ah, but the question is: would I (or someone like me) bother? Would I decide against taking on the resolution of a papercut bug because I would have to learn LESS to understand the implications of changing a line of CSS somewhere...? Jarry1250 (talk) 21:54, 22 August 2013 (UTC)[reply]
If you know CSS, it should take you about five minutes to understand the syntax differences. And if all you want to do is read a LESS stylesheet and make a small patch (as many new contributors start out by doing), you'll have no trouble. As for why... that's why we have a case study and "Why use a CSS preprocessor?" above. :) In general, it's important to note that if this RFC's implementation is merged, there's nothing that will stop you from continuing to use vanilla CSS in your MediaWiki-related projects. It just means that we'll support LESS in a way that makes your life easier should you be willing to use a CSS preprocessor. Steven Walling (WMF) • talk 00:22, 23 August 2013 (UTC)[reply]
Twitter's Bootstrap is written in LESS and it is GitHub's most forked project ever, in any language; the number of upstream contributors is just shy of 400. This suggests that LESS would not pose a high barrier for contributors. I'd also like to note that it may be easy to write CSS, but it is not easy to write CSS for MediaWiki, because it is not easy to understand the set of considerations that inform each style definition. I've tried to show in the example above how LESS can help make these considerations explicit. (For example, by replacing literal values with human-readable names, and by using function application to show how values relate to one another.) I am therefore hopeful that the overall effect will be a lowering of the barriers to contributing. --Ori.livneh (talk) 09:13, 23 August 2013 (UTC)[reply]


Using SASS instead of LESS would significantly decrease the learning curve, as its syntax is much more intuitive. Just because something is popular does not make it good; just look at php. If there is a better alternative, we should use it, even if it does set us apart, so I would seriously suggest reevaluating the decision to use LESS on this basis alone. -— Isarra 17:15, 20 September 2013 (UTC)[reply]

LESS also has issues with syntax errors - its syntax can be quite easy to mistype, and it does not handle this well, potentially resulting in loops and other unintuitive behaviour based on what/where the error lies. SASS tends to handle similar errors much more gracefully, and they are also easier to avoid in the first place. -— Isarra 04:22, 23 September 2013 (UTC)[reply]

@import and ResourceLoader dependencies

[edit]

These two features have partially overlapping functionality. It would be helpful to come up with recommendations when to use which. For example I see some discussion above about using @import for user scripts and gadgets. --Nikerabbit (talk) 21:28, 10 September 2013 (UTC)[reply]

You should never use @import in code using ResourceLoader. That was and remains the convention for reasons unrelated to this RFC. Krinkle (talk)
So if I include other less files via resource loader dependencies, will I be able to use variables and functions defined in those files? --Nikerabbit (talk) 18:14, 11 September 2013 (UTC)[reply]
You should use @import to load LESS files that contain variables and mix-ins. The compiler environment is not shared across compilations of LESS files based on ResourceLoader dependency relationships. Each top-level '.less' file that is referenced in a ResourceLoader module declaration gets a fresh environment. --Ori.livneh (talk) 10:58, 15 September 2013 (UTC)[reply]

Sample usage: MobileFrontend

[edit]

Work in progress adapting MobileFrontend to use the less styles directly. A couple notes:

  • Styles aren't quite working yet.
  • I'm having some sort of trouble where variables aren't interpolating correctly, not sure what's wrong yet. This seems to cause errors when doing math.
  • The -webkit-transform mixin needs to do a string replacement; since JS isn't available we'd have to add an internal less function to do it.
  • Errors are definitely not handled well yet. :) Waiting on some fixes to continue.
  • If using images, don't forget to move them from the css to the less directories!

--brion (talk) 19:16, 13 September 2013 (UTC)[reply]

Brion, thanks very much for this. I think the latest patch-set addresses all the issues. --Ori.livneh (talk) 10:58, 15 September 2013 (UTC)[reply]

wgLESSVars vs. imports

[edit]

Perhaps it would be more modular to import a global definition file like https://github.com/twbs/bootstrap/blob/master/less/variables.less , rather than using wgLESSVars. Particular modules could add additional local files for this as well. Superm401 - Talk 00:43, 17 September 2013 (UTC)[reply]

You'd lose the ability to specify values dynamically, and that's a big loss -- there's a lot you can do with that. Take Wikia's ThemeCustomizer, which loads wiki-specific color schemes based on the wiki's configuration. --Ori.livneh (talk) 04:25, 17 September 2013 (UTC)[reply]

Conventions (particularly, but not only, about @import)

[edit]

Besides the obvious (indenting, spacing, etc.), we will need to have some other conventions, particularly how to use more advanced functionality. The most important of this is @import. We want to allow people to take advantage of features like variables and mixins. It's not enough to say that core has one folder of definitions and mixins. Extensions definitely need to be able to have their own local definitions, and it's probably more modular if core also breaks up the LESS library in a logical way. There's probably one folder that all LESS should be allowed to @import without a special declaration.

But others should follow particular rules. One approach is to have modules depend on modules representing the LESS libraries they use. Because LESS libraries compile to empty CSS, those library modules would not contribute anything to the page unless they're library exports are used. It would be mainly for documentation purposes since currently there is no enforcement of where @import declarations can import from.

Those are just some quick thoughts. The main point is that we need to invest some time in working on this. I'll start Requests for comments/LESS/Conventions in a bit. Superm401 - Talk 22:28, 17 September 2013 (UTC)[reply]