BEM and Coding Standards
Watch videos
Style (SCSS) best-practices:
Scripting (React) best-practices:
BEM
This project uses BEM (Block Element Modifier) approach to organize styles.
Following rules are true specifically in ScandiPWA project case:
Blocks and elements start with uppercase:
HeaderIf block or element has 2 or more words in its name - they both start with uppercase:
MenuItemBlocks and elements are divided with minus sign (
-):Header-MenuItemMods are divided with an underscore (
_):Header-MenuItem_visibleMods start with lowercase
Mods may consist of:
a key and a value:
MenuItem_type_dropdownorMenuItem_type_defaultLocalif modifier has 2 or more words in its key or value.a key without value included in the name:
MenuItem_visible
For boolean modifiers, the value is not included in the name:
MenuItem_visibleIf mod has 2 or more words in its name - it is written as follows:
backgroundColor_redBlock’s element can’t be accessed from outside the block
How to use it in Javascript?
This projects uses rebem-jsx-plugin to implement BEM in this project.
Note:
usage of className prop is prohibited
Defining a block
<div block="Header">
// Results into
<div className="Header">Defining an element of a block
<div block="Header" elem="Message">
// Results into
<div className="Header-Message">Defining a block which is an element of parent block
Note:
string props are declared with double quotes ("), while the object keys are declared with single quotes (').
<div block="Menu" mix={ { block: 'Header', elem: 'Menu' }}>
// Results into
<div className="Menu Header-Menu">Using mods (modificators) prop
Boolean modifier
Note:
the prop name should start with is to immediately represent boolean
<div block="Menu" mods={ { isVisible: true } }>
// Results into
<div className="Menu Menu_isVisible">Single key-value modifier
<div block="Menu" mods={ { type: 'horizontal' } }>
// Results into
<div className="Menu Menu_type_horizontal">Multiple key -> value modifier
<div block="Menu" mods={ { type: 'horizontal', behavior: 'autoClose' } }>
// Results into
<div className="Menu Menu_type_horizontal Menu_behavior_autoClose">How to use it in styles (SCSS)?
Let’s consider following JSX snippet:
<form class="Form Form_state_error">
<div class="Field Form-Field">
<span class="Field-Message">Error</span>
<input class="Field-Input" name="default" placeholder="Please enter a value">
</div>
</form>How to access block:
.Form {
background-color: red;
}How to access block’s element:
Note:
& stands for parent selector, it is very useful in order to not repeat yourself.
.Form {
&-Field {
background-color: blue;
}
}How to access modified block’s element:
.Form {
&_state {
&_error {
.Field-Message {
background-color: red;
}
}
&_warning{
.Field-Message {
background-color: yellow;
}
}
}
}Coding standard: description of ESLint rules
file-structure
file-structureFile structure must comply to the following guidelines:
File structure must be flat, meaning that nesting components inside of other components is prohibited.
Extending root directory
srcwith custom folders is prohibited.File structure regulations imply having files with certain postfixes for certain functionality parts. Allowed postfixes for directories are the following
Component and route:
.component.container.style.config.unstatedStore:
.action.dispatcher.reducerQuery:
.queryStyle, type: none
For files which are in their own directories with functionality related only to them (e.g routes, components), names should match the name of the directory these files are in.
derived-class-names
derived-class-namesClass name must match name of the file it is inside of. Expected class names for all the files other than components are name + prefix (e.g. class inside of AddToCart.container.js file must be called AddToCartContainer and not otherwise).
Examples of incorrect code for this rule:
// in MyComponent.container.js
class Abc { /** ... */ }
// in Hello.component.js
class HelloComponent { /** ... */ }Examples of correct code for this rule:
// in MyComponent.container.js
class MyComponentContainer { /** ... */ }
// in Hello.component.js
class Hello { /** ... */ }use-extensible-base
use-extensible-baseAll components should be extensible. For class to be extensible it should be derived from extensible base. Replacements of non-extensible bases are the following and should not be imported - these are available in any point of the application.
PureComponent->ExtensiblePureComponentComponent->ExtensibleComponentno base->ExtensibleClass
The ExtensiblePureComponent, ExtensibleComponent and ExtensibleClass requires no import.
Examples of incorrect code for this rule:
import { PureComponent } from 'react';
class A extends PureComponent { /** ... */ }Examples of correct code for this rule:
// notice, no import, it is a global variable
class A extends ExtensiblePureComponent { /** ... */ }only-one-class
only-one-classThere should be only one class per file. Multiple classes inside of one file are not allowed.
Examples of incorrect code for this rule:
// A.component.js
class A {
/** ... */
}
class B {
/** ... */
}Examples of correct code for this rule:
// A.component.js
class A {
/** ... */
}no-non-extensible-components
no-non-extensible-componentsNon-extensible components are not allowed. Use extensible bases instead of regular Component or PureComponent.
Examples of incorrect code for this rule:
class A extends PureComponent {
/** ... */
}Examples of correct code for this rule:
class A extends ExtensiblePureComponent{
/** ... */
}export-level-one
export-level-oneVariables and classes if declared on root level must be exported (and not by default!)
Examples of incorrect code for this rule:
const SOME_IMPORTANT_NUMBER = 777;
class A extends ExtensiblePureComponent {
/** ... */
}Examples of correct code for this rule:
export const SOME_IMPORTANT_NUMBER = 777;
export class A extends ExtensiblePureComponent{
/** ... */
}use-middleware
use-middlewareWrap default export classes in middleware function in order to make classes extensible and assign namespaces to them.
Examples of incorrect code for this rule:
// Component/A/A.component.js
export default A;
// Route/B/B.container.js
export default connect(mapStateToProps, mapDispatchToProps)(BContainer)Examples of correct code for this rule:
// Component/A/A.component.js
export default middleware('Component/A/Component', A);
// Route/B/B.container.js
export default middleware('Route/B/Container', BContainer);Last updated