On this page
CSS Reset & Normalize
CSS Reset and Normalize: Build a Solid Starting Point
Focus keyword: CSS reset. Every browser ships with a default stylesheet, called the user agent stylesheet, that gives headings, lists, buttons and margins their first look. These defaults are slightly different between browsers. A CSS reset or a normalize stylesheet gives you a consistent and predictable base before you write your own styles. In this lesson you will learn the difference between reset and normalize, what a modern reset contains, how to write one in a cascade layer, and which defaults you should never remove.
Trending keywords covered in this lesson
CSS reset, modern CSS reset, normalize.css, Eric Meyer reset, user agent stylesheet, box-sizing border-box, CSS reset 2026, reset vs normalize, text-wrap balance, text-wrap pretty, line-height reset, image max-width 100%, form elements inherit font, prefers-reduced-motion reset, @layer reset, :where() zero specificity, focus-visible, list-style accessibility, dvh min-height, text-size-adjust, cross-browser consistency.
1. The Browser Default Stylesheet
Open any page with no CSS and you still see styled content: headings are bold and large, paragraphs have margins, links are blue and underlined, the body has an 8px margin and lists have bullets and indentation. This is the user agent stylesheet. You can see it in DevTools in the Styles panel, labeled as user agent stylesheet. Because browsers differ in details, such as default margins for form controls or fonts in buttons, designs could look slightly different from one browser to another.
2. Reset vs Normalize vs Modern Reset
- Classic reset (Eric Meyer): removes almost all default styles: margins, padding, font sizes, list styles. Everything is a blank slate, so you must style every element yourself.
- Normalize.css: keeps useful defaults and fixes real browser inconsistencies and bugs. Headings stay bold, lists keep bullets, and only the differences are smoothed.
- Modern reset: a short, opinionated set of rules that removes only the defaults that cause layout trouble (box model, image sizing, form fonts, margins) and adds a few quality-of-life improvements. Popular examples are the modern resets published by Andy Bell and Josh Comeau. Since browsers are now far more consistent, most projects today choose this option.
3. Essential Rules of a Modern Reset
- Box sizing:
*, *::before, *::after { box-sizing: border-box; }. Padding and border are included in the width, so sizes are predictable. - Margins:
* { margin: 0; }(or only for headings, paragraphs and figures) so spacing comes from your own system, preferably with gap and logical properties. - Line height and text size:
body { line-height: 1.5; }for readable text, andhtml { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }to stop mobile browsers from resizing text after rotation. - Media:
img, picture, video, canvas, svg { display: block; max-width: 100%; }. Block display removes the small gap under inline images and max-width keeps them inside their container. Addheight: autowhen images have width and height attributes. - Form controls:
input, button, textarea, select { font: inherit; color: inherit; }because browsers do not inherit fonts in form controls by default. - Text wrapping:
p, h1, h2, h3, h4, h5, h6 { overflow-wrap: break-word; },h1, h2, h3 { text-wrap: balance; }for even headline lines andp { text-wrap: pretty; }to avoid a single word on the last line. These are progressive enhancements. - Root stacking:
#root, #__next { isolation: isolate; }for single page apps, so z-index values of your page do not leak into overlays. - Reduced motion: turn off animation for users who asked for it with
@media (prefers-reduced-motion: reduce).
4. Put the Reset in a Cascade Layer
Declare a layer such as @layer reset, base, components, utilities; and place the reset in @layer reset. Because the reset is the first layer, every later rule wins over it no matter how specific the reset selector is. Use :where() to give selectors zero specificity, for example :where(h1, h2, h3) { margin: 0; }. Both techniques make a reset easy to override.
5. What Not to Remove
- Focus outlines: never use
outline: nonewithout a replacement. Keyboard users need to see where they are. Use:focus-visibleto style keyboard focus only. - List semantics: some screen readers stop announcing a list when
list-style: noneis applied. If you remove list markers on a navigation list, addrole='list'to the ul. - Link underlines inside text: links in paragraphs need more than color to be recognizable.
- The hidden attribute: keep
[hidden] { display: none; }working and do not override it with a display rule. - Default font size: do not set
html { font-size: 10px }. Use percentages or leave it at 100% so user preferences are respected, and use rem in your styles.
6. Performance Note on the Universal Selector
Using * for box-sizing and margin is safe and fast in modern browsers. The concern is about complex selectors such as body * * or heavy rules on every element. Keep your reset simple.
7. Using normalize.css or a Library
You can install normalize.css or a modern reset package and import it into the first layer with @import url('normalize.css') layer(reset);. A framework such as Tailwind ships its own base layer, called Preflight, which is a modern reset, so you usually do not need a second reset on top of it. Avoid stacking several resets, because rules can conflict.
8. Suggested Project Order
- Declare the layer order.
- Add the reset layer.
- Add the base layer: variables, typography, link and heading styles, focus styles.
- Add layout, components and utilities.
- Test with default browser fonts, zoom, keyboard navigation and reduced motion enabled.
9. Common Mistakes
- Removing all outlines and breaking keyboard navigation.
- Stacking a classic reset, normalize and a framework reset at the same time.
- Setting a pixel font size on html and ignoring user preferences.
- Forgetting that form controls do not inherit the font family.
- Resetting margins on everything and not adding a consistent spacing system afterwards.
- Writing reset selectors with high specificity so they are hard to override.
- Using a reset that hides list semantics without a role.
Key Takeaways
A reset removes the defaults that get in your way, while normalize keeps the useful ones and fixes inconsistencies. A modern reset is small: border-box sizing, zero margins, readable line height, responsive media, inherited form fonts, balanced text wrapping and reduced motion support. Put it in the first cascade layer, keep specificity low with :where(), and never remove accessibility features such as focus outlines.
Press Run to execute.
Press Run to execute.
Press Run to execute.
Press Run to execute.
Press Run to execute.
Press Run to execute.
Press Run to execute.
Write a short modern reset inside @layer reset with these rules: border-box sizing for all elements, zero margins, body line-height 1.5, images and videos as block elements that never exceed their container, and form controls inheriting font and color. Declare the layer order first.
Press Run to execute.
Show expected output
A layer order statement, then an @layer reset block containing box-sizing, margin, line-height, img and video rules and font: inherit for input, button, textarea and selectThis is a self-check — compare your result with the expected output above.
The form controls use a different font than the page and the focus outline was removed. Make the controls inherit the page font and color, remove the outline: none rule, and add a visible :focus-visible outline instead.
Press Run to execute.
Show expected output
Controls use font: inherit and color: inherit, outline: none is removed, and a :focus-visible rule gives a clear outlineThis is a self-check — compare your result with the expected output above.
Was this page helpful?