How Does CSS Work?
Understanding the Default Behavior of Styles in Our Browsers
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:
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> */
}
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>
.
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:
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.
Thedisplay
property’s initial value isinline
. Because of that, it doesn’t show up in the “inspect element” debugger on our browser.
- But when we inspect the
<div>
HTML element we will see thedisplay: 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 ofdisplay: inline
.
These different styles, such as shown in this example of the<div>
HTML element, come from the “User-Agent-Stylesheet” (Level 2).
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:
(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";
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.
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 theinherit
value. - Non-inherited properties — the
unset
value will act like theinitial
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
.
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:
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.