What’s in a name? A CSS naming convention overview

Oleg Nechiporenko
January 4, 2017
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

SMACSS stands for “Scalable and Modular Architecture for CSS”. In SMACSS, the selectors are divided into five categories:

  1. Base
  2. Layout
  3. Module
  4. State
  5. Theme

Base

Basic selectors applied to elements, for example:

html, body, form { margin: 0; padding: 0; }

Layout

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

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

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 {}

Theme

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;
}

Meaningful CSS

The main emphasis is on new HTML5 elements, their semantics and avoiding redundancy. For example:

 

or

 

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:


  

What’s Your Name

Enter


Using such patterns eliminates redundant information and makes code much cleaner.

Functional CSS

The main focus is on atomicity and reuse of selectors.

Naming Rules

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-”.

Main Principles

  • Simplicity — simple styles are easier to remember and use.
  • Reusable classes — it means that the number of rules in classes is minimal, because more rules leads to poor re-usability.
  • Classes have no side effects — the same class always performs the same role and does not change anything it was not designed for.

Example:

// CSS
.questionCard {
  position: relative;
  margin-top: $ scale1;
  padding: $ scale2;
  background-color: #fff;
  box-shadow: $ boxShadow-2;
}
// HTML
... 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
...

BEM

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.

Block

Functionally independent, reusable component in a page. In HTML, blocks are represented using the “class” attribute.

  • A block’s name describes its meaning (“what is it?” — “menu”, “button”), not visual state (“does does it look like?” — “red”, “big”). 

Here’s an example:

...
...

  • A block must not have an effect on its environment, i.e you should not set any external geometry for units (margins, border and other properties that affect its dimensions) and positioning.
  • In BEM, it is not recommended to use tags or id based selectors.
  • Blocks can be nested.
  • Any nested structure is allowed.

Element

An element is a part of a block that cannot be separated from it.

  • An element’s name describes its meaning ( “what is it?” — “Item”, “the text”: text), not visual state ( “what does it look like?” — “Red”, “big”).
  • The structure of the full name of an element is: block-name__element-name. The element’s name is separated from the block’s name with two underscores: “__”.


 
 
 
 Search

When should I create a block and when an element?

  1. If the code can be reused and doesn’t depend on the implementation of other components on the page, it should be a block.
  2. If the code cannot be used independently without a parent (block), you should probably create 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.

Modifier

Modifiers determine the appearance, state or behavior of a block or element.

  • A modifier’s name describes the appearance (“what size?”, “what theme?”, etc. — “size”: size_s, «theme»: theme_islands), state (‘how different from the rest? “-” off “: disabled, “focused»: focused) and behavior (“how should it behave?”, “how should it interact with users?” — “direction”: directions_left-top).
  • A modifier’s name is separated from the block’s name by one underscore: “_”.

Types of Modifiers

Boolean

  • Used when the presence or absence, and not the value, of a modifier is important. For example: “off”: disabled. It is agreed that the presence of a boolean modifier is equivalent to its value being set to true.
  • The structure of the full name of a boolean modifier follows the following convention:

block-name_modifier-name
block-name__element-name_modifier-name

Here’s a full example:


  
  
  Search

Key-Value

Used when a modifier’s value is important. For example, “menu with theme islands»: menu_theme_islands. 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:


  
  
  Search





  
  Searh

File Structure

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:

  • One block— one directory.
  • Block names correspond to their directores. For example, a “header” block — directory “/header”, “menu” block — directory “/menu”. The implementation of the blocks is split into separate files, for example: header.css and header.js.
  • A block directory is the root directory for all sub-directories of included elements and modifiers.
  • An element’s directory name starts with double underscores: “__”. For example: “header/__logo/” or“menu/__item/”. A modifier’s directory name begins with a single underscore. For example, “header/_fixed/” or “menu/_theme_islands/”.
  • The Implementation of modifiers and elements is split into separate files. For example: header__input.js and header_theme_islands.css.

Namespaces in CSS

It’s also worth mentioning CSS Namespaces. Namespace can be added to all of the above conventions.
  • 0- — objects
  • c- — components, finished UI elements
  • u- — utilities
  • t- — themes
  • s- — scopes. Just like themes, but deal less with visuals.
  • is-, has- — state of the elements
  • _ — for hacks and workarounds.
  • js- — JavaScript related classes
  • qa- — classes that are used for quality assurance and tests

Summary

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 
Hello World!
; }

In Vue.js on the other hand, the style and code are separated and scoped:

scoped</strong>> 
.example { color: red; }

 
  
hi

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.

Are you a web developer looking for an interesting job?

Contact Us