Skip to content
This repository has been archived by the owner on Aug 8, 2023. It is now read-only.

Latest commit

 

History

History

class-naming-conventions

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 

Class Naming Conventions

Table of Contents

Basic Conventions

  • Class names are kebab-case (words-are-dash-separated)
  • Subclasses are indicated with double underscore, such as root-name__sub-component-name
  • Each class is prefixed with either c-, t- or u- (consult this table for details and other rarer prefixes)

CSM

Our CSS class naming convention (which we call CSM or Component, Sub-Component, Modifier) uses BEM principles to denote types of classes while still maintaining full use of the cascade.

BEM stands for Block, Element, Modifier. Because Block and Element already have meaning in CSS, we use the terms Component and Sub-Component instead.

<div class="c-blog">
    <h1 class="c-blog__title">Blog Title</h1>
    <div class="c-blog-post c--featured">
        <h2 class="c-blog-post__title">Blog Post Title</h2>
        <div class="c-blog-post__date">
            <p class="c-blog-post__time">12:03pm</p>
        </div>
    </div>
</div>

This example may seem confusing at first, but if we break down each of the selectors that we have, it begins to make more sense.

.c-blog This is a high-level component. In this example, it describes a wrapper that may contain blog content.

.c-blog__title This is a sub-component. In this example, it's a title for a blog.

.c-blog-post This is another high-level component. In this example, it describes the blog post itself. Notice that it can be its own component instead of a sub-component of c-blog because a blog post does not need to be child of the blog container. This way, the component is able to live anywhere.

c-blog-post.c--featured This is a modifier. Notice that the c--featured class is paired with the component or sub-component it belongs to. In this example, it describes a different way of displaying the c-blog-post component.

.c-blog-post__time Like before, this is another sub-component this time belonging to the c-blog-post component. It's still a sub-component, even though it is not a direct child of the component. In other words, sub-components are not required to be direct children of their parent component.

Components

Components are independent and self-contained units of UI. Styles belonging to a component should only affect the component itself, and any of its sub-components. They should not affect anything external to them, or any other components that might be nested within them.

  • Prefixed with our component namespace c-
  • Kebab-cased
  • Not nested, these classes should be declared at the root level of the file
.c-blog-post {
}

Sub-components

Sub-components are elements that are descendants of their parent component. Their classnames should be formatted as such: c-[parent-component-name]__[sub-component-name]. Sub-components do not, and should not, have sub-components of their own. If you find you need to write a sub-sub-component, instead just treat it as a sub-component.

Like components, these should always live at the root level of a file. Avoid nesting these within the parent component or another sub-component.

  • Prefixed by the parent component and two underscores c-[component-name]__[sub-component-name]
  • Lives below the parent component in the root of the file, un-nested
  • Subcomponents do not have to be direct children of the component in the markup
// Good!
.c-blog-post__title {
}

// Bad!
//
// Note how .c-blog-post__title is nested inside it's parent class? It should
// not be nested, instead it should live at the root level of the file
.c-blog-post {
    .c-blog-post__title {
    }
}

// Bad!
.c-blog-post__title__emote {
}

// Good!
.c-blog-post__emote {
}

Modifiers

Modifiers, as their name suggests, modify components or sub-components. They're always chained to the component or sub-component they belong to.

  • Prefixed with the namespace of the affected element and two dashes (c--, t--)
  • Contained to the scope of a single component
  • Always declared as a chained selector to a component or sub-component.
  • Never declared as a stand-alone rule.
// Good!
//
// Note how we use the parent selector (&) to chain the modifier class to .c-blog-post
.c-blog-post {
    &.c--featured {
    }
}

// Also Good!
//
// Use your discretion and decide for yourself whether this option, or the above
// option makes most sense. See below for more some common scenarios.
.c-blog-post.c--featured {
}

// Bad!
//
// Note how .c--featured is a selector all by itself? That's bad! It
// must be chained to it's parent selector!
.c--featured {
}

Component modifiers that affect sub-components

Sometimes a component modifier will affect its sub-components. There are several methods you can use to accomplish this. As much as possible, stick to one method in your project.

<div class="c-blog-post c--featured">
    <h2 class="c-blog-post__title">Blog Post Title</h2>
    <div class="c-blog-post__date">
        <p class="c-blog-post__time">12:03pm</p>
    </div>
</div>

1. Styles grouped with modifier

Nest the .c-component__sub-component elements inside the .c-component SCSS.

This method allows you to quickly update or edit the styles for all elements affected by a modifier.

.c-blog-post {
    &.c--featured {
        ...

        .c-blog-post__title {
            ...
        }
    }
}

or

.c-blog-post.c--featured {
    ...

    .c-blog-post__title {
        ...
    }
}

In larger files, adding a comment in the .c-component__sub-component notes can be helpful:

// Blog Post Title
// ---
//
// Modified by .c-blog-post.c--featured

.c-blog-post__title {
    ...
}

2. Styles grouped with sub-component

Nest the modifier code inside the sub-component using .c-component.c--modifier &.

This method makes it easier to visualize the differences between a sub-component and its modified states.

.c-blog-post__title {
    ...

    .c-blog-post.c--featured & {
        ...
    }
}

In larger files, adding a comment in the .c--modifier notes can be helpful:

// Blog Post
// ===
.c-blog-post {
    ...

    // Featured Post
    // ---
    //
    // Also modifies .c-blog-post__title

    &.c--featured {
        ...
    }
}

State

When a component or sub-component changes state (in response to a user action or other dynamic behaviour), we often add a class so the state can be styled and made visible to the user. These are almost always added and removed by UI scripts as the user interacts with the page. If a class is being added or removed via JS, chances are it’s a state. Name these state classes similarly to modifiers but with an additional prefix of is. Since states will have the same CSS specificity as modifiers, define your states after modifiers in source-order to avoid modifiers accidentally overriding states.

<ul class="c-select">
    <li class="c-select__option c--is-selected">Item 1</li>
    <li class="c-select__option">Item 2</li>
    <li class="c-select__option">Item 3</li>
</ul>
.c-select {}

.c-select__option {}
.c-select__option.c--tall {} // modifier

.c-select__option.c--is-selected {} // state
.c-select__option.c--tall.c--is-selected {} // state

An alternative construction using the has prefix is reserved for marking a parent component with a sub-component that is in a particular state. These cases should be rare, but when necessary, would look like this:

<ul class="c-select c--has-selection">
    <li class="c-select__option c--is-selected">Item 1</li>
    <li class="c-select__option">Item 2</li>
    <li class="c-select__option">Item 3</li>
</ul>
.c-select {}
.c-select--large {} // modifier
.c-select.c--has-selection {} // state

An exception is the use of ARIA roles for styling state. Where an ARIA role maps exactly to the state to be styled, it should be preferred over a class, since the attribute being styled carries additional value to users. For example, the custom select element being built above should be marked up with ARIA roles to be understood by screen readers as a select control. In this case, styling on aria-selectedis preferred to c--is-selected. CAUTION: don’t add ARIA attributes in order to style a component. Only use them in stylesheets where they are needed for accessibility and make a state class redundant.

<ul class="c-select" role="listbox">
    <li class="c-select__option" role="option" aria-selected>Item 1</li>
    <li class="c-select__option" role="option">Item 2</li>
    <li class="c-select__option" role="option">Item 3</li>
</ul>
.c-select__option {}
.c-select__option[aria-selected] {} // state, using `[aria-selected]` instead of `.c--is-selected`

Class Prefix Conventions

You'll have probably noticed by now that our class names have a variety of prefixes. If not, I will describe their usages now:

Prefix Purpose Location
.c- Component classes: this includes the root component (typically the class that defines the component itself), sub-component class, and the modifier class. See above Project's component directory
.t- Template classes: These class names are declared as the template in the corresponding view. Example template classes include: .t-pdp, .t-home, .t-category. Project's template directory
.u- Utility classes: these are meant as one-off, strongly opinionated, high specificity overrides for very narrowly defined styles. Project's /styles/utilities directory
.pw- Progressive Web classes: these classes are reserved as styling hooks for React components built into Mobify's Progressive Web SDK These classes are located either in the SDK component's source directory in the SDK, or in a Project's theme directory.
.qa- QA classes: these classes are reserved for, and used as selector hooks for tests (unit tests, integration tests, etc.). These classes are not meant as styling hooks, so no CSS should be applied to any qa- classes! In a project's test directories.
.x- Classes that start with x- are considered global states or document states. That means these classes should only be applied to the html or body element. Example states include x-ios, x-portrait, x-retina, x-header-is-sticky, etc. varies
.m- (*) Desktop embedded mobile markup classes: these are classes that we will use if we author Markup that is intended for clients to embed onto their desktop pages, but is for mobile content. n/a
.js- Javascript classes are used exclusively by scripts and should never have CSS styles applied to them. Repeat: Do NOT style Javascript classes. n/a

* The m- class prefix has an old, deprecated use: Mobify Modules. However, Mobify Modules have been replaced with third party plugins, and are treated as third party libraries with their own conventions.

Components That Style Components

Sometimes there are situations when a component makes use of other components, and in so doing needs to style them for within its context. Let's take a simple example of a button and icon component being tightly coupled in this manner:

Note: the examples in this section will use React JSX syntax for sake of brevity.

<button className="c-button">
    <Icon name={icon} />
    {children}
</button>

In situations like this it is tempting to just style the icon's class inside of the button. However, this practice is poor and creates tight coupling between the Button and Icon components that shouldn't exist. As a rule of thumb, a component should only know about what it's responsible for; it shouldn't be aware of anything external to itself. Since Icon is an external component, the Button component should be completely unaware of c-icon.

The solution to this challenge is to instead give the external component a new class that our new component can know about, like c-button__icon. This way, the component is treated like a sub-component of Button, and eliminates any tight coupling between the components. Both components can change, be added or removed, without affecting the other in an unpredictable way.

So, to summarize...

/* Bad! */
<button className="c-button">
    <Icon name={icon} />
    {children}
</button>
// Bad!
.c-button .c-icon {
    // ...
}

Good Practice

/* Good! */
<button className="c-button">
    <Icon className="c-button__icon" name={icon} />
    {children}
</button>
// Good!
.c-button__icon {
    // ...
}

Parsing vs. Decorating

Ideally, we have total control over the HTML markup of a project. However, sometimes there are engineering requirements that force us to retain client markup, or partially wrap it in a container of our own.

If you find yourself wondering "should I be adding a new class, or should I use the classes from desktop?" consider the following: If we're using their class names, we can't follow our CSM syntax. Here's some advice:

When to use our class naming convention

  • When writing your own markup in a template
  • When decorating (adding, moving, or wrapping) markup in a View, Parser, Decorator or UI-Script (Adaptive.js)
  • When adding custom classes to existing markup
  • When you find yourself using @extend

When to use client selectors

  • When it's fastest, easiest or most efficient to use their markup than it is to add our own.
  • When their markup is too inconsistent, or makes parsing too difficult.
  • When functionality is tightly coupled to markup structure.

How to use their existing selectors in our components

This is a list of rules to use when you're mixing desktop selectors with our selectors.

Remember, it's okay to mix our class naming convention with the desktop selectors. If you have to add a class to a sub-component, use our sub-component naming scheme and place it in the standard spot in the file.

Always component classes should always be structured with our naming conventions.

// Do
.c-blog-post {
    .title {
    }
}


// Don't
.blogpost {
    .title {
    }
}

Desktop classes can be added inside their parent component, but adding our own classes should be your FIRST approach so as to avoid nesting.

Constantly evaluate your nesting in situations like this.

// Okay
.c-blog-post {
    .content {
    }

    .image {
    }
}


// Better
.c-blog-post {
}

.c-blog-post__content {
}

.c-blog-post__image {
}


// Bad!
.c-blog-post {
    .content {
        .image {
        }
    }
}

Use their modifiers the same way you would use our modifiers. Chain them to the component or sub-component it directly affects.

// Okay
.c-blog-post {
    &.darkpost {
    }
}


// Better
.c-blog-post {
    &.c--dark-post {
    }
}


// Don't
.darkpost {
}


// Don't
.c-blog-post {
    .darkpost & {
    }
}

Continue on to Responsive Best Practices →