Skip to main content

Command Palette

Search for a command to run...

Why do we keep talking about CSS-in-JS?

Updated
0 min read

Remember the first time you wrote some html/css? It was amazing, right?

<div style="color: blue">Hello world</div>

So simple, so pure.

Then, of course, the senior developer™ told you not to write your css like this and put it a separate file.

<div class="blue-text">Hello world</div>
/* style.css */
.blue-text {
  color: blue;
}

As you build more elements and pages, your style.css starts getting long, so you split it into multiple files.

/* landing-page.css */
.heading {
  font-size: 64px;
}
button {
  background: orange;
}
/* forms.css */
button {
  background: blue;
}

Soon, you realise that styles intended for one element start clashing and overwriting others. You adopt some form of name-spacing to create a scope for these styles.

Maybe something as simple as this:

<form>
  <input placeholder="email" />
  <button>Submit<button>
</form>
/* form.css */
form button {
  background: blue;
}

Or something more advanced like BEM:

<form class="form">
  <input class="form__input" placeholder="email" />
  <button class="form__button">Submit<button>
</form>
/* form.css */
.form__button {
  background: blue;
}

I really liked BEM (and other convention based methods like OOCSS, ITCSS, etc.). You can solve a hairy problem by merely adopting a common convention in your team.

Either way, the problem you are solving here is of scoping styles to a specific context.

The same problems and solutions are carried into React land as well. Take this LoginForm for example:

function LoginForm() {
  return (
    <form className="form">
      <input placeholder="username" type="text " />
      <input placeholder="password" type="password" />
      <button className="button">Submit</button>
    </form>
  )
}

We have no idea if that button is going to clash with another button in the application somewhere else. We should use some sort of scope here. You can still use namespacing like BEM here with React.

<button className="form__button">Submit</button>

This is where the story get's interesting. In a React component, we aren't writing plain HTML anymore, we are writing JSX.

The above line of JSX is converted to this block of javascript at build time:

React.createElement(
  'button',
  { className: 'form__button' },
  'Submit'
)

You have the full power of a programming language (javascript) at your disposal now. You can do things which wouldn't be possible with pure CSS.

 

The promise of CSS-in-JS

You can defer the job of creating a scope or name-spacing to the language instead of doing it manually.

CSS Modules is the gateway drug the of css-in-js ecosystem.

This is what you write:

/* form.css */

button {
  /* look ma, no name spacing */
  background: blue;
}
import styles from './form.css'

function LoginForm() {
  return (
    <form>
      <button className={styles.button}>Submit</button>
    </form>
  )
}

And this is what styles.button get's compiled to:

function LoginForm() {
  return (
    <form>
      <button className="form__button__abc1">Submit</button>
    </form>
  )
}

This is very similar to what you would write by hand, but it frees you up from the responsibility of avoiding conflicts. I find it incredibly liberating to be able to write my styles as if they were locally scoped.

 

The next wave of CSS-in-JS libraries

We were able to leverage the power of the language in the tooling/automation bit, can we also bring it to writing styles?

This is where it becomes controversial. Each CSS-in-JS takes a slightly different approach to enable a certain new feature by making a tradeoff.

For example: jsxstyle lets you write styles that look like classic inline styles on the element but extracts them out into a file through a webpack plugin.

<Block component="button" backgroundColor="blue" />

On the other hand, styled-components lets you mix runtime logic inside css. This means you can start using it without touching your config but you can't extract the styles out.

const Button = styled.button`
  background: ${getBackground};
`

function getBackground(props) {
  if (props.appearance === 'primary') return 'blue'
  else if (props.appearance === 'disabled') return 'grey'
  else return 'white'
}

linaria takes an interesting middle route with its css tag, you can create classes right next to the component and these extract them out during build using a babel plugin. This means you can still use javascript but it can't depend on runtime logic like the previous example.

const button = css`
  background: ${colors.blue};
`

function MyComponent() {
  return <button className={button}>Click me</button>
}

As you can see, all of these libraries bring something to the table in exchange of a something else. This is why it's so controversial, you can take any library and find flaws in it.

css-modules automates the work of namespacing styles but requires some setup which can be looked as over-engineering (we already have manual BEM that works without any setup)

styled-components on the other hand does not require any babel/webpack setup but it requires the library to be present on runtime, increasing the size of your javascript bundle by a small amount.

 

You must choose the tradeoff that works for your project.

With the design system in Auth0, we chose styled-components because it helped us create flexible components based on a set of underlying tokens and design patterns.

Last week, I had to build a bunch of pages with some form logic in them and really enjoyed using css-modules because it helped me write both global and page specific styles without adopting a manual methodology like BEM.

You can use this comparison table that my friend Michele Bertoli created.

Hope that was helpful on your journey

Sid


newsletter

312 views
M

Thank you for your write-up. Just some questions:

  • Why the hate against a little verbose namespace? It helps understand the context of the rule. Do you have any killer-argument or is it your opinion?
  • How do you make sure you separate your concerns, which is the fundamental basic of decoupling and modularization, which is essential for a bit bigger code bases
  • How do you make your styles re-usable, especially in the case of jsxstyle?
  • How do you guarantee a consistent styling without manual intervention?
  • For what kind of project do you recommend which CSS-in-JS style?
  • What is CSS-in-JS's advantage over the following examples:
function LoginForm() {
  return (
    <form class="login">
      <button type="submit">Submit</button>
    </form>
  );
}
.login button[type="submit"] {
  background: blue;
}

function myButtonBar() {
  return (
    <div class="button-bar">
      <button type="button">Primary Action</button>
      <button type="button">Secondary Action</button>
      <button type="button">Tertiary Action</button>
    </div>
  );
}
.button-bar {
  button:first-child {
    background: blue;
  }

  button:nth-child(2) {
    background: white;
  }

  button:nth-child(3) {
    background: white;
    border: none;
  }
}
3
S
Sid7y ago

Why the hate against a little verbose namespace?

I didn't hate on verbose namespacing at all, I just prefer automated as compared to manual.

How do you make sure you separate your concerns?

The way I see it - when working with components, the concerns are split on blocks of interface, not technology/language.

Example: Separate Sidebar, Navigation, not .css,.js

How do you make your styles re-usable, especially in the case of jsxstyle?

This isn't for jsxstyle, but for all component based frameworks - you reuse (and extend) components, not just the styles of the component. A component becomes the smallest unit.

How do you guarantee a consistent styling without manual intervention?

Can you elaborate what you mean by consistent styling?

For what kind of project do you recommend which CSS-in-JS style

There might be some projects that are more suited and some that are less suited. I really think the decision should be made on the basis of "what will make our team more productive". If the philosophy shared in this post don't resonate with your team OR you don't have the problems that CSS-in-JS solves, then it's not the right choice for you :)

What is CSS-in-JS's advantage over the following examples...

Sorry, I didn't understand your question

M

Sid

I just prefer automated as compared to manual.

Then what's the difference between accessing a field on a variable (or adding an attribute to your element) and scoping a class name with SCSS? They are both automated...

the concerns are split on blocks of interface, not technology/language.

First of all, the separation between HTML, CSS and JS is not a separation of tech or language, but of concerns, meaning what they are actually used for. HTML is for layouting, CSS is for styling and JS is for animation, and that's how they are separated. What do you do when you want to have different styles for different components, or change the layout slightly to better fit the context? What about theming?

reuse (and extend) components

Sounds like you fork and edit the components, which I would not define as re-usable. CSS can import and overwrite styles, though, so that base styles do not have to be touched.

Can you elaborate what you mean by consistent styling?

Consistent means the whole site looks the same, you have the same basic coloring, the same gaps, the same typography, etc. If one component is the smallest unit and delivers its own style, and you'd like to re-use what you have, then I get the feeling that you'd have to edit every single component to fit the style of the app you do for a new customer, which sounds like a lot of overhead. Do you have any solution for that?

don't resonate with your team

Everyone hypes this tech, and I really want to know how to make use of it, but I just cannot see the benefits or the kind of situations which require it. That's why I want to take this chance to have a little chat to get to know the tech better by someone who obviously knows more than I and uses them regularly.

you don't have the problems

I do, but they all can be solved without CSS-in-JS, and sometimes even simpler imho.

I didn't understand your question

I made examples of situations which I have to deal with on a daily basis. They do not use CSS-in-JS, and I feel like they are simpler that way, yet you use these examples in order to demonstrate the power of CSS-in-JS. What kind of problem would CSS-in-JS solve there and how would it simplify the example?

S
Sid7y ago

Everyone hypes this tech, and I really want to know how to make use of it, but I just cannot see the benefits or the kind of situations which require it.

Sorry, this feels like a debate I don't want to continue.

Instead, I can send you the conference talks that convinced me there's something useful in the concept:

  1. Mark Dalgleish at CSSconf - A Unified Styling Language
  2. Max Stoiber at ReactNL - Styling React.JS applications
M

Sid

a debate I don't want to continue.

Well, I'm sorry, I don't want to pressure you, though I think it's sad that we have to stop.

Welp, thanks for the links anyway, they actually answered some of my questions and gave me information on other topics I think are important to me.

1
M

I find myself still doing simple inline styles in React often. I think I take a performance hit, but while I'm opinionated on practically everything else, I haven't been able to form strong opinions on this topic. Inline seems more readable for simple things that aren't handled by a Material Design library or equivalent alternative.

M

Matthew Ferrin While I certainly still have my opinion about CSS-in-JS, I really recommend not doing inline-styles. There is a reason why they were banned from the HTML5 spec entirely.

Also, there is Vanilla CSS, which means you do your styling without any library or framework at all. Whenever you need to do simple things, don't use a library and try to workaround it when it doesn't fit 😉

M

Marco Alka, Google search provides no evidence inline styles were removed from spec. I see no reason to do further digging on that vein.