Stop using CSS in JavaScript for web development

Written by gajus | Published 2017/04/27
Tech Story Tags: react | styled-components | css | web-development | web-design

TLDRvia the TL;DR App

9 fairy tales

CSS isn’t going anywhere. A lot of the projects choose to style documents in JavaScript for wrong reasons. This article lists common misconceptions (myths) and the existing CSS solutions to those problems.

Nothing in this article is meant as a personal attack against either a specific project or a person. I define “CSS in JavaScript” as using styled-components as this is the current trend for styling components in React.

The authors of the styled-components (Max Stoiber and Glen Maddern; and all of the contributors) are smart persons, with bright ideas and good intentions.

For full transparency, I should also note that I am the author of react-css-modules and babel-plugin-react-css-modules.

History of CSS and JavaScript

Cascading Style Sheets (CSS) language was created for describing the presentation of a document written in a markup language. JavaScript has been created as a “glue language” to assemble components such as images and plugins. Over the years, JavaScript grew and mutated to adopt to new use cases.

The advent of the Ajax term (2005) marks an important landmark. This is when the libraries such as Prototype, jQuery, MooTools have attracted massive communities into collaborative effort to solve data fetching in background and cross-browser inconsistencies. This created a new problem: how to manage all the data?

Fast-forward to 2010, Backbone.js takes place as an industry standard for state management of the application. Not long after, Knockout and Angular charm everyone with a two-way binding. Not long after, React and Flux make appearance. This starts the era of Single-Page Applications (SPA), applications composed of components.

What about CSS?

In the words of styled-components documentation:

The problem with plain CSS is that it was built in an era where the web consisted of documents. In 1993 the web was created to exchange mostly scientific documents, and CSS was introduced as a solution to style those documents. Nowadays however, we are building rich, interactive, user-facing applications, and CSS just isn’t built for this use-case.

I disagree.

CSS has evolved to capture the requirements of the modern UIs. The amount of the new features that have been in the last decade is beyond reasonable to list (pseudo-classes, pseudo-elements, CSS variables, media queries, keyframes, combinators, columns, flex, grid, computed values, …).

From the UI perspective, “component” is an isolated fragment of a document (<button /> is a component). CSS was designed to style documents, which encompasses all of the components. Whats the problem?

As goes the saying: “Use the right tool for the job”.

Styled-components

styled-components enable writing of CSS in JavaScript using tagged template literals. It removes the mapping between components and styles — component is made into a low-level styling construct, e.g.

import React from 'react';
import styled from 'styled-components';

// Create a <Title> react component that renders an <h1> which is
// centered, palevioletred and sized at 1.5em
const Title = styled.h1`
  font-size: 1.5em;
  text-align: center;
  color: palevioletred;
`;

// Create a <Wrapper> react component that renders a <section> with
// some padding and a papayawhip background
const Wrapper = styled.section`
  padding: 4em;
  background: papayawhip;
`;

// Use them like any other React component – except they're styled!
<Wrapper>
  <Title>Hello World, this is my first styled component!</Title>
</Wrapper>

The result:

Live demo

styled-components is now trending as the new way to style components in React.

Lets make one thing clear: styled-components is just a higher-level abstraction on top of CSS. All it does is parse your CSS defined in JavaScript and creates JSX elements mapped to the CSS.

I dislike the trend because it is surrounded by a lot of misconceptions.

I surveyed people reasons for using styled-components on IRC, Reddit and Discord and compiled a list of common responses for choosing to use styled-components. I call them myths.

Myth #1: It solves the global namespacing and styling conflicts

I am calling this one a myth because it makes it sound like these problems have not been already addressed. CSS Modules, Shadow DOM and a countless number of naming conventions (such as BEM) have solved this problem long time ago in the community.

styled-components (just like CSS modules) take away the responsibility of the naming away from the human. Humans make mistakes; computers make them less often so.

On its own, it is not a good enough reason to start using styled-components.

Myth 2: Using styled-components makes for a more succinct code

Often accompanied with an example such as:

<TicketName></TicketName>
<div className={styles.ticketName}></div>

First of all — it does not matter. The difference is negligible.

Second of all, it is not true. The amount of the total characters depends on the style name.

<TinyBitLongerStyleName></TinyBitLongerStyleName>
<div className={styles.longerStyleName}></div>

The same applies to constructing the styles as you see later in the article (Myth 5: It makes it easy to conditionally style components). styled-components win in brevity only in the cases of the most basic components.

Myth 3: Using styled components makes you think more about semantics

The very premise is wrong. Styling and semantics represent different problems and require different solutions. To quote Adam Morse (mrmrs):

Content semantics have NOTHING TO DO WITH VISUAL STYLES. When I used to build things with legos I never thought ‘oh this is a piece for an engine block’ I thought ‘oh cool this is a 1x4 blue lego and I can do anything I want with it’. It didn’t matter if I was building an underwater aquanauts base or an airplane — I knew exactly how to use that lego block.

http://mrmrs.io/writing/2016/03/24/scalable-css/

(I highly recommend reading Adam’s article about the Scalable CSS.)

Nevertheless, lets play ball and assume there is a link.

Example:

<PersonList>
  <PersonListItem>
    <PersonFirstName>Foo</PersonFirstName>
    <PersonLastName>Bar</PersonLastName>
  </PersonListItem>
</PersonList>

Semantics is about using the right tags to construct the markup. Do you know what HTML tags will these components render? No, you do not.

Compare it with:

<ol>
  <li>
    <span className={styles.firstName}>Foo</span>
    <span className={styles.lastName}>Bar</span>
  </li>
</ol>

Myth 4: It makes it easy to extend styles

In v1 you are able to extend styles using styled(StyledComponent) construct; v2 introduced extend method for extending the existing styles, e.g.

const Button = styled.button`
  padding: 10px;
`;

const TomatoButton = Button.extend`
  color: #f00;
`;

This is great. But you can do this in CSS (or using CSS module composition or using SASS inheritance mixin @extend).

button {
  padding: 10px;
}

button.tomato-button {
  color: #f00;
}

What makes the JavaScript approach easier?

Myth 5: It makes it easy to conditionally style components

The idea is that you can style components based on their props, e.g.

<Button primary />
<Button secondary />
<Button primary active={true} />

This makes a lot of sense in React world. After all, component behaviour is controlled using the props. Does it make sense to directly attach prop values to styles? Maybe. But lets look into the component implementation:

styled.Button`
  background: ${props => props.primary ? '#f00' : props.secondary ? '#0f0' : '#00f'};
  color: ${props => props.primary ? '#fff' : props.secondary ? '#fff' : '#000'};
  opacity: ${props => props.active ? 1 : 0};
`;

Creating a style sheet conditionally using the power of JavaScript gives you a lot of power. However, that also means that the style is a lot harder to interpret. Compare it with CSS:

button {
  background: #00f;
  opacity: 0;
  color: #000;
}
button.primary,
button.seconary {
  color: #fff;
}
button.primary {
  background: #f00;
}
button.secondary {
  background: #0f0;
}
button.active {
  opacity: 1;
}

In this case, CSS is shorter (229 characters VS 222) and easier to follow (subjective). Furthermore, in CSS you’d use a preprocessor to make it even shorter and grouped, e.g.

button {
  background: #00f;
  opacity: 0;
  color: #000;
  
  &.primary,
  &.seconary {
    color: #fff;
  }
  &.primary {
    background: #f00;
  }
  &.secondary {
    background: #0f0;
  }
  &.active {
    opacity: 1;
  }
}

Myth 6: It allows better code organization

I’ve got a few people telling me that they like styled-components because it allows to have styles and JavaScript in the same file.

I can see how having multiple files for the same component can appear tedious, but cramming style and markup into a single file is a horrible solution. Not only does it make the version control hard to track, it makes for a long-scroll on any component thats not a simple button.

If you must have CSS and JavaScript in the same file then consider using css-literal-loader. The latter allows you to extract the CSS at the build time using extract-text-webpack-plugin and use your standard loader configuration to process the CSS.

Myth 7: It makes the DX great. The tooling is awesome!

You’ve obviously not used styled-components.

  • When something goes wrong with styling, the entire app will crash with a long stack trace error (anecdotal experience using v2). Contrast this with CSS, where a “style error” will merely incorrectly render the element.
  • Elements do not have a recognizable className, therefore you will end up switching between React element tree and DevTools DOM tree when debugging (for the record, in v2 this can be addressed using babel-plugin-styled-components).
  • No linting (there is a stylelint plugin in development).
  • Invalid styles are simply ignored (e.g. clear: both; float left; color: #f00; will give no error or warning; good luck debugging, took me about 15 minutes of going through the stack traces, even reading styled-components source code. Only when I copy-pasted the code in a chat to ask for help did someone notice the missing :. You’ve noticed it, right?)
  • Syntax highlighting, code completion, and other IDE niceties are supported in few IDEs. If you are working with finance or governmental agencies, chances are that Atom IDE is not on the table.

Myth 8: Anything better performance, anything smaller bundle size

  • As it stands, styled-components cannot be extracted into a static CSS file (such as using https://github.com/webpack-contrib/extract-text-webpack-plugin). Meaning that your browser is not able to start interpreting styles until after styled-components parses them and adds them to the DOM.
  • Lack of separate files means that you cannot cache separately CSS and JavaScript.
  • All styled components are wrapped in an additional HoC by their nature. Thats an unnecessary performance hit. The same architectural flaw is the reason why I’ve discontinued https://github.com/gajus/react-css-modules (and created https://github.com/gajus/babel-plugin-react-css-modules).
  • Because of the HoC, if you are rendering server-side, this is going to result it a lot bigger markup document.
  • Don’t get me started on animating components using dynamic style values vs keyframes.

Myth 9: It allows developing responsive components

This talks primarily about the ability to style component based on the surrounding environment, e.g. parent container dimensions, number of children, etc.

First of all, styled-components have nothing to do with this. It is outside of scope of the project. You are better of directly setting the style values of the component in this case to avoid the extra overhead.

However, element queries is an interesting issue and one that is becoming a hot topic in CSS, primarily to the project EQCSS and its counterparts. Element queries are similar to @media queries in their syntax except that element queries operate on specific elements.

@element {selector} and {condition} [ and {condition} ]* { {css} }
  • {selector} is a CSS selector targeting one or many elements. Ex: #id or .class
  • {condition} is composed of a measure and a value.
  • {css} can contain: Any valid CSS rule. (Ex: #id div { color: red })

Element queries allow to style element using conditions such as min-width, max-width, min-height, max-height, min-characters, max-characters, min-children, max-children, min-lines, max-lines, min-scroll-x, max-scoll-x, and others (see http://elementqueries.com/).

Meowway!

One day, a variation of EQCSS is going to exist in the CSS spec (fingers crossed).

But wait!

Most (if not all) of these things can be addressed long term, either by the community, changes in React or in styled-components itself. But whats the point? CSS is already widely supported, it has massive community around it and it just works.

The point of this article is not to deter the reader from using “CSS” in JavaScript or from using styled-components. Styling using styled-components has a great use case: a better cross-platform support. Don’t use it for wrong reasons.

So what to use today?

It it too early to use Shadow DOM v1 (51% global support). Use CSS with either of the naming conventions (I recommend BEM). If you are worried about class name collisions (or too lazy to use BEM), use CSS modules. If you are developing for React web, consider using babel-plugin-react-css-modules. If you are developing for React native, styled-components is great.


Published by HackerNoon on 2017/04/27