On React CSS techniques

TL;DR

Material-UI components are great even though they use inline styles, which are not for me. CSS Components are great.


When I was designing reword, I had to decide how to handle CSS. As far as I could tell, there were four options.

  • The standard approach. CSS files are not connected in any explicit way to components. BEM or something like it can help organize CSS classes and connect them conceptually with React components. With this approach, a single Sass or Less stylesheet would probably pull in all the individual stylesheets for components resulting in a single CSS file.
  • The webpack approach. Each component's JS file would include a require ('./style.css'); line which would cause that css file to be included in a CSS bundle file. This seems better than the first option because only those css files that we actually require are included.
  • Inline styles. Using inline styles has become popular in React world. All styling lives right in the component JS file. There are no CSS files. In this way, everything is declared and handled in JS space - application logic, markup (JSX), and styling.
  • CSS modules. CSS Modules are pretty new. After a bit of research, they seemed like a good combination of the other techniques. More on this later.

I also knew I wanted to incorporate Material-UI components into reword. Material-UI's ready-to-use react components looked like they could really speed up development (I was right about that). Material-UI uses inline styling, so that looked like a vote in favor of that technique. But CSS Modules seemed really smart. I decided to go with CSS Modules for my own components and work with inline styles when using Material-UI components.

Why CSS Modules?

With CSS Modules, each component gets its own CSS file. This css file gets required or imported in the component's JS file, just like the webpack approach. And like with webpack, requiring the CSS file marks it to be included in a final CSS bundle. But in addition, the require or import statement gives you a Javascript object with properties that map to the class names in the CSS. You use those properties to assign classNames to elements in the component. In this way, you have an explicit connection between the CSS rules and the your JS code.

So let's say you have component.js and component.css:

component.css

.top {
  font-size: 12px;
  font-weight: 400;
  line-height: 1;
}

component.js

import styles from './component.css';
...
render() {
  return (
    <div className={styles.top}>
      ...
    </div>
  );
}

Here's what the generated CSS and Javascript might look like.

generated CSS

._component__top {
  font-size: 12px;
  font-weight: 400;
  line-height: 1;
}

generated js

...
<div className="_component__top">
...

You never have to deal with those crazy, BEM-like class names. CSS Modules handles that for you. And it removes the problems with a global CSS namespace and gives you a way to extend previously declared CSS rules. It's sorta like BEM without all the boilerplate and with explicit connections to your application code. Read Glen Madden's write up. I do not regret choosing CSS Modules and I'm continuing to experiment with it.

Downside to CSS Modules

My main struggle with CSS Modules had to do with everybody's favorite Javascript topic: tooling. Using CSS Modules requires introducing a new link in the chain of code that processes your JS code.

I actually found that the available tool libraries worked really well for my basic needs. I just had to add something like this to my gulp browserify task:

bundle.plugin(cssModulesify, {
  rootDir: './app',
  output: './build/css/app.css'
});

However, at one point I tried to get hot loading to work with browserify. I kept running into problems, and I think it was because of that extra build step. If I was using webpack like everybody else, this probably wouldn't have been a problem. ¯\(ツ)

So what about inline styles?

I have to say... I'm not a fan.

Using inline styles was a disorienting experience. If you use stylesheets from any external libraries, you always have a mix of inline style rules and traditional style rules. It was hard to think clearly about what styles belonged where.

Inline styles also make for a pretty lousy dev tools experience. I spend a lot of time in browser dev tools. Inline styles mucked that up. Just having all those style rules jammed in the markup makes everything really cluttered. But much worse, inline style rules take the cascade out of cascading style sheets. I rely on dev tools to make changes to CSS rules and see those changes applied throughout the page. But if you make a change to an inline style rule - something like changing the background color of a featured item - you will not see those changes reflected on all similar items. You are only changing rules for that item, never the rules for an associated class.

Conclusions

  • I won't be adopting inline styles any more than I have to.
  • Material-UI is a fantastic project in spite of the inline styles. I highly recommend it.
  • CSS Modules delivered. I'm going to keep using it and I think you should too.