The New CSS Reset

Elad Shechter
11 min readOct 25, 2021

CSS has acquired a number of new features in recent years, allowing us to reset the CSS more succinctly than before. What’s more, now in 2021 all
those CSS features have broad support in all the evergreen browsers. This
urged me to create a new CSS Reset, called “The new CSS Reset”.

Before we jump into how I created my new CSS reset, which is
simpler and more sophisticated than before, I’ll first address some frequently asked questions.

Why Do We Need A CSS Reset?

Every browser loads a style file called the User-Agent-Stylesheet, which defines default styles for HTML elements.

The problem is that different browsers might define different default styles. CSS resets were invented to solve this problem.

Normalize CSS Vs. CSS Reset Approach

When it comes to how to reset the styles, there are two main approaches:

  • The Gentle approachNormalize CSS.
  • The Aggressive approachCSS Reset.

Let’s take a quick look at these two approaches.

Normalize CSS (the gentle approach)

The Normalize CSS's main goal is to resolve browsers’ differences and create consistent basic styles across all of them.

The Normalize CSS works by searching for the most common definition for each element. It then defines the common styles for those browsers that style that element differently and thus override the styles in the User-Agent-Stylesheets that aren’t using in the common “correct” way.

The Normalize CSS project has excellent documentation, and I recommend you go over it and see what it fixes.

Part of a Normalize CSS file:

/** 
* Correct the inability to style clickable types in iOS and Safari.
*/
button,
[type="button"],
[type="reset"],
[type="submit"] {
-webkit-appearance: button;
}

CSS Reset (the aggressive approach)

Unlike Normalize CSS, CSS Reset’s main idea is very different — it assumes that most of the browsers’ default styles do not fit our projects’ needs, and therefore its main goal is to remove most of them.

For example, if we take a look at the HTML section heading elements, <h1> to <h6>, we’ll realize that in most cases we don’t want the default styles coming from the User-Agent-Stylesheet, such as the specialfont-size, margin, and even the font-weight. The styles that we do want to apply, we will define based on our project’s needs.

There are many types of CSS Resets, but Eric Meyer’s CSS Reset is the most well-known one.

A portion of Eric Meyer CSS Reset:

/* http://meyerweb.com/eric/tools/css/reset/     
v2.0 | 20110126
License: none (public domain) */
html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed, figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, section, summary, time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
}

Which is Better to Use?

This section is, of course, my personal opinion. But you will be surprised by my answer.

First, I think that the Normalize CSS is a must-have in any project.

Why is Normalize CSS a must-have?
Normalize CSS addresses issues that the CSS Reset doesn’t address, such as shadow DOM elements.

Shadow DOM elements are inner scoped elements that exist inside certain HTML elements. One example is the inner buttons on some input elements, such as <input type=”search”>, <input type=”file”>, <input type=”number”>, and others.

An example from Normalize CSS:

/** 
* Remove the inner padding in Chrome and Safari on macOS.
*/
[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}

But what about CSS Reset ?!

I also like the idea of CSS resets, which remove most of the default styles that we will want to define differently in most cases.

However, there is a major caveat: Eric Meyer’s CSS Reset is built as a long CSS selector list. The problem with this big chain of selectors is that it’s unreadable when debugging CSS code.

Every inspection of an element in the browser’s Dev Tools will show you this huge
CSS selector.

Since the Reset CSS and the Normalize CSS focus on different concerns, and due to the caveat in the common CSS Resets, I’ve worked with both Normalize CSS and a custom CSS Reset that I developed over time.

The Normalize CSS addresses Shadow DOM elements' differences, and the custom CSS Reset removes unneeded browsers' default styles.

@import "resets/normalize.scss";
@import "resets/reset.local.scss";

The custom CSS Reset only resets those elements that require reset, such as lists, headings, and other elements.

This is My Old Custom CSS Reset:

/****** Elad Shechter's RESET *******//* box sizing border-box for all elements */*,
*::before,
*::after { box-sizing:border-box; }
a { text-decoration:none; color:inherit; cursor:pointer; }
button { background-color:transparent; color:inherit; border-width:0; padding:0; cursor:pointer; }
figure{ margin:0; }
ul, ol, dd { margin:0; padding:0; list-style:none; }
h1, h2, h3, h4, h5, h6 { margin:0; font-size:inherit; font-weight:inherit; }
p { margin:0; }
cite { font-style:normal; }
fieldset { border-width:0; padding:0; margin:0; }

The New Way to Reset CSS in 2021

New reset keywords have been added to CSS in recent years: initial, inherit, unset, revert. Besides these, CSS now also provides the special all property, which allows us to reset all of the properties at once.

Before we get into those features and how we can utilize them to make a new CSS reset, let’s go over a few basics in CSS.

Two Types of Properties

There are two groups of properties in CSS:

  • The Inherited properties group - consists of properties that inherit
    definitions from parent elements by default; they are mostly typographic properties (font-size, color, etc.).
  • The Non-inherited properties group - consists of the rest of the properties, and they aren’t affected by parent elements’ definitions.

MDN has information on what the default type of every property is:

Every Property Has an Initial Value

Another basic of CSS I want to talk about is CSS properties’ initial values.

Before we define any style in CSS, and even before the browser loads its styles (“User-agent-stylesheet”), every property has an initial value (default value).

This is important to know since in most cases we will want to reset the property's value to its initial one.

An example of the max-width property’s initial value:

the max-width’s initial value is ‘none’.

Now that we’ve gone over initial values and how styles affect each of the types of properties that we discussed before, let’s look into the process through which I arrived at my new CSS Reset.

The Basics of Resetting in CSS

Our first challenge is that we have two groups of properties, and they have to be reset differently.

Non-inherited properties
When we reset non-inherited properties, we want to get the property's initial value. We’ll use the initial keyword value to do this.

Examples:

max-width: initial; /* = none */ 
width: initial; /* auto */
position: initial; /* = static */

Inherited properties
When we reset inherited properties, such as font-size, we want to keep the default inherited behavior. We’ll use the inherit keyword value to do this.

Examples:

/* will get parent element value */font-size: inherit;  
line-height: inherit;
color: inherit;

Auto Reset

To reset these two categories of properties, inherited and non-inherited, we will use another special reset keyword: the unset keyword.

Using the unset value on any property will apply its proper reset keyword. For example, font-size: unset; will be equal to inherit, and position: unset; it will be equal to initial.

Examples:

max-width: unset; /* = initial = none */font-size: unset; /* = inherit = get parent element's value */

Reset All Properties Together

CSS has many properties, and we want to make sure that they’re all reset without having to go through them one by one.

Therefore, we will use the exceptional all property. This property enables us to reset all properties at once!

Now, we can effortlessly reset all properties with their respective types.

Example:

/* 
Reset all:
- Inherited properties to inherit value
- Non-inherited properties to initial value
*/
* {
all: unset;
}

CSS’s Default Values Aren’t Always Desirable!

However, in some circumstances, restoring CSS to its default values is
insufficient. For example, if we reset a <div>‘s display property to unset, the initial value that we get will be inline rather than block. Examples:

div { 
display: unset; /* = inline */
}
span {
display: unset; /* = inline */
}
table {
display: unset; /* = inline */
}
/* The inline value will be applied to every HTML element */

The reason for this is that the initial value doesn’t use the default styles from the browser’s User-agent-stylesheet.

Reverting to the Browser’s Default User-Agent-Stylesheet

The default styles in our browser are derived from two sources:

CSS properties' basic behavior — the initial values and the inherited styles on some of the properties, as we already discussed.

User-agent-stylesheet — the default CSS file that the browser loads to create special styles for specific HTML elements.

HTML div tag’s user agent’s base style

This is why you’ll see the display: block; definition when you run Inspect Element on a <div> HTML element, but nothing when you run it on a <span> HTML element.

The browser shows you only those styles that differ from the basic CSS behavior.

How to reset to the User-agent-stylesheet values
To reset to the default values in the User-agent-stylesheet, we have the last special CSS reset keyword: the revert keyword.

The revert keyword first determines whether the browser’s User-agent-stylesheet contains special styles for the HTML element in question. If so, it will reset to those default styles.

Examples:

div { 
display: revert; /* = block */
}
span {
display: revert; /* = inline */
}
table {
display: revert; /* = table */
}

If there are no styles defined for that element in the User-agent-stylesheet, the revert keyword will behave like the unset value. If the property is from the inherited group, it will be reset to the inherit value; otherwise, it will be reset to the initial value.

The CSS Reset Keywords Diagram (inherit, initial, unset, and revert):

Creating “The New CSS Reset”

Now that CSS has all these new reset features, creating a CSS Reset is much simpler than up till now.

In most cases, we want to reset the majority of properties to their default initial values or their inherent behavior of CSS, using the unset value. But as we saw above, we also want to keep the display property declarations from the User-agent-stylesheet.

Basic Idea

With all of this knowledge, I began working on my new CSS reset:

/* 
Reset all the "User-Agent-Stylesheet" styles,
except for the 'display' property
*/
* {
all: unset;
display: revert;
}

Not Everything is Perfect

Having said that, there are a few more details to take care of.

When the height and width properties on some special HTML elements such as <img>, <video>, <svg>, <canvas> and <iframe> —are reset with all: unset; the effect of the height and width attributes on those elements is reset. As a result, those size attributes will no longer have any effect, due to the CSS reset.

To solve this problem, I used some CSS pseudo-classes.

The :not() pseudo-class
I added the :not() pseudo-class, which nowadays accepts multiple arguments.

In the arguments, I added all the “content HTML elements” to which width and height attributes can be applied. In this way, I’m not affecting those elements.

/* works on every element except for content elements */*:not(iframe, canvas, img, svg, video):not(svg *) {    
all: unset;
display: revert;
}

But using the :not() pseudo-class has an unintended consequence: it creates a stronger CSS specificity and thus might override styles define later in your project. For example:

/* these styles don't affect the element when I use CSS reset with the :not() pseudo-class with multiple arguments */div { 
color: red;
}

CodePen Demo

This style won't work because the arguments list in the :not() pseudo-class has a stronger specificity than a selector of one HTML element.

To solve this issue, I used the :where() pseudo-class:

The :where() pseudo-class
I solved this by wrapping the whole :not() pseudo-class with the :where() pseudo-class, which is a special selector that removes the specificity inside this selector.

*:where(:not(iframe, canvas, img, svg, video):not(svg *)) {    
all: unset;
display: revert;
}

More Special Resets

With our 2021 perspective, we can see that some CSS features have been implemented incorrectly. When I built this new CSS reset, I aimed to solve those common problems.

The Box-sizing property
These days, the most common way to use the box-sizing property is with the border-box value, which includes the padding and the border in the sizing calculation:

/* Preferred box-sizing value */*,
*::before,
*::after {
box-sizing: border-box;
}

Images overflow
The default size of an <img> HTML element is its real size. If you place it in a smaller container than the default size of the specific image, the image will overflow from the container. To solve this, I added this style to the CSS reset:

/* For images to not be able to exceed their container */
img {
max-width: 100%;
}

In my new CSS reset project, you’ll discover even more small improvements like this.

Form Elements

In the past, form elements, such as <input type=”checkbox”>, <input type=”radio”>, <select> and others, were difficult to reset. Over the years, I’ve seen many web developers utilize CSS hacks on the <label> element to style those elements.

web forms

With the new CSS reset features, we now can reset even those stubborn elements, and create custom styles for these <form> elements.

An example of designing radio buttons and checkboxes (using Sass):

input[type="checkbox"],
input[type="radio"] {
/* base styles here */ &:checked {}
&:disabled {}
&:focus {}
}
input[type="radio"] { border-radius:50%; }

Live CodePen demo of designed form elements:

Or you can create well-designed checkboxes:

However, if you don’t want to reset those form elements, it is easy to restore the default styles with the revert value and the all property.

Example:

input,
textarea,
select {
all: revert;
}

To Summarize

In this article, we looked at why CSS resets are useful, what sorts of CSS Resets already exist, and how to combine their resets.

In addition, we saw the CSS reset keywords and met the global reset property, all, which has expanded the possibilities of CSS resetting.

With all of these possibilities, you can construct a custom CSS reset and design form components that provide a more consistent experience across all browsers.

To try “The new CSS reset”, you can find it on the GitHub page and as an NPM package.

If you find any bugs, please feel free to contact me via the GitHub project.

The new CSS reset — GitHub page

Browser Support

To create: “the new CSS reset” project, I have used CSS features that while they are cutting-edge, are already implemented in all evergreen browsers:

  • Chrome, Edge: version 88+
  • Firefox: version 84+
  • Safari/iOS: version 14+
  • Opera: version 75+
  • Samsung Browser: version 15+

--

--