Which came first? A class or an id? …and everything you need to know about CSS selector precedence

CSS Selector precedense.

The three words that can either scare you or make your every single coding day a walk in a paradise.

No matter your developer experience, if you are in contact with front-end development, you should strive to get at least little understanding of the whole concept.

When I was starting out, I somehow knew, that if you put two “background-color” rules next to each other, the latter one wins. Or if you put two same classes and in the latter one redeclare previously declared property, it will be overridden. Also I somehow intuitively learnt, that if I first write an “id” selector, then write a “class” selector, the class will not override it.

And it was enough for a couple of years. I build dozens of websites without any issue. But there was always this “what if there is something more?”, “I’ve seen some css selectors like *= and I am like.. what the heck does that mean?”.

So I started looking, and found out there is a whole bunch of CSS selector I was not using and they have really clear rules how they should be used and what kind of selector overrides what”.

Once I grasped this advanced syntax, I thought to myself, “oh man! I could have learnt that years ago easily!”. Would not save my much time, but more importantly would give me confidence that I can code the layouts even more precisely.

And this is the whole point of this whole article. I am going to explain all of the various CSS selectors, in what order they apply to elements so as you get the whole picture of how this whole concept works. I will also provide you with a test at the end of this article, so as you can find out what is the exact place you can improve.

Is this article free?

In this article is included 10 years of my developer experience and X hours of hard working writing it all down so as it is easily understandable by people who are interested in the topic.

This article is a part of my CSS Understanding Course™ – in which I go deep down into the basics of CSS, explaining them in the detail that is actually needed and providing guidance where to go next for expert stuff.

I am charging $5.99 for this article, but providing you access beforehand. I need to make sure it is valuable for you, I would like to see it improved your developer experience and if it does keep the promise, I want you to make the transaction.

XXX STRIPE XXX BUTTON.

That’s all I wanted to say at the beginning, so let’s get right down to it!

Table of contents:

1. CSS Selectors Overview – Quick overview
1.1 Wildcard/universal selector
1.2 Type selectors
1.3 Class selector
1.4 ID selector
1.5 Attribute selectors
1.6 Pseudo selectors
1.6.1 Pseudo-class selectors
1.6.1.1 Pseudo-class-state selectors
1.6.1.2 Pseudo-class-contextual selectors
1.6.2 Pseudo-element selector
1.7 Special combining selectors
1.7.1 Selector list selector
1.7.2 Descendant combinator selector
1.7.3 Child combinator selector
1.7.4 Adjacent sibling combinator selector
1.7.5 General sibling combinator selector

2. Selector precedence
2.1 Wildcard selector
2.2 Element selectors
2.3 Class selectors && attribute selector
2.4 ID selectors

3. What influences selector precedence indirectly
3.1 !important rule
3.2 Places to write stylesheets matters
3.2.1 External stylesheets && <style /> tag
3.2.2 style=”attribute”
3.3 Media queries

4. Examples
4.1 Simple selectors
4.2 Various combined selectors
4.3 Selectors considered to be good practise
4.4 Selectors considered to be bad practise
4.5 Crazy examples you’ve never seen (but I tried and they worked!)

5. How to take everything you’ve just learnt

1. CSS Selectors Overview – Quick overview

Before we start, I’d like us to go through all of the available CSS selectors, but I will do only quick bird-view, not to overwhelm you with the details.

1.1 Wildcard selector

The first one will be the wildcard selector or * or an asterisk is a selector that target all of the elements within the HTML specification.

1.2 Element selectors

An element selectors are selectors with the same name as the element, for example a <div /> element will have `div` selector, <section /> element will have `section` css selector and so on.

1.3 Class selector

A class selector is a selector with the name of your own choice preceded by a dot.

1.4 ID selector

An ID selector, similarly to the class selector, is a selector with the name of your own choice preceded by a

1.5 Attribute selectors

Attribute selectors allows you to target elements based on the element’s attribute name and their respective values. When using attribute selectors. So for example `input[readonly]`, `div[class=”green”]` or even advanced forms like `span[class*=”active”]`.

1.6 Pseudo selectors

The name of this group of the css selectors can be a bit dissuasive, but don’t worry – it’s so simple. CSS selector group’s name starting with “pseudo” represent all css declarations that start with a “:” (colon) or a “::” (double-colon).

1.6.1 Pseudo-state selectors

To understand state selectors you need to know a bit about how HTML elements behave. In short, for example inputs can be focused, a button can be in “active” state, checkboxes can be in indeterminate state, form elements can be disabled etc. And since the html elements can enter various states, those various states can be targeted via css. For example if a user is writing text into an <input /> element, you can make the color of the text inside the input more contract to the background and so on.

1.6.1.1 Pseudo-class-state selectors

:invalid, :required, :empty, :checked, :disdabled…

1.6.1.2 Pseudo-class-contextual selectors

::first letter, nth-child…

1.6.2 Pseudo-element selectors

I am sure you know css declarations, like for instance `::before`, `::after` or `::placeholder` – they are called pseudo-elements and from that name is derived the name for the CSS selectors to target them, i.e. `pseudo-selectors`.

1.7 Special combining selectors

Here we are talking about ways, how you can combine all of the selectors used above to achieve even better results. And it’s not only “I need to style a span inside a div container”; it is also for example “to style only direct descendants of this element”, “to style an element only if followed by another element”… and so on. Let’s go through them real quick.

1.7.1 Selector list selector

Very useful selector, if you want to apply the same style onto multiple elements. For example very useful for css resets or normalizers, for example `main, header, footer, article, section {display block;}`.

1.7.2 Descendant combinator selector

This selectors targets elements, who are descendants of preceding selectors, disregarding the level. They just need to follow the structure, for example the declaration `body a {text-decoration: none;}` will make all `<a />` elements within your website underlined.

1.7.3 Child combinator selector

This selector targets elements, who are direct descendants of preceding selectors – and the word direct is a key word, because unlike aforementioned Descendant combinator selector that targets elements in any level, Child combinator selector targets them only only if they are directly nested. For example the declaration `nav > ul > li` will target only the 1st level of list items within the navigation menu, whereas for example `nav > ul > li > ul li` will target every list item that is nested at least for two levels.

1.7.4 Adjacent sibling combinator selector

This selector allows you to target a html element only if it is preceded by another element. It is declared as a “+” (a plus sign), for example `div + div { color: red; }` << and this example means, that the 1st div will not have a red color applied, but each div that follows, will.

1.7.5 General sibling combinator selector

This selector is very often mistaken for the “adjacent sibling combinator selector” and it is really understandable why. General sibling combinator selector is a “∼” (a tidle character) and it targets all elements that follows a predefined element, but not neccesarily directly.

I know, I know 🙂 – let’s look at the example.

Difference between Adjacent sibling combinator selector and General sibling combinator selector:

Adjacent sibling combinator selector

//css code
div + div { color: red;}

//html code
<div>Peter</div>
<div>Carol</div>
<h3>Jane</h3>
<div>Josephine</div>
<div>Gabriel</div>

Only the DIVs with the text “Carol” and “Gabriel” will have the red color, because they follow a DIV within the html structure.

General sibling combinator selector:

//css code
div ∼ div { color: red; }

//html code
<div>Peter</div>
<div>Carol</div>
<h3>Jane</h3>
<div>Josephine</div>
<div>Gabriel</div>

DIVs with “Carol”, “Josephine” and “Gabriel” will have the red color, because they follow a DIV tag as declared within the css code.

2. Selector precedence

Let’s do it! 😤 We’ve talked about what kind of selectors you can come across in CSS, now’s the time to learn how they affect each other.

2.1 General rule

In CSS there is a general rule you can rely upon all the time, and that is that what comes next – it is applied. And it means if you were to write multiple declarations of styles or selectors, the very last rule applies and everything declared above is ignored.

2.2 Specific rules

Even though general rule applies all the time, CSS offers you a way to be more specific and target specific use cases.

For example, when coding a website from scratch, you might wanna configure a default font size on the website to be always, say 14px, but when it comes to headlines, you would like to have H1 to be 28px, H2 to be 22px, H3 to be 18px… and so on and this is why CSS allows you target those elements by their tag name and override the general rule.

Another use case is that let’s say you have a list of articles, each of them having a <H3 /> headline, but you want the headline in the first article with a red color. You can put an ID to the headline, set the color within the CSS to red and your work there is done…. the story continues… in a month you decide you want have all the headlines to be the orange color, so you find your style.css, write down a “h3 {color: orange}` and it will not get overriden! What’s going on there? 😱

Well, what happened is that you might have wrote that declaration after the ID declaration and as the precedence rules state, that is impossible in CSS.

And now, let’s bring some light into the whole topic and finally understand what should come after what so as you will no longer would have experience any headaches when finding out, what is going on there! 🤔

2.3 Overriding is just a prioritization game

In CSS, each of the selector type has its own priority and does not matter in what order you write them, they will always follow their own priority.

For example, consider the following code:

body { background-color: red; }
div { background-color: blue; }
p { background-color: purple; }

* { background-color: green}

Even though you used the wildcard/universal selector as the last declaration, the body, the div and the p selectors will force the background color declared at the begining of the stylesheet, because they have got higher priority.

And this is also a reason, why the wildcard/universal CSS selectors are written usually at the beginning of stylesheet, because it makes logical sense since they can be overridden by any other selector.

2.3.1 Wildcard selector

The wildcard selector is the one that is at the beginning of the chain, because every single other declaration can override it. That’s why it is a good practise, with resets or normalizers that the very first declaration you’ll see within the stylesheets is declared by the wildcard.

2.3.2 Type selectors

The next in the chain are so called “type selectors“. Type might be misleading here and the better name could have been “tag selectors“, because these selectors got their name by an element tag name. You’ll see them within the resets or normalizers at the top after the wildcard selector. They mostly set global styles for elements. For example every paragraph on the site might share the same line height and font size and that’s why it is easily set at the top of the page.

2.3.3 Class selectors && attribute selector

This group of selectors do have the same priority but let’s talk about them separately.

2.3.3.1 Class selectors

The single most used type of CSS selector is this one. For example `.wrapper`, `.button`, `.block` and do so on.

Here’s an interesting thing. Look at the example below:

.wrapper {
background-color: orange;
}

* {
background-color: blue;
}

div {
background-color: purple;
}

<!-- will have an orange background -->
<div class="wrapper"></div>

As you can see at the example above, the DIV will have an orange background in spite of the wildcard and element selector being declared below it, because it has a larger precedence.

2.3.3.2 Attribute selectors

Attribute selectors are very little used type of CSS selectors, which is pretty sad in spite of their usefulness. They have the same precedence as the class selectors, meaning that if you were to write an attribute selector first and the class selector below, the latter would be used and vice versa.

2.3.4 ID selectors

An ID selector is one of the most famous one. It is a selector you write with a “#hashtag” and it does override every single selector mentioned above. It has precedence from:
– class && attribute selectors
– type selectors
– wild card selectors

3. What influences selector precedence indirectly

And that’s it as far as css selector precedence goes. Now let’s move on a bit and talk about what invisible forces influence CSS selector precedence indirectly.

3.1 !important rule

And the very last rule that is important to mention when talking about CSS selector precedence is !important rule. If you haven’t seen or used it, it looks like this:

.wrapper {
background-color: orange;
}

div {
background-color: blue!important;
}


<!-- will have the blue color -->
<div class="wrapper"></div>

So what is happening above? As I talked about earlier, the class selector does have a precedence before a type selector, but not when an !important rule is used.

And the way the !important rule works is by putting an importance into the declaration so as it can override any previously declared styles.

3.2 Places to write stylesheets matters

There are three way of declaring styles for elements:
– external stylesheets (<link rel=”stylesheet” href=”… )
– <style /> tags
– style=”” attributes

Where you put your styles matters. Just a bit, but still matters 🙂.

3.2.1 External stylesheets && <style /> tag

The two ways of declaring CSS styles are identical and the latter declaration wins.

3.2.2 `style=””` attribute

The style=”” attribute does in fact have precedence before any other CSS selector. Also !important can be used also here and it will override any other !important declarations.

3.3 Media queries

Let’s not forget about this piece of code. Media queries also come into play, when talking about overriding css properties. When using media queries it needs to be noted that you should override the exact selector. For example:

div + div {
margin-left: 10px;
}

@media (max-width: 768px) {
div {
margin-left: 0;
}
}

it will not work, because the selector precedence is coming into play and you’re basically overriding a “compound selector” with a “type selector”, which will not work.

4. Examples

4.1 Simple selectors

Simple selectors are the ones that are basically single declaration selectors, for example:

div {
display: flex;
}

p {
line-height: 1.8em;
}

* {
font-size: 13px;
}

#site-info-item-23 {
display: none;
}

.wrapper {
background-color: green;
}

4.2 Various combined selectors

Those selectors can be named combined, if they consist of more than one CSS selector, for example:

div + div {
margin-left: 10px;
}

h1::first-letter {
font-size: 22px;
}

#main-wrapper article h2 {
line-height: 1.9em;
}

4.3 Selectors considered to be good practise

4.4 Selectors considered to be bad practise

4.5 Crazy examples you’ve never seen (but I tried and they worked!)