In this article, we’re going to dig into the concept of CSS-in-JS. If you’re already acquainted with this concept, you might still enjoy a stroll through the philosophy of that approach, and you might be even more interested in the next article.
Web development is very interdisciplinary. We’re used to working closely with multiple languages. And, as developing web applications becomes more commonplace and nuanced, we often look for creative ways to bridge the gaps between those languages to make our development environments and workflows easier and more efficient.
The most common examples are typically when using templating languages. For example, one language might be used to generate the code of a more verbose language (often HTML). This is one of the key aspects of front end frameworks — what does manipulating HTML look like? The most recent twist in this area was JSX because it’s not really a templating language; it’s a syntax extension to JavaScript, and it makes working with HTML really succinct.
Web applications go through many state combinations and it’s often challenging to manage content alone. This is why CSS sometimes falls by the wayside — even though managing styling through different states and media queries is equally important and just as challenging. In this two-part series, I would like to place CSS in the spotlight and explore bridging the gap between it and JavaScript. Throughout this series, I will assume that you’re using a module bundler like webpack. As such, I will use React in my examples, but the same or similar principles are applicable to other JavaScript frameworks, including Vue.
The CSS landscape is evolving in many directions because there are a lot of challenges to solve and there is no “correct” path. I’ve been spending considerable effort experimenting with various approaches, mostly on personal projects, so the intention behind this series is only to inform, not to prescribe.
Challenges of CSS
Before diving into code, it’s worth explaining the most notable challenges of styling web applications. The ones I’ll talk about in this series are scoping, conditional and dynamic styles, and reusability.
Scoping
Scoping is a well-known CSS challenge, it’s the idea of writing styles that don’t leak outside of the component, thus avoid unintended side effects. We would like to achieve it ideally without compromising authoring experience.
Conditional and dynamic styles
While the state in front-end applications started getting more and more advanced, CSS was still static. We were only able to apply sets of styles conditionally — if a button was primary, we would probably apply the class “primary” and define its styles in a separate CSS file to apply how it’s going to look like on the screen. Having a couple of predefined button variations was manageable, but what if we want to have a variety of buttons, like specific ones tailored for Twitter, Facebook, Pinterest and who knows what else? What we really want to do is simply pass a color and define states with CSS like hover, focus, disabled etc. This is called dynamic styling because we’re no longer switching between predefined styles — we don’t know what’s coming next. Inline styles might come to mind for tackling this problem, but they don’t support pseudo-classes, attribute selectors, media queries, or the like.
Reusability
Reusing rulesets, media queries etc. is a topic I rarely see mentioned lately because it’s been solved by preprocessors like Sass and Less. But I’d still like to revisit it in this series.
I will list some techniques for dealing with these challenges along with their limitations in both parts of this series. No technique is superior to the others and they aren’t even mutually exclusive; you can choose one or combine them, depending on what you decide will improve the quality of your project.
Setup
We’ll demonstrate different styling techniques using an example component called Photo. We’ll render a responsive image that may have rounded corners while displaying alternative text as a caption. It will be used like this: Before building the actual component, we’ll abstract away the srcSet attribute to keep the example code brief. So, let’s create a utils.js file with two utilities for generating images of different widths using Cloudinary:
import { Cloudinary } from cloudinary-core
const cl = Cloudinary.new({ cloud_name: demo, secure: true })
export const getSrc = ({ publicId, width }) =>
cl.url(publicId, { crop: scale, width })
export const getSrcSet = ({ publicId, widths }) => widths
.map(width => `${getSrc({ publicId, width })} ${width}w`)
.join(, )
We set up our Cloudinary instance to use the name of Cloudinary’s demo cloud, as well as its url method to generate URLs for the image publicId according to the specified options. We’re only interested in modifying the width in this component.
We’ll use these utilities for the src and srcset attributes, respectively:
getSrc({ publicId: balloons, width: 200 })
// => https://res.cloudinary.com/demo/image/upload/c_scale,w_200/balloons
getSrcSet({ publicId: balloons, widths: [200, 400] })
// => https://res.cloudinary.com/demo/image/upload/c_scale,w_200/balloons 200w,
https://res.cloudinary.com/demo/image/upload/c_scale,w_400/balloons 400w
If you’re unfamiliar with srcset and sizes attributes, I suggest reading a bit about responsive images first. That way, you’ll have an easier time following the examples.
CSS-in-JS
CSS-in-JS is a styling approach that abstracts the CSS model to the component level, rather than the document level. This idea is that CSS can be scoped to a specific component — and only that component — to the extent that those specific styles aren’t shared with or leaked to other components, and further, called only when they’re needed. CSS-in-JS libraries create styles at runtime by inserting