Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
For Snook

https://twitter.com/snookca/status/1073299331262889984?s=21

‪“‬In what way is JS any more maintainable than CSS? How does writing CSS in JS make it any more maintainable?”

‪Happy to chat about this. There’s an obvious disclaimer that there’s a cost to css-in-js solutions, but that cost is paid specifically for the benefits it brings; as such it’s useful for some usecases, and not meant as a replacement for all workflows. ‬

‪(These conversations always get heated on twitter, so please believe that I’m here to converse, not to convince. In return, I promise to listen to you too and change my opinions; I’ve had mad respect for you for years and would consider your feedback a gift. Also, some of the stuff I’m writing might seem obvious to you; I’m not trying to tell you if all people of some of the details, but it might be useful to someone else who bumps into this who doesn’t have context)‬

So the big deal about css-in-js (cij) is selectors.

The biggest win from cij is that computers generate selectors for you automatically. While strategies like oocss, etc are conceptually great, relying on selectors to enforce those architectures are hard in a global namespace because there is always a chance of a clash; if not now, 3 years in the future when the team has changed and become bigger and whatnot. This is exacerbated with third party libraries. (In fact, scoping selectors is pretty much the only thing css modules does, and it’s hugely popular and useful for just this.)

Further, it’s extremely hard to statically analyse selectors to see whether any rules are broken (or even a typo!) this means constraints have to be enforced via code reviews and education, which is a harder problem to solve and scale. We should use a computer's assistance us on this, and not rely on humans being infallible (they’re not!)

The enforced control over selectors enables some new capabilities that we couldn’t do easily before; eg - we can trivially extract critical css for html by matching the ids to rules, meaning we can load a page with just 1-2k of css that the page needs for initial render. With no runtime! Frameworks like gatsby and next utilise this heavily for the kind of performance numbers they boast of. Inlining that critical css sirectly into the html is also simple now because it’s so small, removing a whole blocking request for the page. Amazing for boosting time to first render/interactive. This also alleviates many concerns about the js bundle size! The savings come from this critical css approach, along with using dynamic import()s and code splitting and actual dead code elimination (as opposed to append-only stylesheets that grow over time - some real data here https://twitter.com/mrmrs_/status/874336748528177152?s=21)

Similarly, theming. You know how css vars, despite being so fun, never really took off for the usecases it was designed for, simply because fallback strategies for older browsers are either too hard or limiting? It’s meant that they’ve been usually used for top level variables, but rarely as generic variables to be filled in whenever. Runtime evaluation means you can mix and match js variables in their place, and truly achieve that dream.

In fact, you’ll love this; with this approach, you can truly, finally, polyfill css variables for all browsers. ie - zero runtime css vars for browsers that support it, and a special runtime build for older browsers (I wrote about it in a little more detail here https://gist.github.com/threepointone/0ef30b196682a69327c407124f33d69a and iiuc, this is the only lib that’s ever managed to do this.)

In an SPA/component world, where assets like scripts and styles are loaded asynchronously, you can’t guarantee style sheet loading order, which means you have to lean on some form of runtime composition to guarantee a shape, else you end up with !important rules even if you follow a given architecture. To clarify - if you have class=“a b” on an element, and a and b are defined in different stylesheets, you can’t be certain of the order of the cascade. The facebook codebase has thousands of !important statements, despite being written by competent engineers with solid engineering practices and deep relationships with design teams.

You mentioned treating css like js - consider that 10 years ago, we were solving similar problems for js - libraries and modules were registering themselves into the global namespace ($, etc), and we had to be very particular about the order that script tags were included into html. But we didn’t rely on convention forever - we eventually moved to modules, and build systems to stitch them together to guarantee order and such. These libraries simply enforce this invisibly.

(One of my observations was that you can still actually implement css architectures (like oocss, smacss, etc in the cij world - elements from those architectures are represented as objects instead of selector+rule units. I use this approach and it serves me well. Wrote something on it here - https://gist.github.com/threepointone/9f87907a91ec6cbcd376dded7811eb31)

Now I’m well aware of the drawbacks of css-in-js. Indeed, that’s why there’s no “canonical” library that truly represents css in js - it’s a spectrum of libraries with completely vanilla static css on the one end, and completely dynamic libraries like styled-components on the other end). (Ps- preprocessors like sass and less feel orthogonal to this spectrum imo, since they can theoretically be used with any of these libraries as an authoring concern.) Each of these libraries have tradeoffs - some do static extraction so there’s no runtime cost, some are tuned for correctness, some for the “developer experience”, others to efficiently do difficult animations, etc etc. This is a reaction to the industry's need for solutions for different usecases, in a hyper-evolving industry. Not just these libs! Even standards folks (via shadow dom etc) are making a valiant effort to solve some of these problems, and it has its own set of tradeoffs, least of which is that it isn’t widely available yet, and the buy in is not acceptable by many teams.

I used to feel very strongly about this, but I’ve tempered my thoughts about this over the last few months. It turns out that the “truth” really lies somewhere in the middle - it’s a function of teams, needs, time, documentation, and so much more. Further, I don’t even think these represents the “final form” of the idea. We should be encouraging of experimentation in this area, if only to learn what we could do better, and maybe even land some of these ideas in browsers.

I’ve written all this out on a phone in the middle of the night, so it might have mistakes/assumptions that I got wrong, but I’m happy to correct and revisit. But I do hope I’ve shown you some perspective to why some teams would use this family of approaches.

@mjackson

This comment has been minimized.

Copy link

mjackson commented Dec 13, 2018

Very well written, Sunil. And on a phone in the middle of the night! And thank you for taking the time to explain it so objectively.

I'm going to refer anyone who is really keen on understanding the appeal of CSS-in-JS. Thanks for your thoughts :)

@seejamescode

This comment has been minimized.

Copy link

seejamescode commented Dec 13, 2018

Great write-up. Thanks for everything you do in the community. When I give an elevator pitch about CSS-in-JS to someone that hasn’t tried it, I usually tell them this: CSS expects a group of humans to write selectors perfectly in the right order. CSS-in-JS takes those pain points out of the equation.

@tomhodgins

This comment has been minimized.

Copy link

tomhodgins commented Dec 13, 2018

@threepointone I'm curious how you feel about jsincss, where you're taking an event-driven approach to reprocessing stylesheets as functions that template CSS as a string, to be re-computed on an as-needed basis.

What this means is that we can write JS functions that extend CSS in a clean way. You can use jsincss from JavaScript if you want, but that's not even the most exciting part. It's possible to write valid CSS that controls JavaScript plugins, so we can actually write CSS that gets extended by JavaScript, and jsincss is the 'virtual stylesheet manager' that keeps it all updated and reprocessing when it should.

Here's an example where I created a DIY polyfill for CSS's :playing pseudo-class that doesn't have browser support yet:

video[--playing] {
  border: 10px solid hotpink;
}

That's the closest we can do right now to video:playing, and we've expressed it here in valid CSS syntax. Suppose we have a function in JS named playing(), like this:

// [--playing] plugin
function playing(selector, rule) {
  return Array.from(document.querySelectorAll(selector))
    .reduce((styles, tag, count) => {
      const attr = selector.replace(/\W/g, '')
      if (tag.paused === false) {
        tag.setAttribute(`data-playing-${attr}`, count)
        styles += `[data-playing-${attr}="${count}"] { ${rule} }`
      } else {
        tag.removeAttribute(`data-playing-${attr}`)
      }
      return styles
    }, '')
}

That's all we need to be able to run that playing() function with the selector video, and the declaration list from the CSS rule inside. We can then use JavaScript to determine which tags matching the CSS selector video should have the styles applied to them, it can give them a unique identifier and output CSS as a string written for only those matching elements.

So there's an example of writing simple, valid CSS, alongside simple, valid JavaScript, and making them work together in a way that extends CSS in the browser. This could work with any DOM, no matter how it got there (not tied to any one framework or way of assembling HTML) and this sort of processing can be done client-side and server-side.

Here I've got a CSS stylesheet that includes custom selectors like:

.characters[--element='{"characters": 5}'] {
  border-color: limegreen;
}

And you can also make use of @supports and the general-enclosed syntax to express a custom at-rule:

@supports (--element(".minwidth", {"minWidth": 300})) {
  [--self] {
    background: greenyellow;
  }
}

For these to work I've written two nearly equivalent element query plugins, one for use as a selector and another for use as an at-rule. If we want to process this stylesheet server-side we can use qaffeine to parse the CSS with JS-powered rules and output the CSS and JavaScript we need, or we can use deqaf to read JS-powered rules from CSSOM client-side in the browser.

Here's an element query demo page using qaffeine to parse the JS-powered rules server-side:

And here's the same element query demo parsing the same stylesheet with deqaf client-side in the browser:

I'm sure this approach is different than what you've seen in the past, but it lets you use 100% of CSS plus 100% of JavaScript, together, using the full power of both languages for what they're each best at. It seems to me like having all of the benefits of CSS, CSS-in-JS, and JS, and without having to make any sacrifices or tradeoffs.

@rickhanlonii

This comment has been minimized.

Copy link

rickhanlonii commented Dec 13, 2018

Instant classic, great write up Sunil 👍

@kentcdodds

This comment has been minimized.

Copy link

kentcdodds commented Dec 13, 2018

👏

I'm with @mjackson. I'll be referring to this as well. Made a short URL that should be easy to remember for folks who want to do the same: https://kcd.im/why-cij

@griimick

This comment has been minimized.

Copy link

griimick commented Dec 13, 2018

I will share this with my team asap. Thanks for the write-up.

@housseindjirdeh

This comment has been minimized.

Copy link

housseindjirdeh commented Dec 13, 2018

"and maybe even land some of these ideas in browsers" is my favorite line. 🎉

@tkh44

This comment has been minimized.

Copy link

tkh44 commented Dec 13, 2018

This is a great write up Sunil 👏

@af

This comment has been minimized.

Copy link

af commented Dec 13, 2018

do static extraction so there’s no runtime cost

Genuinely curious, which cij libraries do this?

@alexboots

This comment has been minimized.

Copy link

alexboots commented Dec 13, 2018

consider that 10 years ago, we were solving similar problems for js - libraries and modules were registering themselves into the global namespace ($, etc), and we had to be particular about the order that script tags were included into html. But we didn’t rely on convention forever - we eventually moved to modules

I've never read such a clear comparison between the evolution of JS architecture, and the current changes in CSS architecture. Sort of makes css-in-js an architectural ripple effect of JS improvements over the last 7 or 8 years thanks to node and transpilers making modules a thing. Wasn't sold on css-in-js until i started working with it a lot, but feel like this read would have sold me on it sooner 👏

@jgilless

This comment has been minimized.

Copy link

jgilless commented Dec 13, 2018

Thanks for the write up!

@smith

This comment has been minimized.

Copy link

smith commented Dec 14, 2018

do static extraction so there’s no runtime cost

Genuinely curious, which cij libraries do this?

@af https://www.styled-components.com/docs/tooling#babel-plugin can do some of this.

@JorgeVV

This comment has been minimized.

Copy link

JorgeVV commented Dec 14, 2018

@snookca

This comment has been minimized.

Copy link

snookca commented Dec 14, 2018

(I’m on mobile and it won’t let me copy/paste the relevant passage so my apologies for paraphrasing.)

consider that 10 years ago, we were solving similar problems for JS

this is really the point I was trying to make. CSS is not inherently harder to manage than JS. These problems still exist in JS. It’s just that they’ve been (somewhat, kinda, sometimes painfully) solved with an entire tooling ecosystem that has grown up around it. Likewise, an established convention has grown up around JS that has existed in many other languages. That convention has become so second nature that I think many developers forget that that convention exists.

To be clear: I am not advocating that CSS be handwritten in a large file and that the way CSS is written and generated shouldn’t evolve. It can, should, and has changed. I’m also not saying that JS can’t be used as a tool to enforce better practices around writing and building and delivering CSS. Use JS, PHP, Ruby, or whatever language allows you to understand the entire application ecosystem to deliver the best product.

cij enables static analysis

Yes and no. You need something that can analyze all three branches of code: HTML, CSS, and JS. Putting all three components into JS might make it easier to do that analysis—and I’d contend that it probably is the easiest way right now. But if you have a tool that can analyze all three, there’s no reason why you couldn’t receive the same benefits.

using JS allows you to polyfill CSS (vars)

Except for the 2-3% of people that use the web without JavaScript. And there’s still a limit to what browsers you can support (and likely a limit to what you’re likely to build and test against). Being able to polyfill isn’t something that I’ve generally been concerned about. For example, I never used Modernizr. I used CSS’s progressive nature to manage compatibility.

if you define class=“a b” and a and b are defined in different files, there’s no way to guarantee the order of the cascade.

If you define var a in different files, there’s no way to guarantee the order. In other words, this is a strawman argument. But this goes back to your earlier statement: the community has developed JS conventions (e.g “don’t use global variables”) and tooling (e.g. enforce encapsulation and naming convention) to ensure that doesn’t happen.

Source order was something we had to solve at Yahoo! 9 years ago and did so without CSS-in-JS and without Sass or anything else. That’s where SMACSS came from. We built components that used naming convention to isolate them and then loaded them at runtime, as needed. Work was divided amongst multiple developers over multiple products over multiple tech stacks.


JavaScript, as a language, is not more inherently easier to manage than CSS, the language. The JS ecosystem is definitely more evolved. There are definitely no lack of tools to help people manage their CSS and their JS (and their HTML). In another 10 years, I think the way we develop web applications will be different to how we develop them today—for both JS and CSS.

@threepointone

This comment has been minimized.

Copy link
Owner Author

threepointone commented Dec 14, 2018

@snookca some quick clarifications

  • “2 - 3% of the world that doesn’t use JavaScript” - that specific solution still works without js, like regular progressive enhancement. Generates a stylesheet and everything.
  • “can’t guarantee order in js files” - correct, but they’re scoped to the modules they’re defined in, and composing them at any place is deterministic. This trips up more people than you’d imagine with css.
  • “yahoo” - I was in yahoo too! 2012, I built out maps.yahoo.com. And I remember the module system yui had for css and js (I wrote a bunch of tooling around that too) it was specifically for the guarantees I’m mentioning here; I’ve been working on these issues since then (and even before) and yahoos still struggled!

I didn’t expect to convince you tbh, but do consider that not everyone is you/me/experts. The community’s been aching for tooling for years to manage this stuff better, and I just wanted to provide some perspective as to why whatever’s happening now, is, er, happening. Thank you for replying!

@itaditya

This comment has been minimized.

Copy link

itaditya commented Dec 14, 2018

My takeaway from @snookca comment is that we should have a evolved tooling around html and css also. Right now tools like webpack convert everything to js and then to do it's thing. I would love that but right now these are the tools we have and we should use them so that others have a motif to write better tools.

@snookca

This comment has been minimized.

Copy link

snookca commented Dec 14, 2018

@threepointone I’m not sure I needed convincing. I’m working on a React app with CSS Modules right now. :) My struggle has been with much of the “bravado” of JS devs thinking that they’ve somehow made things better by writing JSON and use object merging, like that is somehow superior to writing a CSS selector. At the end of the day, the browser still needs HTML and CSS and selectors can be a better way to solve problems over replicating CSS functionality in JS.

@threepointone

This comment has been minimized.

Copy link
Owner Author

threepointone commented Dec 14, 2018

Just wanted to be sure - you’re aware that most of these libraries generate “real” css selectors and rules and stylesheets, yes? As in, these aren’t inline styles, and no one replicates :hover for example. The injection into style tags and sheets is just delayed till compile/runtime.

I’ll ignore the bravado comment, to me it’s people finding solutions for actual problems that they face.

@tonyfinlay

This comment has been minimized.

Copy link

tonyfinlay commented Dec 14, 2018

I work with several large products regularly and we're wanting to make them more maintainable and consistent; we've a new product being built with React, so we've some decisions to make.

How easy is it to reuse cij styles across different platforms which share a common design language but built with different languages?

@giuseppeg

This comment has been minimized.

Copy link

giuseppeg commented Dec 14, 2018

@snookca I use CSS Modules at work too but it has some unsolved problems. To solve the deterministic styles resolution issue for example, we'd either need complex and powerful tools like CSS Blocks (never took off for its complexity), something like that DSS lang thing I shared with you once or JavaScript (runtime insertCssAndReturnScopedClass(Object.assign(one, two))).

SMACSS is a convention/methodology and the only tools we have to enforce it are a linter, common sense and advocating (and getting frustrated when we realize that somebody in another timezone merged a bunch of css which doesn't follow the convention). I think that strict tooling that enforce some rules by making them the default "scale" better (by scale I mean "me + a teammate" to hundreds of devs). We happen to write these "tools" in JavaScript because it is the closer programming language we've got and has a runtime that we can use in the browser but I'd argue that similar tooling could be written in Ruby or Python (CSS in Ruby, how about that :D).

Like Sunil, I am not trying to convince you - I just wanted to leave a comment with some clarification for the less informed ones. At the end of the day I also think that everybody should use the solution that makes them more happy and productive. This is probably why I have stopped caring about winning this "argument" with strangers on Twitter and just use and benefit from the tools I like - probably everybody should do that and respect others' people choices.

@Saifadin

This comment has been minimized.

Copy link

Saifadin commented Dec 14, 2018

Great writeup Sunil, bookmarked for all eternity ^^

@adyz

This comment has been minimized.

Copy link

adyz commented Dec 14, 2018

I used to write CSS in SASS with the BEM methodology and I still do in some projects I maintain.

Recently I created a gatsbyjs project that allowed styled-components.
Styled-components give me all the good stuff mentioned here and I have the same confidence as the old school SASS and BEM gave me PLUS the power of JS (variables, functions, etc)

Awesome stuff

@TejasQ

This comment has been minimized.

Copy link

TejasQ commented Dec 14, 2018

Sunil, you're my hero. You said everything I was thinking about cij. 💞

@RobinMalfait

This comment has been minimized.

Copy link

RobinMalfait commented Dec 14, 2018

@kentcdodds nice link, I shamelessly created a bookmark for it as well: https://robin.click/why-cij

@msand

This comment has been minimized.

Copy link

msand commented Dec 14, 2018

@thepixelworx with react-native-web it (reusing styles in web, ios, android) has felt easy to me at least, e.g. styled-components works nicely with it as well, but plain react-native styles also work good enough for quite a few use-case. Here's an example with 99%ish code reuse: https://github.com/devhubapp/devhub
@threepointone excellent writeup btw!

@harisvsulaiman

This comment has been minimized.

Copy link

harisvsulaiman commented Dec 14, 2018

At the end of the day, an abstraction that helps everyone write better software is welcome.

Who here writes assembly code? Many of the things we take for granted are abstracted away.
If css works for you use it, if css in js works use it.

I'm sticking with css in js specifically styled-components, because I trust max and team to write abstractions to help me avoid pain points than my infallibility.

@phun-ky

This comment has been minimized.

Copy link

phun-ky commented Dec 14, 2018

what @snookca said!

And I am using this argument a lot these days: "Things like that (CSS into JS/ typed JS/Elm) are done because the developers are not competent enough in their field, and instead of learning about it, they try to create their own solution for it". 🔥

I've worked with frontend since 1998 and I've witnessed a lot, things you don't want to see. (trust me on this ,I've been there 🙄 ). I've seen progression and failures, good and bad specs/ideas, but my statement is still valid and can be applied to what I've encountered for so many years.

If you don't fully grasp what CSS is, how it works and what you can or should do with it, do not try to f*ck it up. It's like whisky and cola, great apart, but don't mix them!

cc @selbekk

@zazapeta

This comment has been minimized.

Copy link

zazapeta commented Dec 14, 2018

Well said ! 👍 👍 👍 👍

I tought it's always the same problem at the very beginning : encapsulation
Moreover, I think that every 'tool', 'pattern', 'ideas' that respect and enforce this holy principle deserve a cookie 🍪

Cheers ! 🍻

@selbekk

This comment has been minimized.

Copy link

selbekk commented Dec 14, 2018

@phun-ky hey, sign me up for one of those jack and cokes! 🍸

The "if you use CSS-in-JS, you don't know CSS" argument is one i disagree with strongly. I love CSS, I fully dig the cascade and have done several talks on the subject - but I'm in love with CSS-in-JS for the many reasons @threepointone outlines above. It doesn't take away any of the CSS goodness we all love - but it solves a ton of inherent challenges with CSS.

You know the very best part about css-in-js for me? When I delete a component, I delete the styles as well! No more "not sure if this is used somewhere" css comments!

@rtorr

This comment has been minimized.

Copy link

rtorr commented Dec 14, 2018

Great post, Sunil!

@joshwcomeau

This comment has been minimized.

Copy link

joshwcomeau commented Dec 14, 2018

"Things like that (CSS into JS/ typed JS/Elm) are done because the developers are not competent enough in their field, and instead of learning about it, they try to create their own solution for it".

I vote for this statement to be nominated for Worst Sentiment of 2018

@AndrewIngram

This comment has been minimized.

Copy link

AndrewIngram commented Dec 14, 2018

@joshwcomeau right?

I'd love it if we can avoid conflating preferred development paradigms with competence. Until 2-3 years ago I used to be a staunch advocate of object-oriented programming, but now I prefer functional. Until 5 years ago I was the guy who would clean up everyone else's CSS to keep it lean and maintainable, now I have a moderate preference for CSS-in-JS. These changing preferences are response to repeatedly hitting certain problems and rather than just looking for painkiller solutions, looking for the shifts in thinking that make the problems disappear entirely.

Everyone will go through a different journey just because of differing experiences and different ways of thinking about things. If you've reached a different conclusion to me, I might struggle to understanding your perspective, but it'd be incredibly insulting of me to assume it's just because you don't know as much or aren't as intelligent as me. We need to have these discussions in good faith.

@wmsbill

This comment has been minimized.

Copy link

wmsbill commented Dec 14, 2018

I've never read/heard anything so concise and clear about the topic.
Congrats for the time spent to explain this tough topic that triggers so much passion on both sides in such a nice/friendly way.
I think reading this article is a good starting point for team discussion regarding the adoption of this lib/practice.

@pluma

This comment has been minimized.

Copy link

pluma commented Dec 14, 2018

I too have been making websites since the mid-90s (long enough to remember being excited about the CSS Zen Garden). I've also used SMACSS. These days I identify as a "full-stack" developer but that doesn't mean I don't know what I'm doing (I just had to ping-pong all over the stack throughout my career). I'd like to make the case that the main benefit of CSS-in-JS isn't actually technical, it's social.

Today CSS-in-JS no longer means manipulating inline styles, it mostly means generating stylesheets dynamically and colocating CSS rules with JS components. Depending on the flavor this may indeed mean plain JS objects with camel-cased properties, or it may mean actual CSS code (with nested selectors) in JS template literals. In many ways, CSS-in-JS is an evolution of patterns like SMACSS or BEM for interactive components (i.e. components defined in terms of both of CSS styling and JS behavior).

The big problem with SMACSS and BEM is that they both still require humans to come up with globally unique names. This means generic component names like "Button" or "Header" quickly become misleading in any but the most cookie-cutter designs. What may start out as "the Button" may soon become just "the button we use on the profile page", as opposed to "the new button we now use everywhere else" (but not on the profile page because that button needs to look a certain way for arbitrary business reasons).

The obvious solution is to rename the old component into something more specific so the new component can have the more generic name but that requires a global rename across CSS, HTML and JS (plus templates and possibly across multiple projects) and this can easily go wrong in many ways (not to mention having to communicate this change across possibly multiple teams).

The alternative is "just picking better names" but the actual use of a component may change over time and it may not always make sense to reuse the existing component even when the new component really looks like it's just a variant of the old one.

When developing the JS side of components it really doesn't matter what the components are called and that's a good thing: this makes it possible to refactor JS code to merge convergent components or split divergent variations of one component into separate components, the implementation of the component is mostly an implementation detail.

Alternatively you can use a system like SMACSS alongside a JS component library and just map the component names between the JS/DOM and CSS side but aside from the overhead of always having to look in multiple places this still requires manually naming things and avoiding global naming conflicts.

Even if we disregard the problem of having a single global namespace (which btw even Web Components seem to be beginning to work around lately by letting the user decide what name to register a component with) naming things is hard. Having a human come up with nonsense like "mediabox_widget_inner_wrapper" (or overly specific rules like .mediabox > div > div > div that make the component's inner structure a nightmare to refactor) doesn't provide any benefit compared to just having a CSS-in-JS library call it "MediaBox_a23bef6" (especially if some of the people who have to name things are non-native speakers and may struggle coming up with yet another synonym for the word "wrapper").

Before someone points it out: yes, CSS modules solve some of the same problems. There are technical differences (e.g. without being able to rely on CSS custom properties it's easier to share variables in CSS-in-JS than when using CSS modules) but for most intents and purposes I think it's valid to think of CSS modules as a special case of CSS-in-JS (that it uses a separate CSS file rather than colocating the CSS rules in the JS file is an implementation detail).

@itzikbenh

This comment has been minimized.

Copy link

itzikbenh commented Dec 14, 2018

relying on selectors to enforce those architectures are hard in a global namespace because there is always a chance of a clash; if not now

And this is where a tool like TailwindCSS comes in handy.
The utility classes approach brings the benefits of both, with zero of the downsides.

@mmmeff

This comment has been minimized.

Copy link

mmmeff commented Dec 14, 2018

Awesome summary, thanks so much for writing this.

@w-biggs

This comment has been minimized.

Copy link

w-biggs commented Dec 14, 2018

Yeah, I feel like it has very little to do with JavaScript as a language or anything like that - it's the compartmentalization of styling that makes it easier to maintain. Using something like BEM solves the same problem, and IMO it's a matter of preference whether you prefer a CSS methodology, CSS-in-JS, CSS Modules, etc.

@imrvelj

This comment has been minimized.

Copy link

imrvelj commented Dec 14, 2018

Gold!

@morewry

This comment has been minimized.

Copy link

morewry commented Dec 14, 2018

Good job on this!

@bengfarrell

This comment has been minimized.

Copy link

bengfarrell commented Dec 14, 2018

Thanks for the amazing write-up! I've been eyeing CSS-in-JS myself, but not sure if it solves problems for my specific needs. I've been working with Web Components and the Shadow DOM. Both seem to solve the same major problem if I read this right: CSS encapsulation. I'd love to know, for Web Components, if people see a use case for CSS-in-JS. I think making some sort of CSS-in-JS design system would make it more friendly to use as everything is a module and importable, but beyond that I don't know that theres anything compelling for Web Component users. I'm only making this statement to ask if I'm wrong, I'd love to hear any experiences!

@everdimension

This comment has been minimized.

Copy link

everdimension commented Dec 15, 2018

Hello @threepointone! Thank you for your valuable write up.

I always feel a bit surprised and uneasy when I see arguments for css-in-js that are about selector scoping. This problem has been long solved by CSS Modules

Your write up can be divided into 3 points:

  1. selector namespace
  2. analysis of selector usage (and extraction of critical css)
  3. theming

1 and 2 are solved by CSS Modules in pretty much the same way as css-in-js solutions.

So should we consider CSS Modules to be falling into css-in-js category? 🤔
May be! In any case I feel that this approach is not mentioned enough in these discussions.

To me it feels that writing completely valid css is a very strong advantage. And since we have a solution that solves basically all issues that you think css-in-js is good for, then why not use it?
The problem with most css-in-js approaches is that they make up new syntax. Most commonly for pseudoelements, but for other cases, too.

When I'm writing CSS Modules, I'm confident in my css. I'm confident about the syntax I'm writing. And when I'm not, I don't have to go to some library's documentation to check for an edge case, I can just consult CSS spec (or stackoverflow for that matter, the point is that my search would be library-agnostic).


As for css variables (aka css custom properties), I feel that their recent lack of support is one of the true reasons for popularity of css-in-js solutions. But I do not think this argument is valid anymore. People support IE11 less and less, people use other new css features such as grids more and more, and the support is kinda great. Many developers do not need to polyfill css variables already and less and less will have the need further on.


To sum it up, I feel that discussions for css-in-js solutions often "steal" the arguments from css modules and make it seem as if the only solution to avoid global selector names collision is to write quirky css strings inside our JS files. It might not always be quirky and there are definitely some good libs out there, but I want people to be aware that the problem of "global selectors" has been solved long ago and it's possible to enjoy writing pure css in separate files (colocated with your components) without any custom syntax made up by a library.

@threepointone

This comment has been minimized.

Copy link
Owner Author

threepointone commented Dec 15, 2018

I consider css modules to be well and truly css in js. (Fun fact - one of the creators of css modules, glen, is also one of the creators of styled-components). It matches some key characteristics -

  • rewrites selectors to be scoped
  • usually inlined into js bundles (especially when using import()s
  • exposes/exports a js interface/keys

but it doesn’t solve “all” the things. Specifically, the class=“a b” problem, particularly when the stylesheets are loaded asynchronously. If your setup doesn’t have this constraint, you’re gold.

As for css vars - I use india as a benchmark for whether a feature is ready to be used. caniuse has it at <80%, which disqualifies it for me (and also why I even worked on correctly polyfilling it) further, you can only use css vars for property values, and not whole rule sets. There was a proposal to do so (@apply, I mention it in the linked gist), but that’s dead now.

@ahmadawais

This comment has been minimized.

Copy link

ahmadawais commented Dec 16, 2018

That's a great write-up, Sunil! Will be referring people to this gist for when we have the same discussion. 👍

@catamphetamine

This comment has been minimized.

Copy link

catamphetamine commented Dec 16, 2018

No, CSS is better than the so-called "CSS-in-JS".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.