CSS Tutorial: CSS Selectors Quick Guide

CSS Tutorial on CSS Selectors

Share This Post

Selectors are one of the key items of CSS, if not the key item. In fact, they allow you to identify which parts of the page will receive a given style. Thus, you must know CSS selectors pretty well. In this tutorial, we will see how they work. We will cover both the basics you always need and some not-so-common but useful ones.

As you can see, CSS Selectors are just one of the components you need to style your page. To understand them, you’ll need to know the basics of CSS: if you feel in need of a refresher just check this CSS Tutorial.

The Basics of CSS Selectors

Getting Started

We covered this more deeply in the Getting Started with CSS Guide, but we can just mention the highlights here.

With a selector, you identify the elements in your HTML page that will receive a style. The syntax you have to use is simple: you write the selector and after that, between curly brackets, you put all the styles you want apply.

selector { styles }

Selectors allow you to select elements, and you can do that by referencing some of their attributes. Most notably:

  • Element name, written as-is
  • Class name, prefixed with a dot
  • ID, prefixed with a pound sign

This will result in something like the following examples.

h1 { styles }
.class-name { styles }
#id-name { styles }

You can combine multiple selectors together to target a more specific device. In the following example, we will apply this rule only to an h1 tag if it has the class class-name.

h1.class-name { style }

Now that we have the basics in the rear mirror, we can move onto the advanced stuff.

Advanced Standalone Selectors

So far, we know that we can select based on element name, class, and ID. That’s definitely not it, we can reference more than that. Particularly, we can select based on any HTML attribute.

Selecting by HTML attribute means we will apply a rule only if the element has a specific HTML attribute with a specific value. We must pair this with the name of the HTML tag where we are looking the attribute in.

A common way to use this is on the type attribute of input elements. This is useful, for example, if we want to style passwords differently than text fields.

The syntax to use this is simple, you write just after the tag name the name of the attribute, equal sign, and the value you are searching, all wrapped in square brackets. If we want to select only text inputs, for example, it will look something like this.

input[type=text] {
  background-color: red;
}

Just like before, you can always combine this with other selectors, such as class and ID. Just be sure the part between square brackets is always attached right after the tag name. In other words, these examples are correct:

input[type=text].login { ... }
input[type=password]#bottom-field { ... }

While those ones are wrong:

input.login[type=text] { ... }
input#bottom-field[type=password] { ... }
.login[type=password] { ... }

Select Everything

In some circumstances, we may want to apply a rule to all the items in the page. In that case, we can use the star selector (*).

* { ... }

Use this with caution! However, note that his (by itself) is a weak selector. In other words, if any other selector specifies a different value for a style property defined in this selector, it will override it. That’s because, in CSS, if a property is defined twice the most specific selector always wins.

How to Combine CSS Selectors

So far, we have seen some way to select individual elements by targeting some of their properties, like class or ID. However, CSS selectors can do way more than that. In fact, we can use them to identify items based on their position in relation to other elements. In this section, we will see exactly how to do that.

Descendant Selector

This one of the firsts you need to use. Descendant means that an element, inside the HTML, is contained inside another somewhere. An example will make everything clearer.

<div class="a">
  <div class="b">
    <div class="c">
      <p class="d">Hello!</p>
    </div>
  </div>
</div>

Here, we can see we have one parent element (div.a), which contains all the other elements. All the other elements (div.b, div.c, and p.d) are inside div.a, so they are their descendants.

Often, we want to use this descendant selector to target specific elements in our HTML page. For example, we may target all the elements with class d that are contained as descendants in elements of class a. If an element of class d is not a descendant of class a, it won’t receive the rule. To do that, we can use the following syntax.

.a .d {
  background-color: red;
}

Note the space in the selector. In fact, .a.d means we want to target an item that has both class a and b on it. Instead, .a .d means we want to target all the items that have class d, but only when they descend from class a. The space is crucial!

Direct Child Selector

Sometimes, targeting descendants is still too loose as we end up selecting some parts of the page we did not want. That happens most often when we have large pages with many HTML elements. Sometimes, we want something more specific.

A direct child selector allows you to select only the items that are immediately below another item. We can take our previous example and tweak it a little.

<div class="a">
  <div class="b">
    <div class="c">
      <p class="d">Hello!</p>
    </div>
  </div>
  <div class="c">New stuff here</div>
</div>

Note that we added <div class="c">New stuff here</div>. This, just like div.b, is a direct child of div.a.

To use the direct child selector, we use > symbol. On the left, we write the selector of the parent, and on the right the selector for the children. In this case, we want to select all children of a only when they have class c.

.a > .c {
  background-color: red;
}

We can also get creative and select all the direct children of an element (and only those) by using the star selector.

.a > * {
  background-color: red;
}

Siblings Selector

We can do a lot with descendant and direct child selectors. However, sometimes we might want to select items that are on the same level, that are siblings. A good example of that is the combination of input and label.

<form action="contact.php" method="post">
  <input name="email" type="email" id="email">
  <label for="email">Your email</label>
</form>

In this example, we might want to style only the labels that come right after an input. We can do this with the + sign.

input + label {
  background-color: red;
}

In other words, the syntax is element1 + element2 and will style only element2 when it comes right after element1.

Combine unrelated CSS Selectors

If you want to build a website consistent with your brand identity, some styles will just repeat themselves. You will have always the same kind of fonts and colors all over the page, for example.

In other words, you may want to select multiple items of your page and apply to all of them the same rules. More specifically, each of these elements has nothing to do with the other. Well, you can use multiple selectors separating them simply with a comma.

a,
p,
input[type=text],
form > textarea {
  background-color: red;
}

In this example, we are applying the style block to four separate elements: a, p, input[type=text], and form > textarea. As you can imagine, you can use any type of selector and list as many of them as you want.

Special CSS Selectors

In this part of this CSS Tutorial on Selectors, we will see some special CSS Selectors. They allow us to select some particular elements that are in a particular state.

Links and their States

If you remember, back when we were talking about HTML Links, we covered their various states. In fact, any link can be:

  • Unvisited if you never went to the target page
  • Active if you are clicked on it and the page is loading (so it will be active very briefly)
  • Visited if you already went to that page

We can apply different styles depending on what state the link is on. This is possible with :link for unvisited links, :active for active links, and :visited for visited links. We should apply these special selectors to an a tag. Pretty quickly, we can end up with something like the code below.

a {
  // This would apply to all links
  color: blue;
}

a:link {
  // Only unvisited
  color: green;
}

a:active {
  // Only active
  color: red;
}

a:visited {
  // Only visited
  color: purple;
}

As a link must be either unvisited, visited, or active, the first rule (the one just for a) will never be applied. That’s because rules for the various states are more specific.

Hovering

This one of the special CSS selectors you will use most often. If you want to change the style when the user is hovering with the mouse on top of your element, you can do that with the :hover selector. The rules will be applied as long as the user keeps the cursor over your element.

A common use of that is for links and button, like so:

a {
  text-decoration: none;
}

a:hover {
  text-decoration: underline;
}

In this case, our link will have an underline only when the user passes hover with the mouse.

A tip: you can use this selector on any element, and even use it to compose more complex behaviors like so.

input[type=text]:hover + label { ... }
.menu:hover .menu-item > p { ... }
.menu .menu-item:hover > p { ... }

In the latter two cases, the style will apply to the p element, but only when its parents (respectively .menu in the first case, and .menu-item in the second) will be hovered.

Children

In some particular situations, you may want to apply some rules to only some of the children of an element. In this case, the normal child selector may not be enough. That’s the case if you want to apply different rules, for example, based on the position of this element among all the children.

All this may sound abstracts, but an example will help you understand it isn’t. Imagine you have a table and you want to round its corners. This also means you have to round four of its cells:

  • Top-left corner of the first cell in the first row
  • Top-right corner of the last cell in the first row
  • Bottom-left corner of the first cell in the last row
  • Bottom-right corner of the last cell in the last row

We can do that easily by using the :first-child, :last-child, and :nth-child(n) selectors. Just know that, with :nth-child(n) you have to provide the number of the child. For example, :nth-child(3) means you will select only the third child.

Below the HTML we want to style.

<table>
  <tr><td>1.1</td><td>1.2</td><td>1.3</td></tr>
  <tr><td>2.1</td><td>2.2</td><td>2.3</td></tr>
  <tr><td>3.1</td><td>3.2</td><td>3.3</td></tr>
  <tr><td>4.1</td><td>4.2</td><td>4.3</td></tr>
  <tr><td>5.1</td><td>5.2</td><td>5.3</td></tr>
  <tr><td>6.1</td><td>6.2</td><td>6.3</td></tr>
</table>

To achieve the desired result, we can do something like this.

td {
  background-color: #955E42;
}

table tr:first-child td:first-child {
  border-top-left-radius: 0.5rem;
}

table tr:first-child td:last-child {
  border-top-right-radius: 0.5rem;
}

table tr:last-child td:first-child {
  border-bottom-left-radius: 0.5rem;
}

table tr:last-child td:last-child {
  border-bottom-right-radius: 0.5rem;
}

This will yeld the following result.

Use CSS Selectors like :first-child and :last-child to round the borders of a table
Our selectors in use.

Before and After CSS Pseudoelements

Now we can go in one of the most obscure yet useful CSS selectors. The ::before and ::after pseudoelements. Note that these are pseudoelements, and so we use double colon (::).

We call them pseudoelements because they allow you to use CSS to create a fake HTML element before or after an existing one and style it. So, for example, p.my-class::before will apply the style to a fake element, create right before any paragraph with the class my-class.

Why would you need that? This powerful tool has many uses, and all of them are quite advanced. However, you mostly use it to create beautiful radio boxes or checkboxes.

To test this out, create some HTML like the one below.

<input type="checkbox" name="test_field1" id="test1"><label for="test1">Test 1</label>
<input type="checkbox" name="test_field2" id="test2"><label for="test2">Test 2</label>

Note that for this to work is crucially important that you define IDs and the for property of labels. In this way, when the user clicks on the label the checkbox will go checked or unchecked. As we are going to hide the checkbox itself, this is fundamental.

Then, we can apply the following style. Note that as it’s not really possible to use ::before directly on input, we use a sibling selector to identify the label right after our checkbox.

input[type=checkbox] {
  display: none;
}

input[type=checkbox] + label::before {
  display: inline-block;
  margin-right: 0.6em;
  border-radius: 0.2em;
  border: 2px solid #1E2019;
  color: #1E2019;
  height: 1em;
  width: 1em;
  background-color: #E6CCBE;
  content: ' ';
  text-align: center;
}

input[type=checkbox]:checked + label::before {
  content: 'X';
}

A few more things worth mentioning that may not be so obvious. The :checked selector applies only to checkboxes when they are selected. Instead, the content property sets the text we want inside our pseudoelement.

This will result in the following (we selected the second input to show you the difference).

CSS Selectors ::before and ::after pseudoelements to create a custom style checkbox
Two custom-style checkboxes with CSS.

Become a Full Stack Developer!

In case you are new here, we are running a Full Stack Development course (for free). This will teach everything you need to create amazing applications. Well, this tutorial is part of this larger course, and as part of it there are some exercises we need to do.

In our course, we are building the website of a pretend bakery shop, and now that we are styling it we can apply some of the rules we learned here to make it more beautiful. No, it will still be ugly after this tutorial, we haven’t learned that much just yet. Sorry!

Today’s assignment is simple. We want our links in the header to be bold and without any underline, while keeping them “normal” in the footer. In addition, we want to remove the bullet from the list in the header (list-style: none;). Our final result should look something like this (we are looking at the order page). Be careful, below you will find the solution.

CSS Selector exercise final result
The final result we are looking for, in the order page.

Try to do this on your own! It’s not that hard after all. In case of doubt, remember you only need to modify the CSS file for this exercise. Once you finish, you can check the solution right below.

So here’s the solution for this exercise. Quite simple.

header ul a {
  text-decoration: none;
  font-weight: bold;
}

header ul {
  list-style: none;
}

In case you want to get the full picture, check the repository on GitHub at alessandromaggio/full-stack-course. More specifically, you can find all the changes we made in this tutorial by looking at this commit.

In a Nutshell

In this tutorial on CSS Selectors, we saw how to use these selectors to better identify various elements on our page. If you are looking for an extensive list of selectors, you can check out the one made by the W3C. The following table shows you the most commonly used selectors (TL;DR).

ExampleDescription
pElement “p”.
.customSelect any element with class “custom”.
#testAny element with the ID “test”.
div > pSelect a “p” tag directly contained into a “div”.
div pA “p” tag contained somewhere into a “div”.
a:hoverSelect an “a” tag only when the user hovers it with the mouse
td:last-childSelect the last child “td” element of its parent.
td:first-childThe first child “td” element of its parent.
::beforeCreate a pesudo-element before.
::afterCreate a pseudo-element after.

Keep going! You are building up your CSS skills and sooner than you think you will be able to create an amazing website.

Picture of Alessandro Maggio

Alessandro Maggio

Project manager, critical-thinker, passionate about networking & coding. I believe that time is the most precious resource we have, and that technology can help us not to waste it. I founded ICTShore.com with the same principle: I share what I learn so that you get value from it faster than I did.
Picture of Alessandro Maggio

Alessandro Maggio

Project manager, critical-thinker, passionate about networking & coding. I believe that time is the most precious resource we have, and that technology can help us not to waste it. I founded ICTShore.com with the same principle: I share what I learn so that you get value from it faster than I did.

Alessandro Maggio

2020-11-19T16:30:00+00:00

Unspecified

Full Stack Development Course

Unspecified