The New CSS Reset: Rethinking How We Strip Browser Defaults

Elad Shechter's the-new-css-reset takes a different approach to the well-trodden ground of CSS resets. Where older resets were built on the CSS features available at the time, Shechter's project leans into modern CSS properties specifically designed for resetting styles — and that changes what's possible.

The entire reset is compact:

/*** The new CSS Reset - version 1.2.0 (last updated 23.7.2021) ***/

/* Remove all the styles of the "User-Agent-Stylesheet", except for the 'display' property */
*:where(:not(iframe, canvas, img, svg, video):not(svg *)) {
  all: unset;
  display: revert;
}

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

/*
  Remove list styles (bullets/numbers)
  in case you use it with normalize.css
*/
ol, ul {
  list-style: none;
}

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

/* Removes spacing between cells in tables */
table {
  border-collapse: collapse;
}

/* Revert the 'white-space' property for textarea elements on Safari */
textarea {
  white-space: revert;
}

Two Reset Philosophies, One Goal

Shechter sees two distinct approaches in the history of CSS resets. Normalize.css, by Nicolas Gallagher, is the "gentle" route: it fixes inconsistencies between browser implementations without removing all default styling. Eric Meyer's CSS Reset is the "hard" route: it assumes developers don't want browser defaults like the font-size on <h1> through <h6> or list styling on <ul> and <ol>.

Shechter advocates for using both: Normalize.css loads first, followed by a hard reset. "I love Normalize.css. I think it's a must-have in any project," he says. Normalize.css reaches into shadow DOM elements and handles special pseudo-classes like ::-moz-focus-inner and ::-webkit-file-upload-button, territory a hard reset doesn't cover.

But the resets we have were built on older CSS features, Shechter explains. Recent CSS additions built specifically for resetting styles make it possible to create a more valid reset — which is what his project sets out to do.

The all: unset Foundation

The heavy lifting happens in the first ruleset, centered on the CSS all property, which resets every CSS property at once. To understand why Shechter uses the unset keyword rather than initial or inherit, you have to look at CSS property fundamentals.

CSS properties fall into two groups:

  • Inherited properties: typography properties that inherit by default
  • Non-inherited properties: everything else, including Box Model properties like padding, border, and margin
/* Will get values from the parent element value */
font-size: inherit;  
line-height: inherit;
color: inherit;
max-width: initial; /* = none */ 
width: initial; /* auto */
position: initial; /* = static */

Using all: initial would lose the natural inheritance on typography properties, while all: inherit would force inheritance on properties like margins that shouldn't inherit. The unset value resolves this: it acts like inherit for inherited properties and like initial for non-inherited ones.

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

One operation on all HTML elements removes the default user-agent stylesheet styles — but it also removes some styles you want to keep.

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

Preserving display with revert

That's where the follow-up display: revert; comes in. Every CSS property has a single initial value, and the initial value for display is inline. If the reset stripped display back to its initial value, elements like <div> that rely on display: block from the user-agent stylesheet would break.

div { 
  display: unset; /* = inline */ 
}
span { 
  display: unset; /* = inline */ 
}
table { 
  display: unset; /* = inline */ 
}
/* or any other HTML element will get inline value */

The revert keyword offers a way through: it checks for a user-agent stylesheet default for the specific element and property. If one exists, it takes that value; if not, it behaves like unset. This restores the intended block display for <div> elements while remaining consistent with how modern browsers handle default styling.

A diagram of all the CSS reset keywords

Targeted Exceptions

The universal selector in the reset comes with careful exceptions. Shechter initially imagined a reset that needed no exceptions — testing on real projects changed that. Elements that accept dimensions via HTML attributes — <iframe>, <canvas>, <img>, <svg>, and <video> — broke under a full reset.

The problem: when everything is reset, the width and height attributes on these elements stop working because the reset applies an auto value that overrides them. Shechter prefers the dimensions to come from HTML, not CSS, to avoid page-load glitches. Removing these elements via :not() keeps the reset from interfering.

The :where() selector plays a separate but important role: it removes specificity entirely. "We don't need to describe more significant specificity in our CSS only to override the CSS reset," Shechter says. He expects :where() usage to grow beyond replacing multiple selectors into general specificity management.

A distinct exception for SVG internals — :not(svg *) — exists because touching inner SVG elements can break the visual rendering. "Let the image be an image," Shechter says.

Opinionated Choices: Box Sizing and Lists

Later rulesets move into territory where browsers don't disagree — pure opinion. The box-sizing: border-box reset is one such choice. Shechter sees it as pragmatic: browser engines can't change the default content-box behavior without breaking older sites, but new projects universally adopt border-box anyway. "This is totally opinionated," he acknowledges.

Resetting list styles also follows the Meyer reset tradition. The concern that list-style: none; removes list semantics — particularly on iOS — doesn't worry Shechter. His reasoning: if you don't reset list styles, lists can't be used for navigation without losing semantics in every browser anyway.

"If I need to choose between most browsers gaining these semantics, and no browsers gaining these semantics, I'm choosing the former," he says.

Looking Ahead

Shechter expects to evolve the reset as he encounters omissions in practice. Adding max-width to images, for instance, would qualify: "There is no good case where we want an image to overflow its container."

Cascade Layers (@layer) could reshape reset strategy entirely. While support is still behind flags, Shechter sees a clear future pattern: load Normalize.css, then the reset, then project styles as layers. Each layer automatically overrides the one before it without specificity battles. "This also means that removing specificity with :where(), like I did, will no longer be necessary," he notes.

@layer normalize; /* Create 1st layer named “normalize” */
@layer the-new-css-reset; /* Create 2nd layer named “the-new-css-reset” */
@layer project-styles; /* Create 3rd layer named “project-styles” */