CSS, or Cascading Style Sheets, are used to describe the presentation of an HTML document. We recently organized a workshop where the participants wanted advice on CSS naming conventions. We’ve prepared the summary below, which hopefully you’ll find useful.
The main idea behind CSS naming conventions is to make the CSS selectors as informative and readable as possible. This greatly helps in development, debugging and other issues that can come when creating HTML based layouts. The most popular naming conventions are presented below:
SMACSS stands for “Scalable and Modular Architecture for CSS”. In SMACSS, the selectors are divided into five categories:
Basic selectors applied to elements, for example:
html, body, form { margin: 0; padding: 0; }
A page is divided into several sections, which comprise one or more modules. Style layouts are divided into major and minor. Main section names like header and footer are named using IDs. Recurring items on the page are named using classes, usually prefixed with “l-” or “layout-”.
#header, #article, #footer {
width: 960px;
margin: auto;
}
.l-grid {
margin: 0;
padding: 0;
list-style-type: none;
}
Modules are reusable parts of the design. For example, sidebar sections, lists of products, etc. :
.sidebar {}
.callout {}
Elements related to a module use its name as a prefix:
.sidebar-menu {}
State classes describe how modules or section pages will appear in a particular state. The selectors are written with the state prefix “is-”:
.sidebar {}
.sidebar.is-collapsed {}
Themes define how a particular module might look. However, most projects do not use themes. They can also define colors, images and other external characteristics:
//module-name.css
.mod {
border: 1px solid;
}//theme.css
.mod {
border-color: blue;
}
The main emphasis is on new HTML5 elements, their semantics and avoiding redundancy. For example:
<table class=”table”>
or
<form class=”form”>
are redundant. In both cases, we already know that this is a table and a form.
— It does not make sense to specify class “main”, given that the page can only contain one element .
In HTML5, a role attribute has been added, which allows you to specify the purpose of the element on the page. Examples of values are: banner, contentinfo, definition, navigation, search etc.
Button styles can be defined through the following selectors:
button,
[role=button] {
...
}
Other examples, using Bootstrap classes as a starting point:
.form is replaced by form — most forms usually look the same online. Additional classes can be used to separate them.
.form-group can be replaced with form > p or fieldset > p, since W3C recommends using paragraphs for form elements.
.text-input can be replaced with [type=text] — since we already know that it is a text input field.
.btn .btn-primary can be replaced with [type=submit].
An example of the resulting form:
<form method="POST" action=".">
<p>
<label for="id-name-field"> What’s Your Name</label>
<input type="text" name="name-field" id="id-name-field">
</p>
<p>
<button type="submit"> Enter </button>
</p>
</form>
Using such patterns eliminates redundant information and makes code much cleaner.
The main focus is on atomicity and reuse of selectors.
Objects — unrelated to content classes that define a structure of the document (grid, containers, etc.). Identified by the prefix “o-”.
Components — classes that depend on content, but do not define the visual style. Components are usually buttons or custom form elements. Identified by the prefix “c-”.
Utilities — widely reusable classes that do one thing. Responsible for margins, text alignment, positioning, etc. They are identified by the “u-” prefix.
Visual classes — define visual styles of a component, such as colors, fonts, etc. They are prefixed with “v-”.
Example:
// CSS
.questionCard {
position: relative;
margin-top: $ scale1;
padding: $ scale2;
background-color: #fff;
box-shadow: $ boxShadow-2;
}// HTML
<div class="questionCard">...</ div>
Turns into:
// CSS
.u-relative {
position: relative;
}
.u-mt1 {
margin-top: $ scale1;
}
.u-p2 {
padding: $ scale2;
}
.v-bg-white {
background-color: #fff;
}
.b-bs2 {
box-shadow: $ boxShadow-2;
}// HTML
<div class="u-relative u-mt1 u-p2 v-bg-white b-bs2">...</div>
BEM (Block, Element, Modifier) — a component-based approach to web development. Its fundamentals include separation of an interface into separate components. It allows you to develop interfaces of any complexity quickly and easily and reuse existing code, avoiding copy pasting.
Functionally independent, reusable component in a page. In HTML, blocks are represented using the “class” attribute.
<!-- Good. Semantically meaningful unit `error. -->
<div class="error">...</div><!-- Bad. Describes appearance. -->
<div class="red-text">...</div>
An element is a part of a block that cannot be separated from it.
<!-- block 'search-form' -->
<form class="search-form">
<!-- input element of block 'search-form' -->
<input class="search-form__input">
<!-- button element of block 'search-form' -->
<button class="search-form__button">Search</button>
</form>
When should I create a block and when an element?
The exception is elements that, in order to simplify development, require separation into smaller parts, or sub-elements. The BEM methodology does not allow for creation of elements with elements. In this case, instead one has to create a utility block.
Modifiers determine the appearance, state or behavior of a block or element.
Types of Modifiers
Boolean
block-name_modifier-name
block-name__element-name_modifier-name
Here’s a full example:
<!-- Block 'search-form' has a boolean modifier 'focused' -->
<form class="search-form search-form_focused">
<input class = "search-form__input">
<!--Element `button` a boolean modifier `Disabled` -->
<button class="search-form__button search-form__button_disabled">Search</button>
</form>
Used when a modifier’s value is important. For example, “menu with theme islands»: menuthemeislands. The structure of the full name of a key-value modifier follows the following convention:
unit-name_modifier-name_modifier-value
unit-name__element-name_modifier-name_modifier-value
Full example:
<!-- Block `search-form` is a modifier with` theme` value `islands` -->
<form class="search-form search-form_theme_islands">
<input class="search-form__input">
<!-- Element `button` is a modifier with` size` value `m` -->
<button class="search-form__button search-form__button_size_m">Search</button>
</form><!-- It is not possible to simultaneously use two identical modifiers with different values -->
<form class="search-form search-form_theme_islands search-form_theme_lite">
<input class="search-form__input">
<button class="search-form__button search-form__button_size_s">Searh</button>
</form>
The CSS naming conventions used in BEM can also be applied to your project’s directory and file structure .
Blocks, elements and modifiers are split into separate files, allowing us to include them only when needed. The key points are:
It’s also worth mentioning CSS Namespaces. Namespace can be added to all of the above conventions.
To summarize: while interesting from an educational point of view, Meaningful CSS and Functional CSS seem a little awkward to use in practice. While Meaningful CSS requires us to change our HTML and makes it tricky to customize individual components, Functional CSS results in a very long and hard to decipher values for “class” attributes.
SMACCS gives us a solid pragmatic foundation for naming, but doesn’t offer much in the way for naming conventions of classes inside components (or modules). BEM does take care of this, but seems a bit verbose and heavy. As such, SMACCS is probably a good fit for smaller projects, while BEM is ideally suited for larger codebases.
It’s also worth noting that all front end frameworks now tend to encourage the use of components and offer different means of co-locating the styles with the component HTML and logic. This approach eliminates a common problem of having left over, unused CSS selectors. For example in React, one can inline styles defined in JS:
const divStyle = {
color: 'blue',
backgroundImage: 'url(' + imgUrl + ')',
}; function HelloWorldComponent() {
return <div style={divStyle}>Hello World!</div>;
}
In Vue.js on the other hand, the style and code are separated and scoped:
<style scoped>
.example { color: red; }
</style>
<template>
<div class="example">hi</div>
</template>
The scoping means that you can use the same class name in different components, since the class names are transformed during the build process. This is similar to style encapsulation in Web Components. So, if you end up using these frameworks, then the CSS naming conventions in use can be drastically simplified.
Would you like to learn more about CSS in general, naming conventions in particular or a frontend framework of your choice? Get in touch with us! We would be happy to run a workshop for your team or offer a free consultation.