Skip to content
wannacode

Selectors and the cascade

In short: A CSS selector picks the elements a rule applies to, and the cascade decides which rule wins a conflict: origin, specificity and order.

Problem

The price does not turn red

The shop starts a sale: the price of the bread must be red. A colleague adds a rule for .price at the very bottom of style.css. The price gets bigger and bold, but it stays dark gray.

HTML, CSS, JS

The console is empty: CSS gives no errors. The rule is the last one in the file and it does match the price, because the size and the bold weight come through. Only the colour does not. Another rule wants another colour for the same paragraph, and the browser picked that one.

The idea

Rule and selector

CSS says how each element of the tree looks. It says it with rules:

.price {
color: #c0392b;
font-size: 20px;
}
  • .price is the selector: which elements the rule matches.
  • Inside the braces are the declarations: property, colon, value and a semicolon.

Selectors search the tree, so they are like questions to it:

  • p (type): every <p>.
  • .price (class): every element with class="price". An element can have several classes: class="price sale".
  • #product (id): the element with id="product".
  • [type="email"] (attribute): the elements with this attribute and this value, for example input[type="email"].
  • article p (descendant, with a space): a <p> anywhere inside an <article>, at any depth.
  • nav > a (child): an <a> that is a direct child of <nav>.
  • .price.sale (no space): one element with both classes.
  • h1, h2 (list): the rule applies to both.

Styles are written mostly with classes. A type matches every paragraph on the site, and an id is for one element. A class goes exactly where you need it, and it does not depend on how the tree is arranged.

Try it

Match exactly these

Remove the underline only from the links in the menu (the link at the bottom stays underlined) and make both prices bold without making the descriptions bold. The properties are text-decoration: none and font-weight: 700.

HTML, CSS, JS

The idea

The cascade

The price from the start is matched by two rules, and both set color. The browser has to pick one. This rule for picking is called the cascade. It compares on three levels and stops at the first one that decides:

  1. Origin. The browser has its own styles: that is why <h1> is big and a link is blue and underlined. Your CSS beats them.
  2. Specificity. How precise the selector is. It is three numbers: how many ids, how many classes, how many types. p is 0,0,1, .price is 0,1,0, #product p is 1,0,1. Attributes ([type="email"]) and states (:hover) count as classes. The numbers are compared column by column from the left. As soon as one column differs, it is decided: 1,0,1 beats 0,1,0.
  3. Order. When the specificity is equal, the rule that comes later in the code wins.

The cascade decides each property separately. That is why font-size and font-weight from .price come through: nothing else competes for them. For color something does, and it is stronger.

There is also !important: color: #c0392b !important beats every normal declaration, whatever the specificity. It works, but after that every change to the colour needs a new !important. So it is a sign that a selector somewhere is too strong, not a fix.

See it

Watch the browser pick

Four rules and one element. Watch the browser decide color and font-size separately.

/* <article id="product">
<p class="price sale">€1.80</p>
</article> */
p { color: #555; font-size: 16px; }
.price { color: #c0392b; font-size: 20px; }
#product p { color: #222; }
.sale { font-size: 24px; }
Step by step, in words
  1. One element: a <p> with the classes price and sale, inside <article id="product">. All four selectors match it. Two of the rules want to set color and three want font-size.
  2. The specificity of a selector is three numbers: how many ids, how many classes, how many types. #product p has one id and one type: 1,0,1.
  3. color: three rules set it. The browser compares the first column: 1 beats 0, and it does not look further. #product p wins, even though .price comes after it.
  4. font-size: three rules again. The id column is equal (0). In the class column p drops out with 0. .price and .sale are equal in all three columns.
  5. Equal specificity: the rule that comes later in the code wins. .sale is on line 7, after .price, so font-size is 24px.
  6. Each property has its own winner: color from #product p, font-size from .sale. The rule .price applies to the element and still loses in both.

Here is the same code in the browser. The price is dark gray and 24px, as in the scene. Try swapping .price and .sale: the size changes and the colour does not. Open the developer tools (F12), select the price and look at the Styles panel: the losing declarations are crossed out.

HTML, CSS, JS

The idea

Inheritance

Some properties pass from the parent to the children. If an element has no rule of its own for them, it takes the value of its parent. This is inheritance.

  • The properties of text are inherited: color, font-family, font-size, font-weight, line-height, text-align.
  • The properties of the box are not: border, padding, margin, background, width. A border around the card does not mean a border around every paragraph in it.

An inherited value is the weakest. Any rule that matches the element directly beats it, even p with 0,0,1. Buttons and fields get their own font and colour from the browser, so they do not inherit. inherit asks for inheritance explicitly: button { font-family: inherit; }.

HTML, CSS, JS

The text of the card is green and in Georgia, but the border is only around the card. The button took the font because of inherit, and its colour stayed black: there is no such rule for color.

Try it

Fix the price

Make the price red (#c0392b) without !important. The description must stay dark gray (#222).

HTML, CSS, JS

Try it

Find the bug

The price has the classes price and sale, but it is not red. Why?

Pick the line with the bug.

Try it

Fill in

The menu has links and a nested list with more links. Only the links that are direct children of <nav> must have no underline.

nav  a {
  text-decoration: none;
}
More precisely, if you are curious

Origin. There are three sources: the browser, the user (settings and extensions) and the author, which is you. For normal declarations the author beats the user, and the user beats the browser. With !important the order flips: the user's !important beats yours. This way a person with poor eyesight can force a bigger font on every site.

style="…". A style in the element's attribute beats every selector in the file. So it is a trap too, like !important.

Columns do not overflow. 0,11,0 is not 1,1,0. Eleven classes do not beat one id: the comparison ends in the first column.

What does not count. * is 0,0,0. Combinators (space, >, +, ~) add nothing. Pseudo-elements (::before) count as types.

:is(), :not() and :where(). :is() and :not() take the specificity of their strongest argument. :where() is always 0,0,0: handy for default styles that any rule overrides.

Layers. @layer orders whole groups of rules, and the layer decides before specificity. Big style libraries use it so they do not fight with your CSS.

The keywords. inherit takes the value of the parent. initial is the starting value of the property from the standard (for display it is inline). unset is inherit for inherited properties and initial for the others. revert brings back the browser's style.

Exercise

The sale in the catalog

The catalog has three products, and the milk is on sale (the class sale on its card). All text in the catalog is #222, and the prices are bold. The price of the product on sale is red (#c0392b), and the old price inside it is gray (#777). The −20% label has white text (#fff) on a red background (#c0392b). The other prices stay #222. Do not use !important.

HTML, CSS, JS

Test

Test yourself

1. What is the specificity of #cart .item a?
2. First comes article p { color: gray; }, then .price { color: red; }. What colour is <p class="price"> inside <article>?
3. .card { color: green; border: 1px solid; }. What does a <p> inside the card get, if no other rule matches it?
4. Why is !important a bad sign in code?
5. Why are styles written mostly with classes and not with ids?

Made by Ivo Gergov as part of Evo Tech's campaign for accessible programming education ·