This may or may not seem revolutionary, but quite often I think we can easily forget that CSS is simply data. For some reason, we treat it much differently than any other data. As a result, we have no idea what we’re doing when it comes to creating and consuming that data. Many people argue that CSS is fundamentally broken. I used to argue that is was perfect and just mis-understood. I have since double-downed on that view, though I certainly believe CSS is a lot better than people give it credit for.

There’s probably less than 10 people in the world who can actually write truly scalable and maintainable CSS. I think a lot of people struggle with the cascading and inheritance aspect of it, and the lack of intuitive scoping, and I can’t really blame them. CSS was created in 1996; the problems it was built to solve back then aren’t necessarily problems that exist today. Sure, fundamentally CSS’s goal is to make websites look pretty and usable, but in today’s age where software rules all, it now also has other goals, or rather, other requirements from developers.

Developers demand good experiences from tools and technologies, in the same way users demand good experiences from the software we build using these tools and technologies. Long gone are the days where you open up the website’s singular CSS stylesheet and haphazardly plonk some CSS at the very bottom of the file that is already several hundred lines long. Today, scalability and maintainability are important, and in order to build good looking software that is scalable and maintainable, we don’t really seem to be able to manage it using just HTML + CSS + JavaScript, which is why frameworks like jQuery were so popular, and why frameworks like React currently are popular. It’s also why things like Sass and BEM were pioneered. And to be fair, Sass + BEM, when used right, can actually solve most, if not all, issues that CSS has.

Why is CSS Data?

CSS is essentially a key:value language. There are literally hundreds of valid CSS properties (keys), all with many possible different values (some are continuous, and some are discrete). But this is fine, they’re all documented and we don’t need to memorise them all, only the ones we will use often. Since CSS is just a collection of keys and values supplied to a selector, you can theoretically represent any CSS as JSON, and line-for-line your two samples would be almost identical, the only differences would be subtle syntax nuances. This makes CSS a simple language in theory (just as simple as JSON), so why is it so difficult in practice?

Control and Predictability

We really should be in total control of our stylistic properties. We should know which properties will be applied to which elements under which conditions, and why. I hate to say it, but CSS isn’t respected. People (most people, it seems) will keep throwing CSS properties at something until it does the thing they want it to do, with no regard to how these properties will interact with each other or affect other elements (and without cleaning up after themselves). This means we don’t have proper control over it, and we can’t predict it. This scares me. Every CSS property has a purpose and an effect. If you are in charge of your project’s CSS, you should be able to say exactly what every property on every DOM element is doing, and know exactly how the property will effect other elements.

I can’t speak for other people, but from my own experience, the number of times I’ve fixed bugs by doing nothing more than removing erroneous stylistic properties is too damn high. Maybe CSS is broken.

Mapping CSS Data to the DOM

In order to map some CSS data to the desired DOM elements, we have a bunch of awkward abstractions available to us such as class names, selectors, cascading, nesting etc. We create a bunch of conditions in the form of selectors, and let the browser apply them cascadingly based on the state of the DOM (the position of elements, their class names/IDs/attributes etc). Now remember, these concepts were invented before front-end developers viewed websites as collections of abstract UI components. Historically, in order to update the DOM, you would have separate JavaScript files where you would have things like event listeners for UI events, and data-fetching for business logic. You would then do a load of shit and somewhere in the mess the DOM would get updated to have new classes that would correspond some CSS so you can make the website look different based on the events that have just transpired.

Most modern tools, technologies and conventions work off the premise that each page is a collection of UI components, so naturally things like scoping become a much greater concern. Additionally, modern tools like React allow UI components to have things like state and context. This is important to acknowledge for later.

CSS-in-JS

Love it or hate it, CSS-in-JS is here to stay. Just over a year ago, I would have said this was unfortunate. But today, my mind has been changed. I believe that, as it stands, JavaScript can be used to create stylesheets more effectively than regular CSS, Sass, or any other solution that doesn’t involve JavaScript (from a Developer-Experience perspective).

Despite the popularity of solutions like Styled-Components, I can’t help but wonder where this idea of writing CSS syntax inside Tagged Template Literals came from, and why it gained so much traction.

Incase you don’t know what I’m talking about, take a look at this example from the Styled-Components homepage:

Now, I’m all for letting people like what they like and use what works for them, but honestly, the fact that this code snippet is not only considered acceptable, but good, baffles me. I feel like I’m like I’m taking crazy pills, or that everyone else is lying to themselves…

…but for now I will remain open-minded and assume that I’m the crazy one. Back to the example of CSS inside strings. I have to ask, since we’re using JavaScript, why aren’t we at least writing something like:

…wouldn’t this obviously make more sense to everyone, at least from an API perspective (forgetting the logistics of how either solution might work)? Doesn’t is just make more sense, and look nicer? Apparently it doesn’t. I’ve previously argued that it’s better to use concepts and features of the host’s language where possible (i.e JavaScript objects utilising things like the object spread syntax) rather than writing code inside strings. People seem to argue that because Tagged Template Literals are an ES6 feature, it either doesn’t count as writing code inside strings, or that it doesn’t matter that code is inside strings.

Does it really make sense to take data in the form of keys and values and store it within template strings just to keep with the CSS syntax, when the same data can be represented using objects? You know, like a normal person? If your goal is to write CSS syntax without any mental overheads, consider just learning how to write scalable and maintainable CSS, or use Sass + BEM. For me, the goal of CSS-in-JS isn’t to be able to write CSS syntax within a JavaScript file, it’s to be able to use JavaScript’s concepts and paradigms to style my UI components in a more efficient and pragmatic way.

React and CSS-in-JS

If you’re not familiar with React, this section may not make complete sense.

React is a great framework for creating reusable UI components. It’s also great for handling data. Since CSS is just data, we must surely be able to pass some keys and values corresponding to CSS to our react components using React’s/JavaScript’s concepts, and do it in a way that is both pragmatic and efficient. What if we could remove all these abstractions that exist within CSS to change how we style components to be more inline with modern practices?

This article is currently looking at how CSS can be thought of as data, and how that data can be consumed and used by React components to style them. Its focus is more the theory than the practice. I’d love it if some existing solution already offered the theory that will be discussed in this article, but after my assessment of the 10 most popular CSS-in-JS solutions, none of them quite offered if. I’m currently building a library that is based on the theory that will be discussed.

Since we’re using React, we’re thinking in terms of components, which have props , state and context . So we can consider that based off these values, we may want our UI component to look a particular way; i.e, we may want our UI component (and child components) to have certain CSS properties based off the value of their corresponding props , state and context objects (and nothing more). If we know that the component’s CSS (other than its default/static values) can only ever change based on these objects, and we know upfront exactly what the mapping of the objects’ values to the CSS properties should be (which we would), then we can actually remove the need for class names or any sort of selectors and cascading nonsense. Rather than saying something like:

“On this element, if this value is true, add this class. On this element, if this class is present, add these styles.”

we can simply say:

“On this element, if this value is true, add these styles.”

How would it work?

To consider how this might work in practice, it might help to start with a basic HTML, CSS and JavaScript example of a UI component. I find that using an Accordion works quite well to demonstrate these things. After that, we can look at re-creating the exact same accordion using React and think about how we could apply the same styles to it without the abstraction of class names and selectors (thinking in React terms). Hopefully the benefits will be apparent.

Starting off with the HTML, we will use BEM as a naming convention, leaving us with something like:

…the corresponding Sass might look something like:

…and then finally some JavaScript to make it work:

… hopefully it all makes sense so far. A working example can be seen here:

Now that we have a basis to work off, we can attempt to re-create the same thing using React without CSS. Let’s start with something as simple as possible — moving the original HTML into some JSX, and making the accordion panels dynamic:

We can add functionality quite easily using React Hooks. This would now give our accordion (or at least the accordion’s panels) some state:

Depending on the isActive state, we pass the accordion__panel--active class to the panel element. Depending on the the presence of the accordion__panel--active class, we would have previously (when using Sass) applied some new styles to the heading and content elements. At this point, we haven’t really re-invented anything or introduced any new concepts. To get to the styled accordion we created with Sass using CSS-in-JS techniques, you would have to introduce one (or more) of the following to the above JSX:

a separate provider component

replace div s with user-defined components (e.g. styled-components)

s with user-defined components (e.g. styled-components) CSS syntax within Tagged Template Literals (CSS within strings)

Modification to Webpack configuration

Whilst, arguably, none of these are ideal, my personal favourite choice would be replacing div s with user-defined components. As this is also a feature of Styled-Components, which seems to be the most popular CSS-in-JS solution so far, this provides some confirmation of my bias. Let’s see how the solution might look if we were to use Styled-Components. Without trying to change how we do things too much, I landed with the following:

I wanted to keep the Panel styles at the top (i.e — above Heading and Content ), but for some reason it stopped the heading hover styles being applied when the corresponding panel was active.

Let’s see if we can refactor this code to remove the use of class names (i.e — accordion__panel--active ). Styled-Components lets us pass props to the styles, so instead of assigning the accordion__panel--active class name, let’s see if we can pass a prop instead, leaving our JSX as:

After fiddling around in CodeSandbox it turns out that with a bit of jiggery-pokery, we can do exactly what we want:

This is much better, I get to keep the Panel styles at the top, and everything works as expected with no class names. At this point, our JSX is probably as clean as it’s ever going to get, I’m very happy with it. The API for the styles is certainly acceptable, but I still feel a bit uncomfortable with the template strings, given that the same information and logic could easily be conveyed with plain objects and arrow functions. And, really, I’d feel more comfortable keeping all my styles for a single UI component within a single reference (i.e — const styles = {…} , but there isn’t really a way to do either of these things nicely with Styled-Components.

I feel like something like this would be much nicer:

…it conveys the exact same information, except instead of template strings it’s just plain objects and arrow functions that return objects (and my pet-peeve issue of controlling the heading background color when hovered is much more easily handled). You would fetch parent component props and state by using context (i.e the active prop on the panel component), instead of cascading. I really am struggling to find any arguments that suggest the template-strings API is better at this point.

However, in order to make this example work with the JSX we had with Styled-Components, we would need to manually define some <Panel> , <Heading> and <Content> components, bloating the JSX (this wasn’t so much an issue when using Styled-Components because we were defining the component styles at the same time). And we want to avoid using divs and class names (e.g. <div className='panel'> ). A good compromise could be something like <Component name='panel'> — this leaves us with a situation where we don’t need to define new components for every element within the module, but can keep the JSX more semantically meaningful.

We would also need some sort of provider as well, since all our styles are contained within a single reference. Rather than bloating our JSX with a dedicated provider element, we can treat what was previously the <div class="accordion"> wrapper as the provider, and pass our styles to that. Of course, this means that we won’t be able to use a div , we will some sort of abstraction in the same way we landed on <Component> , so <Module> seems appropriate, and we would pass our styles reference as a prop to it.

Tying this together would leave our JSX now looking like this:

I’m not too precious about this being slightly more cumbersome than what we had with Styled-Components; the benefits in syntax that we get for the styles makes up for it. You can follow my progress with developing this solution if it seems like something you’re interested in — it’s currently in a usable state (wouldn’t use it in production yet, though).

Conclusion

The goal of this article was to see how we could view CSS differently in order to play better with React and JavaScript paradigms. We ended up being able to come up with an approach that doesn’t require class names and instead uses props , state and context . We also looked at how we could use objects instead of template strings to further improve things, from a DX perspective. Ultimately, we are left with a pretty decent and production-ready solution using Styled-Components (using template strings), and a slightly modified solution which I am currently developing to provide an API that works better for me (using objects).

Further reading: