Skip to content

Instantly share code, notes, and snippets.

@preaction
Created August 16, 2016 18:25
Show Gist options
  • Save preaction/56d6080a7c4ae4bb38adfcb08c4a3c35 to your computer and use it in GitHub Desktop.
Save preaction/56d6080a7c4ae4bb38adfcb08c4a3c35 to your computer and use it in GitHub Desktop.
WIP blog post about CSS specificity

CSS Cascade and Specificity (or: My CSS isn't being applied to my markup!)

CSS is a declarative language: You declare that any elements which match a given CSS selector (the part before the curly braces) should apply the property values inside the rule (the part inside curly braces). But what happens when multiple selectors (and therefore multiple rules) apply to the same element? Or, how do you override styles for a particular element that you do not want? Why not just use !important to make sure that what we want is being set?

The steps that CSS takes in deciding which properties are chosen are well-defined, if you want to read the CSS specification on cascading, which is how the rules from different stylesheet types get layered and applied, and its companion CSS specification on specificity, which is how a CSS selector is ranked in importance. Since you're not a browser vendor, it might be a bit easier to read on.

When we're developing a web page, which CSS rules override which other CSS rules is made up of two steps:

  1. Compare the selector specificity, higher specificity wins.
  2. Compare the position of the rule in the stylesheet, later rules win.

Specificity

Calculating a selector's specificity is a bit confusing: The selector is broken into parts, the parts are organized into buckets, and the buckets counted to form the specificity. This results in a specificity that looks something like "1,0,0,1" or "0,2,3,0".

There are four buckets that make up specificity:

  1. IDs (#id).
  2. Elements and attributes (div, [attr=value])
  3. Classes (.class)
  4. I don't know

Each part of the selector is put into one of these buckets.

#foo 				/* 1 ID, 0 elements/attributes, 0 classes, 0 unknown */
#foo.fuzz			/* 1 ID, 0 elements/attributes, 1 class, 0 unknown */
.fuzz #foo			/* 1 ID, 0 elements/attributes, 1 class, 0 unknown */
#foo div.fuzz		/* 1 ID, 1 elements/attributes, 1 class, 0 unknown */
.fizz.buzz			/* 0 IDs, 0 elements/attributes, 2 classes, 0 unknown */

Then the buckets are compared one by one to rank the selectors. First the selectors with the most IDs. If the selectors have the same number of IDs, then the selector with the most elements/attributes wins. If they have the same number of elements/attributes, then the selector with the most classes wins.

/* The above selectors, ordered by specificity */

The more specific a selector, the more important the definition, the more powerful the properties inside. So, if we want to override a property set by a single class selector (.fizz), we can define another class and give our selector two classes (.fizz.buzz) to increase its specificity.

.fizz { color: red; font-weight: bold }
.fizz.buzz { color: blue }

<div class="fizz">This text is red and bold</div>
<div class="fizz buzz">This text is blue and bold</div>

Ordering

If two selectors have the same specificity, the one that comes last is the one that wins. So If we tried to override the color above with just one class, it would work depending on which CSS definition came first:

.fizz { color: red; font-weight: bold }
.buzz { color: blue }

<div class="fizz">This text is red and bold</div>
<div class="fizz buzz">This text is blue and bold</div>

.foo { color: blue }
.baz { color: red; font-weight: bold }

<div class="baz">This text is red and bold</div>
<div class="foo baz">This text is still red and bold</div>

This ordering of rules is defined by the order of <link> and <style> tags in your markup, which can make for confusing results where re-ordering your external stylesheets causes your design to break!

The best practices to prevent this is, unfortunately, constant vigilance. Keep your base stylesheets on top, and your custom site or page overrides on the bottom. If this isn't good enough, you can always make your custom selectors more specific by adding classes (even to the <body> tag).

!important

To complicate the relative simplicity of specificity and ordering, you are allowed to mark any property value as important by adding the !important tag. This was added mostly for users of web browsers to be able to add their own styles to your page, and as such, !important overrides the regular order of style sheet application (more on that below).

The Cascade

User-Agent stylesheet User stylesheet Document stylesheet

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment