What's the difference between reset.css and normalize.css?
A reset removes browser default styles, while normalize.css keeps them and fixes inconsistencies. What each does, and the small modern reset to use today.
A reset removes the browser's default styles so every element starts from zero, and you style everything yourself. normalize.css keeps the useful defaults (headings are still big, lists still have bullets) and only fixes the places where browsers disagree. Today most projects use neither of them in full, but a short "modern reset" like this one:
@layer reset {
*,
*::before,
*::after {
box-sizing: border-box;
}
* {
margin: 0;
}
body {
line-height: 1.5;
}
img,
picture,
video,
canvas,
svg {
display: block;
max-width: 100%;
}
input,
button,
textarea,
select {
font: inherit;
}
h1,
h2,
h3 {
text-wrap: balance;
}
p {
overflow-wrap: break-word;
}
}reset.css
Eric Meyer's reset is the classic one. It sets margin, padding and border to zero and font: inherit on almost every element, removes list bullets and quotation marks, and collapses table borders.
After it, an <h1> looks like body text, <strong> isn't bold and <ul> has no bullets. That makes styling predictable, but you have to restyle everything, including text from a CMS or Markdown, where you actually want those defaults back.
normalize.css
normalize.css takes the opposite approach. It keeps the defaults and fixes inconsistencies, such as <sub> and <sup> changing the line height, or form controls not using the page's font.
It was very useful when browsers differed a lot. It hasn't had a new release in years, and browsers are far more consistent now, so many of its fixes target browsers you no longer support. modern-normalize is a maintained variant for current browsers, and Tailwind's Preflight is built on top of it.
What a modern reset does
It keeps the useful defaults like normalize (heading sizes, bold <strong>, list bullets) and removes the few defaults that get in the way almost every time:
box-sizing: border-boxincludes padding and border inwidthandheight.margin: 0removes default margins frombody, headings and paragraphs, so you add spacing where you want it, often withgap.display: blockon images removes the extra space below images, andmax-width: 100%keeps them inside their container.font: inheritmakes form controls use the page's font instead of the browser's default.text-wrap: balanceevens out line lengths in short headings.
You don't have to write your own. Josh Comeau's custom CSS reset and Andy Bell's more modern CSS reset explain every line. Andy's version removes bullets only from lists with role="list", for the Safari reason described in how to remove bullets from a list.
Keep the reset from winning specificity fights
A reset should be the weakest CSS on the page, so it never overrides your own styles. There are two ways to make sure.
Put it in a cascade layer, as in the first snippet. Styles in a layer lose to styles outside any layer, whatever their specificity. If you use several layers, declare their order once at the top of your CSS:
@layer reset, base, components;Or wrap selectors in :where(), which has zero specificity:
/* Specificity 0,1,1: beats .menu { padding: 1rem } */
ul[role='list'] {
padding: 0;
}
/* Specificity 0,0,0: any class wins */
:where(ul[role='list']) {
padding: 0;
}One thing to watch with layers: third-party CSS that isn't in a layer beats everything in your layers, too.
My recommendation: a short modern reset in @layer reset. If you use Tailwind, Preflight already does this job, so don't add another reset on top.