Summary

Selector specificity is a process used to determine which rules take precedence in CSS when several rules could be applied to the same element in markup. It's a simple enough process, but has caught out a lot of developers at some point in their career.

Author: Gez Lemon

Contents

A Specificity Example

Consider the following snippet of CSS.

ul#blogroll a
{
    color: #c00;
}

a.highlight
{
    color: #00c;
}

Then consider the following snippet of markup.

<ul id="blogroll">
<li><a href="b1.html">Blog 1</a></li>
<li><a href="b2.html">Blog 2</a></li>
<li><a class="highlight" href="b3.html">Blog 3</a></li>
<li><a href="b4.html">Blog 4</a></li>
</ul>

Assuming these are the only two rules, what colour will the anchor element with a class name of highlight be?

  1. #c00?
  2. #00c?

The answer is option 1, as the specificity of the id attribute is higher than that of a class attribute.

Understanding Specificity

The W3C's CSS specification gives the following advice for calculating a selector's specificity.

A selector's specificity is calculated as follows:

  • count the number of ID attributes in the selector (= a)
  • count the number of other attributes and pseudo-classes in the selector (= b)
  • count the number of element names in the selector (= c)
  • ignore pseudo-elements.

Concatenating the three numbers a-b-c (in a number system with a large base) gives the specificity.

A number system with a large base requires having a means of representing each digit. Number bases smaller than 10 are simple enough, as they can use the digits 0 up to the base required minus one. For example, binary (base 2) uses the digits 0 and 1, and octal (base 8) uses the digits 0 to 7. Hexadecimal is a number system with a base of 16, and makes up for the shortfall in numerical digits by using the letters A to F. The problem we have is that we can't be sure how large a base we require for our number system; it could be any amount.

One solution suggested is to count the numbers in each column, and multiply by its denary position. In simple terms, multiply a by 100, b by 10, and c by 1 (remains the same), and then add them together. The problem with this approach is that it falls down when one of the columns has a count of ten or higher. For example, 11 elements (c) would outweigh a single class (b) using this method, but that cannot be true. A single class would outweigh 100 or more elements. The simplest way to cater for the large base is to just accept that b outweighs any number of c, and that a outweighs any number of b (which is true of any number system). Applying this logic to our two selectors, we can see that ul#blogroll a is more specific than a.highlight.

Calculate Specificity
a b c result
ul#blogroll a 1 0 2 1, 0, 2
a.highlight 0 1 1 0, 1, 1

More specific, not more important

I've worked on a few projects where developers have added the !important declaration to overcome specificity. The !important declaration overrides normal declarations, but is unstructured and rarely required in an author's style sheet. By default, author style sheets override user style sheets. To redress the balance, the !important declaration in a user's style sheet overrides the author's declarations, including those declared by the author as !important. Arguably, the !important declaration is best placed in the user's style sheet, and not the authors.

To adjust the specificity of our original selector, merely requires adding the id attribute to make it more specific.

ul#blogroll a.highlight
{
    color: #00c;
}
Calculate Specificity
a b c result
ul#blogroll a 1 0 2 1, 0, 2
ul#blogroll a.highlight 1 1 2 1, 1, 2

The specificity is now higher than the rest of the anchors in the unordered list, and the change of colour will be applied to anchor elements that have a class of highlight.

Category: Style.

Comments

  1. [selector-specificity.php#comment2]

    Selector Specificity eh? That's easy for you to say... *smile*

    LOL - it's one of those phrases that's easy to say until you start to think about it - then it's nearly impossible to say *smile*

    Posted by Gez on

  2. [selector-specificity.php#comment3]

    It took me a couple of reads, but I think I understand ***********

    Posted by Mike Abbott on

  3. [selector-specificity.php#comment4]

    When would there be a need for more than 10 elements in a selector? I think I understand specificity but the large number base has always confused me. I ignore it and it seems to work for me. Can anyone explain in simple speak what it means?

    Posted by Chris on

  4. [selector-specificity.php#comment5]

    When would there be a need for more than 10 elements in a selector?

    It's unlikely, but it's a possibility that needs to be catered for.

    I think I understand specificity but the large number base has always confused me. I ignore it and it seems to work for me. Can anyone explain in simple speak what it means?

    The following explanation is probably completely over the top for determining specificity, but as understanding number systems is important in computing, I'll attempt to try and explain number systems. I'm not very good at explaining things in simple speak, so if you still don't understand after this explanation, shout, and hopefully someone else will chip in with an example that's easier to follow.

    The number system we use and are familiar with is decimal. Like all number systems, the digits are arranged with the least significant digits to the right, and the most significant digits to the left. The right most column contains units (0 to 9), the next column to the left is for tens (represented by the digits 0 to 9), the next columns to the left is for hundreds (represented by the digits 0 to 9), and so it goes on in multiples of 10.

    Whenever any of the columns exceeds 9, the number divided by the base (10 in this case) is passed to the next higher order column, and the modulus remains in the column. For example, if you tried to represent the number 321 in the units column, it wouldn't fit as it's too large. Because the number is already in its denary representation, it's clear that 1 would be in the unit column, 2 in the tens column, and 3 in the hundreds column. When counting the parts of our selectors that contribute to each of the columns, we have no idea how many there will be. If we could guarantee that they would always be less than 10, then we wouldn't have to concern ourselves with the base. Because it could potentially be greater than 9, we have to ensure that our calculations allow for this.

    In any number system, the furthest column to the right is the units column. Each column to the left is the base to the power of its position. The units column's position is considered to be position zero. Any number to the power of zero is one. The next column to the left is the base to the power of 1, the next the base to the power of 2, and so on. For the purpose of simplicity, the following table shows the relationship between eight columns for binary, octal, decimal, and hexadecimal, as these are widely used number systems. As with any number system, the most significant columns are on the left. The values in the column are the decimal values for a single digit; in other words, multiples of that number gives the decimal representation.

    Base to the power of a digit's position in a number system
    Position 7 6 5 4 3 2 1 0
    Binary (base 2) 128 64 32 16 8 4 2 1
    Octal (base 8) 2,097,152 262,144 23,768 4,096 512 64 8 1
    Decimal (base 10) 10,000,000 1,000,000 100,000 10,000 1,000 100 10 1
    Hexadecimal (base 16) 268,435,456 16,777,216 1,048,576 65,536 4,096 256 16 1

    So from the table, 100 in binary is 4 in decimal; 100 in octal is 64 in decimal; 100 decimal remains the same; 100 hexadecimal is 256 in decimal.

    For completeness, let's represent decimal 321 in octal. Octal uses a base of 8, where each column can contain a digit between 0 and 7. Starting with the least significant column (the units), we take the modulus of the number divided by the base (8), and pass the rest up to the next column (multiple of 8s). 321 / 8 = 40, remainder 1. We therefore have 1 in our units column, and try to fit 40 into the 8s column. 40 / 8 = 5, remainder 0. We put 0 into this column, and 5 fits nicely into the next column (multiples of 64). Therefore, 321 decimal is 501 in octal (5 * 64 + 0 * 16 + 1 * 1 = 321).

    Hexadecimal has a base of 16, where each column can take a value between 1 and 15. As the columns need to be represented by a single digit with a value between 0 and 15, the hexadecimal system uses the values A, B, C, D, E, and F to represent 10, 11, 12, 13, 14, and 15 respectively. Going through the process of converting decimal 321 to hexadecimal, we start by dividing the number by 16, and taking the remainder for the units column. 321 / 16 = 20, remainder 1. Therefore we have 1 in our units column. 20 /16 = 1, remainder 4, so we have 4 in our 16s column. And finally, 1 fits nicely into the 256 column. Therefore, 321 decimal is 141 in hexadecimal (1 * 256 + 4 * 16 + 1 * 1 = 321).

    For the purpose of our selectors, we can potentially have an infinite number of id attributes, other attributes and pseudo-classes, and element names. In practice, that may rarely be the case, but it needs to accounted for, and why the W3C state:

    Concatenating the three numbers a-b-c (in a number system with a large base) gives the specificity.

    We have no idea what the base is, so it's simpler to leave each column in its denary representation (not necessarily a single digit), and accept that any number in the column to the left outweighs the current column. We typically use lazy evaluation instinctively when determining the larger of two numbers. For example, consider x = 321 and y = 123. You most likely check the most significant digit in x and y, and compare them first. Unless they were the same, you wouldn't consider the rest of the numbers. In this particular example, 3 is larger than 1, so there's no need to evaluate the rest, as x is clearly a larger number than y without considering the rest of the number. The same is true if we represent our selectors as a, b, c. If a from one selector is larger than a from another selector, that's it; the most specific is determined, and there's no need to continue evaluating the rest. If they're the same, you compare b. If one wins, we're finished, otherwise we compare c. If they end up being identical, whichever selector was defined last wins according to the cascading rules for CSS.

    Posted by Gez on

  5. [selector-specificity.php#comment6]

    Thank you - I get it now. One question. If hexadecimal can represent 4,096 with 4 digits why don't computers use that? You can get larger numbers with hexadecimal for fewer bits.

    Posted by C on

  6. [selector-specificity.php#comment7]

    Thank you - I get it now. One question. If hexadecimal can represent 4,096 with 4 digits why don't computers use that? You can get larger numbers with hexadecimal for fewer bits.

    It's possible to represent numbers up to 65,535 using 4 digits in hexadecimal. There can be 15 in each column: 15 * 4096 + 15 * 256 + 15 * 16 + 15 * 1 = 65535. Chips have digital circuitry that can be in one of two states: on or off. On or off is a binary relationship, so represented in binary. Binary numbers tend to get long very quickly, so for convenience, we may refer to them in octal or hexadecimal, but they're physically represented in binary by setting a series of on and off states. The reason that octal or hexadecimal are used for convenience instead of decimal, is because their base is one of the multiples of 2 used in binary, so can be converted easily. 10 isn't one of the multiples in binary, so is clumsy to convert.

    Converting binary to octal requires grouping the binary digits in 3, and converting them to their octal equivalent.

    So binary 10101010, grouped in threes from the right is 10,101,010 = 252

    Converting binary to hexadecimal requires grouping the binary digits in 4, and converting them to their hexadecimal equivalent.

    So binary 10101010, grouped in fours from the right is 1010,1010 = AA

    Converting the other way around merely requires reversing the process; a single hexadecimal digit becomes 4 digits in binary, and a single octal digit becomes 3 digits in binary. These calculations are very simple and can be done in your head quickly, so preferable to writing long binary numbers that can be difficult to read. This isn't possible with decimal, as groups of binary digits cannot be conveniently represented in a single column of the decimal system.

    Posted by Gez on

Comments are closed for this entry.