Skip to main content
Syllabus
On this page

CSS Reset & Normalize

Badar KhalilUpdated September 30, 2026 8 min read

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.

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

  1. Box sizing: *, *::before, *::after { box-sizing: border-box; }. Padding and border are included in the width, so sizes are predictable.
  2. Margins: * { margin: 0; } (or only for headings, paragraphs and figures) so spacing comes from your own system, preferably with gap and logical properties.
  3. Line height and text size: body { line-height: 1.5; } for readable text, and html { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; } to stop mobile browsers from resizing text after rotation.
  4. 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. Add height: auto when images have width and height attributes.
  5. Form controls: input, button, textarea, select { font: inherit; color: inherit; } because browsers do not inherit fonts in form controls by default.
  6. Text wrapping: p, h1, h2, h3, h4, h5, h6 { overflow-wrap: break-word; }, h1, h2, h3 { text-wrap: balance; } for even headline lines and p { text-wrap: pretty; } to avoid a single word on the last line. These are progressive enhancements.
  7. Root stacking: #root, #__next { isolation: isolate; } for single page apps, so z-index values of your page do not leak into overlays.
  8. 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: none without a replacement. Keyboard users need to see where they are. Use :focus-visible to style keyboard focus only.
  • List semantics: some screen readers stop announcing a list when list-style: none is applied. If you remove list markers on a navigation list, add role='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

  1. Declare the layer order.
  2. Add the reset layer.
  3. Add the base layer: variables, typography, link and heading styles, focus styles.
  4. Add layout, components and utilities.
  5. 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.

Try it Yourself HTML
Output

Press Run to execute.

Try it Yourself HTML
Output

Press Run to execute.

Try it Yourself HTML
Output

Press Run to execute.

Try it Yourself HTML
Output

Press Run to execute.

Try it Yourself HTML
Output

Press Run to execute.

Try it Yourself HTML
Output

Press Run to execute.

Try it Yourself JAVASCRIPT
Output

Press Run to execute.

Exercise: Write a Modern Reset in a LayerHTML

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.

Try it Yourself HTML
Output

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 select

This is a self-check — compare your result with the expected output above.

Exercise: Fix Form Controls and Focus StylesHTML

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.

Try it Yourself HTML
Output

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 outline

This is a self-check — compare your result with the expected output above.

Was this page helpful?