Ditching CSS for style objects — part 1
Throughout my career as a front-end developer, and as you’d expect, I’ve done a lot of styling using CSS and CSS preprocessors like Sass and Less which introduced some awesome features of which the best, in my humble opinion, was mixins that enabled us, developers, to compose styles. These preprocessors, however, didn’t solve my major frustration with CSS, the mother-of-all-headaches, the C in CSS, the “Cascading”, and its narcissistic cousin Mr. “!important”.
This article compares between style objects and utility-classes and tries to prove that the style-object based approach can provide a better way to style your components than the utility-classes based approach.
A utility class is a class that’s associated with a single CSS-property and in some cases, a set of closely related properties and contains one or more declarations that couple these properties with specific values. Libraries like Tailwind and Tachyons provide such classes.
For example, the
hiddenclass in tailwind is a class that contains the single declaration
display: none, while the
my-auto class contains two declarations:
margin-top: auto and
My approach with utility classes was basically to have a base array that contains style classes shared across the component’s different states (default, hovered, focus, etc…) and different arrays that contain classes specific for a particular state and then just concatenate these arrays based on state. Putting these classes in arrays felt so much cleaner and more pleasant to the eye than having a big long string of classes.
These single-purpose non-overlapping classes can help you shift your mindset towards a more composable and functional-style CSS. However, I’ve had my own problems with these libraries.
Problem #1: Changes have been made, respawn
Once you’re done fiddling with these libraries, you want to customize them to your needs, and for that, you need to set up a configuration file where you define all the colors, font sizes, paddings, margins, etc…(basically everything that you’re going to use in your styling), then based on that you generate your customized CSS classes.
I found that to be pretty impractical and to slow down my development speed.
- new color? regenerate
- new font size? regenerate
- changed your phone number? cool, but totally unrelated….
- changed the name of a class? your IDE won’t even notice to let other devs know that they are using a non-existing or an outdated class in the newly generated CSS file because it’s just a string literal, not a reference.
Problem #2: ” It’s a perfect world, let’s predefine everything”
In La la land, they tend to predefine paddings, margins, border colors, supported screen-sizes, and all that you wish for. Here in the real world, however, that doesn’t seem to be the case.
First of all, I don’t get the obsession with having a global fixed set of supported viewports and at the same time emphasize the importance of responsive design. Different UI-components might require different style adjustments in different viewports.
Secondly, as a front-end developer who is implementing some design, it’s very likely that I don’t know in advance what paddings, margins, border colors, and border widths are going to be used across all the different components, and across all the different pages/sub-apps and viewports.
If it’s about achieving consistency! This is then something to be achieved by the design, not the code.
If the design is truly consistent, this will very likely be reflected in the code and its structure.
Problem #3: The dynamic nature of many styling elements
There are many CSS properties that are simply dynamic by their nature. Just to name a few:
- width (especially when calc() is used)
- box-offset properties: top, right, bottom, left
- shorthand properties like flex and margin.
And the list can go on. Add to the list pseudo-elements that have the “ad-hoc” nature. In the end, I couldn’t come up with a clean yet practical way to model these properties and elements using utility-classes.
So my workaround was to mix utility-classes with either custom ad-hoc classes or style objects. But I was not happy about using several styling systems, so the journey continued.
Unlike traditional inline-styling in HTML which is string-based, React’s is object-based, let’s call them “style objects”. Here are the following advantages of using them:
Creating classes for every single style is nonsense, there are a lot of ad-hoc one-off styles. In React, you can just create a style object and pass it to the style attribute.
Simple and straight to the point.
What about composition for non-trivial styles? You use JS’s object composition with the beautiful spread operator.
Advantage #2: Easy-to-reason-about styles
Since inline-styling doesn’t support media queries or pseudo-classes like “:hover” and “:focused”, handling these parts can be delegated to React’s hooks. Your style object is immutable and unaware of any context, and a new one is created when your dependencies change. Using immutable objects, pure functions, and React’s hooks, you have pretty solid, readable, and easy-to-reason-about styles. Take a look at the “Title” component and how it’s styled:
Advantage #3: High productivity
No tools needed to remove unused CSS-classes, no need to transform JSX style tags, no special loaders needed to import CSS files in JS. Plug n’ play