Solving the modularity challenge

A good number of challenges come from having CSS rules live in the global scope. Let’s see how CSS-in-JS and CSS Modules solve them.

Namespacing and avoiding specificity conflicts

CSS-in-JS and CSS Modules are scoped locally by default, which means namespacing is unnecessary. And it also makes it possible to use very short and meaningful names for style rules, which naming conventions like BEM can’t afford. As a result, there’s no risk of naming collisions or specificity issues.

To define styles, CSS-in-JS will use object properties: { image: {} }

And CSS Modules will use simple class names: .image {}

Dead code elimination

With complex CSS selectors, it’s often hard to know if and where some styles are used. With modules and explicit dependencies, it’s much easier to know what’s used or not, and to be fully confident when removing unused styles. And since we’re talking about local variables for both CSS-in-JS and CSS Modules, that means build tools can also spot any unused local variables and remove them.

Composition

Both approaches offer composition as a solution for styling reuse.

To that end, CSS-in-JS can take advantage of anything JS has to offer, such as Object.assign().

CSS Modules allows to compose selectors with the composes rule:

Dependencies

CSS-in-JS is plain JS, so it can work with any module system to export/import styles.

CSS Modules makes it possible to compose class names from other CSS modules:

Conditional styling

Changing styles based on state and other conditions is another good use-case for composition.

CSS-in-JS:

CSS Modules:

Call site customization

A component’s style should be customizable by the one calling it. With reusable components, it’s felt liberating to have the component-specific styles such as its dimensions and default colors scoped to the component itself, and then have each parent handle the styles for positioning it and possibly tweak its look and feel a little.

With CSS-in-JS, passing styles from the parent to the child is a simple matter of merging style objects together:

And with CSS Modules, the same can be achieved by concatenating their class names together:

Cool things JS makes a bit easier than CSS

Both CSS-in-JS and CSS Modules bring great solutions for making styling modular, and that’s really awesome. In addition to modularity, all sorts of other feats can be desirable with styling, which JavaScript – by the nature of it – makes so much easier than vanilla CSS.

Still, those additional things remain achievable with CSS, by either relying on CSS pre-processors such as LESS and SASS, or taking advantage of future CSS features using new syntax ahead of time (hence pre-processing this syntax to make it run in today’s browsers).

Let’s see how CSS-in-JS and CSS Modules compare with those things which, today, feel easier to achieve with JS. For CSS Modules, we’ll be using upcoming CSS features rather than LESS/SASS, but those can also be an option.

Sharing style constants

Style constants are a nice tool for sharing common properties such as colors and sizes across components – or within the same one.

In the case of CSS-in-JS, since everything is JS, this is as easy as using variables, and exporting them to make them available to other components!

When it comes to CSS Modules, sharing constants inside a same stylesheet is also easy with CSS Custom Properties:

However, sharing those constants across stylesheets is a bit less convenient, as CSS Modules are meant to compose styles together (and make that super easy by importing styles from other files), and expect style composition to be used over constant sharing.

The intended CSS Modules way would look like so:

But if sharing constants is your thing, we’ll have to use what CSS Modules is built on: ICSS. It’s a small spec on top of standard CSS that adds :import and :export pseudo-selectors to enable CSS Modules. Using those, we’ll be able to export and import key/value pairs, that can be mapped locally to CSS custom props:

This is quite verbose, but each block has a desirable purpose: the first one explicitly imports a value from an external dependency, the second defines a local custom property that takes this imported value, and then that custom prop can be used as we see fit.

If you’d prefer writing less, at the cost of introducing globals – which we’re actively trying to get rid of, but I can think of pros and cons for sharing constants – there’s also a way! Using global custom props, the above code can be trimmed down to:

Sharing variables between JS and CSS

Even though sharing JS variables with CSS doesn’t feel ideal, many situations call for it.

With CSS-in-JS, JavaScript makes it once again super easy!

As for CSS Modules, well, it’s unfortunately a bit limited as of today.

The attr() CSS function can be used to retrieve the value of a given DOM attribute and use it with CSS, but browsers currently only support this function being used in the content property of pseudo-elements, and with string values. In time, and coupled with other CSS functions like calc(), any DOM attribute will be able to hold values that CSS can read and make calculations with – creating a powerful bridge between JS+HTML and CSS!

The dream:

What that dream would look like in a hypothetical future version of Chrome that’d support the definition of attr() in CSS3:

In the meantime, for any use-case that requires more than pseudo-elements and strings to share variables between JS and CSS, inline styles are the way to go.

Style computation and manipulation

When using CSS Modules, upcoming CSS specs introduce a lot of useful functions to manipulate property values, such as calc() and color().

And JavaScript’s expressiveness makes it an ideal candidate to manipulate styles in any imaginable way, colors included.

Common CSS features that JS struggles with

JS does make some styling pursuits easier than CSS itself.

But let’s not forget that CSS, as a language created to describe how documents should be presented, has been shaped to make styling those documents straightforward in many ways. JS doesn’t serve the same purpose, and as such, lacks some of the simplicity of CSS when it comes to some styling use-cases. What CSS does with pseudo-classes, pseudo-elements and media queries is harder to achieve with JS.

Pseudo-classes

In CSS, pseudo-classes such as :first-child and :nth-child allow styling elements based on their relationship with other elements in the document.

When styling using JS, those pseudo-classes aren’t around, but it’s possible to apply styling selectively to some elements within render loops. For example, the equivalent to :nth-child(even) would look like this using JS:

When the styling of an element in JS should be based on a simple condition such as the index in the loop, the method is slightly more verbose than CSS but still easy to take care of. Here’s a cool cheat sheet for some of those pseudo-classes equivalents in JS (from this great talk on inline styles):

Another thing pseudo-classes are good for, is changing styles based on an interaction with the page. This is where in CSS the ubiquitous :hover, :focus and :active really shine!

In JS, event listeners such as onMouseEnter and onMouseLeave would have to be used to update the component’s state, and apply the “hover” style accordingly.

This is rather unwieldy (although arguably more powerful), and many libraries have abstracted that away; among them, I especially like Radium, which would make a hover style look like this:

Pseudo-elements

In CSS, ::after, ::before, ::placeholder often come in handy to style certain parts of a document, and the same could be said of ::first-letter, ::first-line, and ::selection.

When it comes to JS, those are hard to emulate easily. A case could certainly be made about using actual elements instead of ::after and ::before, but it’d be cumbersome to target ::first-letter or ::first-line, and impossible to target ::placeholder or ::selection without an abstraction that mirrors elements for styling purposes, which sounds a bit overzealous.

Media queries

To handle media queries with JS, relying onwindow.matchMedia() to apply styles conditionally is also less practical than its delightful CSS counterpart. Libraries like Radium can also help with that.