⬆️ ⬇️

How I translated the BEM project ... and translated

A bunch of HTML and CSS (CSS to a greater extent) always seemed to me somewhat "vague", worst of all amenable to control and testing. I invented various rules for myself and tried to standardize my approach one way or another, but there was no feeling that “here, this is it”. I caught a glimpse of BEM several times (and not only), read articles on this topic, but it did not go further than reading. But the further, the stronger was the feeling of the need for a certain rigorous methodology. In the end, I decided to try to introduce BEM on one of my projects, where, in my opinion, it was impossible to do without it. This is a CMS, a simplified page of the backend of which I will give as an example of typesetting:







At once I want to note that BEM is far from a methodology for all situations, and the question of the need to apply it in a particular project should be considered privately (including on the basis of whether you like it or not). Also, due to the fact that I did not use the proposed specific file structure or HTML generation, we will not talk about them (I later divided the CSS file into separate parts corresponding to the blocks, but decided to limit it to this for now). Also, quite a lot (for example, here and now ) has been written about the advantages and disadvantages of this approach as a whole, so we will not talk about it either, I will just share my experience and reflections on this topic, assuming that you are already familiar with the essence.



Getting started



So, in my case, CMS is a large and modular open source project in which each module can have its own backend pieces, using common and adding specific interface elements to them. Ideally, modules should be developed by other developers. I think this is just such a project where the use of BEM (or other serious methodology) is not just relevant, but necessary. This is especially important because many of the web developers consider CSS as side, unworthy of attention and in-depth study of technology, trying to achieve the desired result by copying ready-made layouts and styles, concentrating on what they like more, resulting in unsupported tables. horror quality styles.

')

At first, the easiest thing was not to touch the source code of my project at all, since it is quite large. Instead, I created a simple HTML document (/index.html) and a style sheet (/css/style.css) for it, put it all on the desktop for convenience, and decided to start to create several fragments from the image above, using Notepad ++ and browser. (As a result, I wanted to get a page containing in general all the components I needed, and only then, if successful, transfer it to my project. The simplified result is available for study at the link at the end of the article; a link to how it all turned out in reality , you can also look there.)



Buttons



I decided to start not with the structure, but with the small block of the button - the button . I have buttons of 3 types: positive action, negative action and neutral action. They differ only in color, so I described these differences in the form of Boolean modifiers , respectively, button - positive , button - negative and button - neutral (I chose the alternative syntax for modifiers and instead of one underscore I use two hyphens - for me looks much clearer).



As a result, the HTML button is described as follows:



<button class="button button--positive" type="button">Text</button> 


Also suppose such an option (one of the features of BEM is, ideally, independence of appearance from the tag used, although I consider it sufficient to proceed from what tags the class can be applied to and not try to foresee everything, inflating the style sheet with unnecessary rules):



 <a class="button button--neutral" href="#">Cancel</a> 


Looks quite readable and understandable. Let's take a look at CSS:



 .button { border: none; cursor: pointer; font: normal 15px 'PT Sans', sans-serif; line-height: 20px; display: inline-block; padding: 5px 10px; } 


The button is described very simply. Although I met the recommendations to reset the values ​​of all the rules within their classes (so that they are not influenced by the environment), but, as for me, this is too much, and is required only when there is a real chance that you will reuse your block in which Some other project where the layout style is different from yours (for example, you are developing some kind of widget that cannot be inserted as an iframe into a landing page). The class of the button block, as required by BEM, in no way specifies its size or external indents.



Go ahead:



 .button--positive { background-color: #87b919; color: #fff; } .button--positive:hover { background-color: #a0d71e; color: #fff; } .button--negative { background-color: #ff4100; color: #fff; } .button--negative:hover { background-color: #ff7346; color: #fff; } .button--neutral { background-color: #f0f0f0; } .button--neutral:hover { background-color: #f5f5f5; } 


These classes define modifiers for different types of buttons (depending on the action) and their state when you hover the mouse over them.



Let's look at our buttons live:





In my opinion, good.



Button groups



In my project, I practically never use the buttons by themselves, they are almost always grouped into groups (for example, “Save” and “Cancel” in the form). In each group, the buttons must be located horizontally, at a distance of exactly 1 pixel from each other. In order not to have difficulty maintaining this distance (in the case of inline or inline-block elements, it would depend on HTML formatting, namely, the presence of a space between tags), the easiest way is to add the float: left rule to the buttons, but only when the button is an element of a group of buttons (i.e., it would be wrong to add this rule directly to the button block).



So, we will describe the block of buttons group of buttons with a single buttons__button element representing the button included in the group. Then the HTML group of buttons will look like this:



 <div class="buttons"> <button class="buttons__button button button--positive" type="button">Send</button> <a class="buttons__button button button--neutral" href="#">Cancel</a> </div> 


Consider CSS:



 .buttons { } 


The buttons block class is empty.



 .buttons::after { content: ''; display: block; clear: both; } 


Since the float: left rule will be applied to the buttons inside the group (I described the reason above), I cancel wrapping this way. By the way, I like this method of closing the flow of flow around the float most, although it will not work in older browsers (most often it is not necessary to rely on them). In any case, that's not the point.



 .buttons__button { float: left; margin-right: 1px; } 


Here we directly describe the button element included in the group, indented by one point to the right.



 .buttons__button:last-child { margin: 0; } 


The last button in the group should not have an indent on the right; use the pseudo-class : last-child for this. I used the pseudo-class, not the modifier, since all the buttons in the groups, without exception , if they are the last, should not have this indent to the right. I consider the use of modifiers in this case unnecessary.



In my opinion, it turns out quite well. The blocks themselves do not position themselves in any way, they do not describe external indents. But when we place a block in another block, it seems to be at the same time becoming an element of its block, and it is the element class that allows you to additionally specify all the necessary rules for its location, if they are necessary. By the way, I always have the element classes first, then the element modifier classes follow, and then the block classes and its modifiers. This greatly simplifies reading HTML, because if there are many classes, it is immediately clearer what it is. Another point (just in case). The order of application of CSS classes is determined by the order in which they appear in the CSS file (and not in the class attribute, as it might seem), therefore, classes should be declared with the simplest blocks, and at the very end should be placed the blocks responsible for the overall structure of the page.



Here’s how our button group looks in the browser:





On this with the buttons, we are almost done, go ahead.



Text fields and text areas



Next, I decided to deal with other controls. I described the text-box text box and the text-area text area in a similar way (we will not consider the text area, since the blocks are almost identical - you can see in the source code of the example). The following is the HTML block of the text field. Additionally, a text-box modifier - required is added, meaning that the field is mandatory (it adds a red bar to the right of the field):



 <input class="text-box text-box--required" type="text" /> 


The corresponding CSS classes look like this:



 .text-box { background-color: #f0f0f0; border: none; font: normal 15px 'PT Sans', sans-serif; line-height: 20px; outline: none; padding: 5px 10px; resize: none; } .text-box:hover { background-color: #f5f5f5; } .text-box:focus { background-color: #f5f5f5; } .text-box--required { border-right: 5px solid #ff4100; } 


There is nothing special here, except for, I repeat, the last modifier text-box - required . The text area also has one, but it is called text-area - required .



Our text box looks like this:





Form fields



As with buttons, text fields and fields are rarely used by themselves in my project. Most often they are used in the composition of forms in the form of form fields (a set of title and text field, for example). That is, the forms are assembled from small finished pieces, rather than from individual controls. So I decided to add a field block, and describe how the headers and text fields and areas inside the form field behave using the field__label , field__text-box and field__text-area elements. As a result, the HTML form field with the text area looks like this:



 <div class="field"> <label class="field__label label">Body</label> <textarea class="field__text-area text-area"></textarea> </div> 


It's simple. Once again pay attention to the order of the classes. First, for example, field__label follows, and label - after it, because the label tag is primarily an element field__label of its field block, and only then an independent label block. Such uniformity helps a lot. Consider CSS:



 .field { } 


This class is empty. When displaying form fields directly in forms, we need to have vertical indents between them, but we will describe this in the corresponding form __ element of the form block further.



 .field__label { display: block; margin-bottom: 1px; } 


The headers inside the field block will be displayed on a new line and have a single pixel indent from the bottom.



 .field__text-box { width: 430px; } .field__text-area { width: 430px; height: 190px; } 


With these two classes, we set the dimensions for the text field and the area when they are elements of the form field. The result of all this is the following:





Also, some of the form fields in me are localized (multilanguage). They need an additional visual marker to indicate the language to which their text fields or areas belong. In HTML, a form field with a set of localized text fields is as follows:



 <div class="field"> <label class="field__label label">Subject</label> <div class="field__culture"> <div class="field__culture-flag">en</div> </div> <input class="field__text-box field__text-box--multilingual text-box text-box--required" type="text" /> <div class="field__multilingual-separator"></div> <div class="field__culture"> <div class="field__culture-flag">ru</div> </div> <input class="field__text-box field__text-box--multilingual text-box text-box--required" type="text" /> </div> 


Note the set of text field classes, four of them. Let's go through them again. The field__text-box class defines the size of the text field inside the form field, field__text-box - multilingual adds a small extra indent to the right so that the characters do not crawl under the language marker that is displayed over the text field. The text-box class defines the basic parameters of the text field, and the text-box - required adds a red bar to the right of the field.



New CSS Classes:



 .field__culture { position: relative; left: 450px; width: 0; z-index: 10; } .field__culture-flag { background-color: #323232; color: #fff; cursor: default; font-size: 8px; line-height: 16px; text-align: center; text-transform: uppercase; position: absolute; left: -23px; top: 7px; width: 16px; height: 16px; } .field__multilingual-separator { height: 1px; } 


There is nothing to dwell on here. The first two classes are needed to display the language marker above the text field or text area, and the last one is to add a single pixel height between controls for different languages.



Forms



Now, consider the form block form . Forms consist of form fields and groups of buttons, which we have already described, but add vertical indents to them using the form__field and form__buttons classes . Here is the simplified HTML form block:



 <form class="form"> <div class="form__field field"> <label class="field__label label">Body</label> <textarea class="field__text-area text-area"></textarea> </div> <div class="form__buttons buttons"> <button class="buttons__button button button--positive" type="button">Send</button> <a class="buttons__button button button--neutral" href="#">Cancel</a> </div> </form> 


This is what its CSS looks like:



 .form { } .form__field { margin-top: 10px; } .form__buttons { margin-top: 20px; } 


As you can see, everything is quite obvious. If necessary, we can insert, for example, a group of buttons into some control panel on our site, but if we are talking about a form, then by providing a group of buttons with an additional class form __buttons , we will get the necessary indent from the top.



In a browser, the entire form looks like this:





Tables



Now let's take a little more complicated element - a table. I think everyone knows that tables should be tabled up (since this is semantically true and has good browser support), but in the case of adaptive layout, it is sometimes more convenient to do it all the same using general-purpose div tags with styles like display: table . In this case, on mobile devices, the horizontal table can be easily transformed into a vertical list by manipulating the displayed data in every possible way (something can be hidden, and something can be combined). Anyway, I decided to use table for the implementation of the tables in my project, but, partly as an experiment, before it was folded using a div . The beauty of BEM independence from tags is that by replacing the div tags with table , tr and td , I did not have to change anything in my CSS file, the table looked identical. I gave both options for comparison.



The standard HTML table looks like this:



 <table class="table"> <tr class="table__row"> <th class="table__cell table__cell--header">Cell</th> <th class="table__cell table__cell--header">Cell</th> <th class="table__cell table__cell--header">Cell</th> </tr> <tr class="table__row"> <td class="table__cell">Cell</td> <td class="table__cell">Cell</td> <td class="table__cell">Cell</td> </tr> </table> 


As you can see, each tag is given a class. It may seem unusual, but it makes it possible to safely change the table , tr and td to a div and not visually notice the difference.



CSS tables:



 .table { border-collapse: collapse; display: table; width: 100%; } .table__row { display: table-row; } .table__cell { font-weight: normal; text-align: left; vertical-align: top; display: table-cell; padding: 5px 10px; } .table__row:hover .table__cell { background: #ffff96; } .table__cell--header { background: #f0f0f0; } .table__row:hover .table__cell--header { background: #f0f0f0; } 


As you can see, for the block itself, as well as for its elements, the display: table , display: table-row and display: table-cell rules are established. Due to this, the block becomes relatively independent of the tags. In fact, I repeat, I don’t think that there is a sense in these rules if you are sure that the table will always be wrapped with standard table tags.



Finally, let's take a look at the result live:





Menu



Go to the final stage. Menus are represented by a menu block. Each menu can contain several groups of menu items ( menu__group element), each of which, in turn, can contain one menu item group title ( menu__group-title element) and several menu items ( menu__item element). Here is the corresponding HTML:



 <div class="menu"> <div class="menu__group"> <div class="menu__group-title sub-title"> Group title 1 </div> <a class="menu__item" href="#">Menu item 1</a> <a class="menu__item" href="#">Menu item 2</a> <a class="menu__item" href="#">Menu item 3</a> </div> </div> 


I thought about making the menu__group and menu__item elements separate blocks, but I didn’t find any arguments in favor of this solution: they are not used anywhere else, it would only lead to an increase in the number of classes.



Everything seems to be obvious, but for clarity, I’ll also give CSS:



 .menu { } .menu__group { } .menu__group-title{ } .menu__item { display: block; padding: 5px 0; } 


In this case, I have some classes empty. As you can see, for example, the appearance of the headers of groups of menu items is determined by the general sub-title block (I didn’t stop at it - please see the source code). There is no need for empty classes (rather, there is a need for their removal). I decided to leave them for clarity of our example.



The menu itself looks like this:





General structure



Finally, consider the overall structure of the page. But before that, I would like to touch on another point. The fact is that mainly in BEM articles it is recommended not to have non-block rules in the CSS file. That is, the general rules that apply to the whole document (for example, with a selector on the tag, not on the class). I decided not to go this way, because in this case the number of rules that need to be duplicated in each block or element increases. I see no particular reason for doing this in my case. If you think about it, then all the blocks in my project are described within a single context, and it is immutable, so it’s permissible, for example, to set a general style for the text, and to build on it in all blocks, since they should still have a generalized style.



In addition, it seems to me superfluous to assign a class for each heading, a paragraph in the text, each link. In this case, when using, for example, a WYSIWYG editor, we would need to add these classes manually (or do it automatically when saved). Anyway, this is an extra inconvenience.



Let's return to the general structure. I decided to present it with one master-detail block with two main elements: master-detail__master and master-detail__detail , responsible, respectively, for the left-dark and right-light parts of the page.



I added two menus to the master-detail__master . One menu does not contain any additional classes of the master-detail__master element , since there is no need to supplement it with some CSS rules. The second menu is also the master-detail__secondary-menu element, which positions it at the bottom of the master-detail__master element . Additionally, the elements of this second menu are “mixed” with the master-detail__secondary-menu-item element, which gives them a gray color.



I will not give HTML / CSS of this block, because it is too bulky, and it should be considered in the context of the rest of the page content. Therefore, I suggest to look at the source code of the test example, the link to which can be found below.



Also on the page there is one more block - tabs. I decided that it makes no sense to describe them any more, since the block is very simple.



Well, in the end we get the same result as in the first picture.



findings



Why did I decide to write this? When I began to deal with BEM, I had many questions to which I could not find definite answers. It was a kind of semi-finished ideas. It was interesting to rearrange and take a new look at the process of HTML-layout, abandon the use of cascades and so on. As a result, I somehow found solutions for myself, and I wanted to share this experience in order to try to simplify this “addictive and rebuilding” process for someone, showing another point of view.



I generally liked the methodology. The most important thing, in my opinion, is that it pushes the developer into a fairly rigid framework in terms of structuring, naming style, and so on. As a result, in most cases there is only one answer to the question “how?”, Which is very cool (especially in large and team projects).



Will I apply BEM on small and simple projects? I don't know yet. Although I didn’t experience any unnecessary complications at all and didn’t notice unnecessary “straining” because of the increased number of classes, but still, following the methodology requires a bit more effort than with the “traditional” approach. Although, quite possibly, this is due to lack of experience and skill.



I hope it was interesting. You can see and touch it live here , and here lies the real demo of the project, a piece of the simplified admin panel of which I cited as an example. There you can look at the real scale of how it looks, and also, if desired, compare with what it was before.

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



All Articles