BWC Bootcamp Recap: CSS Selectors
Tags: css, webdev
Published: 2023-08-10 06:01:33

Hello friends and learners, great job making it to week 2 of Bad Website Club’s [free web dev bootcamp](https://badwebsite.club/webdev-bootcamp-2023.html)!

We’ve seen a handful of different CSS selectors in our lessons so far. It can be tricky to remember what all of them do and why you would want to use them, so let’s review!

Reminder: the CSS *selector* is what we call the part that comes before the curly braces `{ }` in a CSS rule. The selector determines which HTML elements the rule will apply to. 

## Basic selectors

### Type selector (`type`)

```css
p {text-align: center;}

div {background-color: green;}
```

This is simplest selector we have! All you write is the name of the tag that you want to style, and then the rule will apply to *every element of that type*. Use this if you want to set the font size for all of your `h2` elements at the same time, for example.

### ID selector (`#id`)

```css
/* CSS file */

#title {font-size: 3rem;}

#red-marker {background-color: red;}

/* HTML file */

<h1 id="title">Bad Website Club!</h1>

<div id="red-marker"></div>
```

On the other hand, maybe you want to give unique styles to just one single element. In that case, you can use the ID selector. Make sure to assign some value to the element’s `id` attribute in your HTML file. Then, you can write the selector by putting a hashtag/number sign `#` directly before the value of `id`.

This is most useful for styles that you know you will only want to apply one time, styles that are uniquely related to one single element. It’s especially useful when you have several elements of the same type but want each one of them to have a different style.

In the “colored markers” freeCodeCamp lesson, we gave each marker its own class and used that class to set the different colors for each marker. But since we knew there would only be one marker of each color, another option would have been to give each marker an `id` and assign the color to that ID, instead of using classes.

### Class selector (`.class`)

```css
/* CSS file */

.large-text {font-size: 1.5em;}

.blue {color: blue;}

.canvas {width: 500px;}

/* HTML file */

<p class="large-text blue">I'm a big blue paragraph!</p>

<div class="canvas"></div>
```

This might be the selector we use the most often. It targets all of the elements on your page that have a certain `class` attribute. Write this selector by typing a period `.` right before the name of the class.

This is very versatile because you can manually pick and choose which elements will be part of your class. It’s especially useful when you have a group of similar elements that you want to share a style, but don’t want to give that style to *all* elements of the same time. Note the `blue` and `large-text` classes in the example above. We might set up our classes this way if we wanted paragraphs with a certain type of information to use larger text, or wanted only a subset of heading elements to be blue. We don’t want to apply those styles to *every* paragraph or heading, just to the ones we chose by giving them the right class attribute.

Remember that the class selector won’t actually do anything until you assign the class to some elements! Make sure to go to your HTML file and write `class="whatever-your-class-is-called"` inside the opening tag of every element that you want to apply this set of styles to.

### Universal selector (`*`)

```css
* {box-sizing: border-box;
margin: 0;
padding: 0;}
```

The universal selector is typed as just the asterisk symbol `*` by itself. It selects *every single element* on the page. 

It’s often used to do what people call a “CSS reset” or “normalizing the CSS”. This means setting certain properties (often including `box-sizing`, `margin`, or `padding`) to be the same on every element in order to override the default values set by the web browser.

### Attribute selector (`[attr]`)

```css
/* CSS file */
input[type="number"] {width: 50%;}

input[required] {background-color: yellow;}

[id*="blue"] {background-color: blue;}

/* HTML file */

<input type="number" id="input-number-blue">

<input type="text" required>

<div id="blue-marker"></div>
```

Attribute selectors can be a little more complex than the others we’ve seen so far. There are a few different ways to use them, but the basic idea is that you’re choosing which elements to apply your rule to based on what HTML attributes they have.  (Remember, attributes are those extra words inside the opening tag.) 

Attribute selectors are written with square brackets `[ ]` around the attribute name (and value, if there is one). If you want to select anything that has the attribute set at all, regardless of its value, then type only the attribute name inside the brackets. On the other hand, if you want to select elements based on the *value* of an attribute, then copy and paste the entire attribute-value pair (both sides of the equals sign `=`) from the HTML tag into the brackets of the CSS selector.

If you want to target elements of *any type* that have a certain attribute, then the whole selector will be wrapped in square brackets. However, when we’re trying to target elements by an attribute, there’s often only a *single type* that we want to include. In that case, we can narrow down the selection by writing a type name directly before the opening bracket.

Let’s walk through the examples above.

- `input[type="number"]` selects any element that *a)* is a tag of the type `input`, and also *b)* has a `type` attribute assigned with the value of `number`. It will select only the first input in this example.
- `input[required]` selects any element that a) is of the type `input`, and b) has the `required` attribute included somewhere in its opening tag. It will select the second input element in the example HTML.
This is a case where you don’t need to use the = sign to specify a value. (But it’s not *just* because the `required` attribute never has a value attached! `input[type]` would be an equally valid selector, and would select all input elements that have *any value* of `type` assigned. I just couldn’t think of any other attribute that you realistically would have a *reason* to select without a certain value!)
- `[id*="blue"]` is a little weirder! It selects any element of *any type* that has an `id` attribute with the word `blue` somewhere in its value. In the example HTML, it would select *both* the first input (with ID `input-number-blue`) and the div with ID `blue-marker`.
The reason it selects elements of all types is because we didn’t include a type name before the brackets. Also, notice how it’s not just an equals sign, it’s `*=` (asterisk equals). This symbol is what makes it search for IDs that *contain* the letters “blue” anywhere in them, whereas writing just `id=blue` would search for IDs that *exactly equal* “blue”.

There are several different kinds of syntax available to search for *sub-strings* (partial matches) of the value. You can read more about all the options on [MDN’s “Attribute selectors” page](https://developer.mozilla.org/en-US/docs/Web/CSS/Attribute_selectors).

I used inputs as an example here because this is a common situation where you might want to style elements differently depending on one of their attributes. Otherwise, attributes *usually* don’t have a close relation to CSS properties. 

## Combining selectors

We can also write more complex CSS selectors that *combine* two or more of the selectors described above. The special characters that we put in between the two different selectors are called *combinators*. Using combinators to construct more complex selectors gives us more control to target elements that fall into very specific regions of the page.

### Descendant selector (`A B`)

```css
/* CSS file */

nav a {text-decoration: none;}

div div {width: 500px;}

section .blue {color: blue;}

/* HTML file */

<nav>
  <ul>
    <li>
      <a>home</a>
    </li>
    <li>
      <a>about</a>
    </li>
    <li>
      <a>gallery</a>
    </li>
  </ul>
</nav>

<main>
<p class="blue">paragraph 1</p>
<section>
  <h1 class="blue">heading 1</h1>
  <p class="blue">paragraph 2</p>
  <div>
    <p class="blue">paragraph 3</p>
    <p class="green">paragraph 4</p>
  </div>
</section>
</main>
```

The descendant selector has the simplest syntax: there’s no special symbol to type, just put a space between the two parts of the selector. This will target elements of the second type that are *descendants* of an element of the first type—which means they’re nested anywhere inside. 

- The selector `nav a` will target links that are found anywhere within a `nav` element—they could be direct children of `nav`, or they could be nested several levels deep, like the ones in the HTML above. This example is very commonly used: we often want links in the site’s navigation bar to look different than links found within the main body of text. 
It does *not* target `nav` itself.
- You can even repeat the same selector twice! `div div` will target any `div` element that is found *within another div*. So if you had many nested divs, it would target all of them *except* the outer-most one.
- They don’t have to be made up of just type selectors—any of the basic selectors we saw in the previous section of this post should work as parts of a descendant selector. So `section .blue` will select any element that *a)* has the class `blue`, and *b)* is found inside of a `section` element. Looking at the HTML above, this selector would target heading 1, paragraph 2, and paragraph 3, but would *not* target paragraph 1 (because it’s not inside a `section`) or paragraph 4 (because it doesn’t have the class `blue`).

### Direct child selector (`A > B`)

```css
/* CSS file */

nav > ul {list-style-type: none;}

/* HTML file */

<nav>
  <ul>
    <li>
      <a>home</a>
    </li>
    <li>
      <a>about</a>
    </li>
    <li>
      <a>gallery</a>
      <ul>
        <li><a>cat gallery</a></li>
        <li><a>dog gallery</a></li>
      </ul>
    </li>
  </ul>
</nav>
```

The direct child selector is similar to the descendant selector, but narrower: it only targets children whose *immediate parent* is the one named first. The combinator is the right angle bracket `>` symbol, so it’s written like `selector1 > selector2`.

In this example, `nav > ul` will target the first list that contains the “home”, “about”, and “gallery” list items, but it will *not* target the second `ul` that says “cat gallery” and “dog gallery”.

### Adjacent sibling selector (`A + B`)

```css
/* CSS file */

#home-button + li {margin-top: 2rem;}

/* HTML file */

<nav>
  <ul>
    <li id="home-button">
      <a>home</a>
    </li>
    <li id="about-button">
      <a>about</a>
    </li>
    <li id="gallery-button">
      <a>gallery</a>
    </li>
  </ul>
</nav>
```

Instead of selecting elements based on their parent, the adjacent sibling selector targets elements based on another element that comes before them at the *same level* of nesting. The combinator is the plus sign `+`.

This selector only ever targets one single element: `#home-button + li` only targets the *first* list item that comes after `#home-button`, and only if they share the same parent. There can’t be any other element between them. Note that we’re talking about what order the HTML tags are *written* in, not how things are visually laid out on the page.

We already said that the ID selector is what you should use if you’re trying to target just one single element, right, so why use this combinator instead? The advantage is that you can change around the HTML without needing to update the CSS when the order of the elements changes. In this example, we might be trying to give the home button some extra space by using the adjacent sibling selector to apply a bigger margin to whatever comes after it. Using the adjacent sibling selector means we don’t have to change the CSS at all if we decide to swap the places of the Gallery and About buttons. (This is not a great example because it would be more straightforward to just set the extra `margin-bottom` on `#home-button` rather than `margin-top` on the thing next to it, but if for some reason you wanted to make the button next to home be a different color or something, that would also use this selector.)

### General sibling selector (`A ~ B`)

```css
#home-button ~ li {color: green;}
```

This one is similar to the adjacent sibling selector, but more flexible. It can target an adjacent sibling *and* any other siblings (elements sharing the same parent) that come further down in the HTML after it. The combinator is the tilde symbol `~`.

Compare this example with the one from the adjacent sibling selector. The only difference in these two selectors is which combinator they use. Given the same HTML, this CSS rule would select *both* the About list item and the Gallery list item. 

We could use this if we wanted every link in the nav aside from the first one to be green, for instance. 

See this [blog post from LogRocket](https://blog.logrocket.com/understanding-sibling-combinators-css/) for more examples of reasons you might use the two different sibling selectors. (Warning: It’s on a website that’s trying to sell you stuff.)

### Grouping (`A, B`)

```css
h2, h3, h4 {color: purple;}

section a, article a {color: green;}
```

If you want to apply the same style rule to various different kinds of elements, and there’s no one selector that applies to all of them, then you can just list different selectors separated by commas! It works with selectors that include combinators too.

## More resources

- Here’s a really fun way to both recap the selectors we already know, and also learn some new ones: an interactive game called [CSS Diner](https://flukeout.github.io/)! This is totally how I started to understand selectors.
- [MDN guide to selectors and combinators](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_selectors/Selectors_and_combinators)
- [Selectors Explained](https://kittygiraudel.github.io/selectors-explained/) translates a CSS selector into a plain English description.