Why I Write CSS in JavaScript
mxstbr.com
mxstbr.com
You have to carefully name your variables using complicated conventions in order to avoid any conflicts or unexpected behavior.
Even your functions don't have local variables; there is no lexical scoping. Any function can overwrite or mutate what any other function is doing inside. There is no real encapsulation.
After you've done this perfectly, your application gets bigger and you start wanting to reuse third-party modules made by other developers, because they've already solved some of your problems.
Uh-oh: they didn't follow exactly the same conventions, and now things conflict. Or their variable names were perfectly unique in their own code base but not when combined with yours (or when combined with several other third-party modules).
This language is CSS and is why people are so fed up with selectors. Even "scoped" selectors really aren't lexically scoped in the sense that other languages handle scoping... it's still just another naming convention hack.
I use CSS-in-JS because CSS wasn't designed to be a language that survives attempts at modularization, third-party code sharing, etc. No sane programming language attempting this would only have global variables. So, we throw out the selectors part of CSS and keep the rest, at least for now.
Nobody using CSS-in-JS thinks this is an ideal situation. Nobody wakes up thinking, "oh my god, I need to put my CSS in my JS, it would be perfect." We're just trying to deal with the hand that's been dealt.
It has different functionalities.
I live in the real world where it's used almost exclusively by programmers.
Whether technically it's a programming language or not is tangential.
It's off brand, but the technology is roughly the same.
Another game: https://thenextweb.com/dd/2018/05/08/some-madman-built-a-mar...
This just is not true. It may be your opinion, but it is objectively false, particularly in the context of this discussion.
But a limitation isn’t necessarily bad, and is often good. Finding the language of least power (ie it is just powerful enough to express the sorts of things you want, and no more) is very useful for safety and productivity, but in CSS’s case it seems like it’s no longer powerful enough to express all the things its users desire.
SQL is one, HTML too.
They both tell a computer what to do.
> Imagine a programming language that only had global variables. You have to carefully name your variables using complicated conventions in order to avoid any conflicts or unexpected behavior. Even your functions don't have local variables; there is no lexical scoping. Any function can overwrite or mutate what any other function is doing inside. There is no real encapsulation.
That simply isn't true; CSS specifies a bunch of declarative rules, which statically evaluate. Though the evaluator may be stateful, reasoning about how the properties for each document node are determined doesn't require state. It requires reasoning about the targets of the rules, and when they overlap, about their relative scopes and priorities.
But it turns out that we're using it and CSS and JavaScript to build full blown applications. So, because we're trying to solve problems with applications rather than documents, we have to map the problems of application programming onto it.
So, to build an application, we run into problems CSS wasn't designed to solve (being a not-programming language.) Whether it is or isn't a programming language is irrelevant - to solve our problems (writing applications), we have to treat it as if it is.
<StackPanel>
<StackPanel.Resources>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="Red" />
</Style>
</StackPanel.Resources>
<TextBlock Text="Red" />
<StackPanel>
<StackPanel.Resources>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="Green" />
</Style>
</StackPanel.Resources>
<TextBlock Text="Green because the default style was reset by the nested container" />
</StackPanel>
<TextBlock Text="Red again because the nested container scope has ended" />
</StackPanel>I’ve wrote the above example because it shows a possible solution to the stated problem, i.e. how to introduce namespaces for styles without also introducing a full-blown programming language with scopes and variables. The exact syntax can be replaced making it more compact and easier to read.
Compared to this mostly-immutable scoped dictionaries, way more things can potentially go wrong with code, regardless on the programming language.
<Box
p={5}
fontSize={4}
width={[ 1, 1, 1/2 ]}
color='white'
bg='magenta'
>
Box
</Box>
It uses a CSS-in-JS library under the hood to translate that into a CSS class for each unique combination of styling props.Very typically, for a given app and page the styles of individual elements and components are not independent. They also depend the app theme, app section, layout area, component group, etc. The element fits the component. The component fits into the section, the section fits into the page. The specific relationships in a particular case are different of course. Styles are visual and the idea that a component on a page has nothing visually to do with the rest of the page is wrong.
The typical scoping rules of programming languages doesn't fit that well here.
To the extent some styling solution encourages you to treat styles that are dependent as independent, that's not a good or useful thing.
(You can also relatively easily implement your own, because JS :))
My point is, when it comes to visual styling, you inherently have a shared visual context.
The post I was replying to was concerned with the "global" nature of CSS.
I'm pointing out that this is inherent to the problem space. You have to deal with it whether you use CSS, code or any other mechanism to style.
Angular implements a polyfill for this. Every angular component has 3 files html, ts, (s)css, and the styles in the components css file are automatically scoped to just the component they apply to. It takes away all the stress of global css selectors, and if you want to share styles you can just have a library of shared styles and import them via scss. It's honestly heavenly and I haven't thought about conflicting selectors in years.
Emacs lisp also has lexical scoping. It optionally had it for a long time using cl.el with lexical-let and friends. It now has it as a file-local variable read by the compiler/interpreter.
I also don’t understand what you’ve written at all. You say “Nowadays it does support semantic scoping” but semantic scoping isn’t really a thing (as you next write lexical scoping, I assume you mean dynamic scoping), but the words “nowadays,” “but,” and “still” imply that you are talking about the new kind of scoping (ie lexical) and comparing with the old (dynamic, which you cal lexical???).
Does the annoyance primarily center around the fact that styles cascade to child elements? Hard to tell from the article.
Let's say you wanted to pull in a button from Bootstrap for one part of your page, a button from Material UI elsewhere, a button from Semantic UI elsewhere, etc.
These things are made to be reusable right?
What are the chances you can do that without issues? Do you think any of their style rules/selectors would clobber each other? What about the base/assumed CSS that they all most likely require? What about components more complicated than a button? Without actually trying it, how much confidence do you have in your guess, vs. your assumptions about importing other things (like functions from npm modules)?
Good software is reusable without worry.
I just spent the whole day trying to fix compatibility issues between different versions of npm libraries transitively imported in a project. Reusability in JS is a joke.
I've had this problem with JavaScript-before-npm-existed, Python, apt, Homebrew, Portage...
nix package manager
The problem:
I use X to create an object with library A v1.0.
I use Y to create an object with library A v2.0.
These objects are not necessarily compatible, so X and Y have problems communicating.
I would be very surprised if you told me nix or stack inherently make objects from multiple versions of the same library magically compatible with each other.
Feel free to close issues like this one if I'm wrong: https://github.com/NixOS/nixpkgs/issues/30551
How would objects created from X and Y can reach each other and communicate when X is not dependent from Y or otherwise? You need global mutable state or your own code witch depends from both X and Y is somehow connecting them.
So one top thing FP movement argues against is global mutable state so of course you don't have that ...Unless you are wrapping some non-FP thing like QT...
And if it's your own code it shouldn't be different that Y creates B:s instead of A v2 since your code is aware from two different versions anyway.
Do you see any weak points?
As a primarily JS/React developer, I think there is irony if JS is held as a good design pattern in comparison to CSS with respect to reusability. Inasmuch as what are being called CSS "workarounds" are in fact workarounds, so too has been the case with JavaScript itself. I am old enough to remember jQuery and Prototype colliding over the dollarsign namespace.
Don't get me wrong. I am not a JS (or CSS) hater. I think these "workarounds" are more than adequate in making these technologies easy to work with. Which I suppose is sort of my point. I see a lot of overengineered codebases with Byzantine solutions to relatively simple problems.
Also, to parent poster:
> Let's say you wanted to pull in a button from Bootstrap for one part of your page, a button from Material UI elsewhere, a button from Semantic UI elsewhere, etc.
1) First of all, don't do this. Perhaps you were just saying this to make a point, but obviously don't include 3 monolothic libs with overlapping functionality in order to implement 3 things with the same functionality.
2) It sounds like your problem with this is more with the libraries themselves. When I write a component, I always namespace it myself. Obviously, if libraries are properly namespaced, then you could easily include buttons from three separate button libraries.
I personally never ever ran into any problem, when I myself named CSS classes and that is because I automatically think about the future and whether a name could conflict with something else, instead of being lazy and typing 3 characters and thinking I did my job. I am simply careful and use prefixes and give unique names, so that it has such a low chance of someone else using that CSS class name, that it simply never happened in practice. So from my personal experience all that mumbo-jumbo about CSS name collisions comes from people not being careful and is a "house made" problem, not a problem of CSS. I never had any need to use JS to do what is CSS's job.
Using JS for doing something CSS should do is furthermore dangerous, since clever people deactivate their JS or block it partially. For me personally, your (not you personally, but you in general, anyone who reads this) JS better comes from your own domain, or it will be blocked by default. If you can live with people seeing your website utterly destroyed by its inability to apply style, then OK, do the styling in JS.
Otherwise I do think that the naming collision problem is really easily fixed.
SASS seems to solve most of the problems i would have with writing basic CSS.
But I've also been writing CSS since 1997. I've deployed CSS to sites like walmart.com, nfl.com, mlb.com, starbucks.com, twitch.tv, etc. Some of these sites have a lot of third-party widgets on them. I can confidently say that the (multiple) issues with CSS are not a "just do <simple thing X>" type of problem.
I'd go so far to say that almost no (real) problem is that type of problem. Maybe problems that are mostly inconsequential, sure.
1- append only? No, each module/feature/widget gets its own CSS file, imported to the main app CSS file. Delete the widget? Delete the file.
2- Specificity issues? Never use IDs, stop using tag names and simple classes and adopt a simple naming structure (doesn't have to be BEM, it could literally be ".widgetName-header")
Since I adopted those 2, I've almost never had dead CSS from deleting features or collisions in my CSS. I do still have a small base set of styles that are shared but it's small enough to be easily manageable.
3- dynamic properties? CSS variables. Not an option? Child classes that apply the dynamic styles serve most use cases. Truly dynamic styles are rare, in my experience.
I'm still keeping an open mind about the possibilities, but my strong opinion (weakly held) is that I still don't see CSS-in-JS as more of a bandaid for people that like CSS and (for many, not all) those that don't have an in-depth understanding of it.
Each component has its own folder with its own css file and each rule starts with .name-of-the-component.
For dynamic properties, we either create conditional classes (e.g. 'success', 'error') or pass the rules through 'style' (e.g. dynamic height).
For dynamic themes, we create an object at the root of the app
const theme = { header: { backgroundColor: black }, button: { backgroundColor: black }, ... }
Then pass it down to the related components <header className="default-header" style={theme && theme.header}>
<button className="default-button" style={theme && theme.button}>
That being said, I tried styled-components once and don't really have negatives about it. It's a viable option.This rarely happens. I would argue that creating an automated system to enforce "rules" should be favored over forcing every developer to know the rules and abides by them.
It is much easier to understand the relationship between css and js when the css is just a js variable.
Having built many large applications using the CSS file method, css modules, each widget gets its own file, co-locating css files with the js, I much prefer using `styled-components`. It provides a ton of flexibility, it opens doors for dynamic css by leveraging js (the hacks I've seen, had to write to get css dynamic is astounding), and I don't have to look very far to see how it's being used because the CSS is literally attached to the component I'm using. All I do in VSCode is cmd+click and bam, I can see the CSS for this component. There's no need for naming convention, there's no worry about global css variables getting into your styles, and there's no digging around looking for css that might be influencing your component. The best part for me is: when I delete the component, the CSS is gone as well. There's no thought put into scouring the FS to find lingering CSS that used to be used.
I love `styled-components` and most of the designers I work with also enjoy it.
Judge all you want, but you can only change technology, not human nature.
Good engineering is to find pragmatic solutions that work in the real world.
It's hard to know when you can delete a rule. Are you positive that all the rules in your component's CSS file only affect that component? Then great, delete the whole file.
What about the individual rules within that file? How do you know whether your app is still rendering any components with the `fancy` modifier anymore? Keep in mind any JS on the page could be be dynamically toggling the `fancy` class, including in very rare circumstances, including from dynamic data that might bring the string "fancy" with it so that you can't just grep your codebase for it. Is it safe to delete the `fancy` rule?
Another problem is that even BEM admits that sometimes nested selectors are necessary. It's right there in the BEM FAQ: "While in general BEM recommends avoiding nested selectors, in this case they are reasonable."
Sometimes you need your button to look a certain way when it's inside a nav menu item.
So what's the solution? Either a nested selector (targeting both the nav item and the button) or a new modifier on your button, like `is-in-nav-item`.
Eventually your app changes and you're not rendering any buttons in nav items anymore. Maybe nav items don't even exist anymore and you at least remembered to delete those. Are you also going to remember all the places in your code you need to track down to delete these obsolete nested selectors/modifiers? Or is there just going to be dead code now?
What if the selector is slightly more general, and you're not sure if it's targeting other things as well? Are you confident that you won't break anything else? CSS selectors are effectively global. Anything could be secretly relying on the styles that a selector applies.
That is the problem. Not deleting a file. Your individual component CSS files are now the "append-only" stylesheets, not necessarily the whole combined stylesheet.
Absolutely
The beauty of CSS modules over a convention-based approach like BEM is that you end up with cleaner, simpler CSS files -- with selectors like `.Widget .search.error` rather than `.Widget__search--error`. The downside is that there's a compilation step, but chances are that you already have _some_ sort of compilation going on -- even if just to bundle things up.
The way to handle "special" cases for buttons -- like when they appear in certain contexts -- is to allow the calling code to add a class to the button; a class which comes from the caller's own CSS module. Alternatively, use CSS variables:
Button.css
.Button {
background: var(--btn-background, turquoise);
color: var(--btn-foreground, octarine);
}
Nav.css .Nav {
--btn-background: fuchsia;
--btn-foreground: lime;
}I have one exception to that in my own work, which is to only ever use IDs for styles that affect the layout of a block/component on a specific page. This makes sense since parts of a page layout are usually very specific and not that dynamic(in which case class names might be more appropriate). At the same time, component-level styles should never make assumptions about how they are positioned on a given page.
Let's say that we have a page `/blog/:entry_id`, and this page has a right-hand column to list related blog entries. This component would have an id `#blog-entry__entries-list`, which would control how the related-entries list appears on the blog-entry page(in this case, putting it in a column on the right), and a `.entries-list` class that controls the global appearance of the component.
This way, the entries-list can be used in different contexts without having weirdness caused by a margin working fine on one page but having to be negated on another page where the margin doesn't work. Try to add styles, but do as minimal overriding as possible.
In combination with BEM, primarily for the benefit of all selectors having the same specificity, I've found this separation of concerns between IDs and class names to be very powerful and simple to understand, especially since it doesn't require more verbosity.
Another part of my styling workflow is Sass mixins.
Let's say that we want the entries-list component to appear more compact on another page. Perhaps it appears somewhere on the site index, and takes up a little more room than we'd like.
Since component styles can't assume anything about their environment, and since layout styles(i.e. our ID selectors) can't know about the inner workings of their components, there needs to be a way for layouts to have a choice of component variations.
In this case, I would create a mixin `entries-list--compact` inside the entries-list Sass file, which would contain CSS properties that would make the entries-list smaller.
Back on in the index Sass file, I would include that mixin:
```
#index__entries-list {
@include entries-list--compact;
}
```But what if we only need the list to be compact at a specific page width?
```
@media (max-width: 640px){
#index__entries-list {
@include entries-list--compact;
}
}
```I've found this pattern of mixins to be very powerful. Any page can decide whether to use any mixin at any page-width. CSS for layout stays very lean, as the only properties it's concerned with 99% of the time is width, height, margin, and padding. A component can be completely replaced with new styles and behave properly on existing pages so long as it conforms to its existing "API" of mixins.
It addresses some of these points at 35:23 - that you can kind of make CSS work if you always do everything right, but it's not as easy in practice (most of the experience stuff is at the beginning - which shows that they did kinda do everything "right").
and suddenly
> I still don't see CSS-in-JS as more of a bandaid for people that like CSS
Why are SCSS or poor-man's-substitute-for-modularity in the form of naming conventions fine, and why JS-in-CSS is bad?
Personally I much prefer using separate css (or sass) file, one for each component (assuming you also have a "layout" component for shared styles) and import each relevant css file as a module.
This way you keep writing our CSS out of your JS, but still can keep it scoped to your component. It's also obvious what css a component uses, and each to grep for when you want to delete some css.
There are quite a few reasons for preferring to not use IDs, here's two:
1- specificity of IDs always trumping things
2- having to change it from an ID to a class later on if it becomes a reusable item (which in a widget, is almost always the case). There is no reason a class cannot apply to just a single item, where an ID is by its nature limited.
1. and 2. attempt to address the lack of modularity in CSS through convention when you can have guaranteed modularity through auto-generated CSS classes that are content-addressed and thereby globally unique by definition with CSS-in-JS. Not to mention you can use a real module system for sharing styles instead of being limited to sharing variables through some global context, which again opens up the possibility of name clashes.
3. recommends a solution for dynamic styles based on yet another CSS feature that relies on global namespacing, and then a fallback based on string concatenation that can't actually address all dynamic use cases. Then it goes on to dismiss the use case entirely, when in a single page application, all styles are inherently dynamic based on the state of the components that are currently being rendered, which is why there's such a painfully obvious impedance mismatch when implementing anything that's truly dynamic using CSS classes, when you have to resort again to concatenating lists of static classes together rather than mapping state to styles directly like you could with CSS-in-JS.
Of course, I'm not saying those approaches can't work at all, nobody is saying that. You can obviously make them work, and people have used those approaches for ages before CSS-in-JS came along. But using the existence of those approaches to dismiss a different approach that tangibly addresses the inherent flaws of those approaches doesn't feel like a particularly compelling argument.
My point with the examples I gave was just to show that it's possible to handle the issues the author wrote about. I'd prefer to see efforts to fix the issues in CSS instead of discarding it completely in favor of a JS solution.
I'm not saying I can specify how to "solve" CSS issue in a couple paragraphs. As regards #3, that's specific to the kinds of dynamic styles I find most often in web apps. Very rarely do I see styles generated dynamically where it isn't effectively a choice between a couple of options (in my experience, of course). And when you do, you can just write out styles from the JS into the template (and I've definitely done fully dynamic styles in that way when it is necessary). Dynamic styles would be a place where I could see a fit for CSS-in-JS, btw.
The global namespacing this is something I believe is being worked on in the CSS Modules specification, but again, that is really easily solved. I can say loading external libraries can mess that up (i.e. Bootstrap and the ilk).
I don't care what people use, I'm glad the argument is what the author (who co-wrote the styled components lib) was what he liked. But I am really concerned that some proponents promote their way as the only way forward. Personally, I haven't found a situation yet where CSS-in-JS would materially benefit the application over a careful, convention-based approach. And I want to make sure people know there are other options that work.
Why don't we have more : "We tried it, it is the worst, especially for products which lifespan is more than two months"
Because that last sentence is just rubbish, CSS in JS is great. I'm using it for large projects already 4 years. If your experience is bad you must be doing something wrong. CSS in JS is almost the same as working with (S)CSS, except for extra power to control dynamic elements without having to juggle with classnames.
Or perhaps there's something to it, and the idea is not as good as one might claim it to be.
Scss is like css.
Jss is css + unnecessary abstraction on top of css + unnecessary memory and cpu cycles + writing part of css rules inside flow control (which can turn into js in css in js.
GPU facing languages have always been ugly and not just in this industry. But they are like that because of their nature. Making them friendlier is always welcome, but replacing them with cpu stuff defeats the whole purpose of a gpu.
I completely disagree.
Time and effort creating frontend design systems, will result in a set of CSS files .. but will also enable efficiency, flexibility, accessibility, maintainability and extensibility. These qualities just aren't going to be as available if you use CSS in JS.
CSS In JS is mostly using similar structure, but often the JS dom interface naming... so fontFamily instead of font-family. It's also far easier to introduce codified variant functions or variable injection in JS than it is in SCSS.
I'm not saying don't use SCSS if it fits your model better... I've been very happy using react-jss via material-ui ... I've extended the baseline theme and that gets used throughout the application with each component defining its' styles that get injected.
Sharing can be a little more awkward, but it's easy enough, just call the same style method(s), or extend the baseline theme that gets injected.
The only thing you mention that might be better with (S)CSS directly would be efficiency and even that's relative. You can absolutely do all the rest with CSS in JS.
If you're building a frontend system, you need to consider the overall (macro) view, as well as the detail oriented (micro) view.
It's very difficult to do this with CSS in JS because of the compromises that need to be made to work in this way.
I love PostCSS, and I'll happily consider CSS-in-JS again when somebody writes a library that doesn't try to reinvent the wheel.
For one thing, it's not CSS in Javascript... there's no cascade and no sheet, so it's just "styles in Javascript". (And, actually, this article is only about a specific implementation.)
The benefits listed are:
- "confidence" - but I think that's a matter of understanding and mastering the tool, whether it's CSS or this library. As the co-creator of "styled-components" the author is no doubt a master of it. For for the general developer, perhaps a roughly equal effort would provide a similar level of mastery in each. So why not invest in the standard?
"Painless Maintenance" - I would have to use it deeply before I could say too much about this. But I would guess that the danger of creating spaghetti code and a spaghetti of .css are pretty much the same and have the same root causes.
"Enhanced Teamwork" - I don't see the connection here. The discussion in this section seems to be about mastery of the tools again. The team can learn how to manage styles though CSS-based techniques or they can learn through techniques based on coding Javascript and the capabilities of this library.
Not that CSS doesn't have its issues. IMO, it's too low-level to use directly for many cases. There are two main problems: (1) missing the tools to manage complexity, like scoping, variables, and modules. (2) allows you a lot of freedom to do bad things. But there are libraries that address these that don't throw out the fundamental design of CSS, as styles-in-code does.
To me it seems like a better approach to build on CSS to address its weaknesses rather than throw it out.
The design of CSS itself came out the recognition that the tight coupling of style and content has drawbacks, and this is signing back up for those drawbacks. And you better neatly divide your presentation code from your business logic code and data access code or you'll get all that coupled tightly as well.
I'm not dismissing this out-of-hand -- I haven't dug into styles-in-code approach so I don't have a deep understanding of it.
But I'm not convinced it's worth the time to look more closely at it. Really, not even close.
But as you pointed out, there is no cascade and no sheet. The styles apparently just apply to the stuff they are near in the code. No twisted logic to unwind, hence no spaghetti.
The potential problem I could see is that changes which would have been easy with a cascade would require lots of changes to copypasta in this approach. Like, "take the spinach out of this ravioli and replace it with mushrooms." Everything is scoped properly and the task is obvious at a glance-- it's just annoying.
Why does copying CSS multiple times (once for each component) not also create spaghetti code?
Many elements share style in a normal webpage. Font, for example. Having to specify the font in every file seems crazily redundant.
And if you separate that out into a separate file that gets included in many places, you lose all the other benefits like "painless maintenance".
Ah, well, things are not that simple.
In a project of any complexity at all, the style of any individual element is typically dependent on a number of things which collectively make up the context of its presentation. E.g, the site theme, the component, and the individual element (at least -- usually there's an app-subsection and components are usually grouped into modules, etc.).
Note that this is inherent to the problem space and is true whether you're specifying styles in code or styles in cascading style sheets or any other way.
CSS provides a basic mechanism for dealing with this -- the cascade (though as I mentioned, IMO it is lacking some necessary features). Styles in code needs an answer as well.
If you were to simply hard-code styles at the element-level (which you can do using either mechanism BTW though its probably easier with style-in-code), you'll have a hard time finding all the places in the code you need to update if you want to make a change. You'll be doing a lot of full code searches, reading code, and trying to put yourself into the mind of the person who wrote the code in the first place.
The "spaghetti" in the metaphor are the lines of dependencies. We have spaghetti code when those lines are disorganized, entangled with irrelevancies, obscured, and otherwise unnecessarily difficult to deal with.
In fact, if you hard-code a style on an element that is dependent on the site theme, app section, component module, and component, you'd be hiding those dependencies, which creates spaghetti code rather than avoids it.
Now, I expect most developers would naturally try to avoid doing something like that. I'm just trying to point out why style-in-code doesn't really help you avoid spaghetti code.
Look at this beauty for comparison: https://vuejs.org/v2/guide/single-file-components.html
https://vue-loader.vuejs.org/guide/scoped-css.html#scoped-cs...
Inspect the result here: https://codesandbox.io/s/3v0pjzoy7q
What language are you talking about? React is Javascript
> Look at this beauty for comparison
Meanwhile Vue is neither HTML [1], nor strictly speaking CSS [1], and has a very wierd concept of how objects work in JS.
[1] It's a templating system with its own rules and mini-DSLs that ends up being compiled into JS.
What is so weird about it? I personally find Vue vastly more approachable than React is, especially once you have to start writing all the plumbing to get all the libraries you need to go along with it to write a real world app.
> ] It's a templating system with its own rules and mini-DSLs that ends up being compiled into JS.
Vue has the option to use Webpack to compile html compliant templates to JS functions just like JSX (which BTW is not JavaScript and not HTML compliant and is also a separate concept that needs to be learned as well.)
CSS is just another add-on through Webpack when using Single File Components. Nothing really ground breaking here except for the fact that it creates a nice separation of concerns. This is especially true on teams with junior devs or designers. Or with a team that works on a site with mixed MPA and SPA features.
I go a bit in depth here: https://news.ycombinator.com/item?id=17471199
> Vue has the option to use Webpack to compile html compliant templates to JS functions just like JSX
Vue's templates are not HTML-compliant. They are just that: templates. That get compiled to JS anyway.
> JSX (which BTW is not JavaScript and not HTML compliant and is also a separate concept that needs to be learned as well.)
JSX is a very thin DSL on top of Javascript. It's just function calls: https://reactjs.org/docs/react-without-jsx.html
Once you understand that, everything falls into place.
> CSS is just another add-on through Webpack when using Single File Components.
It's not a webpack add-on. It's a part of Vue's templating engine that is essentially CSS-in-JS, they just give you a template to work in.
I've read this before. Vue is a framework. It also has a templating system that is not JavaScript, it's valid HTML with attributes that tell Vue how to bind to it. It's really not that difficult.
> Vue's templates are not HTML-compliant. They are just that: templates. That get compiled to JS anyway.
Sorry, but you're wrong here. They are valid HTML.
https://vuejs.org/v2/guide/syntax.html
"Vue.js uses an HTML-based template syntax that allows you to declaratively bind the rendered DOM to the underlying Vue instance’s data. All Vue.js templates are valid HTML that can be parsed by spec-compliant browsers and HTML parsers."
> JSX is a very thin DSL on top of Javascript. It's just function calls: https://reactjs.org/docs/react-without-jsx.html
IMHO only the simplest of components featuring JSX that don't read like a spaghetti PHP coding nightmare to me. For the small number of times you actually need the full power of JavaScript you could also use JSX in Vue with a render function if necessary. I personally prefer to keep my templates and logic separate when possible.
> It's not a webpack add-on. It's a part of Vue's templating engine that is essentially CSS-in-JS, they just give you a template to work in.
No, it's not part of the template engine, it's part of vue-loader which is used by Webpack to create single file components.
You mean it has at least three JS-like DSLs (which are not JS, and have different rules depending on which context they are in). In addition it has a very weird binding system which I described at length in the linked comment.
Yup. "Not that difficult".
Meanwhile JSX is a single consistent DSL that directly maps to function calls:
<Tag prop=(string or valid Javascript expression)>
other tags, strings, or valid Javascript expressions
</Tag>
That's it. Even "html-like" attributes that people seem to not like... are exactly what HTML attributes are in actual Javascript DOM APIs [1] [2]The amount of custom attributes, custom DSLs and gotchas just in the "HTML" part of Vue is staggering in comparison: https://pbs.twimg.com/media/DbVEoKOX0AEEen6?format=jpg&name=...
And funnily enough it ends up being compiled to what React is in the first place (here's the output of a Hello World app in Vue):
var render = function() {
var _vm = this
var _h = _vm.$createElement
var _c = _vm._self._c || _h
return _c(
"div",
{ attrs: { id: "app" } },
[
_c("img", {
attrs: { alt: "Vue logo", src: require("./assets/logo.png") }
}),
_c("HelloWorld", { attrs: { msg: "Welcome to Your Vue.js App" } })
],
1
)
}
Look, it's exactly what React without JSX is [3]. Only in React you don't need to invent a custom scripting language and binding rules.[1] https://developer.mozilla.org/en-US/docs/Web/API/Element/att...
[2] And I'm really sad to see React team seriously discuss caving in to mob and considering replacing className with class etc.
The attributes / DOM properties in JSX are also a mixed bag and not consistent. See https://github.com/facebook/react/issues/13525#issuecomment-...
> Look, it's exactly what React without JSX is [3]. Only in React you don't need to invent a custom scripting language and binding rules.
JSX is a custom extension to JavaScript. It's not part of JavaScript or intended to be part of the spec or implemented in browsers. In fact, you have to use some sort of transpiler in order to use it. So, it's not just JavaScript.
> https://pbs.twimg.com/media/DbVEoKOX0AEEen6?format=jpg&name=....
There's really nothing that complicated in here, if you're familiar with Vue. In fact, it's really easy for me to see what's going on at a glance in one place, which is my personal preference. From my own experience, that's rarely the case when working with React the way JSX tends to get broken up because it doesn't have something as simple as a standard if conditional statement within JSX itself.
I've never claimed you do not need to learn anything when working with Vue templates, you need to understand how the system works, but it took me all of a few hours to understand. The features and benefits that I prefer over React/JSX make that worth it to me.
Yes. However, one thing you fail to see is that it directly compiles to function calls.
<Tag a="b" x={y+z}>{valid JS expression}</Tag>
is React.createElement(Tag, { a: "b", x: y + z }, [<valid JS expression>])
Which means:- you have access to JS variable in scope
- you can use JS facilities (proper if/switch statements, for loops, functional programming, you name it)
- you need no additional scripting or templating features. It's, well, just Javascript.
However in Vue (emphasis mine):
> There's really nothing that complicated in here, if you're familiar with Vue.
Yes. Exactly. If you're familiar with Vue
So:
- custom tag attributes: v-bind, v-if, v-for, v-on, v-show, v-model... (I honestly don't know how long this list is)
- custom attribute shorthands: :key, @key
- attribute extensions: v-on:keypress.prevent
Inside attributes you can have:
- Javascript expressions (much like React inside {})
- or magic binding:
v-bind:href="url", v-if="seen" etc.
- or JS-object-like magic bindings which resolve to a string: v-bind:class="{ active: isActive, 'text-danger': hasError }"
- or JS-array-like magic binding that gets resolved to a string: v-bind:class="[activeClass, errorClass]"
- or a magic array with objects that gets resolved to a string: v-bind:class="[{ active: isActive }, errorClass]"
- or a separate DSL for v-for: v-for="(item, index) in items"
- or a separate DSL for v-for where you can bind entities from the DSL (which looks exactly like the magic binding to data from the component, what happens when they clash?): v-for="(item, index) in items" :key="items.id"
- or JS expressions that may be function references that may be things that look like function calls but actually aren't: v-on:click="counter += 1"
v-on:click="greet"
v-on:click="say('what')"
- with special magic variables v-on:click="warn('Form cannot be submitted yet.', $event)"
- with special magic modifiers v-on:click.stop.prevent="doThat"
"It's not that difficult" ©™ And the list above doesn't include the overview of the JS code that underlies components in view. It's another whirlwind of magic and non-magic: https://news.ycombinator.com/item?id=17471199Oh yes, and this gets compiled down to some JS that bears little to no resemblance to original code.
And somehow people complain about how React is difficult. Wat?
I understand how JSX works, stop patronizing me. The only thing I fail at is seeing how any of your examples are complicated or have magic so deep that you need to continue to burn Vue at the stake for. In fact, I find your Vue template examples completely intuitive and easy understand with minimal time invested in learning how Vue templates work. It's my preference to spend a little bit of effort up front learning how Vue templates work in order to have clearer separation between business logic and templates.
It's regular CSS in the templates and regular CSS in the frontend. There's no JS involved anywhere, unless you count the tooling, but we already use JS tools for CSS processing (PostCSS).
Scoped CSS is an old HTML5 feature that was dropped in favor of style encapsulation using Shadow DOM/Web Components.
It means you can't abuse JavaScript everywhere, creating totally unreadable render function, nest array mapping in Ternary operator and reduce it to what the hell no one can read and destroy all the readabilities.
For me, for you, for everyone, if you using that syntax, then it is simply readable just like html.
Less powerful, but the management definitly worth more.
The problem with all templating systems is that they start with "we need to limit the amount of logic". But all templating systems find out that this is extremely limiting. So they grow. And grow. And grow. And grow.
Same with Vue. The amount of special cases and custom things just grows and grows. See my comment here: https://news.ycombinator.com/item?id=19199423
You need simple conditional logic. So you end up with v-if and v-else. Then you have to add v-if-else.
You need loops. So you end up with a custom DSL for v-for. And then you expand it to also work on objects. And you need to bind to object produced inside the for loop. And...
And you need to handle events. But events are anything but simple. You need to prevent them, you need to stop or start propagation, you need access to their properties. So you end up with v-on:click.stop.prevent. And function calls. And magic variables in the form of $event.
Worse still, all your existing knowledge about how to apply these concepts in JS is void. Template has taken over.
So in the end you end up with a system that is not really HTML, not really JS, but a weird combination of the two. And the amount of concepts is not exactly more readable than plain React that just uses JS everywhere.
With its own syntax. Which looks like HTML but isn't.
It's actually an XML abstraction that closely resembles HTML + JS-DOM, and is then converted into function calls and arguments.
It's also about as close to E4X as an abstraction that I've seen and something I wish was cross browser over a decade ago when it was first introduced by Mozilla and Adobe, but that's a long side-track. In the end, it's very effective and works well imho. Yes there are some more complexity points in some places, but I don't think anything else does it better. Vue imho is comparible, but I don't like it as much and I feel that Angular is far worse in practice.
Vue Templates are a very thin DSL on top of HTML. It's just markup.
Once you understand that, everything falls into place.
Add in Tailwind utility classes and you've got a great design system that can be built on the fly with consistent standards.
Where is template getting assigned to the code inside the script tag?
Does OtherComponent somehow mysteriously map to other-component? How does one pass arguments to it? Is a .vue file actually HTML or is it parsed somehow to be HTML?
The thing I like about React is the explicitness of it. This is a source code file that exports component X. Component X uses style Y as defined above because it's explicitly passed in as a prop, etc etc.
If you want to use CSS-in-JS, you'll either have to use server-side rendering to inject CSP nonces in your index.htm [0], or set up a complex build system to extract the CSS from your JS so you can serve them as .css files.
Using server-side rendering to inject nonces into your single-page client side rendered webpage is such an ugly hack. I'd prefer the second solution but I have not been able to find a solution like that, nor do I have the time to dive into the webpack rabbit-hole to do so.
If style becomes out of fashion, you have to modify files that contain logic. If framework (react/vue) becomes out of fashion, keeping styles will be difficult.
The other down-side is thousands of web developers having to re-learn CSS and the tooling behind your app instead of just changing CSS files.
I think there’s truth to your second point though - reusing styles in parts of a project that does not use the same framework. Something like Tailwind CSS can be a good option then.
Full design yes, but branding/theming is much easier if you use an external styling framework like Bootstrap 4 or Foundation and use their component and utility classnames.
We have a 300+ page app and tried the one stylesheet per component thing with theme and layout all combined in the past, and was a huge mistake.
Banning most custom CSS in place of using the Bootstrap component and util classes has been so much easier.
So just put the styles in separate .js files and import them.
I guess the concern for longevity most devs have is amusing when most software, if its not like systems level stuff, or problems most devs dont want to solve, tends to get disposed pretty frequently.
By the way, most of the software that actually sticks around was written in C and generally solves a problem most people wont tackle. Like zlib or sqlite or nginx or the linux kernel. You can almost pretty much judge the longevity of a piece of software based on how hard the problem it tackles is. Like encryption or compression? Those libraries are going to be around forever, because how many coders do you know that can write a semi compitent encryption library? And how manu coders can write a templating engine?
Like most of web dev is throwaway code. It just is. Arguments based on like "will they be able to restyle it in 10 years?" Well are they even going to be around?
both of these are pretty bad examples, as several modern developments have surfaced in both cases - here are 2 examples
- https://wikipedia.org/wiki/Curve25519 - https://github.com/google/brotli
Better to have a cohesive set of theme/rules and get them to work together as best possible. I've been a big fan of material-ui and react-jss and is imho about the best option I've used in over two decades of working on web based applications.
People not taking the time to learn is not a reason to throw an entire building block of the web out.
(Yes, I realize there are a lot of people who know CSS and still choose CSS-in-JS, but I'm specifically referencing the point re: not learning CSS.)
My theory is that CSS was something that was treated as a task. Make a lot of client/server side development and then make these freaking lines align perfectly. So, most of time engineers were avoiding styles, applying them was the most tedious and hated part of the job.
And it is hard to be good at something you hate.
I don't claim to be an expert on the DOM, and I might be close to expert in JS itself... there's something to be said for knowing at least enough to know what is possible and what to look for BEFORE working around a problem and introducing a bunch of crap.
I am one of those that knows CSS well enough and still chooses JSS, as imho it's easier to coordinate a set of theme values and configuration across a project. IMHO react-jss and material-ui do things VERY right in terms of what works for not only components, but a larger project.
It's definitely a limitation of certain CSS-in-JS implementations, but unfortunately one that inherently comes from "using the platform."
The great thing about React is it is purely JS (or JSX, which is effectively a macro for the underlying JS). Which means it is explicit, and predictable. Perhaps that means there's less sugar, but any sugar and magic comes at a price.
I'm going to have a look at Vue more anyway, out of curiosity, though.
Edit: I note that some of the anger at React has been "Why does it need Redux/MobX if it is so good?". But Vue has Vuex to solve the same problem?
I'd rather have libraries focus on one thing and do it well - the Unix philosophy. This lets me compose the appropriate libraries for the problem at hand.
It reminds me of when I was in college learning C where all of your code is just dumped into one namespace, and then later learning Python (or basically any other modern language) with actual modules and namespaces. After seeing it done that way it just makes no sense that it would ever be done the old way again.
All of those solutions were a huge improvement and scaled better than any of the previous pure-CSS techniques I was using before.
BUT the key here is that those things only work when you rely on React/Vue/etc components. If you have have to rely on copy/pasting markup, things go wrong FAST and you lose track of your app style and things get duplicated. You also to have to enforce SMALL components. If you duplicate markup between components things get hard (but not as nasty as if you weren't using components).
I honestly believe that people not using modern frameworks can't see the advantages of any of those techniques, and would urge everyone to try them out in component-based project.
This basically frees you from making style decisions while coding, depending on your design system, things will just work well together as you create your components, with spacing, colors etc. all being already figured out.
This means that you don't "translate" pixel perfect designs, you use design tools like Sketch or Figma to find the rules and scales that you then translate into the style sheet/design system.
Instead of doing the "configuration" in your CSS when you're coding, you basically have an intermediate step where you set up those rules in advance, and can just focus on composition.
That's true! I find that enforcing a consistent and quickly-changing design system is way easier than when I was using old techniques.
> Instead of doing the "configuration" in your CSS when you're coding, you basically have an intermediate step where you set up those rules in advance, and can just focus on composition itself.
You summed it up perfectly
Want to show a title? Use `<h1>`, and style it with `h1 {color:palevioletred;font-size:18px;}`. Except hardcoding the font-size as pixels is another code smell, the `h1` is already bigger than the text body. Use `2em` if you really need things to stand out.
It's in the name, Cascading.
Of course not: CSS is a bad idea in 2019. In 2004 it solved some of the problems we have React to solve today, but much, much weaker
With regard to media queries, it depends on the library. For the library the author is using, the media queries are defined inline: https://www.styled-components.com/docs/advanced#media-templa...
With SASS, I find it really convenient to define variables for common/brand colours, for example.
Almost all CSS-in-JS solutions (including styled-components mentioned in the article) still support pseudo selectors, media queries, etc.
They've got it figured out. Stick your styles at the bottom of your component. Vertical flexbox by default. And... that's about it. It just works beyond that point.
We liked it so much we ported our webapp from react to react-native-web. It's an amazing library. You can freely mix in regular react, but the styling story is so good with react-native-web that you'd never want to. It powers mobile.twitter.com and the author (same person who made normalize.css) recently joined Facebook, which hopefully means more development to come.
You'd have to pay me to go back to styling the web with css. It really is a hair-pulling experience.
If you do not server-side render, it does not matter whether you put your CSS into JavaScript or not—your JavaScript app is not going to render anything anyway.
By knowing how the sausage is made, I'm also aware that the line marking the aforementioned certain point is often drawn too early, at informational (i.e. document) stuff where HTML and CSS are fully capable on their own.
In fact, JS is extremely useful in the browser. It adds to the richness and interactivity of webpages. We must collectively appreciate this, and stop denying this! Even HN is a better experience with JS.
Some sites do give JS a bad name, and poorly used JS is something I hate too, but JS as a concept is something that I wholeheartedly support. Thankfully, the web is mostly opt-in - if you don't like the way a site employs technology, just don't visit it. Simple.
Slow internet connections are a reason to think about the experience when loading JS is slow and/or failed. It's not just 'poor people' who have slow internet - 'rich people' on inflight wifi or on the internet on the train can result in things not loading.
Ok, we do assume that you have visited our webpage at least once in an area of strong signal, however, once you've done that, all the JS is cached and all we transmit back and forth are lightweight, gzipped json payloads, often less than a kilobyte.
I use my own PWA regularly, and found it actually quite usable in-flight.
It's rather easy too if you're using a (great) library like styled components - this functionality is built in and just works with a couple of extra lines of code.
The deeper question here is will the app work at all without JS. That's quite hard to achieve (every client side interaction must have a server rendered equivalent accessible via a url or url params, essentially) but the CSS part is easy.
NoScript users are like the customer that demands the most but pays least. After some rants of such customer, they're ignored.
- In your react example, I see you created <Title> as styled.h1, but that's an awkward way of bootstrapping a component. Even if I used SC, I would still put the style information outside my component code in the same directory. At this point I don't see the benefit over doing something like:
import * from './login.scss';
What can SC do that regular CSS or SASS can't?How do you deal with mobile, where sometimes I have to override dozens of declarations with media queries?
- This ties into a broader point of having "style components" like <Grid /> and <Row />, which is a very nice way of working in React. styled-components (the library) simply takes that pattern and enforces it across your application. See style-components.com for links to some more resources about this pattern!
Instead we have used CSS Modules (https://github.com/css-modules/css-modules), first by including it manually in webpack, now it's already built into create-react-app so I just have to install node-sass and call my Sass files ComponentName.module.scss and I can use them the same way in my .tsx/.jsx files that you use CSS-in-JS. IMO that's a lot less messy and makes your jsx files smaller and better to read. Also it's easier to write Sass and have the pros of that language on top.
Did you consider this approach at all and what did you not like about it?
Having some experience with CSS Modules myself, I thought they were interesting and definitely an improvement, but still left a lot to be desired.
For example: needing to use a helper like `classnames` to join/toggle many dynamic classes together was very ugly and cumbersome. There is also no way to "directly" tie variable state/props to CSS rules – you're still required to use this indirection where a state/prop relies on there being a predefined class/modifier in the CSS file.
e.g.
font-size: ${props => props.size}px;
color: ${props => props.special ? 'red' : 'blue'};
With solutions closer to the "fully JS" end of the spectrum like Styled Components, there are no extra "modifier" CSS selectors to come up with to handle the above situation.RDD (resume driven development).
Cargo culting.
Ooooh shiney!
"I'm not confident enough to make my own decisions, so I'll let the 'community' decide for me."
Others (I'm thinking Angular and Vue) have their own templating systems that they compile to JS anyway.
Personally, I find the Vue.js concept much nicer, but even given that, I define my styling in LESS (SASS...) to benefit from inheritance/mixins on the level of styling. Semantic approaches like BEM or Bulma make a lot more sense to me.
It seems to me React has made a feature out of a flaw, much like the intermingling of code and HTML back in the days with ASP/JSP/PHP.
I bet that browsers are very efficient at parsing and applying CSS directives to elements. Parsing and executing the JS and additional DOM manipulations could have a massive negative impact on performance, at least intuitively. Does anybody know of a performance benchmark that compares CSS-in-JS to regular CSS?
Supporting "no Javascript" can be costly. It's possible with server-side React and careful engineering, but why bother? What's the impact on the business?
Which human right, specifically? Humans have the "right" to install a modern browser and enable Javascript.
> "Javascript" is costly too - but maybe not your concern since it's not a business cost.
Indeed, the cost you pay to run Javascript does not show up in my budget, just like I don't pay your ISP to serve my site to you.
I don't think supporting noscript version is costly because for me that's how it supposed to be by default. 90% sites that are currently on the internet would work just fine without any js and 95% would work with as little as hackernews has. Modern CSS allows to create most of the experiences and unless you need some interactive forms or serving live broadcasts, games etc., there's just no point in using javascript.
Over the years I've stripped javascript from about 40sites which I visit daily without sacrificing any real functionality. Those sites download and render visibly faster, only relevant cotent is shown and I even implemented dark theme for them. My only javascript fixes are for broken galleries or embedded videos (and those wouldn't even be necessary if developers wrote a fallback to noscript).
It used to be that developers loaded many little scripts so I could only block the useless crap but since everything is minifed and bundled in one huge unreadable file, I just use uMatrix to block it entirely and Stylus and Tampermonkey to fix it.
I can show you "this site requires Javascript" instead, but let's just say you're not part of the target audience and that's fine.
> I don't think supporting noscript version is costly because for me that's how it supposed to be by default.
That highly depends on the site, of course.
> 90% sites that are currently on the internet would work just fine without any js and 95% would work with as little as hackernews has.
99.9% of websites are not actually businesses that employ developers. If that's your standard, I agree. A simple webpage does not require Javascript.
An overview of my tooling and workflow is here: https://github.com/tomhodgins/caffeinated-style-sheets
The big idea being that CSS should be able to apply to _any_ HTML, no matter how it was created before it got to the browser. Many style-related tools integrate tightly into your framework or into one particular workflow but don't give you the flexibility you need to apply any styles to any website unless it happened to be put together with specific tools a specific way, but I don't think that provides enough flexibility.
This is the equivalent of inline styles. We don't call it "inline CSS" because it isn't CSS. Nothing cascades with inline styles, just as they don't with "CSS-in-JS". And you don't have to worry about specificity with either implementation.
In other words - all the things that make CSS the powerful language it is are stripped away to just a listing of arbitrary properties.
So, CSS-in-JS, do yourself a favor and stop calling it that, because what you are coding is nothing even remotely close to CSS.
It took me helping my brother learn how to do web development to shake me out of that mindset. He's not from a traditional webdev background and he got quite far quite fast with React. When he showed me his code, I noticed he was inlining all of his styles in his relatively small React components. I was going to explain why its bad practice to do that, but when you're building small almost atomic components, that approach actually makes a lot of sense, its what the BEM methodology tries to do albeit in a poorer way. CSS-in-JS tools like Styled Components preserve the spirit of this approach and minimise the downsides.
I think CSS-in-JS lends itself quite nicely to atomic design. At my current company, one of the first things we did was build a component library of atoms & molecules that are shared across many applications, keeping our design language very consistent.
We also make heavy use of Styled Components' theming capabilities, so we wrap our React apps with a ThemeProvider and give it an object with theme variables that drive the look and feel of our components. This is super useful when you have to support whitelabeling like we do, but even if you don't, its nice to keep your mystery-meat colors and spacings all in one place similar to how you might with SASS.
One of the gripes a lot of people have with CSS-in-JS is portability, and while that is a drawback, I feel it's an anti-pattern to rely on shared CSS styles to enforce design consistency across many deployables. It's kind of a classic case of developers trying to solve a process issue with technology, which is admirable, but from experience styles go out-of-sync with eachother quite easily and its rare that you can drop a CSS file into another project and have styles just work without having to do rework, so the benefits of sharing CSS rather than writing new styles off a styleguide are marginal at best. Again, this is all my subjective experience, your mileage may vary.
We still have a couple of issues with Styled Components, no technology is perfect and SC is still relatively young, but so far the pros have far outweighed the cons to the point where none of us really feel the itch to go back to SASS or plain CSS. I'm definitively not a magpie developer by any stretch, I'm usually resistant to change for the sake of change, but CSS-in-JS makes a lot of sense when you use it how its intended
Basic front end code isn't that challenging, but it does take some actual practice (not half-ass practice followed by a million poorly considered short-cuts).
Just about everything else on the frontend is absolutely solid cross browser with 5+ years of thoroughly tested support. People don’t write abstractions to fix weak technology, they do it to fix weak developers.
Thank you!
tl;dr because it's convenient.
It's also redundant, goes completely against the features of CSS (the cascading part), doesn't utilize the possibility of selectors, children, etc, ugly, and computationally heavy.
But yeah, why not.
People should not be forced to grant you permission to execute code in order to read text & view images.
$disparaging_paragraph
Thank you
SASS + BEM accomplish the result in a more manageable way.
people disable it
In the context describe in the article, there is nothing cascading, and there are no sheets.
What he / she is doing is going back to the late 90's and inlining all the style'ing. I'm not judging the technique, simply pointing out that it's not actually CSS.
I have built websites in a variety of different ways, with a variety of different CSS schemes. I've worked with SASS, LESS, BEM, every major css framework, in both SPA and SSR environments.
I have also built react SPAs with the above variety of CSS schemes, as well as CSS-in-JS libraries like Aphrodite and Styled Components.
I currently build and maintain a medium-to-large web app with a small team, right now ~200 components, 31k lines of js including test files.
Styled Components has been an immensely positive experience for us, and i would not want to use any other way I have mentioned, or has been mentioned in this thread, to manage our styles in our app.
The reason why is because of the simple fact that it allows us to think of our styles just like any other component. We do not need a separate way of managing styles that we need to keep track of, whether a style is used is 100% the same as whether a component is used or not, which makes eliminating dead code, dead styles, easily managed through tooling.
It allows us to define simple, semantically-named components like <FlexGrid/> and <BigButton/>, and allows us to compose these components together to build up styles just like any other react component.
Styles are easily overridden from their parents. We use Ant Design for our UI toolkit, which does not use Styled Components, but any styles we want to override, as well as styles in nested classes, are easily done by wrapping an antd component. All the parent component needs to do is accept a "className" property. This makes interop with other components simple.
Effectively what this means is that after awhile, we get to live in a world where I can add a component into any given part of application, and be very certain its going to look and behave the same way it does in every other part of the application. When I'm building new features I don't want to think about styles, except when adding styles is part of the feature itself. Styled Components is the only way so far I've found that does that sufficently.
Globally-available CSS is great for web pages, but not so much for widget-based GUI web apps, which creates leaky abstractions. Most ways of managing CSS are systems to get around that, or treat it as a feature.
In short, 10/10 would use again, even over other CSS-in-JS libraries like Aphrodite. Note this opinion is scoped purely to react apps. Vue's approach seems appealing as well but I have not used it before, but to be honest I do prefer being able to have everything in JS or JSX, but thats a different discussion.
"I can add, change and delete CSS without any unexpected consequences. My changes to the styling of a component will not affect anything else. If I delete a component, I delete its CSS too."
This is readily achievable with ordinary CSS. Selectors by ID/class have existed for multiple decades. A Ctrl-F for said ID/class name should be pretty painless to do even in the case where you're doing everything in one giant spreadsheet (though in this day and age of CSS preprocessors, no reason why you couldn't have widget.js and widget.css, the latter of which gets compiled into the final spreadsheet as part of the build process).
"Never go on a hunt for CSS affecting your components ever again."
I'd hardly call Ctrl-F a "hunt". JavaScript can't fix bad code hygiene/organization.
"I cannot expect all team members, particularly juniors, to have an encyclopedic understanding of CSS."
So you expect them to have an encyclopedic understanding of your CSS-in-JS library instead, then?
Or perhaps it'd me more reasonable to be okay with looking things up every once in awhile?
"With CSS-in-JS, we automatically sidestep common CSS frustrations such as class name collisions and specificity wars."
I'm curious how much these are actually problems in the first place (or at least problems inherent in CSS itself), and how a CSS-in-JS framework expects to solve them without completely reinventing the browser's styling engine.
"This leads to a marginally slower time to interactive, but a much quicker first meaningful paint!"
If you didn't try to do everything with a giant blob of JS, you wouldn't have to do these kinds of ridiculous tricks to reduce time-to-first-paint.
"Send only the critical CSS to the user for a rapid first paint."
If the CSS is not critical, then it shouldn't be sent at all. No reason a backend framework couldn't selectively send only the CSS relevant to the HTML being sent.
"Simply style your components with a global theme or based on different states."
<foo class="widget primary"></foo> <foo class="widget secondary"></foo>
So difficult.
"CSS-in-JS combines all these benefits into one handy package and enforces them."
Yes, at the cost of needlessly reinventing stylesheets and making it significantly more difficult to modify the styles downstream (e.g. for accessibility reasons).
"Thousands of companies use CSS-in-JS in production [...] (including this website)"
As an experiment, I switched JS off for that page and refreshed. The styling certainly doesn't seem to be happening client-side. Instead, the HTML source contains a bunch of obfuscatedly-classed divs and a giant obfuscated stylesheet in an inline <style> tag.
Surely JS is not specifically needed to achieve this result. This could've been done with CSS-in-Python or CSS-in-Ruby or CSS-in-C or CSS-in-Lisp or CSS-in-COBOL or CSS-in-Brainfuck or whatever. It also could've surely been done with a dedicated language-agnostic CSS preprocessor. Further, it could've been done the "hard" way (not really hard at all) with CSS selectors alone (since that is indeed the end result).
Just put your CSS in a .css file where it can be cached, delivered by CDNs and can easily be edited in a single place.
Separation of content and presentation has been around for a good reason. One should always avoid mixing logic and design because it will break some day, no matter how well you think you handle it.
.js files are cached and delivered by CDNs too.
> can easily be edited in a single place
If you change the name of a class (or add a new one, delete another one, edit the structure of the component, etc...), then you have to open and edit 2 files. With author's solution you don't. The CSS is collocated with the component.
> Separation of content and presentation has been around for a good reason. One should always avoid mixing logic and design
Author is talking about putting the React component (presentation) and its CSS (presentation) in the same file. He's not mixing business logic and design.
Not in 2019, in the age of excessive A/B testing, where every page load gives you a different set of JS.
Writing your css in JS files also does nothing to prevent you from separating content and presentation. You can write components that just handle styling. I see more problems with separation in css where you end up with one giant file that handles the whole site.