Skip to content

Instantly share code, notes, and snippets.

@troch
Last active October 18, 2020 17:52
Show Gist options
  • Star 23 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save troch/c27c6a8cc47b76755d848c6d1204fdaf to your computer and use it in GitHub Desktop.
Save troch/c27c6a8cc47b76755d848c6d1204fdaf to your computer and use it in GitHub Desktop.
Choosing a CSS in JS library

Choosing a CSS in JS library

Check comments below, this is only my opinion and a choice specific to my use case

We've architected a SPA to be universal-ready. It grew a lot, when we introduced code splitting we realised converting to CSS in JS was unavoidable in order to have pre-rendering and not load all our CSS up front. We've procrastinated on looking at CSS in JS properly, prioritasing immediate business needs, but kept an eye on industry evolutions.

Our CSS solution for components was a CSS companion file per component, imported with style / postCSS loader (with webpack) but no CSS modules: we were missing a tighter coupling between component rendering and styles. We were at the bottom of the CSS and componentization ladder, the following links have influenced us in choosing the right solution for us:

Shortlist

There is a vast number of libraries to choose from: https://github.com/MicheleBertoli/css-in-js.

CSS modules would have worked for us, and would have been a minimal effort (in term of refactoring and changing habits). However, we wanted to go further in our componentization approach. We discarded inline styles: they are restrictive, the trend is about dynamic style sheets, to enjoy CSS full power. We also discarded solutions which simply consist of embedding CSS in JS, and not embedding CSS in components.

We chose to highlight four libraries (non-exhaustive list):

  • styled-components
  • glamorous
  • styletron
  • fela

Other libraries we didn't evaluate but we could have:

  • JSS

Making a choice: fela

All libraries mentioned are excellent. Our experimentation and choice making was time-boxed, and at the end we feel we made what we think is the best choice for us and our circumstances, maybe not for everyone.

They are all inline CSS libraries (and not inline styles), so they support pseudo-elements, pseudo-selectors, media queries, prefixing and value fallbacks.

They also all support server-side rendering. Support for React Native was a nice to have (styled-components, fela, glamorous). We regarded atomic CSS (styletron, fela) as an implementation detail.

Syled-components uses raw CSS, other libraries use object literals.

We weren't going to base our choice on popularity: they all have safe levels of monthly downloads and activity in their repositories.

Fela was discovered when doing our research, we hadn't heard about it before, and it won the argument for mainly two reasons: flexibility and extensibility.

  • Flexible API: you can abstract away class names by wrapping elements with a styling HoC (like with styled-components, glamorous or styletron). Or you can use a styling HoC on a composite component, manually distributing class names. I view this as very important for componentisation (passing class names as props, avoiding to wrap components with elements) and for integration with 3rd party libraries.
  • Extensible: fela has plugins, reminding me of postCSS plugins. It adds to fela's flexibility. Existing plugins for pseudo selectors and media queries make working with object literals much better (onHover instead of :hover, etc...). It gives you power to create your own API / way of writing style objects. It also has enhancers which are basically renderer decorators. Fela uses atomic CSS which is reknown its bad developer experience. Fela offers a monolithic enhancer (with the option to include rule names) for a better DX.
@kitten
Copy link

kitten commented Jul 14, 2017

For styled-components I thought I'd add some comments, in case someone would like to use this to make an informed choice ๐Ÿ˜„

Vendor Prefixing

styled-components doesn't actually use inline-style-prefixer. It uses stylis for CSS flattening and prefixing (which can be summarised as CSS parsing and processing).

Atomic classes

Despite claims that it is vastly more performant compared to other factors this operates on a micro level. CSS injection and processing are hugely more important. CSS injection actually being the most important one by an entire order of magnitude compared to CSS processing.

The biggest factor for a decision

All the listed libraries put forward very different good practices and ways of working with them.

I don't want to list all of them, but in general fela and styletron are more flexible and traditional libraries for CSS-in-JS to which for example aphrodite and glamor count as well. They don't restrict any usage, but that means that DX is going to be worse when starting a project from scratch initially.

Glamorous and styled-components are part of the new component-based CSS-in-JS libraries, and both focus on enforcing best practices. This has the advantage that most concerns are abstracted away, such as performance, CSS injection, component creation and structure, and so on. On the other hand, if someone doesn't prefer a BEM-like approach to CSS, then this won't feel perfect while using it. But a lot of users agree with us in that it makes them feel more efficient, and a project stays more maintainable without effort. (Yes, I know. Careful statement haha)

Preprocessing

One thing that isn't being taken into consideration here is preprocessing. styled-components (and possibly Glamorous in a potential joint-effort) are moving towards preprocessing CSS for inter-compatibility and performance. This will allow customisation of the CSS pipeline (which will then be part of babel or webpack) besides other things.

@kentcdodds
Copy link

I'll also add a response for the same reason @philpl did ๐Ÿ˜„

We chose to highlight four libraries:

You might also consider emotion. If I weren't using glamorous, I would be using emotion.

Support for React Native was a nice to have (styled-components, fela).

Please add glamorous-native to that list ๐Ÿ˜„

you can use a styling HoC on a composite component, manually distributing class names

It is true that glamorous does not currently expose a HoC to forward classNames as props to components, but I would argue that it's actually easier to just use glamor directly to do this (note: glamorous depends on glamor, so you'll have this in your project already anyway). For example:

import {css} from 'glamor'

const inputClassName = css({ /* styles */ })

function MyInput() {
  return (
    <div>
      <label>
        Write something: <input className={inputClassName} />
      </label>
    </div>
  )
}

I honestly don't see much of a difference between that and a HoC solution except this is more straightforward in my eyes...

integration with 3rd party libraries.

glamorous and styled-components have pretty great support for integrating 3rd party components out of the box. Maybe I'm misunderstanding you but this works great:

const StyledThirdPartyComponent = glamorous(ThirdPartyComponent)({/* styles */})

If that's not quite flexible enough for you, then you can always use glamor directly. glamorous also works really well with styles that were generated by glamor's raw APIs too.

Extensible

Because glamorous is built on top of glamor and doesn't hide glamor APIs from you in any way, you can use glamor's plugin API and that will apply to glamorous components as well. Here's a plugin I use to integrate rtl-css-js with my glamor(ous) setup at work:

import rtlConvert from 'rtl-css-js'
import { plugins } from 'glamor'
import serverProps from '../utils/serverProps'

const { locality: { directionality = 'ltr' } = {} } = serverProps
if (directionality === 'rtl') {
  plugins.add(({ selector, style }) => ({ selector, style: rtlConvert(style) }))
}

I should mention that all of these libraries are awesome and while they each come with trade-offs you're really not going to be making a "wrong choice" here.

@davidkpiano
Copy link

My short opinion:

At a 10,000 foot view, it does not matter. Just pick one that your entire team would be happy with, and start shipping stuff.

(But maybe avoid Radium.)

@kentcdodds
Copy link

I agree with @davidkpiano ๐Ÿ‘

@troch
Copy link
Author

troch commented Jul 14, 2017

Thanks for the comments ๐Ÿ‘. I've amended the article, pointed to the comments ๐Ÿ˜„. It was actually not an easy choice to make, all modern and recent CSS in JS libraries are about CSS and componentization, and that's what matters!

@nickbalestra
Copy link

Thank you so much @troch for doing this. I'll follow up with one from our-side once we come up with our decision based on our specific use-case (not spa)

@aselbie
Copy link

aselbie commented Jul 14, 2017

@kentcdodds is your intent with Glamorous to maintain parity and converge with SC for the longer term? I.e. would it be safe to call Glamorous "SC without the parser"? Or do you see your approaches branching? I think I prefer the "just JS" approach, but SC seems to have the most momentum out of the styling-in-JS options, and I'd love to be able to benefit from that community and ecosystem.

@kof
Copy link

kof commented Jul 15, 2017

I wonder why JSS was left out from the final selection, any feedback?)))

@troch
Copy link
Author

troch commented Jul 15, 2017

@kof we missed JSS when making our selection, it would have made the list otherwise. Added a note.

@kentcdodds
Copy link

kentcdodds commented Jul 17, 2017

@aselbie, my intent is to maintain parity with what the users of glamorous want ๐Ÿ˜„ For example, styled-components recently added support for components as selectors and we decided to forgo that because it's already pretty straightforward to do that though less ergonomic, but it's not a practice we care enough to encourage that we want to make the codebase/api any more complex. At the same time, we have added some features because styled-components added them and we thought they were good ideas (after all, styled-components was the main inspiration for glamorous in the first place). In addition, there are several things that glamorous can do that styled-components cannot (like the built-in components that take styles as props, the css prop, and more). So it's mostly community driven. I don't expect the two to converge ever. But maybe!

SC seems to have the most momentum out of the styling-in-JS options, and I'd love to be able to benefit from that community and ecosystem.

It's unclear to me what you'd lose by using glamorous. There are a few libraries that expose styled-components as primitives, but most libraries support both (like polished or styled-system). In addition, there's a world of CSS-in-JS libraries that support objects which you have available to you with glamorous as well. It's not a small community ๐Ÿ˜„ In fact, styled-components has a pretty good head start on glamorous, and we're already almost half of the daily downloads ๐Ÿ˜‰

@mxstbr
Copy link

mxstbr commented Jul 18, 2017

styled-components has a pretty good head start on glamorous, and we're already almost half of the daily downloads

Sidenote: npm stats don't mean jackshit for production usage since they include other npm packages that depend on you. (like glamorous and storybook or styled-components and rebass) A high percentage on top of that is CI installs, not people. shrugs

@troch
Copy link
Author

troch commented Jul 18, 2017

For example, styled-components recently added support for components as selectors and we decided to forgo that because it's already pretty straightforward to do that though less ergonomic, but it's not a practice we care enough to encourage that we want to make the codebase/api any more complex

@kentcdodds sounds sensible. We have a rule around componentization and CSS: it was written in the context of using BEM, but is applicable to CSS in JS too.

"Never reference a parent or child component class name in a CSS file: it is for parents / owners to create components with the right class names (i.e. modifiers), and not for components to override their properties based on the context they find themselves in. Similarly, it is not for a component owner to override CSS properties of its children: it should instead create a component with the right properties, resulting in the right CSS class names being generated.

@streamich
Copy link

streamich commented Feb 6, 2018

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