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.
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.
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 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:
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.
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.
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.
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>
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?
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;
}
}
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.
You’ve obviously not used styled-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} }
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).
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.
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.