Functional CSS (FCSS)

By Matt Baker

We’re big fans of functional programming at Wealthfront. Emphasizing immutability and functional style means fewer “surprises” because side-effects are limited or nonexistent. We can quickly build up large systems from discrete, focused components by way of composition.

Applying functional programming principles is straight forward in most languages, even if they’re not functional by definition. The same can not be said of CSS. Let’s look at some characteristics of our favorite (and most hated) styling language:

  • Everything is global scope.
  • Everything is mutable.
  • The precedence of definitions is calculated based on some interesting rules

So let’s talk about what we can do. Wealthfront’s CSS (really, SCSS) style guide outlines some rules-of-thumb that allow us to gain the benefits of the functional programming paradigm in CSS. Specifically, our guide helps us to minimize surprises by limiting side effects, and promotes composition so our CSS can more effectively scale. In this article I’ll cover some of the primary rules from our style guide.

Introducing scope to a scopeless language

In most languages, variables you define are limited to their scope. In Javascript, variables are scoped to their function, while other languages like Java scope them to the block. Variables I define or change inside my scope can’t be re-defined or changed by someone else outside my scope.

Scope is an important part of programming defensively and minimizing side-effects. If your rule, function, or variable only exists in a limited scope then you can be assured no one will be altering it, whether accidentally or intentionally.

CSS doesn’t have scope. Style definitions can override each other accidentally, and there’s no way to guarantee that the name you pick for your style rule won’t be used by someone else. It could be in a completely different file, nested several selectors deep. If you pick a simple name for your style rule, the odds that a fellow engineer will override it by accident are high. Let’s consider this example:

/* profile.css */
.error { color: orange; }
.success { color: blue; }
/* signup.css */
.error { color: red; }
.success { color: green; }

If we included both CSS files in our HTML we’d be inadvertently overriding one style with another. As a site grows, these kinds of situations become increasingly more complicated and prevalent.

So how do we introduce scope to CSS?

The safest way to mimic a more granular scope in CSS is by using a naming convention. In this case, we “namespace” all our style rules with a prefix. Namespacing rules with a prefix is not a new idea, but it’s important that we understand why we’re doing it. In prefixing our styles we create our “scope”, you might say that our css is “prefix” scoped — our styles only exist within a given set of prefixed rules.

Let’s try the example above with prefixing:

/* profile.css */
.profile-error { color: orange; }
.profile-success { color: blue; }
/* signup.css */
.signup-error { color: red; }
.signup-success { color: green; }

Prefixing allows us to encapsulate our rules and protect them from modification. With these prefixes attached, our rules won’t step on each other’s toes. We’ve insulated them from side-effects by declaring our rules within the scope of our namespace.

Minimize dependencies, foster reusability

It’s tempting to use complex selectors to keep our markup lean and free of classes. We’ve all seen css that looks like this:

.whitepaper-link {
  font-weight: bold;
  font-size:12px;
}

.main-nav .whitepaper-link {
  font-size:16px;
}

.main-footer .whitepaper-link {
  font-size:9px;
}

But what if we want to have our smaller .whitepaper-link somewhere else? By nesting selectors like this we enforce DOM structure in our styles. This says “you can only have a small whitepaper link if it’s in a main-footer”. Enforcing structure through CSS rules prevents us from reusing styles, and mixes the presentation of our data with its representation in markup. When we enforce structure by nesting selectors, we create a dependency between them. Managing dependencies in any area of software engineering is a headache and error prone. We should avoid it all costs.

Instead of enforcing a structure, let’s define it like this instead:

.whitepaper-link {
  font-weight: bold;
  font-size:12px;
}

.whitepaper-link-large {
  font-size:16px;
}

.whitepaper-link-small {
  font-size:9px;
}

Our markup can add both the .whitepaper-link and .whitepaper-link-small class to the footer element to achieve the same effect as our old, nested styles. Now we can reuse the “small” style of our element anywhere in the site, whether or not it’s inside a footer. What we’re really seeing here is the power of composition, we’ll talk about that more in a minute.

Avoiding mutability

Overriding style rules is not an uncommon process in CSS. For example, you may want an error message to appear differently if it’s inside a sidebar container:

/* errors.css.scss */
.error { color: red; }
.sidebar .error { border:1px solid red; }

This is the stuff spaghetti code is made of. It’s not unlike a group of engineers using a global variable. In some of their code certain engineers will redefine the variable (style), while others will expect it to retain its original definition. The .error style becomes unsafe to use, there’s no way to know how it will behave in any given context. The style rule becomes full of surprises, and we hate surprises.

The solution is to never override the definition of a style rule. If you treat rules as immutable – that is, they are set in stone and can never be changed after their definition – you can avoid a host of problems that arise out of global, mutable variables.

We can accomplish this by way of composition, whether we do it in the element’s class attribute, or via Sass’s @extend directive.

Composition is your friend

Let’s look at how we’d use it to handle the example we described above.

/* errors.css.scss */
.error { color: red; }
.sidebar-error { border:1px solid red; }
<!-- example.html -->
<div class="error sidebar-error">Oh no!</div>

We don’t redefine the .error rule, instead we attach a new rule to our error div that augments it. The appearance of our error div is the composition of .error and .sidebar-error.

This is still a little confusing, we don’t override the .error rule itself, but we do override one of its properties. If you’re using Sass, it’s more expressive to define the composition of your styles in scss itself by way of the @extend directive.

/*errors.css.scss*/
.error { color: red; }
.sidebar-error { 
  @extend .error;
  border:1px solid red; 
}
example.html
<!-- example.html -->
<div class="sidebar-error">Oh no!</div>

Now our markup stays slim, and doesn’t give the false impression that this should look exactly like an .error. Any developer that looks at the errors stylesheet will see that .sidebar-error is an .error with an extra border. They can use .error with confidence since it will never be redefined, and we can still have our custom .sidebar-error appearance.

FCSS

Wealthfront has a few more rules we didn’t discuss in this article, but they all fit into the overall guiding principles we’ve discussed. For example, we avoid element and most pseudo selectors because they enforce DOM structure and we prefer classes over IDs to promote reusability (ID selectors are used for very specific, single-element overrides or styles).

To recap:

  • Namespace your classes with string prefixes to fake “scope” and minimize surprises;
  • Don’t override rules on a style with multiple definitions, use composition;
  • Don’t use nesting, element selectors, or excessive pseudo-selectors – they enforce DOM structure in your CSS.

Every team needs its own style guide to enforce constraints a largely constraint-less language. Left to its own devices, ad-hoc CSS grows into a Lovecraft-esque multi tentacled beast. With any luck, our “functional” approach will lend some inspiration as you develop your own.

This article was originally published at https://eng.wealthfront.com/2013/08/functional-css-fcss.html

Previous

Choosing Between npm, Bower and component

Understanding Scope and Context in JavaScript

Next

23 thoughts on “Functional CSS (FCSS)”

  1. “We’ve all seen css that looks like this”. Seen css like that? I’ve written it. Recently.

    But you’ve done a brilliant job of explaining why I shouldn’t. Thanks very much.

  2. Hey Matt,

    Funny how great minds think alike. I basically had born something like this out of tremendous pain building enormously scalable responsive design projects. One thing I do take issue with however is the over use of @extend and the verbose naming convention. The problem that can arise is that you must make a completely unique name for each extended version. This can be quite a lot to track over time as well as it being completely dependent on how you author it ( for example you are using a layout block “side-bar” prepended). I prefer to use the original class as a baseline and use class chaining and ampersand (via SASS) for every different state, version, version within a state, etc etc.

    This way I know that no matter WHERE I am, I can just start with “indicator” or “error” and burrow further with class chains as needed. Think of it as a adding to the “prototype” of an object rather than creating a new object every time. People are most familiar / can best understand a single object tree.

    .indicator
    width: 100px
    height: 300px

    // Default states
    &:hover
    background: lightred
    &:focus
    background: red

    // Begin Class iterations
    &.size-1
    width: 150px
    &:hover
    background: lightblue
    &:focus
    background: blue

    &.size-2
    width: 200px
    &:hover
    background: lightgreen
    &:focus
    background: green

    &.size-3
    width: 250px
    &:hover
    background: lightorange
    &:focus
    background: orange

    Thoughts?
    Thanks, Jeff

    • Damn indenting got lost. Regardless I think its understandable based on my explanation. size-1,2,3 house different version of hover/focus basically. And all of it is nested under indicator.

  3. While your intention is good with your “whitepaper” link example, your example is bad. Creating class names that describe the element’s appearance are also creating a dependency between the CSS and the HTML.

      • I somewhat agree. I did try to use this methodology for one project, and I did not like it at all. I ended up with classes like .smallerFonts .biggerFonts .invertedColors etc. and it was just an additional concern and additional things to memorize. Now I’d rather stick to classes for … classifying or characterizing repeatable elements (eg: item, wrapper, container, whatever….) (and ids for accessing unique elements). Using re-usable classes for visual characteristics sounded like a great idea but did not turn out that well. Maybe it’s just me but that’s my experience 🙂 I hope the author tried his solutions in development before writing about them.

      • See my comment below.

        “A white paper is an authoritative report or guide helping readers understand an issue, solve a problem, or make a decision.[1] White papers are used in two main spheres: government and business-to-business marketing. They may be considered as grey literature.”

    • @Jason, the name “whitepaper”, in this context, has nothing to do with visual presentation.

      “A white paper is an authoritative report or guide helping readers understand an issue, solve a problem, or make a decision.[1] White papers are used in two main spheres: government and business-to-business marketing. They may be considered as grey literature.”
      —https://en.wikipedia.org/wiki/White_paper

  4. Hi, Matt

    I liked your article. This is something I have recently been obsessed with and I would recommend you check out something called Inuit CSS (inuitcss.com) by a chap called Harry Roberts (@csswizardry). He built a framework for making maintainable and scalable CSS.

    Great article!

    Cheers

    Tom

  5. I agree with abstracting as much as possible and defining new class names so as to avoid the case where you don’t know how a class will react in any given context; however, I would avoid using classes that reference sizes (i.e. small, medium, large) as this can cause problems with responsive design.

    In the same way you shouldn’t reference colours in classes, you shouldn’t reference sizes. Sure, you can create a mixin as a helper but this should never go into the markup.

  6. This is why I’m a fan of css frameworks like nocssti, it’s reusable, expandable, fast to learn and implement with. Great article. It’s true what you say about redefining. It’s horrible when it gets to the point where you override an !important with another !important.

    • If you get to that point then you should re-think and re-factor your CSS code.
      !important should only be used for testing and some very rare cases. For all other cases, !important is not a tool, but an “emergency override”. You should think ahead and not rely on it.

Comments are closed.