Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save vviikk/bcd23df17187e09a1ca0b8a65f3c852c to your computer and use it in GitHub Desktop.
Save vviikk/bcd23df17187e09a1ca0b8a65f3c852c to your computer and use it in GitHub Desktop.
CSS in JS (advantages, drawbacks, and how styled-components is hopefully the answer)

CSS in JS (advantages, drawbacks, and how styled-components is hopefully the answer)

Someone is going to unify these three different syntaxes and write a language that just addresses the web platform directly, and it's going to be insanely popular.
Jeremy Askenas, creator of coffeescript, 2015

TL;DR; styled-components FTW!

The journey to styled-components has been long, but with it's simplicity, tooling and level of adoption, it's the future of CSS in JS, and IMHO, the emerging standard that is about to stay. I can mention the disadvantages of CSS in JS, but most of them will not be valid if we're using styled-components. The topic is probably one of the fastest evolving in web development, so I hope you'll see:

  • advantages of CSS in JS
  • drawbacks, and how CSS Modules tried to solve this
  • how styled-components addressed the above two and delivered. The disadvantages of inline styling, does not apply.
yarn add styled-components # no extra transpiling/tooling/build-system needed.

That's it. No extra tooling, setup necessary to use styled-components. To find out why, do read on.

Before I take you on a journey, I wanted to list down the advantages of CSS in JS. I couldn't concisely do it. Why? Because CSS in JS doesn't have the same _dis_advantages as it used to have, even a few months ago.

Premise

My journey in stylesheets looks somewhat like so:

CSS -> SASS/LESS -> PostCSS -> CSS in JS -> CSS Modules -> back to CSS in JS -> Styled-components Assumptions

CSS Modules are one of the CSS in JS options (I have explained why, IMHO, in part II below). I will focus on three (with a React mindset). Inline styles, CSS Modules and styled-components. Since CSS Modules encourage the use of LESS/SASS, I will also list that as a disadvantage.

As more features of future CSS specifications are implemented by browsers, the need to code stylesheets another syntax become unnecessary by the day. i.e. CSS & the browsers are catching up fast.

Problem statement

If ever we analyze how we got here, it seems like every step of the way, the limitations of CSS was glaring. We needed variables, nesting, the ability of breaking down out styles into smaller files, calculations and dynamism - things we normally jump to SASS/LESS for.

Toolchain & Features

One of the reasons CSS in JS is such a nice option is that we don't need an extra build step for CSS. The problem with SASS/LESS is that variables, mixins and other features are transpiled into CSS during the build phase. And you needed to build your css. That's one more library to depend on, one more build step (at least), and the end product is CSS.

Tediously manipulating visual state (a.k.a disadvantages of CSS solved by CSS in JS)

For an element in the DOM to change visually, classnames were the only way to go.

Classnames are redundant. They are an extra link between external stylesheets and result in the parent components dictating through numerous classnames, how the child should look & feel. One might soon face the issue of why a style isn't being applied and it would be because of a classname of a great-grandparent that's forcing styles down or even one of it's own classnames that are conflicting. And no about of block__element--modifier hacks will ease the pain. I sometime spend time thinking of the proper classnames for BEM. Extra cognitive load.

.widget table row cell .content .header .title {
  padding: 10px 20px;
  font-weight: bold;
  font-size: 2rem;
}

That's a deep specification, and it's relying on a specific combination of markup to style that element way down the DOM. It's also something that you'd spend more time debugging in your browser. Once more, there's unnecessary congnitive load when coding/debugging.

The above example would beg for a developer to think "There's gotta be a better way".

Redundancy in the component age

Notice the redundancy below (<Button>, btn, btn-):

<Button class='btn btn-primary'>Click me</Button>
          { // ^— ^— redundant implementation detail }

(We'll come back to the above example)

CSS in JS (Part One: Disadvantages)

If you're considering CSS in JS, there's something that needs to be stated. Javascript is expensive:

The Cost Of JavaScript – Addy Osmani – Medium

Byte-for-byte, JavaScript is more expensive for the browser to process than the equivalently sized image or Web Font  — Tom Dale

There are many CSS in JS libraries out there. Take the following example:

const body = `
  <div className=${generateClassName({
    backgroundColor: 'blue',
    })}>
  </div>
`;

Notice the camelCase. This is prevalent in CSS in JS, including React's built-in way for styling elements:

import React from 'react';

const divStyle = {
  margin: '40px',
  border: '5px solid pink'
};
const pStyle = {
  fontSize: '15px',
  textAlign: 'center' // camelCase!!!
};

const Box = () => (
  <div style={divStyle}>
    <p style={pStyle}>Get started with inline style</p>
  </div>
);

export default Box;

Ermmm. CamelCase? No, thanks, mainly because:

  • Debugging: The browser still understands this as CSS, which means the above styles are displayed as regular CSS in the browser. You will be coding in camelCase and debugging kebab-cased css properties in the browser.
  • Code adoption: Copy & pasting of code from good ol' CSS requires refactoring the case of the CSS property. Unnecessary.
  • More variables: There is an extra 'link' from the style prop (style={divStyle}) to the style object - in large codebases, personally I find myself needing to scroll around for the corresponding style declaration.
  • Inconsistent with CSS Specification: Custom CSS properties use hyphens, and a PR last year allowed for that to be used in React via inline styles. But let's have a look at the code if custom properties are used:
  const LightDarkSpan = ({ children }) => (
    <span style={{
      color: 'var(--text-color, black)'
    }}>
      {children}
    </span>
  )

  /* rendered */
  const Parent = (
    <div style={{
      backgroundColor: 'black', // camelCase
      '--text-color': 'white' // —kebab-case <—
    }}>
      <article>
        <LightDarkSpan>testing</LightDarkSpan>
      </article>
    </div>
  )

I know the above reasons center predominatly around the case of characters, and this shows PR that many prominent React devs are rooting for camelCase CSS. Dan Abramov's suggestion of using the React context API was also downvoted heavily, and IMHO, rightfully so. To end, here's also a crucial discussion to not force camel-case via inline styling in React.

Huge credit goes to CSS in JS 101 for doing detailed assessment.

To list down more disadvantages of inline styling, I'm going to rip it out of the above link.

  • Code duplication in case of SSR.
  • Additional costs in JS payload. Remember that styles which are embedded in JS are not for free. It is not only about download time, it is also about parsing and compiling. See this detailed explanation by Addy Osmani, why JS is expensive
  • No media queries (@media)
  • No CSS animations (@keyframes)
  • No pseudo classes (:hover)
  • No web fonts (@font)
  • No autoprefixer (well there is inline-style-prefixer)

CSS in JS (Part two: CSS Modules)

note CSS Modules was co-created by Glen Maddern, who went on to create styled-components with Max Stoiber. I think it's impossible to head into what CSS in JS now without touching on this technology.

Is CSS Modules CSS in JS? It's a grey area. I feel that since we're dictating what styles should be loaded and mapped to a certain element in javascript, it can't be not CSS in JS. A huge decisive function of the CSS interpretor is heavy-handedly implemented in JS. Importing CSS as modules solves the issue with classnames.

The following is an excerpt from a great article by Object Partners.

CSS Modules is an implementation of CSS in JS, so if you leave here with nothing else, consider integrating CSS Modules support into your application. It’ll generate a unique hash based on a user supplied class name. — CSS in JS: Benefits, Drawbacks, and Tooling | Object Partners

In the briefest possible introduction, css modules do the following:

/* Components/Common/root.css - just assume this is added as global styles - I'll leave that out for brevity */
:global {
            /* System Fonts as used by Medium and WordPress */
  --system: -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;
}

/* components/demo/ScopedSelectors.css */
.root {
  border: palevioletred solid 2px;
  padding: 0 2rem;
  max-width: 40rem;
}

.text {
  font: italic 1.2rem var(--system);
}

A simple React example:

import React, { Component } from 'react';
import styles from './ScopedSelectors.css';

export default class ScopedSelectors extends Component {
  render() {
    return (
      <div className={styles.root}>
        <p className={styles.text}>Scoped Selectors</p>
      </div>
    );
  }
};

What's rendered:

<div class='ScopedSelectors__root___16yOh'>
  <p class='ScopedSelectors__text___1hOhe'>Scoped Selectors</p>
</div>

The random classes mean that you (almost) never need to worry about cross-component style's clashing. However, as we're importing CSS files, developers could resort to use SASS or LESS as they miss the basic features such as mixins & rule nesting, so even in the world of CSS Modules, that extra build step / toolchain is necessary.

CSS in JS (Part 2b: Honorary mention)

The style tag. How?

const MyComponent = props =>
  <div className="styled">
    Hover for red
    <style dangerouslySetInnerHTML={{__html: `
      .styled { color: blue }
    `}} />
  </div>

Which renders to:

<div class="styled">
  Hover for red
  <style>      .styled { color: blue }    </style>
</div>

Cool eh?

This is an escape hatch
I wouldn’t push this approach very far. I wouldn’t try to rally a team around it. It is something I keep in my pocket for times I get sucked into the CSS-in-JS turf wars. It’ll help you kick the can down the road a little.
The style tag and React – learnreact

CSS in JS (Part three: Styled-components)

Visual primitives for the component age. [styled.components.com]

Let's look at the button again. In the world of styled-components, you'd end up with:

const Button = styled.a`
  display: inline-block;
  border-radius: 3px;
  padding: 0.5rem 0;
  margin: 0.5rem 1rem;
  width: 11rem;
  background: transparent;
  color: white;
  border: 2px solid white;
  > span {
    ${/* Supports nesting!! */}
    /* ...more styles */
  }

  /* CSS Specificity is now a prop :)
   * Simple functions generate conditional styling */
  ${props => props.primary && css`
    background: white;
    color: palevioletred;
  `}`

  // only showing the render function for brevity
  render( // beautiful clean component code :)
    <div>
      <Button>Normal Button</Button>
      <Button primary>Primary Button</Button>
    </div>
  );

  // the code below is without styled-components! Let's hope we don't need to go back to using classnames.
  render(
    <div>
      <Button class='btn'>Normal Button</Button>
      <Button class='btn btn-primary'>Primary Button</Button>
    </div>
  );
`

In a nutshell, you get a lot of sassy features like nesting, and the architecture of style-components means your props basically allow you to write mixins in pure javascript.

Getting Sassy with styled-components – 💅 styled-components – Medium

Performance

Rather not go into detail here, but you can get a report from April, 2018. Note, styled-components has only gotten faster since then!

styled-components v4 mounts faster by ~25% for both deep and wide component trees, and updates dynamic styles faster by ~7% Announcing styled-components v4: Better, Faster, Stronger 💅

There are many libraries out there that allow the use of CSS in JS. You can see a good list in this article, when I got the image below from. speed-chart

As you can see, styled-components v4 is blazing fast™️. We’re within a standard deviation of all the fastest libraries out there, both in terms of mounting as well as updating speed, which means performance is officially no longer an issue! 🎉 Announcing styled-components v4: Better, Faster, Stronger 💅

Advantages (a.k.a why styled-components should be taken seriously)

The concept is being ported over other CSS in JS libraries & frameworks

I believe the concept of styled-components is the ultimate CSS in JS option. Writing pure CSS in React without any camelCasing means you debug your styles in the same language as the browser. Also, using props instead of classnames makes testing the component awesome and easy with jest and/or enzyme.

Note, styled-components was created by Max Stoiber and Glen Maddern, the latter who wrote "CSS Modules: Welcome to the future" in 2015.

Because the concept is so simple and neat, there are official Vue & Vanilla HTML implementations of it. I am hoping to see styled-components being implemented with shadow DOM capability for custom elements.

The emotion CSS in JS library predates styled-components, but if you look at their docs, it starts off with:

styled is a way to create React or Preact components that have styles attached to them. It’s available from react-emotion and preact-emotion. styled was heavily inspired by styled-components and glamorous
emotion-docs

You can see that the official VIM plugin for styled-components supports other CSS in JS libraries because said libraries support the styled syntax. This is a pattern you see with many tools that are built for styled-components.

Vim bundle for styled-components, diet-cola, emotion and experimental glamor/styled content in javascript files.
vim-styled-components

If anything, I hope you take away one thing from this. Styled-components is actively developed and other libraries are also supporting the concept of it.

React-native & Web universal components

styled-primitives Gone are the days you need to write two stylesheets for react-native & web. Whilst there are options out there for one shared codebase between native & web, styled-components implements it at a primitive level.

Creating truly universal React component systems – 💅 styled-components – Medium.

primitives in the real world

The future is bright.

Ecosystem

I knew styled-components was going to be big! But checkout out the ! It's not even a small list of plugins - it's, as mentioned, an entire ecosystem.

styled-components-ecosystem-image You can find anything from a set of Sass/Compass-style mixins/helpers, to grid-systems & boilerplates.

Tooling

There is everything from a babel-plugin that transpiles styled-components to be more performant in production, server-side rendering, critical css extraction, automated snapshot testing, debugging, sytax highlighting, linting, type systems, dead code elimination and also a superb theming layer (that's only getting much simpler by the day!).

Read more about the tooling here.

Adoption

Styled-components is so easy to setup - you don't need any extra build settings (to start development). Checkout these amazing libraries:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment