Modular CSS

The maintenance of CSS, short for Cascading Style Sheets, can quickly become a mess out of control. Opposed to tradtional programming languages, which frequently take an object-oriented paradigm to neatly organize repeating and reusable code patterns, CSS stylesheets often look like an unorganized, verbose chaos that does not stick to the DRY-princicple: Don’t repeat yourself. However, this is not the fault of CSS. CSS, as a markup language, has been looked down on and neglected for a long time as a “toy script”, especially by back-end programmers. This viewpoint changed with the invetion of preprocessors like SASS, LESS & Stylus, which made CSS fun again. If you are still writing plain old CSS, you are doing it wrong.

There is now a whole set of CSS style guides and naming conventions, which help you to logically organize your code in modular blocks for efficient reuse. One system that I found particularly appealing is SMACSS by Jonathan Snook. SMACSS stands for “Scalable and Modular Architecture for CSS” and should not be taken as a rigid framework, but rather as a set of recommendations that can help you to improve your workflow. Snook points out that the important aspect “is to establish a standard, document it, and be consistent with it”.

The following article takes a look at the SMACSS principles in combination with SASS. I highly recommend that you also have a look at the related ebook.

1. Categorizing CSS Rules

SMACSS is not a rigid framework, but a way of thinking about and structuring your design and related workflow to build a scalable CSS architecture that is easy to maintain in the long-term. The traditionial CSS aproach was to throw all your CSS styles into a single file, in which the elements were simply sorted by appearance on the web page.

Now, the core principle of SMACSS is to categorize CSS rules into repeating patterns. Jonathan Snook suggests five general categories that every piece of CSS can be assigned to:

1. Base: Base rules determine the website’s default and reset styling. They should include only single element selectors (html, body, form, a {}), attribute selectors (input[type="text"] {}), pseudo-class selectors (a:hover {}), child selectors (ul li, {}, ul > li {}) or sibling selectors (h2 ~ p {}). This should also include heading sizes and font styles, default link styles and the body background. For example:

body {
    background-color: #fff;
    margin: 0;
}

a {
    color: #0089d0;
}

a:hover {
    color: #00537c;
}

2. Layout: Layout rules concern the different sections of a website, which group reusable modules together. These are the header, footer, sidebar, article, main content and grids, which generally have a single ID or class name. For example:

#header { ... }
#footer { ... }
#sidebar { ... }
#main { ... }

HTML5 introduced a number of descriptive, structural tags like <header>, <nav>, <aside> and <footer>, which can perfectly be used with this approach.

3. Module: Modules refer to the reusable parts and components that can be applied multiple times throught your site. These can be navigation bars, carousels, dialogs, widgets, product lists and buttons, which sit inside layout components. Modules can also sit as sub-modules in other modules. Using IDs with modules should be avoided, as the modules can exist multiple times on the site. Child selectors and descendant selectors with element selectors should be applied to style elements within that module. For example:

.module > h2 {
    padding: 5px;
}

.module span {
    padding: 25px;
}

4. State: States are rules that override all other styles and are generally applied to layout and/or module elements, escpecially in combination with JavaScript and jQuery dependencies. They are used to indicate a change in the state of an element, for example with tabs:

.tab {
    background-color: blue;
    color: white;
}
.is-tab-active {
    background-color: white;
    color: black;
}

// visibility state
.is-visible {
    display: block;
    animation: fade 2s;
}

.is-hidden {
    display: none;
    animation: fade 2s reverse;
}

5. Theme: Theme rules are only used in projects, where users can optionally choose a different appearance of the system (colours, images, skins, look & feel). As this is not applicable for the majority of projects I work on, I won’t describe themes in detail.

2. Breapoints & Media Queries

Media queries should be kept within the specific module instead of storing them centrally in a single file to prevent a distribution of rules over several files converning the same element. What can be stored centrally with SASS are the different breakpoints as variables to create a reusable pattern:

/* breakpoints */
$phone-portrait:    320px;      
$phone-landscape:   480px;    
$tablet:            768px;  
$desktop-md:        1024px; 
$desktop-lg:        1140px; 

/* default layout */
.content {
    float: left;
    width: 75%;
}
.sidebar {
    float: right;
    width: 25%;
}

/* alternate state for layout on small screens */
@media screen and (max-width: $phone-landscape) {
    .content, .sidebar {
    float: none;
    width: auto;
    }
}

3. Naming Convention

The SMACC naming convention for css rules follows the previously described categories and uses prefixes to differentiate between state- and layout-rules. Module rules stay without a prefix to keep things cleaner, as they constitute the majority of components in a project. Furthermore, I like to use a BEM-inspired system to identify sub-modules of modules with a hyphen - and modifiers with -- to quickly build variations. Jonathan Snook recommends to “strike a balance between maintenance, performance, and readability” and to avoid “classitis”, i.e. deep nesting with a class declaration for every single element.

/* Different modules */
.btn { ... }
.tab { ... }
.modal { ... }

/* Module with sub-modules & modifier */
.btn { ... }
.btn-small { ... }                // - sub-module
.btn-large { ... }                // - sub-module
.btn-large--orange { ... }        // -- modifier

For state rules, the prefix is- is used:

/* General state rules (placed in state.scss) */
.is-active { ... }
.is-disabled { ... }
.is-pressed { ... }
.is-visible { ... }
.is-hidden { ... }
.is-collpased { ... }
.is-selected { ... }

/* Modules with general state */
.btn.is-active { ... }
.tab.is-collapsed { ... }

/* Element-specific state rules (placed in specific module) */
.is-btn-active { ... }
.is-tab-collapsed { ... }

Layout rules use the l- prefix:

/* Layout rules */
.l-flipped { float: left; }
.l-fixed { width: 850px; }
.l-grid { ... }
.l-inline { display: inline; }
.l-stacked { display: block; }

Furthermore, CSS properties should be consistently ordered to keep them well organized. The major approaches are to sort properties either alphabetically or by property category (fonts, positioning, display & box model, other). A tool that faciliates CSS property sorting and also works with .scss-files is CSScomb for Sublime Text 3, which can be installed via package control.

To use CSScomb inside Sublime Text, press ctrl+shift+c within the respective CSS / SCSS-file or select Run CSScomb from Tools in the menu. This will sort the CSS properties according to predefined rules, which can be found in Preferences > Package Settings > CSScomb > Settings - Default, like in the following example:

/* before */
.container {
    font-family: sans-serif;
    background-color: #fff;
    z-index: 10;
    overflow: hidden;
    width: 100px;
    font-size: 12px;
    color: #000;
    margin: 20px;
    padding: 15px;
    position: absolute;
    top: 0;
    right: 0;
    height: 100px;
    text-align: right;
}

/* after */
.container
{
    font-family: sans-serif;
    font-size: 12px;

    position: absolute;
    z-index: 10;
    top: 0;
    right: 0;

    overflow: hidden;

    width: 100px;
    height: 100px;
    margin: 20px;
    padding: 15px;

    text-align: right;

    color: #000;
    background-color: #fff;
}

4. SASS & File Organization

A major advantage of using a preprocessor like SASS is the opportunity to separate your project’s file structure in a modular way, where each module can be stored in its own file. The scss-partials are then imported within the main file (main.scss). This approach works perfectly with SMACSS and can be adapted to your specific needs. The following shows a sample directory structure:

 * VENDOR
 *     -_normalize.scss...............Normalize 3.0.2.
 *     -_susy.........................Grid System.
 *     -_breakpoint...................Breakpoint mixins.
 *     -_jquery.ui.core.scss..........jQuery UI Core CSS.
 *
 * BASE
 *     -_functions.scss...............Useful functions.
 *     -_mixins.scss..................Useful mixins.
 *     -_variables.sccs...............Variables.
 *     -_base.scss....................Base styling.
 *     -_helpers.scss.................Useful helpers.
 *
 * LAYOUT
 *     -_grid.scss....................Page grid.
 *     -_header.scss..................Page header.
 *     -_sidebar.scss.................Page sidebar.
 *     -_main.scss....................Page main content.
 *     -_footer.scss..................Page footer.
 *
 * MODULE
 *     -_nav.scss.....................Navigation elements.
 *     -_buttons.scss.................Button elements.
 *     -_forms.scss...................Form elements.
 *     -_tabs.scss....................Tab elements.
 *     -_accordions.scss..............Accordion elements.
 *     -_modals.scss..................Modal elements.
 *
 * STATE
 *     -_state.scss...................State rules.
 * 
 * HACKS
 *     -_shame.scss...................CSS hacks & fixes.
 *
 * main.scss
 */

5. Conclusion

SMACSS & SASS can help you to improve and facilitate your front-end workflow, while making your projects more maintainable in the long-term. The important aspect to understand is that these are not rigid rules, but should be understood as a starting point and guide to develop your own system that works well for your team. Once you have developed your own style and approach, be consistent about it and make sure that every team member follows the agreed standards.