A strange reluctance to be critical

My take on functional CSS

I would like to piggyback on a great article posted on CSS Tricks titled The Growing Popularity of Atomic CSS.

Atomic or Functional CSS is a new trend in CSS development in which one tends to create many CSS classes that contain single style assignment and use these classes as building blocks on DOM elements.

Consider this frequently seen functional example :

.relative {
  position: relative;
}

.flex {
  display: flex
}

.pb10 {
  padding-bottom: 10px;
}
<div class="flex relative pb10">
    Hello world!
</div>

From a purely technical standpoint, this approach looks a lot less DRY than any type of component-based CSS in which you would instead see components with encapsulated styling. It may even remind one of inline styles since we are after all sticking styling logic within the templates.

The component friendly way of achieving the same could look like the following:

.article-bottom-block {
  display: flex;
  padding-bottom: 10px;
  position: relative;
}
<div class="article-bottom-block">
    Hello world!
</div>

You see that it does look cleaner. The problem is that this does not necessarily translate to success.

I’ve never seen it working long-term

My personal experience in the matter is not so black or white. I have come across massive SCSS files in which you cannot understand the intent of the component anymore. I have multiple times tried to enforce component consistency in projects but often encountered the frequent exceptions of a predefined component changing just a tad for its different use-cases.

It is because of these frustrating experiences that I have come to be a fan of functional CSS. I don’t feel the trouble behind maintaining component based styles is a matter of complexity in visual design or a matter of lax programmers not writing clean code. I think projects with an ongoing lifespan are inherently doomed to, at one point or another, ruin their component structure as more content is added to the website or more people work on the files.

On the other hand, I have seen functional CSS allowing more flexibility in these real-world examples. Allowing an integrator the ability to add touch ups to one element without having to add a new exception class into global component styles has become an appealing concept to me.

Why not a little of both?

While I think it should be considered a good practice to maintain a list of low-level CSS assignations, I don’t think we have to carry everything into singleton styles.

I don’t agree that you should specify units through class names and therefore disagree with assignments such as these:

.pb10 {
  padding-bottom: 10px;
}

What seems more meaningful semantically would be to skip the unit but use an adjective to describe the modifier. Enforcing a limit on the types and quantity of adjectives collaborators should use on a project seems easier to enforce than preventing them to make a custom change in unit size.

I prefer to use biggest, bigger, big, small, smaller and smallest in my projects. The goal with these is to have the integrator chose the closest neighbor of what was required by the design and allow at most 6 different variations of a single behavior (which is enormous honestly).

.unpadded-bottom {
  padding-bottom: 0;
}

.padded-bottom {
  padding-bottom: 10px;
}

.larger-padded-bottom {
  padding-bottom: 20px;
}

As with not adding units to your functional CSS, I think simple recurring concepts, grids, and visual effects should be grouped conceptually. It is not very useful to say an element needs to be display: flex through a .flex class. What you really mean is that it is .aligned-horizontally.

Such behaviors should be as simple as possible and distinctly separated. You should then treat the class assignation on HTML element as pure behavior injection. This is very powerful because it amounts to granting traits to DOM elements that are not necessarily related to each other conceptually or visually. Both images in a gallery or a page navigation can be horizontally aligned, but they are not the same components.

Take this next example of how I like to have behavioral visual effects. It makes something appear by expanding it’s height when you hover an element. Conceptually, it is a behavior that can be attached to multiple use cases and that has no impact on anything else.

.displays-on-hover {
	&:hover {
		.display-on-hover {
			max-height: 10rem;
		}
	}

	.display-on-hover {
		max-height: 0;
		overflow-y: hidden;
		transition: max-height .2s ease-in-out;
	}
}

Because the behavior (and not the logic of the behavior) is exposed through CSS, you can attach it to both loose elements and actual existing components like the thumbnail-component which potentially has many styles already assigned.

<div class="displays-on-hover">
    <p>Hover on this block</p>
    <p class="display-on-hover">will make this appear</p>
</div>

<div class="thumbnail-component displays-on-hover">
    <img src="example.png">
    <p class="display-on-hover">A caption</p>
</div>

Here is a pattern I like to use to handle the alignment of elements. Two classes define how an element’s children are expected to be spaced and how they are expected to line up.

.spaced-children {
    > * {
        margin-bottom: $default-gutter;
        margin-right: $default-gutter;

        &:last-child {
            margin-right: 0;
        }
    }
}

.horizontal-children {
    display: flex;

    @media (max-width: $min-viewport-size) {
        flex-wrap: wrap;

        &.spaced-children {
            > * {                
                // Ensure the spaced children have no margin when
                // wrapped.
                margin-right: 0;
                width: 100%;
            }
        }
    }
}

You can then inject the behavior when needed while also gaining the ability the inject other style assignments not necessarily related to the component.

<div class="spaced-children horizontal-children alternate-font alternate-color pin-left">
    <div>Hello world!</div>
    <div>Lorem ipsum</div>
    <div>Lorem ipsum</div>
</div>

Why not mixins?

SASS and Less allow you to place what I define as behavioral traits as mixins you can reuse across your CSS. This effectively ensures you do not duplicate your code.

.card {
    @extend %light-radius;
    @extend %box-shadow;
    @extend %default-text;

    background-color: $content-background-color;
}

The issue I think this raises is that you are not leveraging the cascading part of Cascading Style Sheets. If a component reuses a common group of styles, the grouping may warrant being set in the HTML. The compiled CSS will be much smaller without the multiple references to the same styles and the HTML will be more descriptive to what an element does.

To add behavioral definitions in an HTML document is not a regression back to the era of inline styles and table designs. It can be more semantic than hiding all behind a component name when the behaviors are defined correctly.

Elegantly ending with a cop-out

The bottom line, as in most things web, is that it is up to you. It is possible to do well and to do badly following both methods. I adopt functional CSS because the end result of my previous experiences using component-based CSS was not satisfactory. This cannot be true for everyone.

So long as the functional part of function CSS is grouped by semantic behavior and not a mirror to style declarations, I think the concept offers a powerful way of building websites that are easier to maintain.

Read other articles