html {
font-family: Roboto, sans-serif;
}
With the exception of some form elements, you’ve just set a font on every bit of text on a site! Nice! That’s probably what you were trying to do, because of the probably hundreds of elements all over your site, setting that font-family
every time would be tedious and error-prone.
CSS is global by nature. On purpose!
I like how David Khourshid put it:
You ever stop and think about why CSS has a global scope? Maybe we want to use consistent typography, colors, sizing, spacing, layout, transitions, etc. and have our websites & apps feel like one cohesive unit?
Love the cascade, the cascade is your friend.
And yet. The global nature of CSS is perhaps the most-pointed-at anti-feature of CSS. Some people really don’t like it. We all know it’s very easy to write a single CSS rule that has implications all over a site, breaking things you really didn’t want to break.
Two CSS properties walk into a bar.
A barstool in a completely different bar falls over.
— Thomas Fuchs 🎄🕹💾 (@thomasfuchs) July 28, 2014
There are whole new categories of testing to assist with these problems.
Scoped styles aren’t the only reason there is such interest and adoption in the landscape of tools that is CSS-in-JS, but it’s a big one. There are loads of sites that don’t directly author any CSS at all — even preprocessed styles — and go for a JavaScript library instead where styles are authored quite literally in JavaScript. There is a playground demonstrating the syntax of the various options. Here’s how styled-components works:
import React from 'react';
import styled from 'styled-components';
const Container = styled.main`
display: flex;
flex-direction: column;
min-height: 100%;
width: 100%;
background-color: #f6f9fc;
`;
export default function Login() {
return (
<Container>
... Some stuff ....
</Container>
);
}
There are literally dozens of options, each doing things a bit differently while offering slightly different syntaxes and features. Vue even offers scoped CSS directly in .vue files:
<style scoped>
.example {
color: red;
}
</style>
<template>
<div class="example">hi</div>
</template>
Unfortunately, <style scoped>
never quite made it as a native web platform feature. There is shadow DOM, though, where a style block can be injected in a template and those styles will be isolated from the rest of the page:
let myElement = document.querySelector('.my-element');
let shadow = myElement.attachShadow({
mode: 'closed'
});
shadow.innerHTML = `
<style>
p {
color: red;
}
</style>
<p>Element with Shadow DOM</p>
`;
No styles will leak into or out of that shadow DOM boundary. That’s pretty cool for people seeking this kind of isolation, but it could be tricky. You’d likely have to architect the CSS to have certain global styles that can be imported with the shadow DOM’d web component so it can achieve some styling cohesion in your site. Personally, I wish it was possible to make the shadow DOM one-way permeable: styles can leak in, but styles defined inside can’t leak out.
CSS-in-JS stuff is only one way to scope styles. There are actually two sides to the spectrum. You could call CSS-in-JS total isolation, whereas you could author CSS directly with total abstraction:
Total abstraction might come from a project, like Tachyons, that gives you a fixed set of class names to use for styling (Tailwind is like a configurable version of that), or a programmatic tool (like Atomizer) that turns specially named HTML class attributes into a stylesheet with exactly what it needs.
Even adhering 100% to BEM across your entire site could be considered total CSS isolation, solving the problems that the global scope may bring.
Personally, I’d like to see us move to this kind of future:
When we write styles, we will always make a choice. Is this a global style? Am I, on purpose, leaking this style across the entire site? Or, am I writing CSS that is specific to this component? CSS will be split in half between these two. Component-specific styles will be scoped and bundled with the component and used as needed.
Best of both worlds, that.
Anyway, it’s tricky.
The problem is not CSS in JS.
It is CSS's global scope.
Solve the global scope, and CSS in JS will follow.
(I don't know if "follow" means disappear, being fully accepted, or getting a major overhaul.)
(For that matter, I don't know what "solving the global scope" means.)
— ppk 🇪🇺 (@ppk) November 28, 2018
Maybe this will be the hottest CSS topic in 2019.
A wise man once told me after I was complaining about css affecting things I didn’t want it to: “the problem is not the css, the problem is you’re bad at css.” He was correct. I can’t think of css issues that can’t be solved by careful naming and abstracting.
Got a problem with CSS’s global scope? Then apply a non-global scope, duh. Want to only affect fonts in tables? Use the selector ‘table.’ Want to only affect the height of table headers and not the rows of table bodies? ‘table>thead>tr.’ Want to control the size of individual divs without affecting all divs and the divs inside your divs? Use classes.
Anyone who complains about CSS having a global scope is only doing so because they don’t know what they’re doing…
Exactly this. I must be missing something because I don’t know what all the fuss is about. Inheritance, people. There’s even a handy * selector.
Yes. I once argued with a well-known Twitter user who claimed that “the cascade is the broken part of CSS”. I still wonder what he thinks that “C” in CSS stands for.
The moment you think css-in-js is the solution for an problem i got bad news for you. The frontend community should stop over engineering everything they see and looking for solutions in tools/javascript for problems that don’t exist.
css-in-js is one the most stupidest things i have seen in the recent years. The problem isnt css, the problem is between the monitor and the chair!
I think it’s worth looking at the last three comments above and seeing how extremely dismissive and flippant people can be about this idea. It’s so curious to me. I don’t have a single project using CSS-in-JS, but that doesn’t prevent me from hearing people out on why they like the approach. Off the top of my head…
Like I said, my perspective is limited as I’m not actively using any CSS-in-JS libs on any sites I’m working on, but I can still understand the possible benefits. Why the hate? Is it a territory protection thing?
Re: Chris
You’re right that there’s no justification for hatefulness, and that this has truly turned into the new religious war of the web (I believe you wrote a whole article on dogmatism ;) ).
That said, there are two reasons I think people on the CSS side get panicky about CSS-in-JS:
1) CSS is extremely powerful, and allows one to talk very elegantly about large swaths of things at once. Some people argue for CSS-in-JS simply because they don’t want to learn to control that power, or because the syntax is more familiar.
2) CSS is purely declarative. This is one of its biggest strengths; there is no state, which makes it far less fragile at scale if you know how to use it. Turing-complete languages have much more that can go wrong in them, so the ability to describe a sizable chunk of your application’s attributes without one is a big advantage.
In addition, some of the (genuine) advantages people look for in CSS-in-JS could be added to CSS itself. For example, pure functions. We at least have basic expressions now with calc(), but I see no reason not to introduce stateless functions to the language itself. There will also always be cases where you need to pass data from JS, but CSS custom properties have made it much simpler to “fire and forget” from your JS code.
When enough people push for CSS-in-JS, the active innovation shifts there and gets pulled away from CSS itself, which is a shame. It’s still no reason to be unkind to your fellow developers, but I think it’s the reason preference turns into defensiveness.
In every language/environment I have ever developed in, there have always been a significant fraction of developers who either don’t learn the simple solution built into the language/environment, or they don’t find it “challenging” enough, so they reinvent it using a paradigm they like better (usually using one they are bringing from an entirely different language/environment). And I think this is great–the people actually doing the reinventing learn a huge amount by doing it. However, they are often just the ones who like to publish their findings, and often huge swaths of the development community follow them like lemmings in the direction of increasing complexity.
I think the issue is that often the simple solutions take quite a while to find and learn how to finesse, whereas anyone with sufficient time can just go off and build something that meets the need but is not as robust and maintainable. And the web development ecosystem these days is filled with people who never feel they have the time to learn anything to this level. Just as you get comfortable in one “flavor of the month,” your skills are in danger of being hopelessly out-of-date, as its head gets cut off and it is replaced by 5 new frameworks/libraries, each more complex to learn than the last. Recommended reading: Ape and Essence by Aldous Huxley
Absolutely agree with you
I totally agree Chris. It makes sense to me to have some global styles, some scoped.
I’ve always been a CSS savvy person and never really struggled with the global nature of CSS anyway. I usually use ECSS (as I used to work with Ben Frain) and it just stuck with me. But… and maybe the Ben’s disagreement, I may also have some global class names that adhere to a different naming standard, so they can be differentiated from scoped component class names. I usually find myself creating these to style text elements.
It’s all fairly easy really. Not sure what all the fuss is about.
I’m not an active user of CSS-in-JS lib on any of my websites but I still feels that this will bring us a benefits. This could be solution for many problems.
I’ve said it before and I’ll say it again:
CSS does not have a global scope.
Saying CSS rules have a global scope is like writing a whole program in a single function and then complaining that all the variables inside it are “global”. If your CSS is global, it’s because you’re declining to use the tools the language gives you. Any “scoped CSS” proposal is just a redundant meta-mechanism on top of those.
If you think the ‘global scope’ of CSS is a problem, you just don’t understand CSS very wel. Which is not a problem, just let someone who does understand CSS do it. Don’t try to come up with ‘solutions’ for a problem that doesn’t exists and make CSS way more complex than it has to be.
CSS in JS is for developers who don’t want to learn CSS and certainly don’t know anything about Webdesign history. We already had this all-in-one-file mush back in the 90ies. There is a reason separation of concerns is such an important concept.
The level of condescension in the comments is frustrating. This attitude that people exhibit is exhausting.
As a web developer with 20+ years of experience I’ve used just about every tool and technique related to CSS. I’ve had a list of things that I’ve been wrong about in my thinking on CSS and JS over the last couple of decades. It’s important to check your bias.
What I see in the comments is a sense of elitism; the “Step aside noob and let a pro show you how it’s done”. The success of the web is its accessibility to everyone. HTML, CSS, and JS are all in broad use exactly because they are easy to author even though they’re not necessarily easy to master. When you start telling people that they should just leave CSS up to someone who understands it already then you are derailing a bunch of people who might become the next generation of experts.
In a business you have to strike a balance. You will have authors that are experts in CSS and you will have those who are not; Interns, temp workers, business users, etc. Vanilla CSS is great because it’s so easy to affect a whole site with one little change, which is also one of it’s biggest points of friction. What we have to learn to do is separate what should have a global affect and what shouldn’t.
In vanilla CSS we do this with namespaces, but even that’s imperfect and requires training and oversight to get right and maintain in the long term. Things like OOCSS, BEM, and now CSS-in-JS are band-aids on top of a fundamental problem with CSS; We need an easier way of scoping CSS. On top of that we need easier ways of making CSS dynamic and cross-browser compatible. That’s one area I think CSS-in-JS helps with; I can switch themes dynamically, react to screen size and orientation changes more consistently, adjust CSS properties based on browser, and even mix theme/brands for components within the same page. Sure doing these this is possible with vanilla CSS and the libraries and patterns that developers and designers have built up over the years, but the effort and the level of knowledge is higher than in CSS-in-JS.
The thing that’s missing from these conversations is the acknowledgement that our web languages need to remain accessible to a broad range of authors. They should be robust for the experts and forgivable for the novice. That’s the balancing act.
It’s my view not that CSS should be left to “CSS experts”, but that everyone should learn to leverage CSS properly.
I can’t tell you how many otherwise-brilliant programmers I’ve talked to who have decided they just don’t like CSS and don’t want to learn it, so they just cringe and hack it out whenever they’re forced to confront it. It’s become a veritable trope for programmers to hate CSS, without good reason. But CSS is awesome (https://css-tricks.com/css-is-awesome/) when you give it a chance. Most programmers just refuse to.
It’s not elitism; any programmer can learn to use CSS properly. They just need to put in a little bit of effort, instead of trying to pull it downward to meet them in their comfort zone. Keep in mind that the people pushing for CSS-in-JS aren’t amateurs hacking around; it remains accessible for simple use-cases. These are professionals who are at the point where they’re facing scaling issues. They can afford to devote some time to expanding their skillset.
BEM naming convention with ITCSS model has this issue covered. I’m also totally against css in js. Feels like one of the worst ideas of the last couple years in front. To Chris’s point about eliminating unused css this can be achieved with a post processor in normal css or sass.
I have some additional thoughts on that: https://css-tricks.com/heres-the-thing-about-unused-css-tools/
I agree with Chris. Rich Hickey (creator of Clojure) once said of tests, type systems, and other code analysis tools:
“I think we’re in this world I’d like to call Guard Rail Programming… ‘I can make changes because I have tests!’ Who does that? Who drives their car around, banging against the guard rails? Do the guard rails help you get to where you want to go?”
The programmer has to decide “where” she wants her CSS to “go”. That will sometimes diverge with which rules a tool will think are useless. These tools aren’t something you can hand the wheel over to, they’re a means of informing your own decisions.
At our studio we don’t use an unused-css processor because the gains are extremely small compared to possible issues as outlined in Chris’s other article. I thought it was worth pointing out though that this feature does exist.
The main point though is that you can easily scope CSS from within CSS itself through naming conventions and a suitable CSS structure.
It’s not a black and white solution and I get that some designs require highly scoped modules, objects etc. However, my sense is that designs more often than not lend themselves to global scope first and then the scope can be narrowed from there.
Even Facebook itself had problems with React as it relates to their global blue. They had something like a few hundred variations throughout there code probably due to overly scoped nature of React when in fact a more global CSS setup would’ve worked better. My feeling here is that for extremely large applications like Facebook which are rapidly developed and iterative, it may actually be the case that compartmentalised CSS in JS scoping is the way to go to avoid any catastrophic styling errors. But for us mere mortals, like 95% of us, global CSS works fine with the right naming conventions and structure.
In a sense, the solution to global scope in CSS is trivial: Attach a class to your element (make sure it’s unique) then create a rule set targeting that class containing all the declarations (and overrides) that you want there.
Every developer knows this.
The problem is when developers want to both scorn global scope but also take advantage of it, usually because of a misplaced desire for ‘reusability’.
In my experience, reusability is highly overrated in CSS. I’m not saying that we should never strive for it, but we should be very cautious about it. For example, we should only extract a separate ‘reusable’ component from another component when we find ourselves actually reusing it somewhere else. In essence, I believe we should avoid premature reusability in the same way that we avoid premature optimisation.
A great article Chris, and some very interesting points raised in the comments.
I can see why there is a lot of resistance to CSS-in-JS, and I sympathize. I have a few concerns – it adds an extra layer of complexity in terms of how the site is built and maintained. I also like the ability to fiddle with CSS in-browser, and wonder if this power is diminished somewhat when you move to CSS-in-JS? Perhaps I am wrong, and am happy to be corrected.
There seems to be (unfairly, if you ask me) a sense that CSS is only dealt with by people who come from a ‘design’ background, and that ‘programmers’ don’t like how CSS works, and are trying to enforce techniques upon it, which ‘CSS/UI Engineers’ are averse to, because it means they have to learn JavaScript, which not everyone is comfortable with.
I tend to fall into this ‘CSS/UI Engineer’ category – knowing CSS and keeping on top of how browsers interpret, render and help you debug all this, is a profession all by itself.
It will be interesting to see how this pans out in 2019 and beyond!
This is one of my favourite things about the way Vue does it, I have scoped component specific styles and imports for global css files for things like layout and global styles. Works a treat.