Chapter 5. File organisation and naming conventions

Project organisation

If we want to facilitate the easy removal of modular parts from our website/application, we need to think about the way we organise the files that make up the modules.

Ordinarily, when building web stuff, it's a common pattern to split up files in a project by technology type.

Consider this basic folder structure:

my-project/
- html/
- js/
- css/

In each of these folders you might name related files. For example:

my-project/
- html/
    - v2ShoppingCart.html
- js/
    - v2ShoppingCart.js
- css/
    - v2ShoppingCart.css

The rub though is that beyond a certain point, even giving the files related names, it's difficult to reason about how each style sheet, logic file and template relate. There might be 80+ CSS partials in the CSS folder and 50+ template stubs in the html folder.

It then becomes increasingly necessary to rely on 'find' in the text editor to find any templates that a certain class is being used on. The same is true in reverse; 'find' is needed to locate the partial(s) that contains the styles needed for a certain module template.

This structure doesn't make things unworkable, just inefficient and it typically requires a little mental orientation to remember what goes with what.

While not essential for ECSS, it's generally preferable that rather than organise by technology type, files are organised and grouped by visual or logical component. So, instead of this:

html/
- shopping-cart-template.html
- callouts-template.html
- products-template.html

js/
- shopping-cart-template.js
- callouts-template.js
- products-template.js

css/
- shopping-cart-template.css
- callouts-template.css
- products-template.css

We aim for something like this:

shopping-cart-template/
- shopping-cart.html
- shopping-cart.css
- shopping-cart.js

callouts-template/
- callouts.html
- callouts.js
- callouts.css

products-template/
- products.html
- products.js
- products.css

At first glance this may seem like a seemingly unimportant distinction but it brings important benefits.

The code for each component becomes physically self-enclosed. Then, on our enduring project, when features need changing or are deprecated, all associated code for that module (styles, view logic (HTML) and JS) can be easily updated/removed.

With the exception of intentionally 'global' CSS, all code that relates to the presentation of a component or module should be included in the partials that sit alongside the HTML/JS of that component.

Where the CSS and application logic (e.g. JS/PHP/Ruby) can be stored in the same folder, so much the better. This makes it even easier to know what CSS code is relevant to a module and what is not.

When a module is deprecated, all language files associated with it can be easily removed from the codebase in one go; just delete the folder containing the module.

Just to be crystal clear, consider this folder structure for our imagined ShoppingCart component:

ShoppingCart/
    - ShoppingCart.js
    - ShoppingCart.css

Now suppose we create a new shopping cart:

v2ShoppingCart/
    - v2ShoppingCart.js
    - v2ShoppingCart.css

As soon as our v2 shopping cart is finished, it's easy to remove the code for the prior version from our code base; we just delete the folder containing the original ShoppingCart.

When same folder organisation isn't possible

It may not be possible or preferable to contain style sheets and application logic within the same folder.

In that situation, the next best choice is to mimic the structure of the logic. To exemplify. Suppose the logic for a component is stored in a folder structure like this:

src/app/v2ShoppingCart/v2ShoppingCart.js

We should mimic this structure as far as possible. On any sizeable application this will make locating related files easier. So we might do this — matching the folder hierarchy of the logic part of the module as much as possible:

src/app/css/v2ShoppingCart/v2ShoppingCart.css

Same parent folder should definitely be considered the 'gold' standard when using ECSS but in the absence of that, mimicking the structure of the logic files should provide some of the benefits.

With a concrete idea of how to organise the files within our project, let's turn to the principle way in which we can convey additional meaning and developer convenience to our selectors/classes.

Naming classes and selectors with ECSS

Back in Chapter 3, I recognised the benefits that the BEM approach of naming CSS selectors gave us. Naming a block and then naming any child elements in relation to that block created a namespace for the child elements.

Name-spacing the CSS of a module creates a form of isolation. By preventing name collisions with other elements, chunks of CSS can be more easily moved from one environment to another (from prototype to production for example). It's also far less likely that a change of styles on one selector would inadvertently affect another.

ECSS takes the notion of selector namespacing and turns it up to 11. Selectors are effectively namespaced in two ways:

Let's look at these in more detail. The 'micro' namespace is a simple 2–3 letter namespace for each module. Building a shopping cart? Try .sc- as your micro namespace. Building the next version of that same shopping cart? That'll be .sc2- then. It's just enough to isolate your component styles and allow the styles to be more self documenting. Let's consider a more involved example.

For example, suppose the micro namespace was being used to convey the parent or origin of the logic that created it. Back to our shopping cart example. We might have a file called 'ShoppingCart.php' that contains all the logic relating to our imaginary shopping cart. We could therefore use sc- as an abbreviation of that file name so we know that any elements that begin with that namespace relate to the shopping cart and are rendered by that related file.

In this case, we would then have selectors like:

Here the selectors are quite compact - aesthetically pleasing if a selector can even be described in that way. However, suppose we have a shopping cart which can live in multiple contexts. A mini cart view and a full page view. In that instance we might decide to use the micro namespace to convey context. For example:

Neither of these is the one true way. Part of ECSS philosophy is that while some core principles are essential, it can adapt to differing needs. Generally speaking, for smaller scale use cases, the former approach is fine. However, despite the comparative verbosity of the selectors in the second approach, it is the most resilient and self-documenting. With the second approach you know context, the file that generated the selector (and therefore the module it belongs to) and the element it relates to.

Reiterating the benefits

As namespaced modules and components are almost guaranteed to not leak into one another, it makes it incredibly easy to build out and iterate on new designs. It affords a hitherto un-thinkable blanket of impunity. Just make a new partial file for the thing you are building, assign a suitable micro-namespace and module name and write your styles, confident in the fact you won't be adversely affecting anything you don't want to. If the new thing you are building doesn't work out, you can just delete the partial file, also confident that you won't be removing the styles for something else. CSS authoring and maintenance confidence - finally!

Source order becomes unimportant

As our rules are now isolated, it makes the order of rules in a style sheet unimportant. This benefit becomes essential when working on a large-scale project. In these scenarios it is often preferable for partial files to be assembled in any order. With rules isolated from each other, this is simple. With our 'self-quarantined' rules, it makes file globbing of partial styles sheets simple and risk free. With some basic tooling in place you can compile all the CSS partials within a module in one fell swoop like this:

@import "**/*.css";

No more writing @import statements for every partial in a project and worrying about the order it comes in.

Anatomy of the ECSS naming convention

As the naming of items is so useful and essential to achieving our goals, the following section documents the naming convention of ECSS in more detail. Think of this like a Haynes manual for your CSS selectors.

Here's a breakdown of an ECSS selector:

.namespace-ModuleOrComponent_ChildNode-variant {}

To illustrate the separate sections, here is the anatomy of that selector with the sections delineated with square brackets:

.[namespace][-ModuleOrComponent][_ChildNode][-variant]

Explanation of selector sections

Let's go back over the various parts of the ECSS selector and the allowed character types:

Using this syntax, each part of a class name can be logically discerned from another. More information on what these sections are and how they should be employed follows:

Namespace

As discussed above, the first part of a HTML class/CSS selector is the micro namespace (all lowercase/train-case). The namespace is used to prevent collisions and provide some soft isolation for easier maintenance of rules.

Module or Component

This is the visual module or piece of logic that created the selector. It should be written in upper camel case. I've seen ECSS applied to great effect when the module or component directly references the name of the file that creates it. For example, a file called 'CallOuts.js' could have a selector such as sw-CallOuts (the sw- micro namespace here used to denote it would be used 'Site Wide'). This removes any ambiguity for future developers as to the origin point of this element.

Child Node

If something UpperCamelCase is preceded by an underscore (_) it is a child node of a module or component.

For example:

.sc-Item_Header {}

Here, _Header is indicating that this node is the ‘Header’ child node of the ‘Item’ module or component that belongs to the 'sc' namespace (and if it it were a component, that namespace could indicate the parent module).

Variant

If something is all lowercase/train-case and not the first part of a class name it is a variant flag. The variant flag is reserved for eventualities where many variants of a selector need to be referenced. Suppose we have a module that needs to display a different background image depending upon what category number has been assigned to it. We might use the variant indicator like this:

.sc-Item_Header-bg1 {} /* Image for category 1 */
.sc-Item_Header-bg2 {} /* Image for category 2 */
.sc-Item_Header-bg3 {} /* Image for category 3 */

Here the -bg3 part of the selector indicates that this .sc-Item_Header is the category 3 version (and can therefore have an appropriate style assigned).

Doubling up on ECSS selectors

Our previous example indicates a perfect situation where it would be appropriate to use 2 classes on the element. One to assign default styles and another to set specifics to a variant.

Consider this markup:

<div class="sc-Item_Header sc-Item_Header-bg1">
    <!-- Stuff -->
</div>

Here we would set the universal styles for the element with sc-Item_Header and then the styles specific to the variant with sc-Item_Header-bg1. There's nothing revolutionary about this approach, I'm just documenting it here to make it clear there is nothing in the ECSS approach that precludes this practice.

Summary

We've covered a lot of detail in this chapter. The two main areas we looked at were how to organise the language files of our project so that they can be more easily maintained and how to name classes and selectors in ECSS so that the class of an element in the DOM can tell us everything we need to know about its origin, purpose and intended context. We also had a detailed look at the accepted syntax for ECSS selectors: where and how to apply casing differences to delineate different parts of the selector. So far, we have only concerned ourself with static elements. In the next chapter we will look at how ECSS deals with the changing state of a website or application.