BEM stands for Block, Element, Modifier. It is a convention on how to structure CSS rules. Read more here.
For Turnkey, the natural Block in BEM is one placed ViewModel column/widget/control.
Out of the box (this is extended with UI overrides), you will see these Blocks:
BEM_BlockName.Add(CSSControlType.ctStaticText, "tk-static-text"); BEM_BlockName.Add(CSSControlType.ctImage, "tk-image"); //new BEM_BlockName.Add(CSSControlType.ctTextEdit, "tk-text-field"); BEM_BlockName.Add(CSSControlType.ctCheckbox, "tk-checkbox"); BEM_BlockName.Add(CSSControlType.ctDatePicker, "tk-datepicker"); //new BEM_BlockName.Add(CSSControlType.ctCombobox, "tk-select"); BEM_BlockName.Add(CSSControlType.ctGrid, "tk-data-table"); BEM_BlockName.Add(CSSControlType.ctGroupbox, "tk-groupbox"); //new BEM_BlockName.Add(CSSControlType.ctButton, "tk-button"); BEM_BlockName.Add(CSSControlType.ctFile, "tk-file-upload"); BEM_BlockName.Add(CSSControlType.ctLink, "tk-link"); BEM_BlockName.Add(CSSControlType.ctTextArea, "tk-textarea"); //new , multiline text BEM_BlockName.Add(CSSControlType.ctNumberEdit, "tk-number-field"); //new BEM_BlockName.Add(CSSControlType.ctFloatNumberEdit, "tk-float-field"); // new
A Block is the outer div that we place in the CSS grid to position it in the same position you designed it to be.
The Element in BEM is then the inner parts of a BEM Block.
You will typically find these elements inside blocks in Turnkey-generated HTML:
BEM_ElementName.Add(CSSBEMElement.bEm__native, "__native"); // the actual html control BEM_ElementName.Add(CSSBEMElement.bEm__interactive, "__interactive"); // the html control the user can change, similair to __native but for certain controls (checkbox) the __native is hidden and a span is __interactive BEM_ElementName.Add(CSSBEMElement.bEm__label, "__label"); // the label BEM_ElementName.Add(CSSBEMElement.bEm__content, "__content"); // enclosing inner div
Looking at HTML to exemplify, we will get this HTML:
<div class="tk-component tk-text-field"> <label class="tk-text-field__label">Name</label> <input class="tk-text-field__native"> </div>
And it will be default rendered like this:
The aim of our BEM approach is to allow you to target a single element inside a specific block in order to override the style to your needs.
The third letter of BEM is Modifier. A Modifier is in the BEM convention typically described as an additional appendix to the element rules. In Turnkey, we treat all the style references that you add in the Designer as Modifiers. These modifiers will be added as separate classes to block levels to make it easier for you to create modifier styles that match a block or element within a block by normal CSS rules.
Match All Turnkey-controls, Disregarding the Type of Control
To match all Turnkey-controls - regardless of the type of control - placed by the CSSGrid for the ViewModel, you can use .tk-component. The tk-component CSS class is added to all BEM-Blocks.
If you want to style all <label> used by turnkey controls: .tk-component label{}
Our BEM Strategy
In our BEM strategy, we follow the "intent of BEM" rather than "BEM by the letter". Consider, if you have a text box control on a ViewModel and get this HTML:
<div class="tk-component tk-text-field"> <label class="tk-text-field__label">Name</label> <input class="tk-text-field__native"> </div>
Now, add a style reference in your ViewModel column: "red". We consider "red" to be a Modifier-style and your HTML will now look like this:
<div class="tk-component tk-text-field red"> <label class="tk-text-field__label red">Name</label> <input class="tk-text-field__native red"> </div>
In "true" BEM, we would expect this HTML:
<div class="tk-component tk-text-field tk-text-field--red"> <label class="tk-text-field__label tk-text-field__label--red">Name</label> <input class="tk-text-field__native tk-text-field__native--red"> </div>
Our standpoint is that the explicit modifier tk-text-field--red style brings no additional benefit that we cannot get by simply using the tk-text-field.red in CSS.
The CSS rules will look like this in the two cases:
CSS rule | Comment | |
---|---|---|
.tk-text-field__label.red | Match labels for text field controls that has modifier red | This is how we do it |
.tk-text-field__label--red | Match labels for text field controls that has modifier red | We do not do this since the above rule is just as easy and requires fewer unique styles |
It has been proposed that we should add the modifier ONLY to the outer div like this (this was more like our old strategy):
<div class="tk-component tk-text-field red"> <label class="tk-text-field__label">Name</label> <input class="tk-text-field__native"> </div>
We have chosen to abandon this strategy since this strategy leads to CSS rules looking less like BEM rules:
CSS rule | Comment |
---|---|
.red tk-text-field__label | To match the label intended to be red, we now need to write red in front of the Element specifier - and this a far from what BEM suggests |
.tk-text-field__label--red | This is what the BEM convention stipulates |
.tk-text-field__label.red | This is what we do - very BEM-like and true to the general idea of Block__Element--Modifier |
Possible Breaking Changes Introduced 2019-06-30
We aim to keep backward compatibility - this time, we needed to harmonize according to the BEM convention.
Was | Is | |
---|---|---|
.tk-button | .tk-button__native | |
.tk-button.tk-button--narrow | .tk-button__native--narrow | |
.tk-checkbox__label | .tk-checkbox__content | the style did not target the label surrounding the __native |
.tk-checkbox | .tk-checkbox__interactive | |
.tk-data-table | .tk-data-table__content | the style targets the div containing the table |
.tk-data-table__content | .tk-data-table__native | this is the true table |
.tk-select__native-control | .tk-select__native | native-control should be native as everywhere else |
.tk-text-field | .tk-text-field__native |
If you need to adapt your old CSS to our new standard, chances are you can do so by changing modifier style rules from ".red" to match block div with ".tk-component.red" instead.