The CSS language syntax is simple to learn in matter of hours, newbies can copy tons of CSS snippets and create a delightful website, even designers can write CSS. Things go well until you start to use many !important
or inline styles to achieve expected styles.
People tend to learn general programming language like JavaScript more deeply than HTML/CSS which often considered as already perfectly working ignorable stuff.
Besides CSS architecture, solid understanding of specificity and inheritance will help you write less code in a predictable style. The real power of CSS lies in the fact that it allows you specify styles from multiple sources and automatically calculates final styles.
The aim of this post is to develop your understanding of some of the most fundamental concepts of CSS —cascading, specificity, inheritance— which control how CSS is applied to HTML and how conflicts are resolved.
The actual process of calculating final CSS property for a given HTML element is the result of very complicated multiple steps:
At this step, all style declarations for a given element are collected from multiple sources like user agents, authors or users.
These declarations must go through all kinds of filtering and validating to see if they are from stylesheets that currently applies to this document and syntactically valid.
This is the core concept of CSS lies in its name Cascading Style Sheets. Solid understanding this step is a must because it is the only step which hugely influenced by developers as author
origin.
This step takes an unordered list of declarations from previous step collecting
, sorts them according to following criteria, in descending
order of priority:
Origin and Importance: based on combination of declaration origins (user agent, user, author, transition, animation) and !important
annotation. Using !important
as pros and cons depending on circumstances, use it sparingly.
- Transition
- User agent + `!important`
- User + `!important`
- Author + `!important`
- Animation
- Author
- User
- User agent
Specificity: based on selector’s specificity (The declaration with highest specificity wins) calculated for a given element as follows:
- Inline styles (1000)
- ID selectors (100)
- Class selectors, attributes selectors, and pseudo-classes (10)
- Type selectors and pseudo-elements (1)
- Universal selectors (0)
Order of Appearance: the last declaration in document wins in case of equal specificity.
- Imported style sheets are substituted in place
- Linked style sheets are concatenated in linking order
- Inline styles are place after all style sheets
This step comes in to place when there are no declarations try to set value for a CSS property of an element. There are several ways of defaulting:
Initial value: this initial value of a CSS property is defined in its definition table, the usage of initial value depends on whether it is inherited or not.
Inheritance: inherited
property get value from parent element, root inherited
property get initial value
, non-inherited
property gets initial value
all the time.
Explicit defaulting: instead of setting custom value for a property, you can explicitly use initial
keyword for resetting a property, inherit
for explicit inheritance and unset
for erasing all declarations.
We’re using many relative units (auto
, em
, rem
, vh
), relative URLs, percentages, or some human readable keywords (small
, normal
, bold
) to gain maximum flexibility in responsive design. This step will try to absolutize all values as follows:
This step will try to resolve property value as far as possible without laying the document, resolving network requests, or retrieving values from somewhere excepts its parent.
This step will format the whole document and finish remaining works from previous step why trying to calculate the absolute theoretical value used in the layout of the document.
This step especially focuses on cases like relative coordination between elements, auto layout, or flex layout. It requires a lot of calculations and yield almost perfectly usable absolute values for browsers to use.
This final step will make some adjustments based on a browsing environment like browser engine, media types, device pixel density, or operating systems before actually drawing.
Some common adjustments are rounding float values to integer values or font size based on available fonts.
This post focuses on an abstract
way how to parse CSS styles from multiple sources in a predictable ways. Understanding this flow will help us write more elegant and maintainable code. It also help us debug CSS styling easier with confidence.
The actual process how a browser applying CSS is different. It has many phases related loading CSS, parsing CSS, creating CSSOM tree and attaching style to DOM nodes. Those steps we’ve just read implemented in CSS parsing phase.