CSS Resets are broken

It is my opinion that CSS resets are broken. My educated, argumented, and I’m sure, valid opinion. You might share it, you might agree, or you might not agree - at the end of the day, it just depends on what your use case is. Let me tell you why CSS resets today are broken and why we need a new deal.

CSS resets grew out of the observed differences from browser to browser. Their use case was to address the observable differences in rendering of particular elements, most notably padding, margin, borders. One of the first CSS resets I used was this:

* { padding: 0; margin: 0; }

That was a good start of building your own project, as you could be sure that all your elements started with the same empty margin and padding. Of course, as the project continued, you had to make a choice, of how you will add margins to headings - above or below? And tables? Paragraphs, divs, etc. - reasonable browser defaults were out the window, and in my opinion, that’s a good thing. The thing about what’s reasonable is a matter of opinion, or design.

So, okay, some css resets were trying to be a bit more smart about it - only resetting properties for tags that differed from browser to browser. And that’s where the whole thing stopped. The focus of the CSS reset is to start with a consistent style for your project. Today UI projects use their own flavour of CSS resets to solve the same inconsistencies.

A change in focus

Two things have changed since the birth of CSS resets. One is the way we develop applications - web plugins like Disqus, Intercom and others live on pages where they share the same CSS, the same DOM, and hopefully, play well with eachother.

A common problem in such cases is that frameworks pollute the global CSS namespace. Bootstrap defines a plethora of generic classes like .alert, which are quite easy to re-use accidentally if you are starting a new project and don’t give much attention to probable conflicts.

"CSS pollution of the global CSS namespace is a scourge of modern web development" via @TitPetric

Click to Tweet

What we need today is to reset the CSS in a specific DOM element, where an external plugin / component provides their interface. With CSS specificity we can make a style enforced only in this container, but, we can’t guarantee that no CSS outside of this container will affect it.

Mitigation strategy - nesting

With frameworks for writing CSS like LESS/SASS, it’s relatively easy to nest classes, so the pollution to the global name space is relatively small. If the whole CSS itself was nested, you could remove conflicts on one side, as the root element would be a requirement for applying the complete CSS from any given framework.

The problem of nesting classes is CSS specificity. The CSS rule with a higher specificity wins, and in case you want to style how buttons look, you’d have to match their specificity in the cascading order, or declare a new CSS rule with a higher specificity. God forbid somebody used !important - fuck that guy.

Really, fuck you very much

Mitigation strategy - BEM

Since it’s quite a pain to handle specificity in large projects, an important focus of anybody writing CSS splits into two camps, depending on their use case.

We have the friendly web developer who wants to keep specificity low, to make his framework or product work and play well with other people. If somebody wants to change styles defined in his framework, they can do it with low specificity, which means that they only have to style one class, or nest just once.

And then we have the product developer from Disqus. His goal is to throw away any conflicting CSS declerations, so that his product always looks the same, and it doesn’t matter which page it’s forced into. If they don’t use iframe’s, their work is made very difficult.

This job is hard for all of us, because a lot of developers fall somewhere in between these two extremes. Adoption of the BEM principles (block, element, modifier) in a wider target audience, would slightly decrease the median, and make life a bit easier for people who build web sites, as well as people who build plugins for them.

"BEM and scoped styling are helpful mitigation strategies that help to prevent CSS conflicts" via @TitPetric

Click to Tweet

Mitigation strategy - Scoped styles

Scoped styles could be applied to only part of the document. While browser support is almost unexistent, it solved half of our problem. It applied a style block to only the current element and it’s children.

I say that it solved half of the problem, because it still doesn’t deal with inheritance of CSS rules from the “parent” document. If you define a random, unique ID to the DIV you’re styling, you have achieved the same functionality, by just using CSS specificity and nesting. Of course, not having to prefix/nest css would be nicer.

So what’s missing?

If our focus is basically to invalidate the main page CSS, we should pay attention to the web browser developments which happened since the CSS resets appeared. Analyzing common practices will shed light what things are needed to be reset. I’m listing a few examples based on my experience and practice.

pseudo elements

A very common thing in frameworks today is the styling for pseudo elements. While this styling is usually bound to specific classes (FontAwesome for example is bound to .fa-{icon}:before), I have seen it being bound to type selectors (h1:before), attribute selectors (a[href^="https"]:before);

Eric Meyers CSS reset 2.0 only clears :before and :after for blockquote and q elements. It should be extended to many other block and inline elements.

pseudo classes

A very common, if not de-facto standard approach for any page today and for the last 15+ years, is to style the link colors when you move a mouse over them, or when a link has been visited. This is done by using class pseudo classes like :hover;

Since these pseudo elements are again used on many tags like paragraphs, list items and table rows, their CSS should be reset as any other element.

CSS rules

CSS rules itself evolve over time. The problem with CSS rules is two fold: rules are being introduced often, and some have reached maturity in browsers. A common rule being used today is border-radius. Many CSS rules must be reset in order to start with a “blank” canvas for development of a plugin which lives in a DIV.

"Why is resetting CSS properties such hard and unreliable work?" via @TitPetric

Click to Tweet

The second problem with CSS is that many browsers introduced these features before they were finalized under vendor prefixes. In case you need to support any older generation of browsers, it’s worth considering that you reset vendor-prefixed CSS rules also.

Even more obscure selectors

Have you ever heard of the placeholder selectors? Adoption of placeholders is quite common in web forms recently, but I haven’t seen this selector pop up many times. And then there’s :first-child, :last-child, :empty, :not(), :nth-of-type

But… it sounds like a lot of work

Yes. It is. And it’s unreliable at best. It takes a great deal of time and effort to figure out what exactly the CSS subset that you want to reset is. Sure, resetting padding and margin is an easy start, but, just looking at the Bootstrap framework, you’d have to reset about 15 pseudo selectors. In the end, you do what you can. But I really wish it could be done with CSS or at least a switch in HTML.

Some progress is made with Shadow DOM but at the time of writing, official browser support is limited to Chrome, Opera and last-gen Android browser. Shadow DOM is a good opportunity how to “scope” your component in such a way that it stands apart from the rest of the CSS styles defined in the page, which is crucial for 3rd party plugins. Lack of browser coverage means, that it will take some time, before it’s a viable way to solve our issues. That is, unless something better comes along.

What are your experiences and approaches with developing 3rd party plugins?

Update: there is apparently a shorthand property in CSS called all, which is used for resetting all CSS properties except direction and unicode-bidi to their initial states. Browser coverage is quite good, but still lacks IE and Safari/iOS support. If you are targeting exclusively Firefox and/or Chrome, it seems to be the simplest approach yet.

While I have you here...

It would be great if you buy one of my books:

I promise you'll learn a lot more if you buy one. Buying a copy supports me writing more about similar topics. Say thank you and buy my books.

Feel free to send me an email if you want to book my time for consultancy/freelance services. I'm great at APIs, Go, Docker, VueJS and scaling services, among many other things.