📜 ⬆️ ⬇️

Why we began to use the Stylus preprocessor in Yandex. Mail, as well as about the library that helps to live with IE

image Today I want to talk about why and how we came to use the Stylus preprocessor in the development of Yandex. Mail, and also describe the method we use with styles for IE. It is very easily implemented using preprocessors and makes IE support easy and convenient. We have developed a special library for this, which we also share - if-ie.styl .

This is only the first article in a series of articles on the use of the Stylus preprocessor in Yandex.Mail, which we are preparing for publication.


How we come to use preprocessors


Although externally, Yandex.Mail looks like a one-page application, inside it contains a huge number of various blocks, their modifications and contexts, in which these blocks and modifications can be.
')
In addition, she already has more than thirty themes. There are themes with a light background and a dark one, there are themes that differ only in colors, and there are also those in which almost the entire interface is molded manually from plasticine ( http://habrahabr.ru/company/yandex/blog/110556/ ). In some themes, only one background image, and in others, the background may change - randomly or depending on the time of day and weather.

Because of all this, there are many variations of the visual presentation of the interface, which makes it a little different to relate to the development process, to look for tools that are more suitable for solving the problem.

When we just started the “neo2” interface, we chose a familiar solution - Template Toolkit 2 template engine, with a somewhat non-standard script to use it to generate CSS, rather than HTML. At first, we only needed variables, but over time the topics became more complicated, and as a result it turned out that such a tool was inconvenient. The cumbersome syntax, the lack of specialized CSS functions and the general sense of misusing the tool made it necessary to look for other options. We realized that we can not do without a preprocessor.

Preprocessor selection


Choose between three options: Sass, Less and Stylus. The process was quite simple: we took several of the available blocks, after which we tried to reverse them using each of the preprocessors.

Less seemed at first very simple and convenient: it uses the familiar CSS syntax and it can be used in the browser, which is convenient for debugging. But when trying to do something complicated, its capabilities are no longer enough. What can be done without arrays, cycles and normal conditional constructions? Not so much.

After Less Sass was very pleasant: powerful, with a good community and all sorts of additional libraries like Compass . However, there was one serious drawback: Sass has a very inflexible parent reference (using the & symbol in selectors to point to the parent selector). Because of this, several problems arise, and all of them consist in the fact that & cannot be used for the prefix definition of multiple classes, the refinement of an element or the concatenation of classes. Here are some examples of what other preprocessors can do, but which can cause an error in Sass:



And everything would not be so bad if Sass could have used a pointer to the parent selector in interpolation. But no - Sass first expands the interpolation and only then tries to apply the selector, producing an error.

The most annoying thing is that this behavior is not a bug, but a feature . And it will not change. This is the ideology of the Sass syntax.

Stylus was the newest preprocessor, and in the end our choice fell on it. Yes, it is damp, there are unpleasant bugs in it, its community is not so big, and the development is not going as fast as we would like. But for our tasks, it was best suited, and this is why:



In the Stylus, there are still a lot of very different useful things, but the ones listed above made us make a choice in his favor.

Of course, besides the benefits, Stylus has some drawbacks. And the main one is the flexible syntax - the authors of the preprocessor consider it the main advantage. Chasing the flexibility, they only implemented the indentation based syntax, while the “a la CSS” option was somehow screwed on top, and it would not be possible to just take and rename .css to .styl — not all the CSS spellings will work and in Stylus. But we decided that the possibilities that this preprocessor gives us make its shortcomings not so significant, so we had to put up with some of the capriciousness of the parser (and start using syntax based on indents).

Summing up the story about the choice, it is worth noting that Sass and Stylus are two almost equivalent options. Each of them has its own advantages and unique features, as well as disadvantages. If you already use one of these preprocessors and you are satisfied with everything - great, you can not think about finding a new one. But if you are only suited to the choice, or if the preprocessor you are using becomes cramped, try to compare all the options. The best way to do this is to try each preprocessor on its task. By completing a part of your project on each of the preprocessors, you will understand which features are important to you and which are not. Just do not forget that the preprocessor is not just a different syntax, but also a different approach: with such an overhaul, you can at the same time refactor the code by doing something better than it was with simple CSS.

Library if-ie.styl


During the translation of Yandex.Mail to Stylus, it became clear that the preprocessor can provide features that we did not have using regular CSS. We have long used in the Post separation of styles for conventional browsers and for older versions of IE. All styles were decomposed according to the BEM file structure, and for each block two files were created side by side: b-block.css and b-block.ie.css .

Styles for all browsers went to the first file, and only those that were needed for Internet Explorer, respectively. It should be noted here that for a number of reasons we are switching IE8 to IE7 compatibility mode, and also, having ceased to support IE6, we are sending it to the “lightweight” version of Mail. Thus, we have two different versions of styles: one for all browsers, the second for all older versions of IE. Everything was collected automatically - first a “for all” style sheet was collected, after which all styles from the .ie.css files were added to it - and the style sheet for IE was obtained.

Each browser receives only its own style sheet - for this we use conditional comments, like this:

 <!--[if gt IE 7]><!--> <link rel="stylesheet" href="style.css" /> <!--<![endif]--><!--[if lt IE 8]> <link rel="stylesheet" href="style_ie.css" /> <![endif]--> 


At first, this separation of styles worked well, but with the advent of the preprocessor, the question arose: Is it possible to do better?

It turned out that you can.

This is how the library for Stylus appeared - “ if-ie.styl ”. In fact, this is not exactly a library, but rather a methodology of how to work with styles for IE. There is nothing secret in this methodology, so we decided to post it on GitHub under the MIT license. You can easily use it for your project, report any errors found or even correct them yourself - Open Source, everything. Or maybe someone will be able to rewrite it for another preprocessor? Fork a project or create a new one, taking the methodology as a basis - it will be great.

The basis of the methodology


The methodology is based on a very simple idea: we create a variable ie , which will be equal to false for all regular browsers, and true when we want to get styles for IE. After that, you can use only one main style sheet, in which you can delineate styles for different browsers under normal conditions. A simple example: take the style.styl file:

 ie ?= false .foo overflow: hidden zoom: 1 if ie 


This default Stylus code - for regular browsers - will be like this CSS:

 .foo { overflow: hidden; } 


Since the ie variable was false , the if ie condition did not work, and the zoom property was not included in this style sheet.

Now create the next style_ie.styl :

 ie = true @import style.styl 


If we feed such a Stylus code, we get:

 .foo { overflow: hidden; zoom: 1; } 


This is exactly what we needed - a separate table, in which there are styles needed only in IE.

At the same time, if we want to write some styles only for ordinary browsers, we will be able to use the condition if !ie - and filtering styles will become very simple.

Additional features if-ie.styl


Of course, this seemed to us a little and we decided to add all sorts of useful functions that would optimize much in our code. So it turned out "library".

To use it, connecting styles will look a little different.

The file style.styl will be like this:

 @import if-ie.styl .foo overflow: hidden zoom: 1 


A style_ie.styl so:

 ie = true @import if-ie.styl @import style.styl 


There are two differences in comparison with the version without additional functions:

  1. In both files you need to connect the library to all other styles, and be sure to both times. (It may seem that only the first would suffice - after all, the style sheet for IE already contains the main style sheet - but then some subsequent features will not work). In this file, ie ?= false already defined, and therefore it is not necessary to explicitly state this in the main style sheet.
  2. If IE condition is missing in the style sheet for IE, the first feature of the library is shown: the zoom property automatically appears only in the style sheet for IE. Of course, this property can also be used in ordinary browsers, but in practice this need happens extremely rarely, so you can ease the main style sheet at least a little.


In the Stylus code, such functions are very easy to define. In this case, the new transparent mixin will look like this:

 zoom() zoom: arguments if ie 


With this code, we create a zoom function that assigns the corresponding property with any passed arguments only if we are currently collecting styles for IE.

inline-block


It is widely known that in older versions of IE, the out-of-box display property does not support inline-block values. In IE there is a similar mechanism, but it works only for inline elements and only if they have the hasLayout mechanism enabled. A completely random display: inline-block in IE just turns it on, but “forgets” to switch an element to display: inline , so such an entry will not work for initially block elements like <div> . So the easiest way to guarantee to apply inline block behavior to any element in IE is to register only a zoom: 1; display: inline; for it zoom: 1; display: inline; zoom: 1; display: inline; .

In the if-ie.styl property, the display property will do this automatically and only for IE. Regular browsers will see display: inline-block , while IE will only receive a zoom: 1; display: inline; zoom: 1; display: inline; . Then someone might have noticed that the second entry is longer than the first one, and for the initially inline blocks it would be possible to save ... But if you carefully calculate, the savings will be just one byte. This is not worth keeping an eye on - whether the block is inline or not initially. Moreover, ideally, the layout should not depend on which element it is applied to.

Override CSS3 properties


If by default we don’t give normal browsers a zoom: 1 , dividing all styles into two files (as it was, in general, before the introduction of preprocessors), then with preprocessors we can think about how to facilitate the style sheet for IE.

With the help of Stylus, this is done very simply, while you can save quite a lot of bytes: after all, what we don’t need in old IE is the properties with prefixes.

Above, I mentioned in the article that we use the nib library — this library defines transparent mixins for most new CSS properties, for example, for transients. As a result, in styles for Stylus we write simply transition: opacity .3s and as a result we get this property with all the necessary prefixes. But in IE, we not only do not need prefixes, but the property itself! So, we can redefine this and many other properties in our library so that they do not return anything.

This is done like this:

 if ie transition() z if 0 transition-property() z if 0 // … 


Everything is almost clear here: with one condition, we only redefine all necessary properties at once for IE. However, due to the peculiarities of Stylus, it is necessary to write at least one rule in the function definition — z if 0 is pretty short.

As a result, IE, instead of the mixin defined in nib transition sees the one that we defined in if-ie.styl, and the resulting style sheet will be much easier than before - almost everything unnecessary from it will be cut.

rgba-ie


We will not inflate the article and describe everything that is in the if-ie.styl - this is already described in the project documentation .

However, we need to tell you about one more function, which turned out to be very useful to us in the framework of the thematisation of Yandex.Mail. This is the rgba-ie function. In fact, this function could simply be called rgba , but Stylus has a bug: the functions defined in JS cannot be redefined in the same way as those defined in Stylus, so I had to create a new one.

What does she do? Older IEs do not support color values ​​specified in rgba format. Therefore, usually the developers either prescribe the corresponding colors twice - first for old IE in the usual hex-format, and then to all normal browsers in the desired rgba - or use modernizr and use it and the .rgba class .rgba set the corresponding colors where it is needed. But for folbek in IE, every time you still have to calculate the approximate color of what we will degrade into it. Most often it will be the desired color superimposed over the background of the page or the middle background of the element over which the color will be applied in rgba .

The rgba-ie function from if-ie.styl greatly simplifies this task: duplicating the capabilities of the usual rgba function, we get one more optional parameter that can be passed to the function — the background color for the foldback. By default, this parameter is set to #FFF .

A simple example:

 .foo color: rgba-ie(0,0,0,0.5) 


In normal browsers, this color will be normal rgba(0,0,0,0.5) , but in IE it will turn into #808080 - that is, into the corresponding color superimposed over white.

A more complex example, with a target background as the last argument (and using one of the Stylus features - the ability to specify a color in hex instead of three digits r , g and b ):

 .foo background: rgba-ie(#FFDE00, .42, #19C261) 


In this example, normal browsers will have the color rgba(255,222,0,0.42) , but IE will get the correct #7ace38 .

At the same time, it is possible to set the default folback using the $default_rgba_fallback variable.

As a result, you can greatly simplify your life if you use the function rgba-ie instead of the usual rgba - in this case, you can almost not remember about IE.

Source: https://habr.com/ru/post/169415/


All Articles