📜 ⬆️ ⬇️

Object Oriented Design ... in CSS

I will warn in advance that I absolutely do not consider myself an expert HTML / CSS / JS. But, as an architect, I have always been interested in organizing and systematizing code in its most diverse manifestations, including those presented in the form of CSS. Especially this interest was fueled by BEM, at the first meeting with which the subconscious responded with cognitive discomfort. And since the BEM-style in projects began to appear more and more often, I felt an urgent need to understand, finally, my attitude towards the organization of styles. Thus, this topic-reflection, topic-discussion appeared. I understand that I took on the border task, because not all designers are familiar with the subtleties of object-oriented design, and most architects have not written a single CSS-style. And, as a result, I had to balance awkwardly so that everyone could understand. But 'this risk has further stirred my interest in the topic :)

Questions to BEM


I decided to start by saying that it was not quite clear to me in BEM:

1. Many believe that a priori technique is always and everywhere true, since Yandex stands behind it. Why should something that suits a large company with a huge staff and a small number of projects of the same type work just as well, say, in a small team that custom builds a wide variety of websites? Take as an example the approaches from jQueryUI, KendoUI, etc. They are replicated on hundreds of thousands of projects, and in this respect they have much greater maturity. And Yandex itself on bem.info site clearly describes the conditions of application, but many do not even reach this paragraph, limiting themselves to the first lines and soothing: “this was invented in Yandex”.

2. Why is it considered that forced contextual independence is good, and this very dependence of the element on the block is immediately introduced, if you use the BEM terminology? Suppose I want all the buttons on my site to look different from the way the author of the block wants, but in accordance with a single, thoughtful style. If it is some standard button, then in the same context it should look the same. In another, just, can be different. In the sidebar - smaller, in the body of the page - more, on the big screen - text and icon, on the smartphone - icon. But if this is a standard button in the sidebar, then in such a situation it should and should look standard. Here, the standard means a successful solution, which was found by the designer together with the UX-specialist, and not the layout designer of a single unit.
')
To illustrate my doubts, I took the first input forms from the Yandex website: mailbox creation, registration and feedback.



I do not know who and what I saw in these pictures, but I see a complete anarchy. Obviously, it was not those who should be engaged in the design of these forms that were engaged in the design, but the authors of the blocks sculpted as each of them sees. Why is a single standard for how input fields and buttons in forms should look like - is that bad?

Yandex on the site bem.info writes: “Every new project or interface element should not be written from scratch. If somewhere in the company a similar task has already been performed, you need to reuse the resulting code as much as possible. The code should not have contextual dependencies, it should be able to be easily transferred to another place. ” Let's look at an example of such a reuse.



Two login blocks on one page. If I did it in the wrong way, I would write the module once, and then, depending on the context, I would slightly adjust the styles so that it looked reduced in the sidebar. Here, I see two different modules and two different authors. One considers that it is necessary to “remember the password”, the other offers it to “remind”. The first one believes that the link “register” is not part of this functional block and made it out of the visual circuit, the second one does not agree with it. They even differ on how to headline this block. The author of the pop-up version of the reasonable believes that the user must make an “input”, while the minimalist wants the visitor to make a “market”. They even implemented in different ways, one - tabular layout, the other - in layers. Reuse - zero. But both used BEM.

3. Why is the lack of brevity in the class names considered an advantage? And how can you like it - <div class = "menu__item menu__item_position_first menu__item_state_current">? In the last layout, which I deprived of BEM page size fell twice. I am not saying in this case that this is a sufficient reason to reject BEM and the meaning of the appearance of this overhead is clear. Just stating that for such a bloated syntax with low entropy, I would remove the score.

4. Why do you need to deny many features of CSS? Be it selectors using identifiers, contexts, etc.

The first reaction to BEM was such that I deciphered this abbreviation as Bardak Eventual Metastasizing. But all these grievances smacked of just another private opinion, which did not convince me very much, let alone talk about persuasiveness for others. And then I called myself to comprehend the very concept of CSS classes in an architectural way, as I used to.

Design by contract


I started the system role of classes. The particular is usually less important than the whole, since the latter is the essence of their sum. So here, the participation of CSS classes in interaction with the rest of the system can be much more important than any improvements in the field of layout alone. And their participation here is very serious. In essence, class names are the only strong link between HTML and JS. The main role of JS, anyway, is to deal with the DOM-model. And what is the best way to get to the right nodes? Selectors with class names. All other options do not scale. Bypassing childNodes / parentNode? Tomorrow there may be a wrapper and the code will stop working. Do you use tag names? Tomorrow <input> will become <textarea>, <i> will become <em>, <pre> will be replaced with <code> and the scripts will stop working. The class name is that fundamental interaction interface that we can put in HTML and use in JS without fear for the further evolution of the system.

I intentionally used the word “interface” here because I wanted to draw an analogy with object-oriented practices. We must somehow work out the title. At one time, Bertrand Meyer announced the design paradigm of Design by contract as part of the development of the Eiffel language. I will not go deep into its details, I will confine myself to a key concept. Objects bear the burden of "contractual" obligations and interact with each other through these very contracts. For example, there is a certain widget that can accept other drag'n'drop objects. He tells everyone: “I can do this, for this purpose I have, say, a CanDrop function, to which you pass an object as an argument, and I return a Boolean value, accept objects of this type or not.” Thus, the existence of this function, its signature and essence are stated in its contract. It is absolutely not important for the system whether this contract is taken over by a widget for file upload, a tree with the ability to drag and drop nodes or is it such an advanced basket of goods where they can be transferred from the catalog with the mouse. With the operation of drag'n'drop, it only needs to know that a certain widget bears these contractual obligations. And when you drag a file to the basket of goods, it will call the CanDrop method and the basket will receive a file and signal you that this operation is impossible.

In the future, the essence of the contractual approach was actively developed and most of the modern design patterns are based on the use of object interfaces. And the object's interface is the essence and there is a contract that lists all the methods that this object can perform. So we are talking about a very mature and familiar approach for many programmers.

I will try now to shift this idea to HTML + CSS + JS. Imagine for a start two simple contracts - container and item, they are also the names of CSS classes. The first one tells us that he undertakes to work with the item. He doesn’t care what the tag or DOM node stands for. This can be a list item, a menu item, a tab on a tab control. He can work with anyone, if this someone is ready to execute the contract item. Thus, we tame the task according to the principle of “divide and rule” by breaking it up into separate functional areas. Working, for example, from the menu, we divide all its variety of functions into those that are responsible for organizing menu items according to the principle of container + item, and all the rest.

For the item contract, we can define some function getParent () {return $ (this.dom) .closest (". Container"); }. For a container, I have a bunch of functions like remoteItem, upItem, downItem, etc. Using only .container and .item in selectors, you know that you can use the same functions for lists, menus, bookmarks, and any other types of collections. Or, in other words, you write functions for working with lists and containers once, then you simply use them. If you add a new function to your library, then it can automatically be used wherever the containers were organized in this way. It works great in programming, so why shouldn't this principle work here?

Example


Let's try to extrapolate this example, for example, on tabs.

<div class="tabbed"> <ul class="_tabs container selectable"> <li class="tab item selected">…</li> <li class="tab item">…</li> … </ul> <div class="_pages container selectable"> <div class="panel item selected">…</div> <div class="panel item">…</div> … </div> </div> 

The tabbed interface undertakes to provide access to the _tabs and _pages containers. This means that if you have an object in your hands that implements this contract, then you can call the getTabs and getPages methods in order to access the corresponding containers. We know about the latter that they implement the container contract, which means they have all the necessary functions for working with a collection of bookmarks, including processing first / last / odd / even and other typical styles for lists. That is, it will not be necessary to reinvent the function to remove the tab.

One more contract has been added to the containers - selectable. He can undertake to provide a function to determine the currently selected element, change the selected element, etc. For example, functions:

 getSelected: function() { return $(this.dom).find(".item.selected").first(); }, deselect: function() { $(this.dom).getSelected().removeClass("selected") ; }, select: function (item) { this.deselect(); $(item.dom).addClass("selected"); } 

And again, these functions will work equally well for lists, drop-down lists, panel bars, sliders and all other types of widgets where the selected element is supposed to be present, and not just for tabs. When you find and correct an error in these functions, you correct it everywhere.

If you, say, want to add the ability to drag tabs with drag'n'drop, then simply add drag / drop contracts, which will allow you to immediately use all those typical functions that have already been prepared for this purpose.

But one has only to write in the BEM-style tabbed_tabs__item, as the colors immediately fade. After all, all the code that has always worked well with the ".item" selector for various widgets has stopped working. You had skins with a variety of tab displays, but now they don't work either. It may be objected that there are special extensions for the same jQuery, which will help to deal with all this syntactic garbage. But I still do not understand why create problems, and then solve them.

Standards


Naturally, all these "contracts", as well as interfaces in object-oriented design, should be systematized and laid out on the shelves. In programming, this is often solved by using namespaces. But this is done once, and then used indefinitely long and on any number of projects.

On the basis of such contracts, it is much easier to introduce internal standards and implement validators that will keep the quality of the code under constant control. This will allow all participants in the process, including the newly connected, to think in the same vein. In the beginning - imperative, and then out of habit. After all, as in object-oriented design, contracts are not entered just like that, but are the result of a deliberate act, so in CSS the class names should not occur spontaneously, thoughtlessly. After all, although BEM is trying to control the structural aspect of development, it completely misses the system aspect. Someone tabs call tabs, someone tab-control, another tab-widget. For example, the Yandex login form from the example at the beginning of the article is called “b-domik”. It's so nice to call her not login, signup, or any other meaningful way, but “bi-house”. I remember the old institute "pi with a house and pi with a darling." Top creative, but hell for validation and refactoring. If you suddenly want to find all forms of login and add new options there (say, identification through some new social service), then you have to catch this bi-house, find out that in Yandex mail it is already called b-mail-domik, The link “Enter to mail” from the title page already contains a login form with the class b-popupa__wrap. And how many “wonderful discoveries” have been prepared for you ...

It seems to me that the designers should not invent names of classes at all. Providing interfaces between HTML and JS should be done by the designers. They develop contracts, organize and deliver this information to HTML / CSS workers. This gives JS code scalability, reuse, and refactoring, and validation in accordance with corporate standards, which means quality. It seems to me that this is quite enough in order to have a reason to reflect.

CSS preprocessors


At the end, I note that such styles are quite easily and organically described in the spirit of LESS / SASS / Stylus preprocessors:

 .tabbed { … ._tabs { …. .item { …. .selected { … } } } … } 


In this way, you can easily collect and reuse different skins. Moreover, when compiled, all this will give rise to semantically similar to BEM selectors, since there is no significant difference between .tabbed ._tabs .item {...} and .tabbed_tabs__item {...}.

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


All Articles