Tag: Bridging

Bridging the Gap Between CSS and JavaScript: CSS Modules, PostCSS and the Future of CSS

In the previous post in this two-part series, we explored the CSS-in-JS landscape and, we realized not only that CSS-in-JS can produce critical styles, but also that some libraries don’t even have a runtime. We saw that user experience can significantly improve by adding clever optimizations, which is why this series focuses on developer experience (the experience of authoring styles).

In this part, we’ll explore the tools for “plain ol’ CSS” by refactoring the Photo component from our existing example.

Controversy and #hotdrama

One of the most famous CSS debates is whether the language is fine just the way that it is. I think this debate stays alive because there is some truth to both sides. For example, while it’s true that CSS was initially designed to style a document rather than components of an application, it’s also true that upcoming CSS features will dramatically change this, and that many CSS mistakes stem from treating styling as an afterthought instead of taking time to learn it properly or hiring someone who’s good at it.

I don’t think that CSS tools themselves are the source of the controversy; we’ll probably always use them to some extent at the very least. But approaches like CSS-in-JS are different in that they patch up the shortcomings of CSS with client-side JavaScript. However, CSS-in-JS is not the only approach here; it is merely the newest. Remember when we used to have similar debates about preprocessors, like Sass? Sass has features, like mixins, that aren’t based on any CSS proposal (not to mention the entire indented syntax). However, Sass was born in a much different time and has reached a point where it’s no longer fair to include it in the debate because the debate itself has changed — so we started criticizing CSS-in-JS because it’s an easier target.

I think we should use tools that let us use proposed syntax today. Let’s use JavaScript Promises as an analogy. This feature isn’t supported by Internet Explorer, so many people include a polyfill for it. The point of polyfills is to enable us to pretend like the feature is supported everywhere by substituting native browser implementations with a patch. Same goes for transpiling new syntax with tools, like Babel. We can use it today because the code will be compiled to an older, well-supported syntax. This is a good approach because it allows us to use future features today while pushing JavaScript forward the way preprocessing tools, like Sass, have pushed CSS forward.

My take on the CSS controversy is that we should use tools that enable us to use future CSS today.


We’ve already talked a bit about CSS preprocessors, so it’s worth discussing them in a little more details and how they fit into the CSS-in-JS conversation. We have Sass, Less and PostCSS (among others) that can imbue our CSS code with all kinds of new features.

For our example, we’re only going to be concerned with nesting, one of the most common and powerful features of preprocessors. I suggest using PostCSS because it gives us fine-grained control over the features we’re adding, which is exactly what we need in this case. The PostCSS plugin that we’re going to use is postcss-nesting because it follows the actual proposal for native CSS nesting.

The best way to use PostCSS with our compiling tool, webpack, is to add postcss-loader after css-loader in the configuration. When adding loaders after css-loader, it’s important to account for them in the css-loader options by setting importLoaders to the number of succeeding loaders, which in this case is 1:

{   test: /\.css$  /,   use: [     'style-loader',     {       loader: 'css-loader',       options: {         importLoaders: 1,       },     },     'postcss-loader',   ], }

This ensures that CSS files imported from other CSS files will be processed with postcss-loader as well.

After setting up postcss-loader, we’ll install postcss-nesting and include it in the PostCSS configuration:

yarn add postcss-nesting

There are many ways to configure PostCSS. In this case, we’re going to add a postcss.config.js file at the root of our project:

module.exports = {   plugins: {     "postcss-nesting": {},   }, }

Now, we can write a CSS file for our Photo component. Let’s call it Photo.css:

.photo {   width: 200px;   &.rounded {     border-radius: 1rem;   } }  @media (min-width: 30rem) {   .photo {     width: 400px;   } }

Let’s also add a file called utils.css that contains a class for visually hiding elements, as we covered in the the first part of this series:

.visuallyHidden {   border: 0;   clip: rect(0 0 0 0);   height: 1px;   margin: -1px;   overflow: hidden;   padding: 0;   position: absolute;   width: 1px;   white-space: nowrap; }

Since our component relies on this utility, let’s include utils.css to Photo.css by adding an @import statement to the top:

@import url('utils.css');

This will ensure that webpack requires utils.css, thanks to css-loader. We can place utils.css anywhere we want and adjust the @import path. In this particular case, it’s a sibling of Photo.css.

Next, let’s import Photo.css into our JavaScript file and use the classes to style our component:

import React from 'react' import { getSrc, getSrcSet } from './utils' import './Photo.css'  const Photo = ({ publicId, alt, rounded }) => (   <figure>     <img       className={rounded ? 'photo rounded' : 'photo'}       src={getSrc({ publicId, width: 200 })}       srcSet={getSrcSet({ publicId, widths: [200, 400, 800] })}       sizes="(min-width: 30rem) 400px, 200px"     />     <figcaption className="visuallyHidden">{alt}</figcaption>   </figure> )  Photo.defaultProps = {   rounded: false, }  export default Photo

While this will work, our class names are way too simple and they will most certainly clash with others completely unrelated to our .photo class. One of the ways of working around this is using a naming methodology, like BEM, to rename our classes (e.g. photo_rounded and photo__what-is-this--i-cant-even) to help prevent clashes from happening, but components quickly get complex and class names tend to get long, depending on the overall complexity of the project.

Meet CSS Modules.

CSS Modules

Simply put, CSS Modules are CSS files in which all class names and animations are scoped locally by default. They look a lot like regular CSS. For example, we can use our Photo.css and utils.css files as CSS Modules without modifying them at all, simply by passing modules: true to css-loader’s options:

{   loader: 'css-loader',   options: {     importLoaders: 1,     modules: true,   }, }

CSS Modules are an evolving feature and could be discussed at even greater length. Robin’s three-part series on it is a good overview and introduction.

While CSS Modules themselves look very similar to regular CSS, the way we use them is quite different. They are imported into JavaScript as objects where keys correspond to authored class names, and values are unique class names that are auto-generated for us that keep the scope limited to a component:

import React from 'react' import { getSrc, getSrcSet } from './utils' import styles from './Photo.css' import stylesUtils from './utils.css'  const Photo = ({ publicId, alt, rounded }) => (   <figure>     <img       className={rounded         ? `$  {styles.photo} $  {styles.rounded}`         : styles.photo}       src={getSrc({ publicId, width: 200 })}       srcSet={getSrcSet({ publicId, widths: [200, 400, 800] })}       sizes="(min-width: 30rem) 400px, 200px"     />     <figcaption className={stylesUtils.visuallyHidden}>{alt}</figcaption>   </figure> )  Photo.defaultProps = {   rounded: false, }  export default Photo

Since we’re using utils.css as a CSS Module, we can remove the @import statement at the top of Photo.css. Also, notice that using camelCase to format class names makes them easier to use in JavaScript. If we had used dashes, we’d have to write things out in full, like stylesUtils['visually-hidden'].

CSS Modules have additional features, like composition. Right now, we’re importing utils.css into Photo.js to apply our component styles, but let’s say that we want to shift the responsibility of styling the caption to Photo.css instead. That way, as far as our JSX code is concerned, styles.caption is just another class name; it just so happens to visually hide the element, but it might be styled differently in the future. Either way, Photo.css will be making those decisions.

So let’s add a caption style to Photo.css to extend the properties of the visuallyHidden utility using composes:

.caption {   composes: visuallyHidden from './utils.css'; }

We could just as well add more rules to that class, but this is all we need in this case. Now, we no longer need to import utils.css into Photo.js; we can simply use styles.caption instead:

<figcaption className={styles.caption}>{alt}</figcaption>

How does this work? Do the styles from visuallyHidden get copied over to caption? Let’s examine the value of styles.caption — whoa, two classes! That’s right: one is from visuallyHidden and the other one will apply any other styles we add to caption. CSS-in-JS makes it too easy to duplicate styles with libraries, like polished, but CSS Modules encourage you to reuse existing styles. No need to create a new VisuallyHidden React component to only apply several CSS rules.

Let’s take it even further by examining this uncomfortable class composition:

rounded   ? `$  {styles.photo} $  {styles.rounded}`   : styles.photo

There are libraries for these situations, like classnames, which are useful for more complex class composition. In our example, though, we can keep on using composes and rename .rounded to .roundedPhoto:

.photo {   width: 200px; }  .roundedPhoto {   composes: photo;   border-radius: 1rem; }  @media (min-width: 30rem) {   .photo {     width: 400px;   } }  .caption {   composes: visuallyHidden from './utils.css'; }

Now we can apply the class names to our component in a much more readable fashion:

rounded ? styles.roundedPhoto : styles.photo

But wait, what if we accidentally place the .roundedPhoto ruleset before .photo and some rules from .photo end up overriding rules from .roundedPhoto due to specificity? Don’t worry, CSS Modules prevent us from composing classes defined after the current class by throwing an error like this:

referenced class name "photo" in composes not found (2:3)    1 | .roundedPhoto { > 2 |   composes: photo;     |   ^   3 |   border-radius: 1rem;   4 | }

Note that it’s generally a good idea to use a file naming convention for CSS Modules, for example using the extension .module.css, because it’s common to want to apply some global styles as well.

Dynamic styles

So far, we’ve been conditionally applying predefined sets of styles, which is called conditional styling. What if we also want to be able to fine-tune the border radius of the rounded photos? This is called dynamic styling because we don’t know what the value is going to be in advance; it can change while the application is running.

There aren’t many use cases for dynamic styling — usually we’re styling conditionally, but in cases when we need this, how would we approach this? While we could get by with inline styles, a native solution for this type of problems is custom properties (a.k.a. CSS variables). A really valuable aspect of this feature is that browsers will update styles using custom properties when JavaScript changes them. We can set a custom property on an element through inline styles, which means that it will be scoped to that element and that element only:

style={typeof borderRadius !== 'undefined' ? {   '--border-radius': borderRadius, } : null}

In Photo.css, we can use this custom property by using var() and passing the default value as the second argument:

.roundedPhoto {   composes: photo;   border-radius: var(--border-radius, 1rem); }

As far as JavaScript is concerned, it’s only passing a dynamic parameter to CSS, then when CSS takes over, it can apply the value as-is, calculate a new value from it using calc(), etc.


At the time of this writing, the browser support for custom properties is… well, you decide for yourself. Not supporting these browsers is (probably) out of the question for a real-world application, but keep in mind that some styles are less important than others. In this case, it’s not a big deal if the border radius on IE is always 1rem. The application doesn’t have to look the same way on every browser.

The way we can automatically provide fallbacks for all custom properties is to install postcss-custom-properties and add it to our PostCSS configuration:

yarn add postcss-custom-properties
module.exports = {   plugins: {     'postcss-nesting': {},     'postcss-custom-properties': {},   }, }

This will generate a fallback for our border-radius rule:

.roundedPhoto {   composes: photo;   border-radius: 1rem;   border-radius: var(--border-radius, 1rem); }

Browsers that don’t understand var() will ignore that rule and use the previous one. Don’t let the name of the plugin fool you; it only partially improves the support for custom properties by providing static fallbacks. The dynamic aspect can’t be polyfilled.

Exposing values to JavaScript

In the previous part of this series, we explored how CSS-in-JS allows us to share almost anything between CSS and JavaScript, using media queries as an example. There is no possible way to achieve this here, right?

Thanks to Jonathan Neal, you can!

First, meet postcss-preset-env, the successor to cssnext. It’s a PostCSS plugin that acts as a preset similar to @babel/preset-env. It contains plugins like postcss-nesting, postcss-custom-properties, autoprefixer etc. so we can use future CSS today. It splits the plugins across four stages of standardization. Some of the features I’d like to show you aren’t included in the default range (stage 2+), so we’ll explicitly enable the ones we need:

yarn add postcss-preset-env
module.exports = {   plugins: {     'postcss-preset-env': {       features: {         'nesting-rules': true,         'custom-properties': true, // already included in stage 2+         'custom-media-queries': true, // oooh, what's this? 🙂       },     },   }, }

Note that we replaced our existing plugins because this postcss-preset-env configuration includes them, meaning our existing code should work the same as before.

Using custom properties in media queries is invalid because that’s not what they were designed for. Instead we’ll use custom media queries:

@custom-media --photo-breakpoint (min-width: 30em);  .photo {   width: 200px; }  @media (--photo-breakpoint) {   .photo {     width: 400px;   } }

Even though this feature is in the experimental stage and therefore not supported in any browser, thanks to postcss-preset-env it just works! One catch is that PostCSS operates on a per-file basis, so this way only Photo.css can use --photo-breakpoint. Let’s do something about that.

Jonathan Neal recently implemented an importFrom option in postcss-preset-env, which is passed to other plugins that support it as well, like postcss-custom-properties and postcss-custom-media. Its value can be many things, but for the purpose of our example, it’s a path to a file that will be imported to the files PostCSS processes. Let’s call this one global.css and move our custom media query there:

@custom-media --photo-breakpoint (min-width: 30em);

…and let’s define importFrom, providing the path to global.css:

module.exports = {   plugins: {     'postcss-preset-env': {       importFrom: 'src/global.css',       features: {         'nesting-rules': true,         'custom-properties': true,         'custom-media-queries': true,       },     },   }, }

Now we can delete the @custom-media line at the top of Photo.css and our --photo-breakpoint value will still work, because postcss-preset-env will use the one from global.css to compile it. Same goes for custom properties and custom selectors.

Now, how to expose it to JavaScript? When experimental features like custom media queries get standardized and implemented in major browsers, we will be able to retrieve them natively from CSS. For example, this is how we would access a custom property called --font-family defined on :root:

const rootStyles = getComputedStyle(document.body) const fontFamily = rootStyles.getPropertyValue('--font-family')

If custom media queries get standardized we will probably be able to access them in a similar way, but in the meantime we have to find an alternative. We could use the exportTo option to generate a JavaScript or JSON file, which we would import into JavaScript. However, that option wasn’t designed for this workflow because webpack would try to require it before it’s generated. Even if we generated it before running webpack, every update to global.css would cause webpack to re-compile twice, once to generate the output file, and once more to import it. I wanted a solution that’s unencumbered by its implementation.

For this series, I’ve created a brand new webpack loader called css-customs-loader just for you! It makes this task easy: all we need to is include it in our webpack configuration before css-loader:

{   test: /\.css$  /,   use: [     'style-loader',     'css-customs-loader',     {       loader: 'css-loader',       options: {         importLoaders: 1,       },     },     'postcss-loader',   ], }

This exposes custom media queries, as well as custom properties, to JavaScript. We can access them simply by importing global.css:

import React from 'react' import { getSrc, getSrcSet } from './utils' import styles from './photo.module.css' import { customMedia } from './global.css'  const Photo = ({ publicId, alt, rounded, borderRadius }) => (   <figure>     <img       className={rounded ? styles.roundedPhoto : styles.photo}       style={         typeof borderRadius !== 'undefined'           ? { ['--border-radius']: borderRadius }           : null       }       src={getSrc({ publicId, width: 200 })}       srcSet={getSrcSet({ publicId, widths: [200, 400, 800] })}       sizes={`$  {customMedia['--photo-breakpoint']} 400px, 200px`}     />     <figcaption className={styles.caption}>{alt}</figcaption>   </figure> )  Photo.defaultProps = {   rounded: false, }  export default Photo

That’s it!

I created a repository demonstrating all of the concepts discussed in this series. Its readme also contains some advanced tips about the approach described in this post.

View Repo


It’s safe to say that tools like CSS Modules and PostCSS and upcoming CSS features are up to the task of dealing with many challenges of CSS. Whichever side of the CSS debate you’re on, this approach is worth exploring.

I have a strong CSS-in-JS background, but I’m very susceptible to hype, so keeping up with that world is hard. Which library should I use now, styled-components or emotion? Also, while having styles next to the behavior can be succinct, it’s also mixing two very different languages — CSS is very verbose compared to JavaScript. This incentivized me to write less CSS because I wanted to avoid getting the file too crowded. This may be a matter of personal preference, but I didn’t want that to be an issue. Using a separate file for CSS finally gave my code some air.

While mastering this approach may not be as straightforward as CSS-in-JS, I believe it’s more rewarding in the long run. It will improve your CSS skills and make you better prepared for its future.

Article Series:

  1. CSS-in-JS
  2. CSS Modules, PostCSS and the Future of CSS (This post)

The post Bridging the Gap Between CSS and JavaScript: CSS Modules, PostCSS and the Future of CSS appeared first on CSS-Tricks.


, , , , ,

Bridging the Gap Between CSS and JavaScript: CSS-in-JS

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 (coming tomorrow!).

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 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.


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.


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:

<Photo publicId="balloons" alt="Hot air balloons!" rounded />

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 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 <style> tags in the <head>.

One of the first libraries to put this concept to use is JSS. Here is and example employing its syntax:

import React from 'react' import injectSheet from 'react-jss' import { getSrc, getSrcSet } from './utils'  const styles = {   photo: {     width: 200,     '@media (min-width: 30rem)': {       width: 400,     },     borderRadius: props => (props.rounded ? '1rem' : 0),   }, }  const Photo = ({ classes, publicId, alt }) => (   <figure>     <img       className={classes.photo}       src={getSrc({ publicId, width: 200 })}       srcSet={getSrcSet({ publicId, widths: [200, 400, 800] })}       sizes="(min-width: 30rem) 400px, 200px"     />     <figcaption>{alt}</figcaption>   </figure> ) Photo.defaultProps = {   rounded: false, }  export default injectSheet(styles)(Photo)

At first glance, the styles object looks like CSS written in object notation with additional features, like passing a function to set the value based on props. The generated classes are unique, so you never need to worry about them clashing with other styles. In other words, you get scoping for free! This is how most CSS-in-JS libraries work — of course, with some twists in features and syntax that we’ll cover as we go.

You can see by the attributes that the width of our rendered image starts at 200px, then when the viewport width becomes at least 30rem, the width increases to 400px wide. We generated an extra 800 source to cover even larger screen densities:

  • 1x screens will use 200 and 400
  • 2x screens will use 400 and 800

styled-components is another CSS-in-JS library, but with a much more familiar syntax that cleverly uses tagged template literals instead of objects to look more like CSS:

import React from 'react' import styled, { css } from 'styled-components' import { getSrc, getSrcSet } from './utils'  const mediaQuery = '(min-width: 30rem)'  const roundedStyle = css`   border-radius: 1rem; `  const Image = styled.img`   width: 200px;   @media $  {mediaQuery} {     width: 400px;   }   $  {props => props.rounded && roundedStyle};    const Photo = ({ publicId, alt, rounded }) => (   <figure>     <Image       src={getSrc({ publicId, width: 200 })}       srcSet={getSrcSet({ publicId, widths: [200, 400, 800] })}       sizes={`$  {mediaQuery} 400px, 200px`}       rounded={rounded}     />     <figcaption>{alt}</figcaption>   </figure> ) Photo.defaultProps = {   rounded: false, }  export default Photo

We often create semantically-neutral elements like <div> and <span> solely for styling purposes. This library, and many others, allow us to create and style them in a single motion.

My favorite benefit of this syntax is that it’s like regular CSS, minus interpolations. This means that we can migrate our CSS code more easily and we get to use our existing muscle memory instead of having to familiarize ourselves with writing CSS in the object syntax.

Notice that we can interpolate almost anything into our styles. This specific example demonstrates how we can save the media query in the variable and reuse it in multiple places. Responsive images are an excellent use case for this because the sizes attribute contains basically CSS, so we can use JavaScript to make the code more DRY.

Let’s say that we decided we want to visually hide the caption, but still make it accessible for screen readers. I know that a better way of achieving this would be to use an alt attribute instead, but let’s use a different way for the sake of this example. We can use a library of style mixins called polished — it works great with CSS-in-JS libraries making it great for our example. This library includes a mixin called hideVisually which does exactly what we want and we can use it by interpolating its return value:

import { hideVisually } from 'polished'  const Caption = styled.figcaption`   $  {hideVisually()}; `  <Caption>{alt}</Caption>

Even though hideVisually outputs an object, the styled-components library knows how to interpolate it as styles.

CSS-in-JS libraries have many advanced features like theming, vendor prefixing and even inlining critical CSS, which makes it easy to stop writing CSS files entirely. At this point, you can start to see why CSS-in-JS becomes an enticing concept.

Downsides and limitations

The obvious downside to CSS-in-JS is that it introduces a runtime: the styles need to be loaded, parsed and executed via JavaScript. Authors of CSS-in-JS libraries are adding all kinds of smart optimizations, like Babel plugins, but some runtime costs will nevertheless exist.

It’s also important to note that these libraries aren’t being parsed by PostCSS because PostCSS wasn’t designed to be brought into the runtime. Many use stylis instead as a result because it’s much faster. This means that we unfortunately can’t use PostCSS plugins.

The last downside I’ll mention is the tooling. CSS-in-JS is evolving at a really fast rate and text editor extension, linters, code-formatters etc. need to play catch-up with new features to stay on par. For example, people are using the VS Code extension styled-components for similar CSS-in-JS libraries like emotion, even though they don’t all have the same features. I’ve even seen API choices of proposed features being influenced by the goal of retaining syntax highlighting!

The future

There are two new CSS-in-JS libraries, Linaria and astroturf, that have managed zero runtime by extracting CSS into files. Their APIs are similar to styled-components, but they vary in features and goals.

The goal of Linaria is to mimic the API of CSS-in-JS libraries like styled-components by having built-in features like scoping, nesting and vendor prefixing. Conversely, astroturf is built upon CSS Modules, has limited interpolation capabilities, and encourages using a CSS ecosystem instead of deferring to JavaScript.

I built Gatsby plugins for both libraries if you want to play with them:

Two things to have in mind when using these libraries:

  1. having actual CSS files means that we can process them with familiar tools like PostCSS
  2. Linaria uses custom properties (a.k.a. CSS variables) under the hood, be sure to take their browser support into consideration before using this library


CSS-in-JS are all-in-one styling solutions for bridging the gap between CSS and JavaScript. They are easy to use and they contain useful built-in optimizations — but all of that comes at a cost. Most notably, by using CSS-in-JS, we’re essentially ejecting from the CSS ecosystem and deferring to JavaScript to solve our problems.

Zero-runtime solutions mitigate some of the downsides by bringing back the CSS tools, which ascends the CSS-in-JS discussion to a much more interesting level. What are the actual limitations of preprocessing tools compared to CSS-in-JS? This will be covered in the next part of this series.

Article Series:

  1. CSS-in-JS (This post)
  2. CSS Modules, PostCSS and the Future of CSS (Coming tomorrow!)

The post Bridging the Gap Between CSS and JavaScript: CSS-in-JS appeared first on CSS-Tricks.


, , ,