We use cookies to give you a better experience. Carry on browsing if you're happy with this, or read our cookies policy for more information.

Skip main navigation

How we write CSS

Previously we’ve written about our design process and how we’ve moved to thinking in terms of systems rather than pages. Here, James Mockett, Front-end Developer, explains our approach to writing modular and scalable CSS, and how this ties into our modular design process.

how_we_write_css

When you’re working on a large website it’s important that the CSS you’re writing is modular, easily maintainable and scalable. It should be possible to quickly build and prototype new features or pages using existing modules, and it should be easy to add new modules, or update existing ones, without inadvertently affecting styles elsewhere. The site’s CSS should be written and structured such that it is approachable and understandable, and its intent clear.

As your site grows in size, the number of people working on the code is also likely to increase, so the CSS should work equally well for a lone developer as it does for a large team. The CSS embodies the design and should be part of a common language between designers and developers.

In short, your CSS shouldn’t hinder the process of building new features for your users. This post is an overview of how we write and structure CSS at FutureLearn, and how our approach tries to solve these problems.

A note on pre-processors

If you’re writing CSS at scale, you’ll almost certainly want to use a pre-processor such as Sass or Less. A pre-processor allows us to keep our CSS DRY by using variables and mixins to define abstract user interface patterns such as colours, spacing and typography.

We’ve been using Sass and its SCSS syntax since the beginning, and currently have no plans to change (having said that, I’d be interested in trying post-processing instead).

The ghost of CSS past

When I joined the team, the existing codebase was following the SMACSS guidelines and was already very modular, with a styleguide in place to document the various modules and base styles, and provide examples of usage and implementation.

However, some styles had very general names and far-reaching selectors, such that attempting to update the styles for a particular module would often have unintended consequences elsewhere.

Once we have these very specific selectors, our modules become highly dependent on their context and where they are used. We lose the ability to take a module from one page and add it to another without having to override and undo styles, leading to even more complex selectors and CSS, which are difficult to work with and maintain.

Modular design

Back in April, Alla wrote about modular design and how we have moved away from designing and building pages to designing and building systems.

The key to this was Brad Frost’s post on atomic design, which fitted what we were trying to achieve perfectly. Alla and Brad’s posts cover this in far greater detail, but essentially we’ve broken our user interface into small, reusable building blocks — “atoms”, “molecules” and “organisms” — which we can use to construct complete pages.

For this to work, we need to build modules that work independently, are scalable and can be reused across the site. Deeply nested and overqualified selectors don’t allow for this, so we began refactoring our styles and moving to BEM (Block, Element, Modifier) as a naming scheme and a way to define the structure of our modules.

.block {}
.block--modifier {}
.block__element {}
.block__element--modifier {}

.block represents the module itself, whilst .block__element is an element that forms a constituent part of the module. .block--modifier and .block__element--modifier are variations of .block and .block__element respectively.

As an example, this is a simplified version of the class names and markup for our billboard module:

.billboard {}
.billboard--large {}
.billboard__heading {}
.billboard__cta {}

<div class="billboard">
  <div class="billboard__heading">Heading</div>
  <div class="billboard__cta">
    <a class="button" href="#">Button</a>
  </div>
</div>

Just by looking at the class names you can see what the module is, its structure and how the different elements relate to each other.

In naming our classes this way, we ensure that our styles only affect the billboard module and don’t leak out into other modules and elements on the page. It also makes the module independent, as there are no dependencies upon other styles, so we can easily reuse the module in multiple contexts. In the above example, we have a button component nested inside the billboard, but both are completely independent of each other.

As each selector consists of a single class they all have the same low specificity. This allows you to use selector order to override styles rather than battling specificity with overly complicated selectors. So long as the .billboard--large modifier is declared after .billboard, it can override any of the styles in the parent block without requiring a more specific selector. This is also helpful if we wish to define utility classes that take precedence over existing styles.

Naming and namespacing

We have a separate Sass partial for each distinct module with a filename that matches the class name of the block. In the case of the billboard this is _billboard.scss.

Partials are organised into separate folders for atoms, molecules and organisms, so the file structure looks roughly like this:

/base
  _colours.scss
  _type_scale.scss
  _utils.scss
/modules
  /atoms
    _button.scss
    _section.scss
    _stamp.scss
  /molecules
    _brackets.scss
    _hint.scss
    _standfirst.scss
  /organisms
    _billboard.scss
    _splash.scss
    _welcome-section.scss
application.scss

Recently we’ve also introduced the idea of namespacing so classes are prefixed with a-, m- or o-, to indicate whether the corresponding module is an atom, molecule or organism (there is also a u- prefix for utility classes).

This builds on the BEM naming convention and conveys even more information when looking at the class names. If you’re working on .a-button you can immediately see that you’re dealing with an atom that’s likely to be reused in multiple places, so any changes you make are likely to be far-reaching. Harry Roberts has written an excellent article on this very subject.

Looking at our billboard module again, the class names now look like this:

<div class="o-billboard">
  <div class="o-billboard__heading">Heading</div>
  <div class="o-billboard__cta">
    <a class="a-button" href="#">Button</a>
  </div>
</div>

Just from the markup, you can see that the billboard module is an organism with another atom — the button — nested inside it. The class names also give us enough information to locate the corresponding Sass partial: /modules/organisms/_billboard.scss.

The important thing to note here is the common language. We have a single name for each module that is understood by everyone in the team and can be traced all the way back from the designs to the markup and CSS.

Other things

  • There are 127 .scss files on the learner-facing site (which get compiled into a single stylesheet).
  • We use Normalize.css to provide consistent cross-browser default styles before we apply our own.
  • Pixels are used for spacing as they’re easier to work with and visualise, but these are converted into rems via a mixin.
  • We have a responsive typographic scale that is gradually being rolled out across the site (typography and spacing warrants its own post in the future).
  • We currently use Bourbon, but mainly just for vendor prefixing. In order to simplify things we’re planning to remove it and handle vendor prefixing with Autoprefixer instead.
  • We lint our Sass with SCSS-Lint to maintain consistency in the style and formatting of the code.
  • Development effort and testing is mainly targeted towards modern browsers, but everything we build is progressively enhanced, to try to ensure that all browsers and devices can access the site and its content.

The future

Our CSS is in a state of flux at the moment, as we still have a lot of old code that we’re slowly refactoring or rewriting. The existing user interface is gradually being broken down into reusable modules that are being brought into existence and added to the Pattern Library for reuse elsewhere.

While there is still a lot to be done, the changes we’ve already made have given us a solid foundation that allows us to build new features quickly and easily. The overall look and feel of the site is more consistent, and, as the size of the development team grows, anybody working with the CSS will find that it’s structure and purpose is clear.

Want to know more about how we work? Check out our other “Making FutureLearn” posts.

FutureLearn - Learning For Life

Our purpose is to transform access to education.

We offer a diverse selection of courses from leading universities and cultural institutions from around the world. These are delivered one step at a time, and are accessible on mobile, tablet and desktop, so you can fit learning around your life.

We believe learning should be an enjoyable, social experience, so our courses offer the opportunity to discuss what you’re learning with others as you go, helping you make fresh discoveries and form new ideas.
You can unlock new opportunities with unlimited access to hundreds of online short courses for a year by subscribing to our Unlimited package. Build your knowledge with top universities and organisations.

Learn more about how FutureLearn is transforming access to education

Related stories on FutureLearn