How Does CSS Work?

Understanding the Default Behavior of Styles in Our Browsers

Elad Shechter
15 min readJan 19, 2021

When you start creating a new website, even before writing a single style, the browser applies styles to it — its default styles.

Have you ever asked yourself who decides what those basic default styles will be?

In this article, I want to teach you and show you the different levels of CSS in the browsers, how they work, and how we can control them.

When I started learning CSS, quite a long time ago, I was taught that all the browser’s basic styles come from the browser’s basic CSS file called the “User-Agent Stylesheet”. But over the years I came to understand that this is only a small part of where the basics styles come from.

Let’s explore the basics of CSS together!

Level 1 — CSS Properties Default Styles

Every CSS property has a Formal Definition section. These Formal Definitions are a part of the W3C’s standardization of CSS, the organization that is in charge of the web’s standards.

The Initial Value

Every CSS property has an initial value. This value doesn’t depend on the HTML element that it applies to. That is, a property’s initial value applies to all the HTML elements the same way.

I think that the majority of web developers are not aware of this basic thing.

Let’s take for example CSS positions:

.some-class{ 
position: absolute;
top: 20px;
left: 20px;
}

Have you ever asked yourself what the default values of the CSS Positions properties are?

Many web developers will fail to answer this simple question. The answer is that the position property’s initial value is static.
The other direction properties of CSS positions, top/bottom/left/right — their initial value is auto.

Remember — the basic styles of those CSS properties exist before you write a single style.

CSS Positions’ initial values:

position: static; 
top: auto;
bottom: auto;
left: auto;
right: auto;

The best way to find each of the CSS properties’ initial value is by looking it up on the MDN website in the “Formal Definition” section.

Example:

The CSS initial value for CSS Position —on MDN

CSS Inheritance

The second interesting definition that some of our CSS properties have is the “inheritance behavior”. This behavior means that a style applied to an element is inherited by its descendent elements.

By default, the primary properties that have this behavior are text ones, such as font-family, font-size, color, text-align, and other typography properties.

Think about it: when you give the <body> HTML element a font-size: 20px style, all the inner HTML elements are affected and inherit that font-size, until you declare a new font-size on an inner HTML element.

But when you declare padding: 20px on the <body> HTML element, it doesn’t affect the inner HTML elements. This is because the padding property doesn’t have the inheritance behavior by default.

We take this inheritance behavior for granted and use it almost without noticing.

HTML

<body>   Some text in the body HTML element
<div> some text on the div HTML element </div>
</body>

CSS

body{
font-size: 20px; /* affects both the <body> and the <div> */
padding: 20px; /* affects only the <body> */
}
“inherited properties” vs “non-inherited” properties

How can we know if styles are “inherited properties” or “non-inherited properties” when debugging in the browser?

When we debug the code above by using the “inspect element” feature on the <div> HTML element, the browser’s developer tools will show us that there are inherited styles from the <body> HTML element.

We will see the font-size property in intense colors because it’s an inherited property. But the padding property will be in faded colors because it isn’t inherited and it doesn’t affect the <div>.

Developer toolbar in Chrome — Show inheritance in CSS

The Formal Way to Check Inherited Behavior

The way to know if a property has the inheritance behavior, besides trying it yourself, is by looking it up on the MDN website under the “Formal Definition” section.

In the image below: we can see that some properties are inherited and some aren’t. This difference creates two types of CSS property groups:

  • “Inherited properties”
  • “Non-inherited properties”

Examples:

Inherited or not (MDN)

Level 2 — User-Agent-Stylesheet

By default, as we explored in “CSS Properties Default Styles” (Level-1), all the HTML elements get the same values for all CSS properties. Again, this is according to the “CSS Properties Initial Styles” (Level-1), which declares that all CSS properties have only one initial value.

But now comes the part that distinguishes between different types of HTML elements — the “User-Agent-Stylesheet” (Level 2).

The “User-Agent-Stylesheet” (Level 2) is the default CSS file that the browser companies implement in their browsers. This level establishes a direct connection between CSS and HTML by creating styles that distinguish between some of the tags. This is as opposed to the “CSS Properties Initial Styles” (Level-1) where the initial styles have no relation to the HTML elements.

For example, the display property’s initial value is always equal to inline. But as you may already know, the <div> HTML element has a display: block value by default. This change comes from the “User-Agent-Stylesheet” (Level-2).

The best way to understand these differences is by inspecting the <span> and <div> HTML elements:

  • When we inspect the <span> HTML element we won’t see any styles. That’s because the dev tools don’t show you the “CSS Properties Initial Styles” (Level-1) in the “inspect element” panel, and the user agent styles (“User-Agent-Stylesheet” (Level 2)), which the dev tools do show, don’t have any additional definitions for the <span> HTML element.
    Think about that: you don’t want to see all the CSS properties with their initial value (according to“CSS Properties Default Styles” (Level-1)) for every HTML element which you are debugging.
    The display property’s initial value is inline. Because of that, it doesn’t show up in the “inspect element” debugger on our browser.
The <span> HTML element — no styles from the browser
  • But when we inspect the <div> HTML element we will see the display: block style defined for it. This style differs from the CSS specification’s “CSS Properties Initial Styles” (Level-1), in which every element has a default value of display: inline.
    These different styles, such as shown in this example of the <div> HTML element, come from the “User-Agent-Stylesheet” (Level 2).
The <div> HTML element — display:block comes from the User-Agent-Stylesheet

Who Decides What the HTML Elements’ “Special” Styles are?

The decision of what the default styles on some of the HTML elements should be is again part of the W3C/WHATWG web standard. However, unlike the “CSS Properties Initial Styles” (Level-1), there are sometimes differences in how different browsers decide to implement this part.

An example of default Styles for HTML 4 — from the CSS 2.2 Specification:

The HTML 4 default style sheet

(If you want to see what these specifications look like, here are some of them: CSS 2.1, CSS 2.2, CSS 3).

Level 3 — Normalize CSS

Since the implementation of the “User-Agent-Stylesheet” (Level-2) doesn’t always follow the W3C web standard in all browsers, the “Normalize CSS” was invented. The “Normalize CSS” gives us an easy way to create consistent styles across different browsers.

Whats is “Normalize CSS” and How Does It Work?

The “Normalize CSS”’s main idea is to create a default HTML style that is the same across all browsers, overriding those styles in the “User-Agent-Stylesheet” (Level 2) that aren’t implemented equally by all browsers.

Normalize CSS” adds styles that override those user-agent styles that weren’t implemented equally across all the browsers. The normalize.css is a CSS file that addresses these issues so that browsers display all HTML elements the same way.

Example (from normalize.css):

/**
* Render the `main` element consistently in IE.
*/
main {
display: block;
}

The Normalize.css Project:

The normalize.css is a highly recommended project created by Nicolas Gallagher:

Level 4 — CSS Reset

There are many cases in which we wouldn’t want to use the browsers’ basic style. In most cases, we don’t want the default font-size and the margin values that we get from the “User-Agent-Stylesheet” (Level-2) styles. That’s the reason that “CSS Reset” was invented.

What is a “CSS Reset” and How Does It Work?

“CSS Reset” is a CSS file that overrides those styles of the “User-Agent-Stylesheet” (Level-2) whose effect we don’t want right from the beginning of our projects.

Using the example of the <h1> to <h6> HTML elements, the “CSS Reset” will override and thus remove the default styles from those HTML elements that are given by the “User-Agent-Stylesheet” (Level-2).

Example of heading reset:

h1, h2, h3, h4, h5, h6{ 
margin: 0;
font-size: inherit;
font-weight: inherit;
}

There are many types of “CSS Reset” files, and the most known one is by Eric Meyer. But I use my own “CSS Reset” file and in it I reset only the elements that I find necessary. An example of my “CSS Reset”:

/****** Elad Shechter's RESET *******/
/*** box sizing border-box for all elements ***/
*,
*::before,
*::after{ box-sizing:border-box; }
a{ text-decoration:none; color:inherit; cursor:pointer; }
button{ background-color:transparent; color:inherit; border-width:0; padding:0; cursor:pointer; }
figure{ margin:0; }
ul, ol, dd{ margin:0; padding:0; list-style:none; }
h1, h2, h3, h4, h5, h6{ margin:0; font-size:inherit; font-weight:inherit; }
p{ margin:0; }
cite { font-style:normal; }
fieldset{ border-width:0; padding:0; margin:0; }

But don’t be mistaken, “CSS Reset” doesn’t remove all the “User-Agent-Stylesheet” (Level-2) styles. For example, we still keep all the basic styles of display: block on HTML elements like: <div>, <ul>, <section>, <article> and many others.

Because of this, think of “CSS Reset” as an 80 percent reset of the default styles that the “User-Agent-Stylesheet” (Level-2) gives us.

Combining “Normalize CSS” and “CSS Reset”

In my projects I use both of these methods — Normalize and Reset. First, I use the “normalize.css”, which brings all the browser styles to the standard of “User-Agent-Stylesheet” (Level-2). Then I load my own “Reset CSS” file that removes all the styles that I don’t need.

Example (Using “Sass” import):

@import "resets/normalize.scss";
@import "resets/reset.scss";
“Normalize CSS” vs “CSS Reset”

If you want to go deeper into “Normalize CSS” and “CSS Reset” you can read my earlier article: “Normalize CSS or CSS Reset?!”.

Summarizing the Four Basic CSS Levels

We started by learning about the default CSS levels that we have in our browsers:

  • “CSS Properties Default Styles” (Level-1) — this is in charge of creating all the default values of all the properties we have in CSS.
  • “User-Agent-Stylesheet” (Level-2) — this is in charge of adding different default styles for some of the HTML elements.

Then, we learned how to control the CSS with your own CSS basic layers:

  • “Normalize CSS” (Level-3) — is in charge of keeping the default HTML styles, “User-Agent-Stylesheet” (Level 2), the same way across all browsers.
  • “CSS Reset” (Level-4) — overrides almost all the default “User-Agent-Stylesheet” (Level-2) styles whose effect we want to eliminate right from the beginning of our projects.

These four basic CSS layers create the basic styles of our project.

From here, let’s dive now into the “CSS Reset Keywords” (Part-5) and “the all Reset Property” (Part-6), which will give us the power to reset CSS any way we want.

Part 5 — The CSS Reset Keywords

In the last several years, CSS has given us the CSS reset keywords: initial, inherit, unset, and the revert, which have been fully implemented by browsers only in the last few months.

The CSS Reset Keywords

These CSS reset keyword values enable us to reset CSS any way we want. Let’s go over them:

The “inherit” and “initial” keywords

At the beginning of this article, I talked about CSS’s basic styles, “CSS Properties Default Styles” (Level-1). And you saw that there properties that have an inheritance in CSS and those who don’t have.

The ‘inherit’ value:

We will use the inherit value if we have a selector for which we want to reset a specific property that has a default inherited behavior, and keep that behavior.

For example, when I create “CSS Reset” styles in my project, I don’t want the <h1> to <h6> HTML elements’ default styles. Therefore I (re)set their font-size and font-weight properties to the inherit value. This way, they behave like regular HTML elements that didn’t get any font styles, and they will inherit the font-size and font-weight from the parent HTML element.

Example:

h1, h2, h3, h4, h5, h6 {
font-size: inherit;
font-weight: inherit;
}

The ‘initial’ value:

All the other CSS properties (which are the majority) don’t inherit by default. To keep this behavior when we want to reset these other properties, we will use the initial value.

This way, if we already declared some styles on a CSS selector, we can reset those properties to their initial value — those being the “CSS Properties Default Styles” (Level-1), by using the initial keyword.

Example:

.foo{ 
position: absolute;
top: 20px;
left: 20px;
}
.bar .foo{
position: initial; /* = static */
top: initial; /* = auto */
left: initial; /* = auto */
}

The ‘unset’ value:

As you can see, in most cases, we will want to reset the first group of properties — the ones with the “inherited properties” — to their inherit value. In the second group of properties — the “non-inherited properties” — we will want to reset with the initial value.

To enable this, we got a new keyword value called unset. The unset value will automatically reset a property according to its type:

  • Inherited properties — the unset value will act like the inherit value.
  • Non-inherited properties — the unset value will act like the initial value.

For example, position: unset will be equal to position: initial.
But when we use the unset value on the font-size property, for example, font-size: unset, it will be equal to font-size: inherit.

CSS types of Properties

This way you can reset all the properties with the same keyword value of unset.

Example:

.common-content * { 
/* inherit properties */
font-size: unset;
font-weight: unset;
/* non-inherited properties */
border-width: unset;
background-color: unset;
}
/* work the same way as */.common-content * {
/* inherit properties */
font-size: inherit;
font-weight: inherit;
/* non-inherited properties */
border-width: initial;
background-color: initial;
}

The Problem with the initial Value

In some cases, when we reset “non-inherited properties”, we might get an effect that we don’t want. For example: if we reset the display property on an HTML element whose default is block, such as the <div> HTML element, this will return us to the inline value.

display: unset; /* = initial = inline */

The reason for this, if you remember from the beginning of this article, is the distinction between “CSS Properties Default Styles” (Level-1) to “User-Agent-Stylesheet” (Level-2). In this distinction, we saw the reason that a <div> HTML element has the display: block declaration. This is because of the default style that the “User-Agent-Stylesheet” (Level-2) applies to the <div>s.

The initial style doesn’t come from the “User-Agent-Stylesheet” (Level-2), and because of this, the <div> HTML element with display: initial will return us to display: inline.

div{ 
display: initial; /* = inline */
}

To solve this problem, we have the fourth CSS reset keyword, which is called revert. The revert keyword allows us to reset to the browser’s default style, the “User-Agent-Stylesheet” (Level-2).

The ‘revert’ value:

The revert keyword is the smartest keyword of all the other “CSS reset keywords”. It does three operations for us:

  • It gives the default “inherited properties” — will get the inherit value.
  • It gives the default “non-inherited properties” case-1 — which have styles from the “User-Agent-Stylesheet” (Level-2), the default style of the specific HTML element that they are applied to.
  • It gives the default “non-inherited properties” case-2 — which have no styles on the “User-Agent-Stylesheet” the initial style of the “CSS Properties Default Styles” (Level-1).

Example:

div{ 
font-size: revert; /* = inherit */
display: revert; /* = block (Level-2) */
position: revert; /* = static (Level-1) */
}

A Summary of the CSS Reset Keywords

As you can see, these CSS reset keywords can be a little confusing in the beginning. But after you understand the browser’s basic logic they become a lot easier to use in controlling the browser’s basic styles.

I created this diagram that presents you how the CSS reset keywords work:

How the ‘initial', ‘inherit’, ‘unset’ and ‘revert’ CSS keywords are working

Creating A-Normal styles

It’s very important to remember that we can still reset “inherited properties” such as font-size with the initial value. This way we get the browser default font-size that’s not affected by the font-size in your stylesheet, example:

.common-content p { 
font-size: initial;
/* = 'medium' value - browser default size */
}

In the opposite direction, we can inherit properties like padding, which are by default “non-inherited properties”. This way, we can create, for example, an inner HTML element that inherits the padding size from its parent HTML element.

Example:

.box{ 
padding: 40px;
}
/* inner <div> of the <div class="box"> will inherit
the 40px padding */
div {
padding: inherit;
}

Live Code:

* This isn’t recommended, but if you can think about a real use-case, you can use this thing.

Part 6 — The “all” Reset Property

If you want to reset an HTML element that has a lot of CSS defined properties, there is a new property called all that you can use.

all: revert;

This new property resets several properties at once. This property’s common usage is with the unset and the revert keyword values (“CSS Reset Keywords” — Part 5), which define the behavior according to the property type and/or the HTML element’s default styles, which are the “CSS Properties Initial Styles” (Level-1) and the User-Agent-Stylesheet (Level-2).

This way, instead of going over each one of the properties, we can reset all of them together.

Example:

/* Good */
.common-content *{
all: revert;
}
/* Bad */
.common-content *{
border: revert;
font-size: revert;
display: revert;
position: revert;
top: revert;
left: revert;
}

We can use this, for example, to reset all the styles defined on a container, and be sure that we don’t get styles from the parent elements.

“The CSS Reset of the Future”

We can assume that several months from now, after the revert keyword gets implemented by the last minor browsers like “Samsung Internet” that still don’t support it, we will see new ways to create “CSS Reset” (Level-4).

Think how easy it will be to reset with the all property and the unset/revert values. Here is an example of the “future CSS reset”, as I imagine it:

Future CSS Reset (can look like):

/* remove all the styles of the "User-Agent-Stylesheet" styles
except for the 'display' property */
* {
all: unset;
display: revert;
}
/* preferred box-sizing value for web developers these days */*, *::before, *::after{
box-sizing: border-box;
}

* The “Samsung Internet” browser is built on the “Chrome” engine but gets its updates in a delayed fashion.

To Summarize

In this article, I tried to give all the necessary information on how the CSS that appears in the browser is built, how to customize it to your website’s needs, and how to control the CSS properties any way you want.

For more of my articles on this specific topic:
Normalize CSS or CSS Reset?!
Understanding the “Initial”, “Inherit” and “Unset” CSS Keywords

Sources I used for this article:
User Agent Style Sheets: Basics and Samples
StackOverflow — Browsers’ default CSS for HTML elements

Final Words

That’s all.
I hope you’ve enjoyed this article and learned from my experience.
If you like this post, I would appreciate applause and sharing :-)

You can follow me via Twitter.

Who Am I?
I am Elad Shechter, a Web Developer specializing in CSS & HTML design and architecture. Working at Yad2.

--

--