Precise Targeting with CSS Selectors

Writing CSS Selectors Like a Boss

Carson Ford
5 min readJan 5, 2017

Modern websites are built around the concept of separating content from presentation: HTML defines the structure of a page while CSS applies the styling. The page that users interact with is the result of multiple files of code working together in unity.

Separating content and presentation is valuable for many reasons, but it means style rules need to be able to target the element they are intended for from another file. CSS achieves this with selectors.

Diagram of a CSS rule

Selectors specify which elements in the HTML a set of declarations are supposed to target. There are many types of selectors that make it easy to traverse the DOM. It is up to developers to know the best one to use for the task at hand.

CSS Selectors


Selecting an element by its HTML tag is the most obvious way. Including parent elements in the selector increases specificity.

ul li { … }


Using combinators is an effective way to refine tag selectors. Combinators make it easy to target descendants and siblings of elements. For instance, the following selector will only target li elements that are the direct descendant of a ul. Any nested li elements will be excluded.

ul > li { … }

There are three types of combinators: child, adjacent sibling, and general sibling.

> Child: targets the immediate child of an element

+ Adjacent Sibling: targets the following adjacent sibling of an element

~ General Sibling: targets all the following siblings of an element


Adding an ID attribute to an HTML element is a common way for CSS to target it. Keep in mind that an ID can only be used once on a HTML page. In a stylesheet the # character indicates an ID.

#main-menu { … }


Unlike an ID, class attributes can be applied to multiple elements, making it easy to use the same style declaration over and over. This is especially useful for keeping code DRY. A class selector is proceeded by the . character.

.menu-item { … }


Elements like links and form inputs tend to require multiple attributes to work properly. Instead of adding even more attributes to set a class or ID, the attribute selector can be used to target specific elements. Attribute declarations are placed inside [] .

input[type=”checkbox”] { … }

Substring Matching

Attribute selectors can be further specified with substring matching by comparing letters or entire words against the attribute’s value. The following code will select anchor elements that have a href value beginning with the letters “https://”, effectively targeting only external links.

a[href^=”https://”] { … }

^ matches characters at the beginning of a value

$ matches characters at the end of a value

* matches any part of the value


Psuedo-classes are a convenient way to select elements based on their state or location in the DOM. The syntax works by appending the desired pseudo-class to a tag, ID, or class name.

li:first-child { … }

Here are a few useful psuedo-classes:

:first-child selects the first instance of an element relative to its parent

:last-child selects the last instance of an element relative to its parent

:only-child selects elements that are the only child of the parent

:empty selects elements that do not contain any content

:nth-child() selects the nth child element of a parent specified by the index value provided in parenthesis

:nth-of-type() is like :nth-child, but only indexes elements with the same specified tag

:not() selects all elements except what is specified in the parenthesis

There are many more psuedo-classes in addition to the ones listed here. Check the Mozilla Developer Network for a complete list with examples.

Selector Example

The amount of CSS selectors to choose from means that there is usually more than one way to select a HTML element. Take the following code block for example:

<img id=”logo” src=”logo.png” alt=”logo”>
<ul id=”social-menu”>
<li class=”menu-item”>
<a href=”">
<img src=”facebook-icon.png” alt=”facebook”>
<li class=”menu-item”>
<a href=”">
<img src=”twitter-icon.png” alt=”twitter”>

This markup provides the structure for a simple header with a logo and social media links. Let’s see a few different ways to target the unordered list.

ul { ... }

Since the unordered list is the only one in this scenario the ul tag alone is sufficient as the selector.

#social-menu { … }

Using the ID affords much more precision in case other unordered lists are included later.

header > :last-child { … }

Though somewhat elaborate, selecting the last child of the header’s immediate children also targets the desired element. It is important to note that without the child combinator the last child of each of the header’s descendants would also be selected.

The psuedo-classes :nth-child(2) or :nth-of-type(2) or even :not(:first-child) would also work in the last example, but :last-child makes the most sense here.

Clearly there are many ways to select an element — some simpler than others — but what about reaching an element nested deeper? Below are a few ways to select the image element in the first list item of the unordered list.

li:first-child img { … }

The psuedo-class :first-child targets the first list item, making it a simple matter to then select the img descendant.

img[alt=”facebook”] { … }

The alternate property for the desired image is the only one with the value “facebook”. The attribute selector works perfectly for this.

a[href*=”facebook”] img { … }

This selector also looks for an attribute value, but uses the * matching symbol to specify a link containing the word “facebook”. The image in question is the descendant of the only anchor in this example that directs to Facebook — a perfect fit.

CSS selectors that use an element’s tag, ID, or class generally suffice for the majority of a project. Attribute and pseudo-class selectors are used less frequently, but can be life-savers when the HTML is difficult to access or modify. Best practice is to use the simplest selector possible while maintaining the minimum required specificity.

Using appropriate selectors will help keep HTML clean and CSS organized. If you find yourself relying heavily on one particular type of selector, try branching out and using other methods that might be a better fit.

Plus, it is very satisfying to come up with an eloquent selector for a particularly difficult-to-reach element.

Happy coding!