The CSS Reset Every Developer Should Know (But Most Don't)
Browser defaults are a 1996 mistake. Here's the one file that fixes everything — and why the entire industry uses it.
You write width: 200px. You add padding: 20px. You open the browser and the element is 244px wide. You didn't do anything wrong — the browser did.
This is the reality of building for the web in 2025. Browsers ship with a built-in stylesheet from 1996 that adds unexpected margins, broken box models, and inconsistent defaults across Chrome, Firefox, and Safari. Every developer hits these bugs. Most just Google the fix each time.
There’s a better way.
Why Do Browsers Have These Defaults ?
In 1996, CSS was designed for text documents — think Word documents, not web apps. The width property was defined to mean "the width of the content area only." Padding and border were added outside, like margins on a printed page.
By 2010, the entire industry had moved to building complex layouts, and these defaults were causing daily pain. But here’s the problem: billions of websites had already been built assuming these defaults. Changing them would break the entire existing web.
The W3C has a formal principle called “Don’t break the web” — browser vendors will almost never change a default if it would break existing sites, even if the default was a mistake.
So the defaults stay. And every developer, every framework, every design system ships a CSS reset to fix them. Tailwind does it. Bootstrap does it. Material UI does it. Now you will too — and you’ll understand exactly why.
The Complete Reset CSS
/* ============================================
reset.css — Global CSS Reset
Include this FIRST before any other styles
============================================ */
/* ── Box model ── */
*, *::before, *::after {
box-sizing: border-box;
}
/* ── Remove default margins & padding ── */
* {
margin: 0;
padding: 0;
}
/* ── Document ── */
html {
/*
Prevent font size inflation on mobile after orientation change.
Note: -moz-text-size-adjust is NOT a real Firefox property (Firefox
never implemented it). Only -webkit- and unprefixed are needed.
*/
-webkit-text-size-adjust: none;
text-size-adjust: none;
/* Smooth scrolling for anchor links. Overridden by prefers-reduced-motion below. */
scroll-behavior: smooth;
}
/* ── Body ── */
body {
/* WCAG recommended minimum line height for readability */
line-height: 1.5;
/*
Improves text rendering on macOS/iOS (subpixel → antialiased).
No effect on Windows or Linux. Progressive enhancement only.
*/
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
/* ── Typography ── */
h1, h2, h3, h4, h5, h6 {
/*
Balances heading line breaks to avoid single orphaned words.
Progressive enhancement: Chrome 114+, Firefox 121+.
Older browsers silently ignore it — safe to use.
*/
text-wrap: balance;
}
p, li, figcaption {
/*
Prevents orphaned words at end of paragraphs.
Progressive enhancement: Chrome 117+, Firefox 121+.
*/
text-wrap: pretty;
}
/* ── Media ── */
img, video, canvas, svg, picture {
/* Prevent overflow outside container */
max-width: 100%;
/*
Images are inline by default, which creates a gap below them
caused by the text baseline. display:block removes it.
*/
display: block;
}
img, video {
/* Maintain aspect ratio when width is constrained */
height: auto;
}
/* ── Forms ── */
input, button, textarea, select {
/* Form elements use a different font than body by default */
font: inherit;
}
button {
cursor: pointer;
/*
Strips default browser button border and background.
Note: you must restyle every button intentionally after this.
Remove these two lines if your team prefers to style buttons explicitly.
*/
border: none;
background: none;
}
/* ── Lists ── */
ul[role], ol[role] {
/*
Scoped to lists with an explicit role attribute only.
Reason: VoiceOver on Safari stops announcing <ul> as a list
when list-style:none is applied without role="list".
Add role="list" to any <ul>/<ol> that is semantically a list.
*/
list-style: none;
}
/* ── Links ── */
a {
color: inherit;
/*
Removes underline. WCAG 1.4.1 requires links to be distinguishable
from surrounding text by more than color alone.
Ensure your links have sufficient color contrast OR restore underlines
where links appear inline within body text.
*/
text-decoration: none;
}
/* ── Tables ── */
table {
border-collapse: collapse;
border-spacing: 0;
}
/* ── Accessibility: Reduced Motion ── */
@media (prefers-reduced-motion: reduce) {
/*
Disables animations/transitions for users with vestibular disorders.
WCAG 2.1 Success Criterion 2.3.3 (AAA) requires this.
Uses 0.01ms instead of 0 so JS animationend/transitionend events still fire.
*/
*, *::before, *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}
/* ── Accessibility: Focus Ring ── */
:focus-visible {
/*
Shows focus ring only for keyboard navigation, not mouse clicks.
Never use outline:none without :focus-visible — it breaks keyboard nav.
currentColor adapts to any text color automatically.
*/
outline: 2px solid currentColor;
outline-offset: 3px;
}
What Each Part Fixes
1. Box Model — The Biggest One
This is the most impactful line in the entire file.
With border-box, the width you set is the width you get. Padding and border are included inside it, not added on top. This is how every developer intuitively expects sizing to work.
2. Margins & Padding
Browsers add default spacing to elements you never asked for:
Setting margin: 0; padding: 0; on * removes all of them. You add back only what you intentionally need.
3. Font Size Inflation on Mobile
When a user rotates their phone from portrait to landscape, mobile browsers automatically inflate font sizes to fill the wider screen. It looks broken. text-size-adjust: none; prevents this.
4. Text Rendering on macOS
On macOS, browsers use subpixel rendering by default, which makes text look heavy and bold. -webkit-font-smoothing: antialiased; makes text thinner and crisper — matching how designers see it in Figma.
5. Text Wrapping
text-wrap: balance; on headings prevents the last line from having a single orphaned word. text-wrap: pretty on paragraphs does the same for body text. These are new CSS properties from 2023 — use them.
6. The Image Gap Bug
Images are inline elements by default. Inline elements sit on a text baseline, which creates a mysterious gap below every image. Setting display: block; removes it instantly.
This is one of the most Googled CSS bugs of all time: “why is there a gap below my image?” The answer is always this.
7. Form Elements Use a Different Font
Inputs, buttons, textareas, and selects use a completely different font than the rest of your page by default — usually the system UI font at a different size. font: inherit; makes them match your body font.
8. Lists and Links
Navigation menus, card grids, and tag lists are all built with ul — but almost never need bullet points. Links are blue with underlines by default. Both are reset here so you style them intentionally.
The Two Accessibility Fixes
Reduced Motion
Some users have vestibular disorders — inner ear conditions where animations cause dizziness, nausea, or seizures. macOS, Windows, and iOS all have a “Reduce Motion” system setting for this.
The prefers-reduced-motion: reduce; media query detects that setting and disables all animations and transitions. The 0.01 ms duration (instead of 0) ensures JavaScript animationend events still fire correctly.
This is not optional. WCAG 2.1 requires that users can disable non-essential animation. This one media query handles it for your entire site.
Focus Ring
Many developers write outline: none; to remove the ugly default focus ring. This completely breaks keyboard navigation — users who can't use a mouse have no way to see which element is focused.
:focus-visible is smarter. It only shows the focus ring when navigating by keyboard, not when clicking with a mouse. Mouse users don't see it. Keyboard users do. Everyone wins.
How to use it ?
<head>
<link rel="stylesheet" href="/styles/reset.css" /> <!-- first -->
<link rel="stylesheet" href="your-styles.css" /> <!-- then yours -->
</head>
That’s it. One file, included once, fixes browser defaults for your entire project permanently.
Quick Reference
Great reminder 👏 🎯 CSS reset solves so many hidden browser quirks: 📦 Box model consistency 🖼️ Image spacing fixes 📝 Form font alignment Sometimes one small file can remove hours of debugging. 🚀
I'm not for modifying the values for every selector. I prefer adjusting only the selectors that really need to be adjusted. In a goal of avoiding unnecessary overwrites. Furthermore, in some cases, you will define even a third value because you want to add margin of 1rem for example on something. So the browser will do a, well no it have to do b, well no it have to do c... Not great in my opinion.
Wait, why is the 244px example wrong? I'm no CSS expert, far from it, which is why I'm intrigued. I would think you get 40px of padding (20 on both sides) and 4px of border (2 on both sides). Makes sense to me, so what am I missing?