Skip to content
Courses CSS Nouveau Vanilla Has Never Tasted So Hot The New CSS Reset…

The New CSS Reset…

…Doesn’t Exist? #

What if I told you that starting with a stylesheet which attempts to “reset” default browser styles and “equalize” all major HTML elements is—by and large—a waste of time and more frustrating than useful?

Morpheus meme: What if I told you your CSS doesn't need to be reset

In the age of design systems, we can do better, and by “do better” I mean stop doing oh so much. You’ll thank me later. 😂

But first, a little history.

A long time ago in a browser galaxy far, far away…

…different browsers sometimes exhibited startlingly different visual behaviors when rendering the same HTML, sometimes even the same CSS.

There were also major leapfrogs at various times in the HTML spec itself (HTML4 > HTML5 was a huge shift) which didn’t always translate to certain older browsers (*cough* Internet Explorer *cough*).

So the idea of a CSS “reset” or “normalize” stylesheet was born. (Those aren’t the same thing, but for the purposes of this discussion we’ll use the term “reset” to refer to both approaches.) By starting your website build off using a variety of style tweaks intended to bring parity to all major browsers and also clean some things up for development (aka no margin/padding on <body>, collapsed table borders, etc.), you wouldn’t be caught flat-footed with your design looking off or broken in one browser vs. another.

However…

…as time has passed and we’ve moved into a brave new world of evergreen browsers constantly progressing in lock-step (for the most part) with excellent, well-defined open standard specs, that todo list of fixes/hacks needed in a CSS reset has shrunk exponentially.

Furthermore, with the rise of components and even further with techniques such as using shadow DOM (much more on that later), we can in a sense “reset” what we need to on a case-by-case basis.

Confusion Around Why and How to Reset #

As is so often the case with web dev, the rationale for a hard reset has lingered in popular imagination.

Is it because it’s actually necessary? Or is it simply inertia, force of habit, a thing you do because you’re “supposed” to? (Thus sayeth the CSS gods. So let it be written, so let it be done.)

Another point to consider is that most CSS frameworks come with their own form of a reset, and you often don’t have any choice in the matter…in fact you might not even be aware of what it is they’re “resetting” at all! (Believe me, it’s been painful over the years jumping from project to project and discovering that an <h2> or a <p> or a <ul> can look and feel wildly different depending on the reset involved. Argh!)

I also see confusion about what exactly is a reset and how you should approach the topic. For example, I once saw a post by someone who said: “In my CSS reset the first thing I do is change the font to sans-serif.”

Is that really a “reset” though?

Or is that your theme, your design system? You can choose whatever typographical system you like, but I wouldn’t exactly consider that a reset. It’s just, like, your opinion, man.

The Big Lebowski meme: Yeah, well that's just like, uh, your opinion, man

Prefer square bullets to round ones for unordered lists? Cool! Also not a reset. Neither is adjusting font sizes of headings, or tweaking table row backgrounds. Again, that’s your theme. That’s your visual design opinions in action.

It’s worth mentioning that there are evolutions of the “normalize” approach out there which offer intentionally opinionated defaults for a variety of HTML elements (from headings to form controls to semantic sectioning, etc.). You might call them “starter kits”—and lately people have started referring to them as “classless” stylesheets/themes/frameworks. We’ll cover a few of these tools and why you might or might not want to use one in Episode 5.

But back to basics. So what—if anything—DO we legitimately need to reset, if modern browsers are all so similar and useful out-of-the-box? Let’s look at a few common options I feel are still relevant today in most scenarios.

Box Sizing #

This is probably the most critical reset…although I legit forgot to include it in a side-project recently and ended up fine without it! 😅

Without going into a long history of the CSS box model, virtually everyone seems to agree by now that this is one thing Internet Explorer got right way back in the day (even if it was somewhat by accident). Borders and padding should flow inward and “remove” size from the edges of an element, rather than grow the size of the element. In other words: width: 200px should specify an element that’s always 200 pixels wide, regardless of any additional padding or border width added. Otherwise, your “200” pixels-wide element might actually turn into “222” pixels-wide (if you have 10 pixels of padding on either side plus a 1 pixel border on either side).

Here’s a simple way to apply this to all elements in the DOM, except for the ones who have specifically had their box-sizing property changed to be something different.

html {
  box-sizing: border-box;
}
*, *:before, *:after {
  box-sizing: inherit;
}

Again, this is a good one to have…and yet the number of times it may actually apply is fast disappearing due to the rise of responsive/intrinsic design, CSS flexbox & grid, and other techniques which mean you’re rarely setting specific width/height values on elements. Still, I’d generally recommend you keep it around.

Flush body #

This one’s a no-brainer. You don’t want the <body> element to add any margin or padding, otherwise none of your elements like headers, footers, etc. could ever sit flush with the edges of the browser.

body {
  margin: 0;
  padding: 0;
}

Although, again, I’ve seen folks do cool stuff where the body has one background along with some padding, and then main containers have a different background. As with so many of these things, YMMV!

Taming Overflows #

Ah yes, the dreaded scenario of an overly-long word or technical term like a URL breaking out of its parent bounding box and ruining your layout (particularly on mobile!).

Thankfully, there’s a pretty simple fix for that: use overflow-wrap. There are a few different ways you can handle this. Josh Comeau’s version looks like this:

p, h1, h2, h3, h4, h5, h6 {
  overflow-wrap: break-word;
}

I like covering a few more bases, so I’ll usually set this in my headings and then again across a few of the usual content elements:

p, ul, ol, blockquote, figure {
  overflow-wrap: break-word;
}

One item of note: this feature has gone through a few revisions over the years in terms of which property you should actually use…for example in the past I’ve used word-break: break-word, but my understanding is that overflow-wrap is the currently-recommended approach. Also if you’ve used word-wrap before, that is now named overflow-wrap as well.

Alongside this, you may want to experiment with adding hyphenation using hyphens: auto (also prefixed as -webkit-hyphens: auto). But buyer beware: this can do some funky things to the flow of paragraphs, particularly on mobile, so you should probably limit this to headings or other contexts where the word count is fairly low.

Blocks, Cursors, and Controls: Oh My! #

OK, so I haven’t used the following styles presented by Open Props yet on a project, but I find them sufficiently interesting enough to comment on here.

Before we proceed, you may not yet be familiar with the :where pseudo-class function which was introduced a few years ago. Using :where in a reset is very appealing, because it brings with it no specificity. For instance, you could place a :where(p) literally anywhere in your project in any source order, and the moment you style p directly it will override any properties in the :where(p) properties list.

So here’s the issue: the default styling of media and embedded content like images, videos, iframes, etc. is display: inline, which on the face of it is rarely what you’d expect or need. It seems more reasonable to display them as blocks, and then you can override them with display: inline-block on a case-by-case basis.

In addition, you want to make sure images won’t overflow their container. You may know of the common max-width: 100% trick, but here Open Props is using fancy new logical sizing properties which honor writing modes. In Western languages, inline is horizontal and block is vertical, so in the following code max-inline-size is equivalent to max-width and block-size is equivalent to height in most cases.

:where(img, svg, video, canvas, audio, iframe, embed, object) {
  display: block;
}

:where(img, svg, video) {
  max-inline-size: 100%;
  block-size: auto;
}

You also may want to set the cursor to a pointer (aka a hand) for certain elements like buttons, summaries (for <details> tags), dropdowns, etc.

:where(a[href], area, button, input:not([type="text"], [type="email"], [type="number"], [type="password"], [type=""], [type="tel"], [type="url"]), label[for], select, summary, [tabindex]:not([tabindex*="-"])) {
  cursor: pointer;
}

:where(a[href], area, button, input, label[for], select, summary, textarea, [tabindex]:not([tabindex*="-"])) {
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
}

Speaking of inputs, a worthwhile reset might be to make sure form controls inherit the font styling of their containers. From there, in your design system or on a case-by-case basis, you can apply additional form control styling.

:where(input, button, textarea, select),
:where(input[type="file"])::-webkit-file-upload-button {
  font: inherit;
  font-size: inherit;
  color: inherit;
  letter-spacing: inherit;
}

Mobile Font Size Fix #

I recently picked this one up from Andy Bell’s “more modern reset”. This is to ensure that your layouts on mobile phones won’t inadvertently trigger a change where font sizes are suddenly larger than you’re expecting. We’re all working hard to ensure our sites look good on mobile, right? Right?? So let’s let the browser off the hook, and keep our font sizes looking design-accurate:

html {
  -moz-text-size-adjust: none;
  -webkit-text-size-adjust: none;
  text-size-adjust: none;
}

(and yes, apparently we still need to use vendor prefixes for this one. Boo.)

Further Down the Rabbit Hole… #

My preference these days is to reset as little as possible, and instead address your page layout, colors, typography, controls, and other aspects of your design as parts of your design system. For example, I wouldn’t want all my headings and paragraphs and lists and blockquotes and whatnot to have no margin by default. I’d rather keep the browser defaults, then eventually get to the point where my design system overrides them for the project or page or section at hand.

Still, it’s useful to look at various resets, normalizations, and other building blocks of “classless” themes out there to get a feel for what’s possible and what you may be comfortable with. As mentioned above, the optional normalize stylesheet provided by Open Props is a good reference. I’ve also found Josh Comeau’s Reset to be quite helpful and well documented in Josh’s article. And Chris Coyier recently nerded out over Andy Bell’s latest reset which is an entertaining read in and of itself.

In the end though, I highly recommend you build your own reset/normalize/baseplate/whatever-you-want-to-call-it from the ground up. And from there, feel free to tweak it or throw it out from project to project if it’s getting in your way. The whole point of it is to eliminate any gotchas! as you’re starting to build out your layouts, pages, and components—such as a large image destroying your container, or a long unbroken word stretching way off to the right, or a card changing width on you just because you’re tweaking the padding.

It may take a bit of trial and error to figure out what works best for you. But the effort is definitely worth it.

In the next episode, we’ll move on from initial resets and take a look at how you might organize your initial global stylesheets as you start to build out a CSS architecture.

Next Episode:
How to Organize Your (Global) Stylesheets