As a beginner diving into the world of web development, one of the fundamental technologies you'll encounter is Cascading Style Sheets (CSS). CSS is the magic behind the beautiful and responsive designs that make websites visually appealing. To become a proficient web developer, it's crucial to understand three key concepts: the CSS cascade, inheritance, and specificity.
The CSS Cascade
The CSS cascade is the process by which the browser determines which CSS rule will be applied to an element. The cascade works by assigning a weight to each CSS rule and then comparing the weight of each rule to determine which rule should be applied.
These CSS rules can come from various sources which include:
User Agent Stylesheets: These are the default styles applied by web browsers to HTML elements. Different browsers apply different styles depending on the element. For example, each browser styles a range input elements differently.
<input id="name" type="range" />
Author Stylesheets: These are the styles you, the developer, write in your CSS file(s). You define the colors, spacing, and so on. This allows your websites to have consistent design/views across all browsers.
User Stylesheets: These are the styles users apply to websites, which overrides the author's styles. However, this is uncommon among regular web users. Some frontend developers apply these stylesheets either during or after development to see what results could have been achieved.
Inline Styles: These are styles applied using the
**style**
attribute. Inline styles take precedence over external stylesheets.<input id="name" type="range" style="display: none" />
The weight of CSS rules is determined by the following factors:
Importance: CSS provides the
!important
keyword to mark certain rules as more important than others. However, it is recommended not to use the!important
keyword as it makes managing your styles more difficult. For example, the heading below will have a red color.h1 { color: red !important; } h1 { color: green; }
Specificity: Specificity is the algorithm CSS use to determine which rule/property should be applied to an element. More specific selectors take precedence over less specific ones. In short, the more specific a CSS rule is, the higher its weight will be. We'll delve deeper into specificity later.
Source order: If two CSS rules have the same importance and specificity, the rule that appears later in the CSS file will be applied. For example, the previous heading will have a green color cause it comes later in the source order.
h1 { color: red; } h1 { color: green; }
Inheritance
Inheritance is a process that allows certain CSS properties to be automatically passed down from a parent element to its child element(s). Think of inheritance as a family tree, where grandparents pass some traits to parents, and parents pass some traits to children.
However, not all traits from parents are passed to their children. Similarly, not all properties are passed down from parent to child elements. Text properties like color
, font
, line-height
, opacity
are inheritable, while other properties like borders
, padding
, margin
are not inheritable.
Here is an example that demonstrates how inheritance works:
<body>
<h1>Inheritance</h1>
</body>
body {
display: flex;
align-items: center;
justify-content: center;
height: 100vh;
color: coral;
font-family: 'Courier New', Courier, monospace;
border: 1px solid green;
}
In the above example, notice that the h1
inherits the color
and font-family
properties from its parent, body
, but it doesn’t inherit other properties. Specifying the same font-family
on the h1
isn’t necessary due to inheritance. Knowing which properties inherit and which don't can save you from unnecessary duplication of style declarations.
h1 {
font-family: 'Courier New', Courier, monospace; // not necessary
}
While some properties are inherited by default, you can use the inherit
keyword to explicitly inherit any parent property. From the previous example, if we want the <h1>
to inherit the parent's border, we can do so like this:
h1 {
border: inherit;
}
Specificity
Specificity is a method used to determine which CSS rule should be applied to an element in situations where conflicting rules exist. It involves assigning a weight to different CSS rules to determine which one is the most specific and therefore takes precedence.
Specificity is calculated using various selectors, which are organized in order of decreasing specificity:
id selector
: Styling byid
is more specific than any other selector. Think ofid
as the king of a village. You can only have a king in a village, which means you can only have a uniqueid
.#myId { color: red; }
class selector
: This selector includes classes like.myClass
, attributes like[type="search"]
, and pseudo-classes like:hover
. No matter the number of class selectors combined, they will never override an id selector. Think of them as the queens within a village..myClass { color: brown; }
Type selector
: This selector includes HTML tags likediv
,table
,section
and pseudo-elements like::before
and::placeholder
. Think of them as the heirs of a village.section { color: yellow; }
Universal selector
: This selector matches all elements of any type. It is denoted by*
. It does not impact the specificity value in any way.* { color: coral }
In the absence of all selectors, all elements on a page will have a coral color. The universal selector is commonly used to reset the
padding
,margin
andbox-sizing
of all elements on a page.* { padding: 0; margin: 0; box-sizing: border-box; }
Combining Selectors and Calculating Specificity
You might be wondering, what happens when these selectors are combined? When selectors are combined, their specificity values are also combined. Specificity is usually represented as a three-component value: [a, b, c]. These components represent the specificity of ID selectors, class selectors, and type selectors, respectively.
For example, let's say we have the following selectors on this element:
<section id="myId" class="myClass anotherClass">
Hello
</section>
#myId.myClass {
color: purple;
}
section#myId.myClass {
color: orange;
}
#myId.myClass.anotherClass {
color: orange;
}
section#myId.myClass.anotherClass {
color: brown;
}
section.myClass.anotherClass {
color: orange;
}
section {
color: blue;
}
In this scenario, each specificity here is:
myId.myClass
:[1, 1, 0]
section#myId.myClass
:[1, 1, 1]
#myId.myClass.anotherClass
:[1, 2, 0]
section#myId.myClass.anotherClass
:[1, 2, 1]
section.myClass.anotherClass
:[0, 2, 1]
section
:[0, 0, 1]
We will follow the following process to determine which specificity prevails:
First, eliminate any of the specificity without an
id
. This leaves us withmyId.myClass
,section#myId.myClass
,#myId.myClass.anotherClass
andsection#myId.myClass.anotherClass
.Then pick the ones with the highest number of class selectors. This leaves us with
#myId.myClass.anotherClass
andsection#myId.myClass.anotherClass
. Notice that even thoughsection#myId.myClass
has another selector, we discarded it because aclass selector
is always greater thantype selector
no matter the number of type selectors.Lastly, pick the one with the highest number of type selectors. This leaves us with
section#myId.myClass.anotherClass
which means thatsection
will have a brown color.
Combining the three: The CSS Cascade, Inheritance and Specificity
To combine these three concepts, consider the following example:
<section id="parent">
<header class="child">
<h1 class="grandchild">Hello, World!</h1>
</div>
</div>
#parent {
background-color: red;
}
.child {
background-color: blue;
}
.grandchild {
color: white;
}
The CSS rules above will apply a red background color to the parent section
, a blue background color to the child header
, and a white color to the text of the grandchild h1
.
If you change the color of the grandchild to black, it will override the parent and child rules because it has a higher specificity. Similarly, if you change the background color of the child to green, it will override the parent rule because it has the same specificity but comes later in the cascade.
#parent {
background-color: red;
}
.child {
background-color: blue;
}
.grandchild {
color: white;
}
.child {
background-color: green;
}
Finally, if you add an !important
keyword to the child's background color, it will override all other rules regardless of their specificity or position in the cascade.
#parent {
background-color: red;
}
.child {
background-color: blue;
}
.grandchild {
color: white;
}
.child {
background-color: green;
}
header {
background-color: aqua !important;
}
It's important to keep these concepts in mind when writing CSS to ensure that your styles are applied as intended and that you can easily manage and maintain your codebase.
Conclusion
To summarize, understanding the CSS cascade, inheritance, and specificity is crucial to becoming a CSS master.
The cascade determines which CSS rule will be applied to an element.
Inheritance allows certain CSS properties to be passed down from a parent element to its child element(s).
Specificity determines which CSS rule should be applied to an element in situations where conflicting rules exist.
!important
should be a last resort.Avoid combining multiple selectors. e.g
section#myId.myClass.anotherClass
Avoid
id selectors
, instead use other selectors likeclass
.
By mastering these concepts, you can write efficient and maintainable CSS code for your web projects.