Cascade - CSS Fundamentals

Cascade - CSS Fundamentals

bottom up, the core of CSS

Let’s begin with the cascade, the C in CSS. How it works and how to work with it practically.

The Cascade

The cascade is an algorithm that defines how to combine property values originating from different sources. It lies at the core of CSS, as emphasized by the name: Cascading Style Sheets.

Fundamentally, CSS is about declaring style rules. There are often several ways to accomplish the same thing in CSS. Depending on which solution you use, you may get wildly different results when the structure of the HTML changes, or when the styles are applied to different pages. A key part of CSS development comes down to writing rules in such a way that they’re predictable. While predicting how rules behave requires an understanding of the cascade.

When two or more rules target the same element on the page, the rules may provide conflicting declarations. For example in the following markups:

<header class="page-header">
  <h1 id="page-title" class="title">Wombat Coffee Roasters</h1>
</header>

with the following CSS rules:

h1 {
  font-family: serif;
}
#page-title {
  font-family: sans-serif;
}
.title {
  font-family: monospace;
}

The title can’t have three fonts at one time. So, which one will it be?

To determine the answer, you should know that the browser follows a set of rules, so the result is predictable. In this case, the rules dictate that the second declaration, which has an ID selector, wins; the title will have a sans-serif font.

The cascade is the name for this set of rules. It determines how conflicts are resolved, and it’s a fundamental part of how the language works. When declarations conflict, the cascade considers three things to resolve the difference:

  • Stylesheet origin - where the styles come from. Your styles are applied in conjunction with the browser’s default styles.
  • Selector specificity - which selectors take precedence over which.
  • Source order - order in which styles are declared in the stylesheet.

High-level flowchart of the cascade showing declaration precedence - CSS in Depth (2018)

These rules allow browsers to behave predictably when resolving any ambiguity in the CSS.

Note that @import and @charset obey specific algorithms and aren’t affected by the cascade algorithm.

Stylesheet Origin

There’re different types, or origins, of stylesheets. Yours are called author styles; there are also user agent styles, which are the browser’s default styles. User agent styles have lower priority.

Some browsers let users define a user stylesheet. This is considered a third origin, with a priority between user agent and author styles.

The user agent styles set things you typically want, so they don’t do anything entirely unexpected. When you don’t like what they do to a certain property, just set your own values in your stylesheet.

!important Declarations

A declaration can be marked important by adding !important to the end of the declaration, before the semicolon:

color: red !important;

These declarations marked with !important are treated as a higher-priority origin, so the overall order of preference, in decreasing order, is this:

  1. Author important;
  2. Author;
  3. User agent.

The cascade independently resolves conflicts for every property of every elements on the page.

Specificity

If conflicting declarations can’t be resolved based on their origin, the browser next tries to resolve them by looking at their specificity. The browsers evaluates specificity in two parts: styles applied inline in the HTML and styles applied using a selector.

Inline Styles

If you use an HTML style attribute to apply styles, the declarations are applied only to the element. These are, in effect, “scoped” declarations, which override any declarations applied from your stylesheet or a <style> tag.

To override inline declarations in your stylesheet, you’ll need to add an !important to the declaration, shifting it into a higher-priority origin. If the inline styles are marked important, then nothing can override them.

Selector Specificity

The second part of specificity is determined by the selectors. Different types of selectors have different specificities. An ID selector has a higher specificity than a class selector. Similarly, a class selector has a higher specificity than a tag selector (also called a type selector).

The exact rules of specificity are:

  1. If a selector has more IDs, it wins (that is, it is more specific).
  2. If that results in a tie, the selector with the most classes wins.
  3. If that results in a tie, the selector with the most tag names wins.

For example:

/* (1) four tags */
html body header h1 {
  color: blue;
}

/* (2) three tags and one class */
body header.page-header h1 {
  color: orange;
}

/* (3) two classes */
.page-header .title {
  color: green;
}

/* (4) one ID */
#page-title {
  color: red;
}

The most specific selector here is (4), with one ID. The next specific is (3), with two class names. Selector (3) has a higher specificity than selector (2), despite its length: two classes are more specific than one class. The least specific selector is (1), even it has four element types (tag names).

Pseudo-class selectors (e.g., :hover) and attribute selectors (e.g., [type="input"]) each have the same specific as a class selector. The universal selector (*) and combinators (>, +, and ~) have no effect on specificity.

If you add a declaration to your CSS and it seems have no effect, often it’s because a more specific rule is overriding it.

A Notation for Specificity

A common way to indicate specificity is in a number form, often with commas between each number. For example, “1,2,2” indicates a specificity of one ID, two classes, and two tags. IDs having the highest priority are listed first, followed by classes, then tags.

The selector #page-header #page-title has two IDs, no classes, and no tags. We can say this selector has a specificity of “2,0,0”. The ul li, with two tags but no IDs or classes, has a specificity of “0,0,2”.

It now becomes a matter of comparing the numbers to determine which selector is more specific. A specificity of “1,0,0” takes precedence over a specificity of “0,2,2” and ever over “0,10,0”.

Occasionally, people use a four-number notation with a 0 or 1 in the most significant digit to represent whether a declaration is applied via inline styles. In this case, an inline style has a specificity of “1,0,0,0”. This would override styles applied via selectors, which could be indicated as having specificities of “0,1,2,0” or something similar.

Specificity Considerations

To raise the specificity, the quickest fix is to add an !important to the declaration you want to favour. This works because the !important annotation raises the declaration to a higher priority origin. Sure, it’s easy, but it’s also a naive fix and not recommended.

Instead of raising the specificity of the selector, we can also lower the specificity of the selector to get the same result. It is generally best to keep specificity low when you can, so when you need to override something, your options are open.

Source Order of Stylesheet

The third and final step to resolving the cascade is source order. If the origin and the specificity are the same, then the declaration that appears later in the stylesheet, or appears in a stylesheet included later on the page, takes precedence. This means you can manipulate the source order to style your featured link. If you make the two conflicting selectors equal in specificity, then whichever appears last wins.

For styling links (<a> tags), it should go in a certain order. That’s because source order affects the cascade. This listing shows styles for links on a page in the “corret” order, the so-called “LoVe/HAte” order:

a:link {
  color: blue;
  text-decoration: none;
}
a:visited {
  color: purple;
}
a:hover {
  text-decoration: underline;
}
a:active {
  color: red;
}

The cascade is the reason this order matters: given the same specificity, later styles override earlier styles. If two or more of these states are true of one element at the same time, the last one override the others. If the user hovers over a visited link, the hover styles take precedence. If the user activates the link (i.e., clicks it) while hovering over it, the active styles take precedence.

Cascaded Values

The browser follows these three steps — origin, specificity, and source order to resolve every property for every element on the page. A declaration that “wins” the cascade is called a cascaded value. There’s at most one cascaded value per property per element.

Inheritance

If an element has no cascaded value for a given property, it may inherit one from an ancestor element. It’s common to apply a font-family to the <body> element. All the ancestor elements within will then inherit this font.

Note that NOT all properties are inherited, however. By default, only certain ones are. In general, these are the properties you’ll want to be inherited. They are primarily properties pertaining to text: color, font, font-family, font-size, font-weight, font-variant, font-style, line-height, letter-spacing, text-align, text-indent, text-transform, white-space, and word-spacing.

A few others inherit as well, such as the list properties: list-style, list-style-type, list-style-position, and list-style-image. The table border properties, border-collapse and border-spacing, are also inherited.

Above is not quite a comprehensive list, but very nearly.

Special Values

There are two special values that you can apply to any property to help manipulate the cascade: inherit and initial.

Using the inherit keyword, you can override another value with this, and it will cause the element to inherit that value from its parent.

Sometimes you’ll find you have styles applied to an element that you want to undo. You can do this by specifying the keyword initial. Every CSS property has an initial, or default, value. If you assign the value initial to that property, then it effectively resets to its default value. The benefit of this is you don’t have to think about it as much. If you want to remove a border from an element, set border: initial. If you want to restore an element to its default width, set width: initial.

Declaring display: initial is equivalent to display: inline. It won’t evaluate to display: block regardless of what type of element you apply it to. That’s because initial resets to the initial value for the property, not the element.

THE END
Ads by Google

林宏

Frank Lin, PhD

Hey, there! This is Frank Lin (@flinhong), one of the 1.41 billion . This 'inDev. Journal' site holds the exploration of my quirky thoughts and random adventures through life. Hope you enjoy reading and perusing my posts.

YOU MAY ALSO LIKE

Using Liquid in Jekyll - Live with Demos

Web Notes

2016.08.20

Using Liquid in Jekyll - Live with Demos

Liquid is a simple template language that Jekyll uses to process pages for your site. With Liquid you can output complex contents without additional plugins.

Practising closures in JavaScript

JavaScript Notes

2018.12.17

Practising closures in JavaScript

JavaScript is a very function-oriented language. As we know, functions are first class objects and can be easily assigned to variables, passed as arguments, returned from another function invocation, or stored into data structures. A function can access variable outside of it. But what happens when an outer variable changes? Does a function get the most recent value or the one that existed when the function was created? Also, what happens when a function invoked in another place - does it get access to the outer variables of the new place?

Setup an IKEv2 server with strongSwan

Tutorials

2020.01.09

Setup an IKEv2 server with strongSwan

IKEv2, or Internet Key Exchange v2, is a protocol that allows for direct IPSec tunnelling between two points. In IKEv2 implementations, IPSec provides encryption for the network traffic. IKEv2 is natively supported on some platforms (OS X 10.11+, iOS 9.1+, and Windows 10) with no additional applications necessary, and it handles client hiccups quite smoothly.