DEV Community

loading...

Elements of Functional CSS

promhize profile image Promise Tochi ・6 min read

The first time I wrote a reasonable amount of CSS rules was in 2015 while building a Shopify theme. I wrote over a thousand lines of CSS in a single style.css file. I had written the theme styles in a single file and didn't follow any of the existing class naming methodologies (BEM, OOCSS..) despite knowing the problem they were trying to solve. BEM was the most popular and widely adopted naming methodology back in 2015 but when you do a google search for "BEM what is a block?", this is what you get:

The Block, Element, Modifier methodology (commonly referred to as BEM) is a popular naming convention for classes in HTML and CSS. ... In this CSS methodology a block is a top-level abstraction of a new component, for example a button: . btn { } . This block should be thought of as a parent.

A block is a top-level abstraction of a new component, what does that mean? abstraction, component esoteric terms for a programming language that aims to be as approachable and beginner-friendly as possible. Plus when you start writing BEM class names, you discover there are grey lines when determining which element should be a Block and you have to make subjective decisions. I knew what BEM was back in 2015, I had read several articles about it, I had tried to use it but couldn't. It got unwieldy shortly after chaining a block, its children and some modifiers. Additionally, class naming methodologies like BEM, OOCSS encourage selector nesting, a CSS feature that I've learnt to best avoid over the years.

Somewhere between 2015 and 2016, I stumbled on something called csswizardry grids, written by Harry Roberts and it lit a light-bulb in my head. If you take a look at the Github page at https://github.com/csswizardry/csswizardry-grids/blob/master/csswizardry-grids.scss, all of the rulesets have exactly one rule in them.

Screen Shot 2021-01-21 at 11.29.40 AM

I only had to use it for a few weeks to realize I needed to rewrite all of my CSS rulesets to have a single rule. Although I loved it and encouraged everyone I worked with to write CSS in the same way, I didn't realize I had stumbled on something called functional CSS until 2017/2018 when I discovered Tachyons and later TailwindCSS.

What follows next are principles that make functional CSS magnificent and were originally written in October 2017, but never published.

Functional Rulesets

Functional CSS is made up of mostly functional rulesets. A large project with simple layouts and little to no animations can be built entirely with functional CSS. If functional CSS is made up of functional rulesets, what makes a ruleset functional? The snippet below describes a simple ruleset

/* A simple ruleset */

.menu { //opening block
    padding: 1rem; //rule declaration
}//closing block

//.menu = selector
Enter fullscreen mode Exit fullscreen mode

Let's make the above ruleset functional in the snippet below:

.padding {
    padding: 1rem;
}
Enter fullscreen mode Exit fullscreen mode

Simple, huh? A few things of note:

  1. Functional rulesets have selectors that describe what they do. The name you give your class should describe what it does.
  2. Functional rulesets do one thing only. Normally they should contain only one rule, but the snippet above can be re-written as follows:
.padding {
     padding-bottom: 1rem;
     padding-left: 1rem;
     padding-right: 1rem;
     padding-top: 1rem; 
}
Enter fullscreen mode Exit fullscreen mode
  1. A functional ruleset uses classes and classes only as its selector.
//wrong

p { //specificity is different, tagname selectors should be used for reset and normalizing purposes only
   font-size: 1.2rem;  
}

//wrong

#padding { // should only be used when targeting third party elements
   padding: 1rem;
}
Enter fullscreen mode Exit fullscreen mode

Let's look at how Functional CSS makes CSS more readable, reusable, composable, consistent.

Readability

<header class='header'>
    ...
</header>
Enter fullscreen mode Exit fullscreen mode

Looking at the header element, it's unclear what its .header class does or if it does anything at all. Compare the above snippet to the one below:

<header class='flex flex-wrap justify-center text-grey-dark'>
    ...
</header>
Enter fullscreen mode Exit fullscreen mode

The snippet above is unambiguous, understandable and developer-friendly.

Re-usability

With functional class rules, rules aren't coupled to a specific element or component in any way.

<header class='flex flex-wrap justify-center text-grey-dark'>
    ...
</header>
<main class='flex flex-wrap justify-left text-grey-dark'>
    ...
</main>
Enter fullscreen mode Exit fullscreen mode

The beautiful part is that we did not have to rewrite the same declarations for the <header> and <main> elements. The usual way, you might have,

.header {
    display: flex;
    ...
}

.main {
    display: flex;
    ...
}
Enter fullscreen mode Exit fullscreen mode

Repeating rules severally and unnecessarily.

Composability

Because these classes serve a single purpose, we can easily compose them to create similar and distinct designs. With functional CSS, it is possible to code up entire pages without writing additional CSS. The screenshots below were styled without a single line of additional CSS.

  • https://res.cloudinary.com/practicaldev/image/fetch/s--9rYlU79S--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/x7emktl3u7ax8ilz1fe2.png

  • https://res.cloudinary.com/practicaldev/image/fetch/s--VWJxeXfR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/kqt3gk5efdj1wv555wb2.png

Click the button below to see the code on Codesandbox

Edit Functional CSS Timer Markup

Most times I find myself writing additional CSS when I'm writing animation related CSS, overwriting library styles or building extreme layouts (layouts requiring absolute, fixed pixel positioning)

Consistency

Consistency is cheap and beautiful. This article talks about its value (in design) in detail. Pre-processors via variables make it easy to use consistent values in CSS declarations, but functional CSS also means we avoid gotchas like in the snippet below

.component {
    margin-right: 2rem;
}

/*thousands of lines and several files later*/

.similar-component {
    margin-right: 2.5rem;
}
Enter fullscreen mode Exit fullscreen mode

With Functional class rules, values are written once and reused, eliminating the likelihood of inconsistency creeping in.

//component
<div class="mr-2"></div>

//similar component
<div class="mr-2"></div>
Enter fullscreen mode Exit fullscreen mode

Speedy development

It's quite straightforward, you are faster when you don't have to write CSS or think about appropriate class names. Functional CSS provides this convenience.

When you can whip up an entire webpage without writing a single line of CSS what we gain is tremendous speed and more time for other tasks/goals.

Miscellanous

These are useful guides not specific to functional CSS.

Selectors & Naming

CSS selectors have ranging levels of specificity. The more levels of selector specificity a code-base has, the more difficult it is to work with. For example

.header .menu {
 color: $blue;
}

.menu.is-open {
 color: $red;
}
Enter fullscreen mode Exit fullscreen mode

The intention in the above snippet is to overwrite the colour blue with red when the is-open class is present. However, the desired effect doesn't happen due to .header .menu having a higher specificity than .menu.is-open. Issues like this can lead to the proliferation of !important rules in a codebase. The .header .menu also tightly couples your styling to the structure of the HTML file, which is bound to change in the future.

Maintaining a single low level of specificity across the entirety of a project can help eliminate issues like this. Whenever you find yourself wanting to use a higher specificity than that of a simple class selector, consider name-spacing instead. So the snippet below

.header .menu {
 color: $blue;
}
Enter fullscreen mode Exit fullscreen mode

becomes

.header-menu {
 color: $blue
}
Enter fullscreen mode Exit fullscreen mode

It should be a goal to maintain the same level of specificity across the entirety of a project, although this usually won't be possible.

Values

Just as you maintain consistency with your colors, font-sizes, you should also strive to maintain consistency with dimensions and spacing (pixel related values in general).

How do you keep values consistent?

  • Define your values as variables as much as possible
  • Always prefer relative units em, rem, % ... over pixel units px
  • For sizing and spacing values, try to use values in increment of the previous lesser value. Here is an example:

      html {
       font-size: 16px; /*base value*/
      }

      padding-025 {
       padding: 0.25rem; /* quarter */
      }

      padding-05 {
       padding: 0.5rem; /* Half */
      }

      padding-1 {
       padding: 1rem; /*scale*/
      }

      padding-2 {
       padding: 2rem; /*double*/
      }

      padding-4 {
       padding: 4rem;
      }

      font-size-small {
       font-size: 0.8rem; /* Fractions */
      }

      font-size-normal {
       font-size: 1.2rem; /* Fractions */
      }
Enter fullscreen mode Exit fullscreen mode

A more advanced example:

html {
   font-size: calc(16px + 8 * ((100vw - 768px) / 2440));
}

/* same scales */
Enter fullscreen mode Exit fullscreen mode

Now as the font-size changes, all spacing values change and resize to maintain the same symmetry.

When you are trying to mirror a perfect representation of mockups in the browser, it can be tempting to reach for exact pixel values such as 31px17px93px431px etc from the design tool or the designer. These sort of values are inconsistent, difficult to manage, difficult to reuse, difficult to maintain, and would have the next developer unnecessarily digging through several rules to know how things work or fix issues. A good UI design should already have proper visual consistency and hierarchy in its spacing and sizing.

Discussion (0)

pic
Editor guide