Lessons from CSS modules

16 Apr 2013 by Chris
5 Comments
Lessons from CSS modules

Futuristic, or retro? I can't quite decide...

Last year I read the book “Scalable and Modular Architecture for CSS (SMACSS)” by Jonathan Snook, and it’s made quite a difference to the way I approach writing CSS. I don’t actually like to use everything advocated in the book, but I’ve really taken to the idea of using modules in my CSS. I’d like to share how I implement modules, and the benefits it can bring to your stylesheets.

What is a module and what does the CSS look like?

In the Primate CSS style guide we define a module as a “self contained, stand alone component of a webpage, composed of decorative and layout styles”. As an example, on this webpage we have a ‘newsletter sign up’ box, either on the right hand side or below the post (depending on screen width). The sign up box can be easily visualised as a module, anything within the dark box constitutes the module. There are other sections on this page which are prime candidates as modules, such as the comments form, and the related article list.

Using the newsletter box as an example, the base CSS structure (in SASS) for the module might look like the following:


@mixin mod-newsletter_box {
  background: $dark_grey;

  .title {}
  & > form {}
  .social_links {}
}

.newsletter_box { @include mod-newsletter_box; }

@mixin mod-newsletter_box_horizontal {
  // Module variant
}

Something important that cannot be seen in the example above, is that the module style declaration `.newsletter_box` is not nested at all. In other words, I’m not doing something like this: .sidebar .newsletter_box {}. It might seem painfully obvious that this is the case, but if a module is to be a stand alone component it has to be removed from any containing context.

The base of a module is initially defined with a @mixin, and then with a class selector. The @mixin contains the base module styles, and the class selector .newsletter_box is what is used to connect the base styles with markup. Any module variations are defined in another separate @mixin. The variant styles are not standalone, and instead they are an extension to the base styles, and must be applied to an element which has the base module class.

In markup, the base module definition .newsletter_box is the only class applied to the module container. The module variant is never applied as a class in markup, and I’ll touch on this later in this post.

Taming the cascade

The important thing to note about selector choice within a module, is that we aim to control the cascade as much as possible. Used correctly, the cascade makes for concise and efficient CSS, but you don’t really wan’t to let every style cascade.

The first way to achieve this is by using classes. You’re far less likely to have class names cascading, than you are a P tag. For example, if your module contains a piece of text that is red and it happens to be contained within a paragraph, don’t use the P tag as your CSS selector. Paragraphs are commonly used, and somewhere down the cascade you’re going to end up with red text where you don’t want it. Use a meaningful class name such as ‘price’, and avoid applying your styles to a generic element.

If you do wish to use elements that’s OK, but if you do, limit the cascade to one level. By this I mean using child selectors. I would assume that with clean markup, you will more often than not be able to target an element which is at most two levels deep within your module. The child selector effectively stops the cascade, and you can rest assured that your styles will only be applied within your module. If you feel uncomfortable using a child selector, don’t as they are widely supported.

Class selectors give markup flexibility

A module is potentially going to be used in a variety of locations on a site, and we ideally have to have a bit of flexibility in the markup we choose to use. One way to achieve this is to use class selectors in your module CSS.

To illustrate this, consider the use of header tags within a module. Depending on where a module is placed, the header levels within may vary. If you attach a header style to a H2 in your module CSS, you have to use a H2 in your markup, even if it conflicts with the other header levels on a page. It makes a lot more sense to use something like class="title", as you are free to use whichever level of header tag that makes sense in the context of the entire page.

As a side note, another reason I try to use class selectors as much as possible is because it makes for very readable CSS. If you give your classes meaningful names, it’s very easy to identify styles in the resulting CSS.

Module variants, and responsive modules

It’s quite common to have module variants, which differ slightly in layout and dimensions for example. Especially in a responsive site, it’s quite common to have a module vary as the screen real estate shrinks or expands. Variants aren’t necessarily only used on responsive sites, as modules may vary page to page on a non-responsive site.

The first thing I would say is that it’s best not to use class names to apply variant styles. In other words avoid applying multiple classes like the following:

<div class="module module-variant">…</div>

The variations you apply are purely stylistic, and shouldn’t be controlled through markup. Instead use context within your CSS to apply variant styles, and by context, I mean containing element.

I use SASS, so my variant styles are defined within a @mixin. This allows me to define my styles once, and reference them multiple times (you could also use a placeholder). If I would like a module to be displayed differently when the screen is larger than 1400px for example, I would @import my variant style @mixin within a media query. If I would like my module to display differently within a sidebar, I would do something like the following:

.sidebar .module { @import module-variant; }

I’ve read blog articles recently bemoaning that modules cannot be self contained on a responsive site. There is a feeling that you cannot contain all modules styles within one style block. In my approach to modules, I define one style block for the base module, and then one block for each variation. It’s my opinion that you cannot contain a module within one single block when using media queries based on page width. The ideal situation would be to have media queries based on the containing element width or height. If we had this, perhaps the idea of a one style block module would be a reality. If any browser Gods are listening, pretty pretty please can we have them?

Why stop at modules?

I’ve title and based this post around modules, but some of the CSS principles discussed can be applied anywhere in your stylesheets. There is a definite benefit to limiting, or making conscious decisions on what styles cascade. Doing so can lead to more maintainable and stable stylesheets. If you’ve not done so already, I really recommend you experiment with breaking pages down into modules, with the aim to create re-usable and stable CSS.

If you liked this article then why not subscribe to our RSS feed.

Author: Chris Lamb

Chris is terrible at coming up with taglines.

Comments

Showing 5 Comments

Leave a comment
  1. Dale Sande 20 Apr 2013 at 3:22 am

    This is pretty awesome, but if you are looking to take the Module approach even deeper, I would suggest reading, Sass – Clear out your Junk Drawer

    http://blackfalcon.roughdraft.io/4436524

  2. Hugo Giraudel 20 Apr 2013 at 9:49 am

    Great article Chris!

    One question though, why not an argument in the mixin conditioning some styles inside it?

    Something like:

    @mixin my-module($variation: none) {
    /* Default styles for my-module */
    @if $variation == “horizontal” {
    /* Styles for my-module horizontal */
    }
    }

    .my-module { @include my-module; }
    .footer .my-module { @include my-module(horizontal); }

    Any thought?

    • Chris 22 Apr 2013 at 3:52 pm

      Thanks Hugo,

      I hadn’t thought about using arguments in the module mixins, but that might be really nice. Though, when I @include a variation, I wouldn’t want to include the default styles, so I would have a conditional statement on the default styles as well, like so:

      @mixin my-module($variation: default) {
      @if $variation == default {
      /* Default styles for my-module */
      }
      @if $variation == horizontal {
      /* Styles for my-module horizontal */
      }
      }

      .my-module { @include my-module; }
      .footer .my-module { @include my-module(horizontal); }

      Having ‘default’ (or ‘none’) as the default argument value works well, and it means when you pass ‘horizontal’ as the argument you don’t get the default styles :-)

      Thanks for the shout Hugo, your suggestion makes a lot of sense, and it would be nice to contain the default and variation styles within one mixin instead of multiple.

  3. [...] Lessons from CSS modules – I’ve really taken to the idea of using modules in my CSS. I’d like to share how I implement modules, and the benefits it can bring to your stylesheets. [...]

Leave a Reply