Oro Frontend Development Guidelines
Oro Frontend Development Guidelines describe the Code style, a set of conventions that describe the way to write code.
Naming Conventions
The main idea of the naming convention is to create names as informative and clear as possible.
Selector Naming
Selector names are lowercase and their logical parts are divided by a dash (-).
Acceptable
product-gallery-widget
Unacceptable
productgallerywidget, productGalleryWidget, product_gallery_widget
Block Name
A block is a logical self-contained functional component of the user interface.
A block identifier should match the corresponding layout block type identifier.
Block names (part of the block identifier) may be prefixed with a short namespace or a bundle identifier if similar blocks are provided by multiple bundles to eliminate confusion, for instance.
Examples:
product-info, shopping-cart, currency-switcher
order-group-totals and quote-group-totals, or even crm-quote-group-totals and commerce-quote-group-totals
Element Name
The namespace of an element (which is equal to the parent block ID) identifies the element as belonging to the block.
In the element identifier, the element name is delimited from the element namespace by a double underscore (**__**):
block-name\_\_elem-name
If a block has several identical elements (e.g., menu items), all of them will have the same name (e.g. menu__item).
Modifier Name
The namespace of a modifier (which is equal to the parent block ID or the parent element ID) identifies the modifier as belonging to the block or the element.
The modifier name is delimited by a single underscore (_) from the modifier namespace:
for Boolean modifiers - owner-name_mod-name
for key-value type modifiers - owner-name_mod-name–mod-val
This gives the following advantages:
The logic of naming enables you to immediately understand what a particular class represents.
Decreases the possible conflict issues between classes.
Each element is in the namespace.
Components are easily transferred from project to project.
HTML Coding Standards
Base Code Style
Do not add a slash at the end of single elements.
The attributes in use are “ “, not ‘ ‘.
No spaces or tabs after the closing tag.
Indent only with spaces.
The attachment elements are indented with 4 spaces.
Simple Names
<div class="product">
<p class="product__name">Product name</p>
<div class="product__prices">...</div>
<div class="product__info">...</div>
</div>
The Order of the Attributes
The required and optional attributes for the tag (e.g., name, type, src, href, etc).
Attributes used for UI customization (e.g., class, data-*, etc).
Attributes with JSON content.
<input name type id
class
data-*
data-entity="{{ {
id: entity.id,
title: entity.title
}|json_encode }}"
/>
CSS Coding Standards
Base Code Style
Built on SASS preprocessor.
Focused on web standards.
The Principles of CSS Architecture
Predictability — Predictability for CSS means that your rules are behaving as expected.
Reusable — CSS rules should be abstract and decoupled enough for you to build new components quickly from existing parts without having to recode patterns and problems that you have already solved.
Scalable — Scalable CSS means that it can be easily managed by a single person or a large engineering team.
Support — When new components and features need to be added, updated or rearranged on your site, doing so should not require refactoring existing CSS.
Responsive — We use CSS to resize, hide, shrink, enlarge, or move the content to make it look good on any screen.
SASS Code Standards
Use the .scss syntax.
Indentation only with spaces.
Indent size: 4 spaces
Continuation indent: 4 spaces
The attributes in use are ‘ ‘ not “ “.
Use: {}, :, ;.
Put a space before the opening brace { in rule declarations.
Put the closing braces } of rule declarations on a new line.
Each component is written in a separate file.
Do not write vendors’ prefixes.
Format
Add a space before the opening brace and a line break after. Add a line break before closing brace.
Acceptable
.element {
color: $color;
}
Unacceptable
.element{color: $color;}
Selector Delimiters
Add a line break after each selector delimiter. Delimiter should not have spaces before and after.
Acceptable
.element1,
.element2 {
color: $color;
}
Unacceptable
.element1, .element2 {
color: $color;
}
Type Selectors
Unless necessary (for example with helper classes), do not use element names in conjunction with IDs or classes. Avoiding unnecessary ancestor selectors is useful for performance reasons.
Acceptable
.element {
...
}
Unacceptable
div.element {
...
}
div#element {
...
}
Combinator Indents
Use spaces before and after combinators.
Acceptable
.element1 + .element2 {
color: $color;
}
Unacceptable
.element1+.element2 {
color: $color;
}
Properties Line Break
Use line break for each property declaration.
Acceptable
.element {
position: absolute;
top: 0;
left: 0;
}
Unacceptable
.element {
position: absolute; top: 0; left: 0;
}
Properties Colon Indents
Use no space before property colon and a space after.
Acceptable
.element {
color: $color;
}
Unacceptable
.element1 {
color : $color;
}
.element2 {
color:$color;
}
.element3 {
color :$color;
}
End of the Selector
Each selector should be finished with a new line.
Acceptable
.element1 {
color: $color;
}
.element2 {
color: $color;
}
Unacceptable
.element1 {
color: $color;
}
.element2 {
color: $color;
}
Shorthand
If you use more than 2 parameters (three indents, for example), write short:
.element {
margin: 10px 0 5px;
}
If less, then:
.element {
margin-top: 10px;
margin-right: 2px;
}
Floating Values
Do not add zero for fractional numbers.
Acceptable
.element {
opacity: .5;
}
Unacceptable
.element {
opacity: 0.5;
}
Zero and Units
Omit the units for zero value.
Acceptable
.element {
margin: 0;
}
Unacceptable
.element {
margin: 0px;
}
Border
Use 0 instead of none to specify that a style has no border.
Acceptable
.element {
border: 0;
}
Unacceptable
.element {
border: none;
}
Nesting
When selectors become quite long, you may write CSS that is:
Strongly coupled to the HTML
Overly specific
Not reusable
Selector Nesting
Be careful with selector nesting. In general try to use 2 nested levels max.
Exceptions are pseudo elements and states.
Acceptable
.block {
&__element {
&--modifier {
...
}
}
&--modifier {
...
}
}
Unacceptable
.block {
...
.block__element {
...
&.block__element--modifier {
// STOP!
}
}
&.block--modifier {}
}
No Elements of Elements
According to BEM methodology, there are no elements of elements. It makes the elements dependent on the block only. So, you can easily move them across the block when providing changes to the interface.
Acceptable
.block {
...
.block__some-element {
...
}
.block__other-element {
...
}
}
Unacceptable
.block {
...
.block__some-element {
...
.block__some-element__other-element {
// STOP!
}
}
}
Place of @media Rules
All @media rules are placed at the end of file. The block applies only to the common styles for all devices. @media describes individual styles for each type of device. This enables us to change or add styles only for a specific type of device in the future.
Acceptable
.block {
width: 50%;
padding: spacing('md');
background-color: get-var-color('neutral', 'dark');
&__element {
font-size: $base-font-size--xs;
}
}
@include breakpoint('tablet') {
.block {
width: 100%;
}
}
@include breakpoint('mobile') {
.block {
padding: spacing('sm');
&__element {
font-size: $base-font-size--large;
}
}
}
Unacceptable
.block {
width: 50%;
padding: spacing('md');
background-color: get-var-color('neutral', 'dark');;
@include breakpoint('tablet') {
width: 100%;
}
@include breakpoint('mobile') {
padding: spacing('sm');
}
&__element {
font-size: $base-font-size--large;
// STOP!
@include breakpoint('mobile') {
font-size: $base-font-size;
}
}
}
Work with Colors
- To work with a color, use:
get-color($color-palette, $color-key): returns a color from a predefined color scheme;
get-var-color($color-palette, $color-key): returns a color from a predefined color scheme with CSS variable syntax. This allows you to easily change the color for different themes, such as a dark theme.
Example:
.block {
border-color: get-color('neutral', 'white-50');
color: get-var-color('neutral', 'dark');
}
If you need darker, lighter or more transparent color, use the native sass:color functions color.scale(), color.adjust(), etc.
@use 'sass:color';
.block {
// Makes color more transparent
background-color: color.scale(get-color('neutral', 'focus'), $alpha: -60%);
// Makes color more darken
border-color: color.adjust(get-var-color('neutral', 'dark'), $lightness: -10%);
// Makes color more lighten
color: color.adjust(get-var-color('neutral', 'dark'), $lightness: 10%);
}
Group Properties
Group properties are grouped in the following order:
variables
positioning
block model
typography
visualization
other (animation, opacity)
mixins
Each group should be followed by an empty string.
In CSS, each property can be treated in different groups depending on their use: vertical-align, overflow, clear, resize, transform.
Acceptable
// variables
$element-color: get-var-color('text', 'primary'); !default;
$element-border: 10px solid get-var-color('neutral', 'grey2') !default;
$element-background: get-var-color('neutral', 'dark') !default;
$element-color: get-var-color('text', 'inverse'); !default;
$element-font: $base-font-size--xs !default;
$element-line-height: $base-line-height !default;
.element {
// positioning
position: absolute;
top: 0;
right: 0;
z-index: z('fixed');
// block model
width: 100px;
height: 100px;
margin: spacing('sm');
padding: spacing('md') spacing('sm');
// typography
font-size: $element-font;
line-height: $element-line-height;
text-align: center;
// visualization
border: $element-border;
background: $element-background;
color: $element-color;
// other
cursor: pointer;
opacity: .2;
// mixins
// grouping @includes at the end makes it easier to read the entire selector.
@include ellipsis();
}
Unacceptable
.element {
text-align: center;
margin: 0;
$color: #000;
@include clearfix;
color: $color;
right: 0;
position: absolute;
}
Use @extend Directive
Use @extend only selector that is a single class.
Helper class include after variables.
Helper class has maximum 5 rules.
Helper class has abstract name and overall design style.
Examples:
$default-size: 400px !default;
$default-offset: spacing('sm') auto !default;
$default-inner-offset: spacing('base') !default;
$default-background: get-var-color('neutral', 'dark') !default;
%dialog {
width: $default-size;
margin: $default-offset;
padding: $default-inner-offset;
background: $default-background;
}
.modal {
// other modal styles
@extend %dialog;
&__close {
// other button styles
@extend %dialog__close;
}
&__header {
// other header styles
@extend %background-gradient;
}
}
Logical Sense
Use the logical number of modifiers for the element.
Acceptable
“Quiet classes”
%modifier {}
%another-modifier {}
%yet-another-modifier {}
.block {
&__element {
&--modifier {
@extend %modifier;
@extend %another-modifier;
@extend %yet-another-modifier;
}
}
}
<div class="block">
<div class="
block__element
block__element--modifier">
</div>
</div>
Unacceptable
<div class="block">
<div class="
block__element
block__element--modifier
block__element--another-modifier
block__element--yet-another-modifier">
</div>
</div>
The Main Mixins and Functions
Out-of-the-box, OroCommerce offers offers a wide range of helper utilities to handle common layout challenges, including clearing floats, positioning pseudo-elements, managing z-index stacking, and applying styles based on media queries, rtc. The entire list of available mixins and functions see in StyleBook General Look and Feel.
Using the helper to clear inner floats.
.block {
@include clearfix;
}
Using the helper for the positioning of pseudo-elements.
.block::after {
@include after;
}
Using the helper function for managing z-index stacking
.dialog {
z-index: z('popup') + 1;
&-overlay {
z-index: z('popup');
}
}
Using the helper mixin for organizing media queries
@include breakpoint('tablet') {
// styles for tablet version
}
@include breakpoint('mobile') {
// styles for mobile version
}
Best Practices
$block-font-title: get-font-name('main'),'helvetica', arial, sans-serif !default;
$block-offset: spacing('md') !default;
.block {
display: flex;
flex-wrap: wrap;
&:hover {
background-color: get-color('secondary', 'c1');
}
&__element {
width: 25%;
padding-left: $base-ui-element-offset * 2;
font-size: $base-font-size--s;
@extend %transition;
&:hover {
border-color: get-color('additional', 'c2');
}
// compound class
&-title {
font-size: $base-font-size--large;
line-height: 1.1;
}
&--first {
padding-left: inset;
}
}
&__content {
padding: $base-ui-element-offset;
}
// State written &. (the active state of the menu item, for example).
// Usually dynamic.
&.expand {
...
}
}
@include breakpoint('tablet') {
.block {
width: 100%;
&__content {
padding: spacing('md');
font-size: $base-font-size--large;
}
}
}
@include breakpoint('mobile') {
.block {
&__element {
width: 100%;
&-title {
margin-bottom: 0;
font-size: $base-font-size--large * 1.5;
}
}
}
}
Comments
Prefer the // line comments to block comments.
Prefer comments on their own line. Avoid end-of-line comments.
Acceptable
Unacceptable