Why can’t we use Functional CSS and regular CSS at the same time?

Avatar of Robin Rendle
Robin Rendle on

Harry Nicholls recently wrote all about simplifying styles with functional CSS and you should definitely check it out. In short, functional CSS is another name for atomic CSS or using “helper” or “utility” classes that would just handle padding or margin, background-color or color, for example.

Harry completely adores the use of adding multiple classes like this to an element:

So what I’m trying to advocate here is taking advantage of the work that others have done in building functional CSS libraries. They’re built on solid foundations in design, people have spent many hours thinking about how these libraries should be built, and what the most useful classes will be.

And it’s not just the classes that are useful, but the fundamental design principles behind Tachyons.

This makes a ton of sense to me. However, Chris notes that he hasn’t heard much about the downsides of a functional/atomic CSS approach:

What happens with big redesigns? Is it about the same, time- and difficulty-wise, or do you spend more time tearing down all those classes? What happens when you need a style that isn’t available? Write your own? Or does that ruin the spirit of all this and put you in dangerous territory? How intense can all the class names get? I can think of areas I’ve styled that have three or more media queries that dramatically re-style an element. Putting all that information in HTML seems like it could get awfully messy. Is consistency harder or easier?

This also makes a ton of sense to me, but here’s the thing: I’m a big fan of both methods and even combine them in the same projects.

Before you get mad, hear me out

At Gusto, the company I work for today, I’ve been trying to design a system that uses both methods because I honestly believe that they can live in harmony with one another. Each solve very different use cases for writing CSS.

Here’s an example: let’s imagine we’re working in a big ol’ React web app and our designer has handed off a page design where a paragraph and a button need more spacing beneath them. Our code looks like this:

<p>Item 1 description goes here</p>
<Button>Checkout item</Button>

This is just the sort of problem for functional CSS to tackle. At Gusto, we would do something like this:

<div class="margin-bottom-20px">
  <p>Item 1 description goes here</p>
  <button>Checkout item</button>
</div>

In other words, we use functional classes to make layout adjustments that might be specific to a particular feature that we’re working on. However! That Button component is made up of a regular ol’ CSS file. In btn.scss, we have code like this which is then imported into our btn.jsx component:

.btn {
  padding: 10px 15px;
  margin: 0 15px 10px;
  // rest of the styles go here
}

I think making brand new CSS files for custom components is way easier than trying to make these components out of a ton of classes like margin-*, padding-*, etc. Although, we could be using functional styles in our btn.jsx component instead like this:

const Button = ({ onClick, className, children }) => {
  return (
    <button
      className='padding-top-10px padding-bottom-10px padding-left-15px padding-right-15px margin-bottom-none margin-right-15px margin-left-15px margin-bottom-10px ${className}')}
      onClick={onClick}
    >
      {children}
    </button>
  );
};

This isn’t a realistic example because we’re only dealing with two properties and we’d probably want to be styling this button’s background color, text color, hover states, etc. And, yes, I know these class names are a little convoluted but I think my point still stands even if you combine vertical and horizontal classes together.

So I reckon that we solve the following three issues with functional CSS by writing our custom styles in a separate CSS file for this particular instance:

  1. Readability
  2. Managing property dependencies
  3. Avoiding the painful fact that visual design doesn’t like math

As you can see in the earlier code example, it’s pretty difficult to read and immediately see which classes have been applied to the button. More classes means more difficulty to scan.

Secondly, a lot of CSS property/value pairs are written in relation to one another. Say, for example, position: relative and position: absolute. In our stylesheets, I want to be able to see these dependencies and I believe it’s harder to do that with functional CSS. CSS often depends on other bits of CSS and it’s important to see those connections with comments or groupings of properties/values.

And, finally, visual design is an issue. A lot of visual design requires imperfect numbers that don’t properly scale. With a functional CSS system, you’ll probably want a system of base 10, or base 8, where each value is based on that scale. But when you’re aligning items together visually, you may need to do so in a way that it won’t align to those values. This is called optical adjustment and it’s because our brains are, well, super weird. What makes sense mathematically often doesn’t visually. So, in this case, we’d need to add more bottom padding to the button to make the text feel like it’s positioned in the center. With a functional CSS approach it’s harder to do stuff like that neatly, at least in my experience.

In those cases where you need to balance readability, dependencies, and optical adjustments, writing regular CSS in a regular old-fashioned stylesheet is still my favorite thing in the world. But functional CSS still solves a ton of other problems very eloquently.

For example, what we’re trying to prevent with functional classes at Gusto is creating tons of stylesheets that do a ton of very specific or custom stuff. Going back to that earlier example with the margin beneath those two elements for a second:

<div className='margin-bottom-20px'>
  <p>Item 1 description goes here</p>
  <Button>Checkout item</Button>
</div>

In the past our teams might have written something like this instead:

<div className='cool-feature-description-wrapper'>
  <p>Item 1 description goes here</p>
  <button>Checkout item</button>
</div>

A new CSS file called cool_feature_description_wrapper.scss would need to be created in our application like so:

.cool-feature-description-wrapper {
  margin-bottom: 20px;
}

I would argue that styles like this make our code harder to understand, harder to read, and encourages diversions from our library of components. By replacing this with a class from our library of functional classes, it’s suddenly much easier to read, and to change in the future. It also solves a custom solution for our particular needs without forking our library of styles.

So, I haven’t read much about balancing both approaches this way, although I assume someone has covered this in depth already. I truly believe that a combination of these two methods is much more useful than trying to solve all problems with a single bag of tricks.

I know, right? Nuanced opinions are the worst.