Why We Use Styled Components at Decisiv

Written by _alanbsmith | Published 2017/10/23
Tech Story Tags: web-design | react | web-development | javascript | css

TLDRvia the TL;DR App

and why you might consider it as well

Overview

A couple days ago, I posted this somewhat-snarky tweet:

body[data-twttr-rendered="true"] {background-color: transparent;}.twitter-tweet {margin: auto !important;}

┳┻| ┻┳| ┳┻| ┻┳| ┳┻| ┻┳| ┳┻| ┻┳| ┳┻| ┻┳| ┳┻| ┻┳| ┳┻| ┻┳| ┳┻| ┻┳| ┳┻| _ ┻┳| *.*) I think CSS-in-JS is the future for #ReactJS. ┳┻|⊂ノ ┻┳|

— @_alanbsmith

function notifyResize(height) {height = height ? height : document.documentElement.offsetHeight; var resized = false; if (window.donkey && donkey.resize) {donkey.resize(height); resized = true;}if (parent && parent._resizeIframe) {var obj = {iframe: window.frameElement, height: height}; parent._resizeIframe(obj); resized = true;}if (window.location && window.location.hash === "#amp=1" && window.parent && window.parent.postMessage) {window.parent.postMessage({sentinel: "amp", type: "embed-size", height: height}, "*");}if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.resize) {window.webkit.messageHandlers.resize.postMessage(height); resized = true;}return resized;}twttr.events.bind('rendered', function (event) {notifyResize();}); twttr.events.bind('resize', function (event) {notifyResize();});if (parent && parent._resizeIframe) {var maxWidth = parseInt(window.frameElement.getAttribute("width")); if ( 500 < maxWidth) {window.frameElement.setAttribute("width", "500");}}

It surprisingly led to some good discussion in this thread:

body[data-twttr-rendered="true"] {background-color: transparent;}.twitter-tweet {margin: auto !important;}

@AdventureSteady A few reasons. What React got right was tightly coupling JS & markup. Binding CSS to JS + markup has been slow b/c the tooling was terrible.

— @_alanbsmith

function notifyResize(height) {height = height ? height : document.documentElement.offsetHeight; var resized = false; if (window.donkey && donkey.resize) {donkey.resize(height); resized = true;}if (parent && parent._resizeIframe) {var obj = {iframe: window.frameElement, height: height}; parent._resizeIframe(obj); resized = true;}if (window.location && window.location.hash === "#amp=1" && window.parent && window.parent.postMessage) {window.parent.postMessage({sentinel: "amp", type: "embed-size", height: height}, "*");}if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.resize) {window.webkit.messageHandlers.resize.postMessage(height); resized = true;}return resized;}twttr.events.bind('rendered', function (event) {notifyResize();}); twttr.events.bind('resize', function (event) {notifyResize();});if (parent && parent._resizeIframe) {var maxWidth = parseInt(window.frameElement.getAttribute("width")); if ( 500 < maxWidth) {window.frameElement.setAttribute("width", "500");}}

Unfortunately Twitter is not ideal for providing context and longer explanation, and I thought this might be a good way to follow up. Given that, a lot of this article describes what led to our decision to use CSS-in-JS, and provides specific reasons for choosing Styled Components further below. I hope what follows is useful for you and your team.

Disclaimer

There are a lot of strong opinions surrounding CSS-in-JS, so I’d like to start with a few clarifications. If you’re already familiar with CSS-in-JS, you can probably skip this section.

I Don’t Hate CSS

I don’t want CSS do die. I’m not trying to avoid it. I don’t think it’s obsolete, outdated, or stagnant. And I certainly don’t want a JavaScript overhaul of CSS. That’s not what CSS-in-JS is about regardless of the hype.

Styled Components Isn’t the Right Choice for Every Team

I’m not saying Styled Components is better than Radium, Aphrodite, Glamor, Glamorous, Emotion, or any of the other CSS-in-JS libraries. All of these libs have done incredible work and solved a lot of challenging problems.

I’m also not saying CSS-in-JS is right for your team. Styled Components works really well for us, and I’d like to share why and how.

A Little History | The Early Days of React

Evolving Ideas and Implementations

Ben Alman has this great tweet I like to reference from the early days of React.

body[data-twttr-rendered="true"] {background-color: transparent;}.twitter-tweet {margin: auto !important;}

Facebook: Rethink established best practices™

— @cowboy

function notifyResize(height) {height = height ? height : document.documentElement.offsetHeight; var resized = false; if (window.donkey && donkey.resize) {donkey.resize(height); resized = true;}if (parent && parent._resizeIframe) {var obj = {iframe: window.frameElement, height: height}; parent._resizeIframe(obj); resized = true;}if (window.location && window.location.hash === "#amp=1" && window.parent && window.parent.postMessage) {window.parent.postMessage({sentinel: "amp", type: "embed-size", height: height}, "*");}if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.resize) {window.webkit.messageHandlers.resize.postMessage(height); resized = true;}return resized;}twttr.events.bind('rendered', function (event) {notifyResize();}); twttr.events.bind('resize', function (event) {notifyResize();});if (parent && parent._resizeIframe) {var maxWidth = parseInt(window.frameElement.getAttribute("width")); if ( 500 < maxWidth) {window.frameElement.setAttribute("width", "500");}}

When React was first announced it seemed so backwards. Colocating JavaScript with markup? Haven’t we proven this was a bad idea? What about separation of concerns?

As it turns out, the idea wasn’t bad, but the implementation needed improvement. React provided the tooling to prove the concept. As more people began using React, we realized that JavaScript and markup have the same concern and there are a lot of benefits to colocation.

Style Management Patterns

React also has a built-in pattern for colocating CSS using inline styles. But inline styles didn’t feel like writing CSS, was difficult to transfer from stylesheets, and didn’t have Sass / preprocessing support. All of this created enough friction to keep styles separate. From this tension the CSS Modules pattern developed, and it’s great. Component-specific CSS / Sass files are directly imported and provide a great pattern for organizing styles.

But the problem of managing global styles remains. If you’ve been working in frontend for any time at all, you know that managing styles, especially at scale, is really challenging. BEM, SMACSS, and other CSS patterns provide a lot of great guidelines for managing styles. But guidelines only go so far. And as your application grows, it becomes more difficult to avoid unintended side-effects. Mark Dalgleish has a great talk on CSS-in-JS and says this much better here.

Enter CSS-in-JS

The premise for CSS-in-JS is that styles, JavaScript, and markup all have the same, shared concern and therefore should be tightly coupled. Problems with potential collisions are resolved by scoping styles to the component. We have a 0% chance of styles leaking. And if styles are updated in the component there are no ripple-effects across the DOM. What was once a best-practice and guideline is now strictly enforced by the nature of the tooling. A common critique of modern CSS-in-JS libraries is complexity (or at least a feeling of complexity), and that’s fair. But the appeal of CSS-in-JS is not simplicity, rather predictability and consistency.

As Glamor, Glamorous, Styled Components, Emotion, Aphrodite, etc. became available, they created a surge in popularity for colocating styles. Our implementations finally caught up the the concept. Similar to colocating JavaScript with markup, the idea of colocating styles wasn’t flawed, rather our previous tooling was inadequate. We are now able to stand at a vantage point where we can see the benefits of the concept.

What’s Next

Our UIs are more complex than ever and continue to trend in that direction. Not only are individual clients more complex, but it’s not uncommon for a team to support multiple web UIs and native mobile clients. As this complexity increases, so does our need for predictability. This trend raises a larger question: How do we keep UI consistent across multiple applications?

Our frontend team at Decisiv is solving this issue by building an internal component library. It allows us to version, test regressions, collaborate with our design team, and share our UI across multiple applications. A lot of other teams are doing this as well. Custom component libraries were once reserved to large dev shops and thought-leaders. But as our tooling improves, I see more teams building their own internal component libraries. Design and development is becoming less of a handoff and more continuous collaboration. (Which is awesome! 🎉)

From my experience, building a component library is the best way to keep UI consistent and predictable across applications, and CSS-in-JS has been the best tooling available to build these libs.

Why We Chose Styled Components

We ❤️ Styled Components

We were drawn to CSS-in-JS for the reasons mentioned above, but Styled Components in particular has been a great tool for our team. Below are the main reasons we chose it.

Large & Thriving Community

Being on the edge of a new technology is inherently risky, but we were really encouraged by the community surrounding Styled Components. The team is connected to the community and always asking for feedback. We recently started contributing back to this community by open sourcing our internal styled-components-modifiers library, and have other internal tooling we are hoping to open source when it’s ready.

Template Literal Syntax

Using template literals to write CSS syntax in our components is a huge boost to our team’s productivity. Developers who were brand new to CSS-in-JS could immediately hop in and start writing styles. I probably could get used to writing { fontSize: “24px” }, but having a familiar syntax is really a great feature.

Sass Support & Polished

We also really liked that Sass support was bundled into the lib.

const Link = styled.a`
  cursor: pointer;
  text-decoration: none;
  &:hover {
    color: blue;
    text-decoration: underline;
  }
`;

Again, writing our styles in this way feels really natural and reduces lines of code at the same time. Along with the basic Sass support, there’s also Polished, a small toolset created by Styled Components to provide additional Sass functionality and other helpful tooling.

Native Mobile Support

Our team is also in the process of developing a native mobile app with React Native. It was really important for our UI to feel consistent on mobile, and having that bundled into Styled Components is a huge win.

We’re Not There Yet

These recent expressions of CSS-in-JS are still really new and far from perfect. There is still a need for established patterns and a more robust ecosystem. I listed a few in this thread:

body[data-twttr-rendered="true"] {background-color: transparent;}.twitter-tweet {margin: auto !important;}

@_alanbsmith Out of curiosity, what's the "almost" part there? What are you missing?

— @mxstbr

function notifyResize(height) {height = height ? height : document.documentElement.offsetHeight; var resized = false; if (window.donkey && donkey.resize) {donkey.resize(height); resized = true;}if (parent && parent._resizeIframe) {var obj = {iframe: window.frameElement, height: height}; parent._resizeIframe(obj); resized = true;}if (window.location && window.location.hash === "#amp=1" && window.parent && window.parent.postMessage) {window.parent.postMessage({sentinel: "amp", type: "embed-size", height: height}, "*");}if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.resize) {window.webkit.messageHandlers.resize.postMessage(height); resized = true;}return resized;}twttr.events.bind('rendered', function (event) {notifyResize();}); twttr.events.bind('resize', function (event) {notifyResize();});if (parent && parent._resizeIframe) {var maxWidth = parseInt(window.frameElement.getAttribute("width")); if ( 500 < maxWidth) {window.frameElement.setAttribute("width", "500");}}

However, I’m encouraged to see the adoption and enthusiasm of CSS-in-JS moving it forward. I’m also encouraged by the dialog between the various lib communities. Those conversations will help establish patterns for best-practices leading us to more consistent and predictable UI.

Final Thoughts

Styled Components has been great for our team, and I think it could be really useful for a lot of other teams as well. I hope some of the ideas here were useful and thought-provoking. I’ve been thinking about this for a while now, and I have other posts queued up, so stay tuned and thanks for reading!


Published by HackerNoon on 2017/10/23