Weighing in on CSS selectors. Style Priorities in CSS

So, you already know that when several contradictory rules are applied to the same element at once, a competition occurs between them - they compare their specificities. The rule with the most specificity wins.

Of course, in order for the result of application to be predictable for you, you must learn to see which rule will prevail. It's time to figure out how specificity is calculated.

What does it look like

Specificity is a group of four numbers. For example, 0,0,0,0 or 0,0,1,2.

How to calculate specificity

Specificity is calculated by the selector. The counting rules are very simple:

  • each identifier present in the selector adds 0,1,0,0 to the specificity;
  • each class, pseudo-class or attribute adds 0,0,1,0 to specificity;
  • each element or pseudo-element adds 0,0,0,1 to specificity;
  • The universal selector and combinators are not taken into account.

Let's consolidate what we've learned and practice calculating specificity.

P (/*some definitions */) div p (/*some definitions */) p.note (/*some definitions */) form.feedbackForm input (/*some definitions */) #conten a:hover (/*some definitions */)

The first line is a single type selector. Specificity 0,0,0,1.

The second line is two type selectors. Specificity 0,0,0,2.

The third line is a type and class selector. Specificity 0,0,1,1.

The fourth line is two type selectors, one class and one attribute. Specificity 0,0,2,2.

The fifth line is the identifier, type, and pseudo-class selector. Specificity 0,1,1,1.

Who has won?

Comparing specificities is very simple. Whichever number is greater, the definition wins.

For example:
0,0,1,4 is greater than 0,0,1,2.
0,1,2,0 is greater than 0,0,2,1.

Keep in mind that the first (those to the left) numbers will always prevail over the subsequent (more right) numbers. They seem to be in a more senior category.

For example, 0,1,0,0 is greater than 0,0,8,9.

If it’s more convenient for you, you can mentally discard the commas and consider the previous example as 100>89. Just don’t get confused if some of the specificity digits are more than nine (which can happen with a very sophisticated selector). For example, if the specificity is 0,1,10,14, the commas cannot be discarded, otherwise all the digits will be confused.

Specificity and declarations

Specificity refers not to the entire rule as a whole, but to each specific ad. Therefore, it may turn out that the rule “works” not completely. For example:

Experimental text

Div ( color: #0f0; /* Specificity 0,0,0,1. */ font-weight: bold; /* Specificity 0,0,0,1. */ ) .box ( font-weight: normal; / * Specificity 0,0,1,0 */ )

Declaration from line 2 for element

will work fine, and the declaration from line 3 will be interrupted by the declaration from line 6 (since it has more specificity). The text inside our div will be green, but not bold.

This simplest example shows how important it is to be able to count specificities. Firstly, I can always create the right selector so that the ad I need wins. Secondly, if some ad did not work, you will immediately understand why and save a lot of time on debugging.

Note that the specificity of an identifier selector (0,1,0,0) is always higher than that of an attribute selector (0,0,1,0). Even if this attribute is ! That's why:

Form.feedback input ( color: #f00; /* Specificity 0,0,2,2. */ ) #name ( color: #0f0; /* Specificity 0,1,0,0. - win! */ )

Built-in styles

In theory, someone should have asked a long time ago, why, in fact, the very first digit in specificity? After all, we haven’t used it in any way yet!

That's right. The first digit is reserved for inline styles. Their specificity is considered to be 1.0,0.0. Thus, an inline style always overrides the style specified in an external or nested style sheet. For example:

This text will be green

Even if we use an identifier selector, we will not override the inline style.

Div#box ( color: #00f; /* Specificity 0,1,0,1. - not enough */ )

What does this mean? There is no control over built-in styles?

Don't worry. Of course, there is a way to override inline styles (and at the same time all other declarations).

Announcement!important

If you really need to, you can mark some ad as important. Such an ad will be considered the winner when comparing specificities. Yes, yes, including the winner and built-in styles. Let's change the CSS a bit for the previous example:

Div ( color: #00f !important; /* important announcement - immediate victory! */ )

Even the type selector, which was weak in specificity (0,0,0,1), overcame the built-in style, because its declaration has now become important!

Application details!important are well described in our CSS reference. If you want to know more details, follow the link.

Now that you're armed with the knowledge of calculating specificity, you can continue exploring inheritance in CSS.

IN large projects when growing CSS files, the situation is not very happy. Because of large quantity rules, it becomes difficult to determine which styles should be applied to a particular element. Some styles are inherited, others are defined through a whole chain of various selectors, and are used somewhere .class, somewhere #id, and somewhere in general inline-style.

To change styles for an element, we have to play with the selector weights. To make our work easier, we can follow several guidelines:

  1. Never use ID selectors in CSS. They have no advantages over class.
    • Everything you can do with ID, can be done with class.
    • ID cannot be reused.
    • Weight ID very big. Interrupt ID not even a hundred chain ones classes.
  2. Don't create unnecessary selectors. If .header-nav() works fine, then don't use the definition .header .header-nav(). In this case, nothing will change and there will be no benefit from it.
  3. Don't make selectors specific until you really need to. If .nav() works, then don't use it ul.nav(). Such a notation will only reduce the options for using this class. .nav, and will also increase the weight of the selector without obvious benefit.
  4. Force yourself to use .class, because these are ideal selectors.

Never use selectors heavier than necessary.

It's all very simple rules and following them is not so difficult.

ID weight reduction

Let's say you have a widget on your page and you want to style it:

...

And, for example, we cannot change HTML widget code to get rid of ID. So we do this:

#widget ( ... )

As a result, we have a definition for ID V CSS file, which is not good at all. Instead we can do the following:

{ ... }

This is an attribute selector. IN in this case this is no longer a definition for ID, and for the element. To be precise, the selector is saying, “Hey, find me an element that has the attribute id with meaning widget».

The beauty of this approach is that we have reduced weight ID up to class weight. But this is a hack.

Safe weight gain

We can increase the weight of the selector like this:

Btn.btn.btn.btn ( ... )

But I hope that I will never have to use such recording on projects.

Here we see that the color specified in .box a(), overwrites the color of the button text. As a result, the text merges with the background of the button.

Of course we can fix this if we put !important(jsfiddle.net/csswizardry/3N53n/1) but no thanks, let's get rid of that!

We can add an additional selector to the section .btn()(line 23) jsfiddle.net/csswizardry/3N53n/2 , but this is not the most The best decision. What if the problem with the button is not only .box, and anywhere else? Adding a new selector every time is a bad option.

Therefore we will duplicate .btn.btn: http://jsfiddle.net/csswizardry/3N53n/3

This is also not the best solution, but still we have increased the weight of the selector and the color on the button is now what it should be.

Now we know 2 ways to change the weight of the selector, but remember that these are still hacks and you shouldn’t get too carried away with them.

From the author: think of specificity as a score or degree that decides what styles to apply to an element. The universal selector (*) has low specificity. The id selector is high. Parent selectors of type p img and child selectors of type .panel > h2 have higher specificity than type selectors p, img and h1.

It seems difficult to calculate at first exact value specificity. The Selectors Level 3 specification says that to do this you need:

ignore the universal selector.

The values ​​of A, B and C together give final value specificity. An ID selector of type #foo has a specificity of 1,0,0. Type attribute selectors and type.chart classes have specificity 0,1,0. If you add a pseudo-class of type:first-child (for example, .chart:first-child), the specificity becomes 0.2.0. But simple standard or elemental selectors like h1 and p give only 0,0,1.

Note: Specificity Calculation

You can learn and calculate selector specificity using resources Specificity Calculator from Keegan Street and CSS Explain by Joshua Peek.

Complex selectors and combinators naturally provide greater specificity. Let's sort it out CSS example:

ul#story-list > .book-review ( color: #0c0; ) #story-list > .book-review ( color: #f60; )

ul #story-list > .book-review (

color : #0c0;

#story-list > .book-review (

color : #f60;

These rules are similar, but not the same. The first selector, ul#story-list > .bookreview, contains a type selector (ul), an ID selector (#story-list), and a class (.bookreview). The specificity is 1.1.1. The second selector #story-list > .book-review stores only the ID and class. The specificity is 1.1.0. Even though #story-list > .book-review is declared below ul#story-list > .bookreview, the high specificity of the latter causes elements with class .book-review to turn green rather than Orange color.

The pseudo-classes:link and:invalid have the same specificity as classes. a:link and a.external will have the same specificity of 0,1,1. Likewise, pseudo-elements like ::before and ::after have the same specificity as type and element selectors. If two selectors have the same specificity, cascading comes into play. Example:

a:link ( color: #369; ) a.external ( color: #f60; )

a : link (

color : #369;

a. external(

color : #f60;

Keeping specificity low helps prevent selector creep, or the tendency for specificity to continually increase. This often happens when new developers join the team, or when new forms of content are added to the site. Selector creep also adds to long-term maintenance headaches. You will either use increasingly more specific selectors to rewrite the rules, or you will have to refactor the code. Long selectors increase CSS weight files.

In Chapter 2 we will discuss how to maintain low specificity.

Conclusion

After reading this chapter, you should have a clear understanding of CSS selectors. In particular you should know:

how to use selectors and apply styles to specific elements, pseudo-elements and pseudo-classes;

understand the differences between pseudo-elements and pseudo-classes;

use the new pseudo-classes introduced in the Selectors Level 3 and 4 specifications;

calculate specificity.

In the next chapter, we'll cover the golden rules of writing maintainable and scalable CSS code.

An HTML element can be a target several CSS rules. Let's use a simple paragraph as an example:

We can change this paragraph simply by using tag name:

P ( color: blue; )

Or we can use class name:

Message ( color: green; )

Or we can use identifier:

#introduction ( color: red; )

Since the browser can only choose one color and apply it to this paragraph, then he must decide which CSS rule It has a priority over others. This is called priority in CSS (or specificity).

In our example, the paragraph will be red, because identifier more specific and thus more important than other selectors.

Order of CSS Rules

If you have identical selectors in your CSS, the last one will take precedence.

P ( color: green; ) p ( color: red; ) /* The paragraph will be red */

Calculation 100

There is one quick way find out how "strong" a CSS rule is by calculating specificity selectors:

  • identifiers cost 100;
  • classes cost 10;
  • selectors tag cost 1.

The selector with the highest "score" will prevail, regardless of the order in which the CSS rules appear.

#introduction ( color: red; ) .message ( color: green; ) p ( color: blue; )

MarkSheet is a free guide to HTML and CSS.

The #introduction ( color: red; ) rule is more specific than others because the identifiers must be unique throughout the entire web page, so there can only be one target element.

Message ( color: green; ) will target any An HTML element with a class="message" attribute and therefore less specific. The same applies to p ( color: blue; ) , which can be intended for any paragraph.

How to avoid conflicts

While writing your CSS, problems can easily arise. conflicting rules, in which the same property is applied several times.

To avoid this:

  • use only classes: Use .introduction instead of #introduction even if the element only appears once on your web page;
  • avoid using several classes to one HTML element: don't write

    A

    Which is semantically more descriptive;

  • do not use inline styles, such as
    .

Like any of my articles, this one begins with an introduction. So as not to speak different languages, let's start with the very basics in the world of CSS, that is, with terminology. I feel like a teacher at a university - how cool is that.

In order to successfully understand CSS and become a great specialist in this field, you first need to understand what is called a selector. The image below shows simplest structure CSS rules.

Selector is a string representing formal description structure on the basis of which an element or group of elements is selected in the document tree and the declared property block is applied.

There are different types of selectors. There are simple selectors, for example, consisting of one letter or one word, and complex ones, consisting of a large number of words and various syntactic structures.

I won't go into detail about selectors having to start with a letter and some other characters. I will also not talk about uninteresting truisms that can be found in any article on the topic CSS basics. This article will talk about the weight of CSS selectors, and if we talk in simple language- about him numerical representation and understanding.

Selector weight

No, we are not talking about them here real weight(there is none), as well as the number of characters. Such weight cannot be measured with scales, since it is not material in the usual sense and does not exist at all in our world of people. But it exists in the form of zeros and ones. It turns out that the selector can be weighed using a certain system, which we will talk about further.

What is selector weight?

The selector weight is the conditional four positions x, x, x, x, which are filled with zeros and ones in accordance with the contents of the selector. Each position has its own content:

  • Inline styles
  • Identifiers
  • Classes, attributes and pseudo-classes
  • Tags and pseudo-elements

How to read this?

Very simple. From right to left. The numbers on the left are of the highest order, so they have more weight, the numbers on the right, on the contrary, have the least weight. All this will become clear later, so you don’t even have to think about the meaning of this paragraph.

How to fill it out?

H1 ( color: #777; )

In this example, the selector is the h1 heading, which consists of a single tag. It turns out that opposite the “tag” column we put a one. The result is the following picture: 0, 0, 0, 1.

All this is great, but in real projects it is found only in the core of styles or normalize, which means that the task needs to be complicated.

#main .container article.post > header h1.giga ( color: #777; )

The example turned out to be more hardcore than the previous one and in real life worthy of the "most redundant selector" award. Such redundancy should be prosecuted by law, but we will talk more about this topic in a separate part of the article. Okay, let's go back to the example and work with scales a little.

Let's start from the left, since the only identifier at the beginning is #main . Next we see three classes.container, .post and .giga, as well as three tags article, header and h1. For even greater clarity, I will write this out in the form of stages:

// Selector #main .container article.post > header h1.giga // Initial weight 0, 0, 0, 0 // Identifiers #main 0, 1, 0, 0 // Classes, attributes and pseudo-classes.container 0, 1 , 1, 0 .post 0, 1, 2, 0 .giga 0, 1, 3, 0 // Tags and pseudo-elements article 0, 1, 3, 1 header 0, 1, 3, 2 h1 0, 1, 3, 3 // Summary #main .container article.post > header h1.giga => 0, 1, 3, 3

Let's write some crazy selector that I hope I will never see in anyone's code:

// Selector body.page-posts #main .container article.post ul.list-unstyled > li:first-child h2.article-title:hover ( color: #333; ) // Initial weight 0, 0, 0, 0 // Identifiers #main 0, 1, 0, 0 // Classes, attributes and pseudo-classes.page-posts 0, 1, 1, 0 .container 0, 1, 2, 0 .post 0, 1, 3, 0 . list-unstyled 0, 1, 4, 0:first-child 0, 1, 5, 0 .article-title 0, 1, 6, 0:hover 0, 1, 7, 0 // Tags and pseudo-elements body 0, 1 , 7, 1 article 0, 1, 7, 2 ul 0, 1, 7, 3 li 0, 1, 7, 4 h2 0, 1, 7, 5 // Summary body.page-posts #main .container article. post ul.list-unstyled > li:first-child h2.article-title:hover => 0, 1, 7, 5

And finally, for a complete understanding of the topic, there will be an example with attributes and pseudo-elements.

// Selector.main:before ( content: "3 .column.size-1of3"; ) // Initial weight 0, 0, 0, 0 // Identifiers 0, 0, 0, 0 // Classes, attributes and pseudo-classes. main 0, 0, 1, 0 0, 0, 2, 0 // Tags and pseudo-elements:before 0, 0, 2, 1 // Summary main:before => 0, 0, 2, 1

That's how things are.

In fact, the topic is very simple, but very important in practice for a mediocre (minimal) understanding of how the browser determines which ad block should be applied to a particular element on the page.

What if the weights of the selectors are the same?

Let's say that you have two or more selectors that somehow point to the same element. And so it happened that you counted or just looked at them, and the weight turned out to be the same. Don't despair, just the last selector declaration block in your CSS code from this group will be applied to the element. Something like this. This seems logical to me. Just like in the saying “those who didn’t have time were late,” but on the contrary: “those who were late were on time.”

Why is this necessary?

This is very interesting point, because you can understand which declaration block will be applied to an element without calculating the weight of the selector, that is, just by looking at it and thinking a little. However, the eye may fail, but the machine will not fail, it will calculate the weight and ruin the layout. The error, of course, is not the machine, but the layer between the computer and the chair, but that’s a completely different story.

Another application of this kind of calculation is services or scripts that build CSS specificity diagrams. This can be very useful for analyzing and assessing the redundancy of your code.

An interesting fact is that the only time I had to calculate the weight of the selector was a test from Mail.ru for some kind of certificate. If you're interested, I'll look for this test in my history.

Oh, I have a service especially for you that I came across while preparing to present this material: Specificity Calculator is a simple and effective selector weight calculator.

Selector specificity

Since we are talking about the weight of CSS selectors, you inevitably think about how to evaluate it: when it needs to lose weight, and when, on the contrary, it needs to gain weight. Just like humans, selectors still have an optimal weight.

It so happens that many web developers consider three nestings to be the optimal selector specificity. The maximum specificity of selectors is five nestings, and it is recommended to try not to exceed this number. Of course, there may be more investments, since anything can happen in life. Although, it is best if you still follow this recommendation, at least indirectly. This approach will help you save time in the future.

Okay, that's all great, but what does weight have to do with it? - Yes, it’s very simple, it directly depends on it. The more investments, the more weight. It's logical, though.

You can evaluate your CSS code using the CSS Specificity Graph Generator resource. Based on the CSS code you suggested, it is built interactive chart the specificity of your code, on which you can visually identify problem areas of your styles.

conclusions

Try to maintain optimal selector weights and refactor your code occasionally. At first it seems redundant and unnecessary, but in the end it can play a bad joke on you.