The first part of writing CSS is specifying what you want to target with the styles you're about to write. It helps to know what all of the options are. In addition, being able to read, and write, good CSS depends on writing good selectors. However, it is very easy and common to not take the time to fully learn all of this stuff and go straight onto just writing CSS, and resort to hacky code to resolve conflicts when they inevitably arise. This is what makes style sheets unpleasant to work with as sites grow. You don't want to just start slapping !important
all over the place. As style sheets grow, that just makes them more confusing and difficult to debug. You want to be as specific as it makes sense to be when it comes to writing your selectors, so that they are easy to read and clearly communicate what they target. You can go too far, be too specific, with selectors, and that can make your CSS more difficult to read and provides no additional value. In order to write selectors that make sense, clearly communicate what they're targeting, and avoid conflicts–you need to know selectors and specificity. So, in this blog post I set out to cover CSS selectors and specificity, in order to provide a solid foundation for writing better CSS.
Selectors
CSS selectors can be divided into 5 categories:
- Simple selectors (select elements based on name, id, class)
- Combinator selectors (select elements based on a specific relationship between them)
- Pseudo-class selectors (select elements based on state)
- Pseudo-elements selectors (select and style part of an element)
- Attribute selectors (select elements based on an attribute or attribute value)
Simple Selectors
All CSS Simple Selectors
Selector | Example | Example description |
---|---|---|
#id | #firstname | Selects the element with id="firstname" |
.class | .intro | Selects all elements with class="intro" |
element.class | p.intro | Selects only p elements with class="intro" |
* | * | Selects all elements |
element | p | Selects all p elements |
element,element,.. | div, p | Selects all div elements and all p elements |
Combinator Selectors
A CSS selector can contain more than one simple selector, this is what is referred to as a combinator selector. Between the simple selectors, we can include a combinator.
There are four different combinators in CSS:
descendant selector (space)
- The descendant selector matches all elements that are descendants of a specified element. The following example selects all
p
elements insidediv
elements:
div p { background-color: yellow;}
child selector (>)
- The child selector selects all elements that are the children of a specified element. The following example selects all
p
elements that are children of adiv
element:
div > p { background-color: yellow;}
adjacent sibling selector (+)
- The adjacent sibling selector selects an element that is directly after another specific element. The following example selects the first
p
element that is placed immediately afterdiv
elements:
div + p { background-color: yellow;}
general sibling selector (~)
- The general sibling selector selects all elements that are next siblings of a specified element. The following example selects all
p
elements that are next siblings ofdiv
elements:
div ~ p { background-color: yellow;}
Pseudo-Class Selectors
A pseudo-class is used to target the specific state of an element. For example, it can be used to style an element like a link or a button when a user mouses over it, or style visited and unvisited links differently, or style an element when it gets focus, etc.
The syntax of pseudo-classes is:
selector:pseudo-class {
property: value;
}
There are many pseudo classes, a full list can be referenced here: https://developer.mozilla.org/en-US/docs/Web/CSS/Pseudo-classes
For simplicity, we'll just demonstrate some of the pseudo classes used on links, which are very common.
/* unvisited link */
a:link { color: #FF0000;}
/* visited link */
a:visited { color: #00FF00;}
/* mouse over link */
a:hover { color: #FF00FF;}
/* selected link */
a:active { color: #0000FF;}
Pseudo-Element Selectors
CSS pseudo-element selectors are used to style specific parts of an element. For example, they can be used to:
- Style the first letter, or line, of an element
- Insert content before or after an element
The syntax of a pseudo-element selector is similar to a pseudo-class selector, but has 2 colons (::
):
selector::pseudo-element {
property: value;
}
Multiple pseudo-elements can be combined. To show a simple example, the following code will set these styles on the first letter and first line of each element, but everything after the specified pseudo-elements will not be targeted by these rules.
p::first-letter {
color: blue;
font-size: 2em;
}
p::first-line {
color: green;
font-variant: small-caps;
}
A full list of pseudo-elements with specifications can be found here: https://developer.mozilla.org/en-US/docs/Web/CSS/Pseudo-elements
Attribute Selectors
Attribute selectors make it possible for you to target HTML elements that have specific attributes or attribute values. The [attribute]
selector is used to select elements with a specified attribute. The following example selects all a
elements with a target
attribute:
a[target] { text-decoration: underline; }
The [attribute="value"]
selector is used to select elements with a specified attribute and value. The following example selects all a
elements with a target="_blank"
attribute:
a[target="_blank"] { border: 1px solid yellow; }
The [attribute~="value"]
selector is used to select elements with an attribute value containing a specified word. The following example selects all elements with a title
attribute that contains a space-separated list of words, one of which is "header":
[title~="header"] { border-bottom: 5px solid yellow; }
In addtion, you have the following attribute selectors:
[attribute|="value"]
- Select elements with the specified attribute starting with the specified word.- Note: The value has to be a whole word, either alone, like class="top", or followed by a hyphen( - ), like class="top-text"!
[attribute^="value"]
- Select elements whose attribute value begins with a specified value.[attribute$="value"]
- Select elements whose attribute value ends with a specified value.[attribute*="value"]
- Select elements whose attribute value contains a specified value.
Attribute selectors can be particularly helpful for styling forms without using classes or IDs.
Specificity
If there are two or more conflicting CSS rules that point to the same element, the browser follows some rules to determine which one is most specific and therefore wins out. This is what is called specificity. Specificity is a common culprit behind why your CSS doesn't apply to some elements at times when you think they should. CSS Specificity goes in this general order (from most to least specific):
Inline styles -> IDs -> Classes, attributes and pseudo-classes -> Elements and pseudo-elements
At a high level, it's important to remember that any inline styles will win out over any other styles. ID selectors will win out over class or element selectors. Class selectors will win out over element selectors.
You can calculate specificity using the following method.
- If the element has inline styling, that automatically wins (1,0,0,0 points)
- For each ID value, apply 0,1,0,0 points
- For each class value (or pseudo-class or attribute selector), apply 0,0,1,0 points
- For each element reference, apply 0,0,0,1 point
You can generally read the values as if they were just a number, like 1,0,0,0 is “1000”, and so clearly wins over a specificity of 0,1,0,0 or “100”. The commas are there to remind us that this isn’t really a “base 10” system. You could technically have a specificity value of like 0,1,13,4 – and that “13” doesn’t spill over like a base 10 system would. The universal selector and inherited values have a specificity of 0.
If 2 rules have the exact same specificity, or are the same rules, in the external style sheet, then the lower rule in the style sheet is closer to the element to be styled, and will be applied.