Skip to content

Complete Guide to CSS Selector Types (Beginner Friendly)

Knowing the full range of CSS selector types available when writing modern CSS today is crucial to using CSS to the fullest.

In the past 10 years or so, the official specification has added a number of different CSS selectors and many of those selectors have strong support across all modern browsers.

So in this complete guide to CSS selector types, I hope to offer a selector toolset that you can comfortably use in all your production sites. I’ll also include some useful code snippets to assist your learning and remembering these selectors.

CSS Selector Types

Here’s what we’re going to cover:

  1. Universal selector #
  2. Type selector #
  3. Class selector #
  4. ID selector #
  5. Attribute selector #
  6. Attribute selectors with operators and flags #
  7. Descendant combinator #
  8. Child combinator #
  9. General sibling combinator #
  10. Adjacent sibling combinator #
  11. Pseudo-class selector types #
  12. Pseudo-element selector types #
  13. Combining CSS selector types #

Universal selector

The universal selector, as the name suggests, selects all elements. You can use it on its own or along with another selector. It’s declared using the asterisk characters (*).

* {
  box-sizing: border-box;
Code language: CSS (css)

The above example uses the universal selector to target all elements on the page. This gives the elements a box-sizing value of border-box. This is a common use of the universal selector that ensures more intuitive sizing of page elements.

If you use the universal selector along with another selector, it might look like this:

section * {
  background: lightblue;
Code language: CSS (css)

The above selects all child elements (including deeply nested elements) of <section> elements. Another example that’s a little more complex might be:

footer * span {
  font-size: .8em;
Code language: CSS (css)

In the above example, I’m selecting all span elements that are inside any element that are children of <footer> elements. You won’t see this type of use for the universal selector very often, as it’s quite unintuitive.

Type selector

The type selector, also referred to as an element type selector, selects elements based on the element’s tag name in the HTML. Below are a few examples:

span {
  color: red;

article {
  margin-top: 20px;
Code language: CSS (css)

The above includes two CSS rules that apply CSS to all <span&gt elements or all <article> elements. You can target any valid HTML element using its equivalent type selector name.

You can also technically invent HTML tag names, then use the custom name in your CSS. For example, you could have this in your HTML:

Code language: HTML, XML (xml)

Then the following in CSS:

hotdog {
  color: pink;
Code language: CSS (css)

The above works, but it is strongly recommended not to do this. If you need a generic HTML element, use <div> or <span>, then use classes and data attributes as needed to customize it.

Nonetheless, the above should illustrate how the type selector works from a CSS standpoint.

You may also be interested in:

Class selector

The class selector is the most-used and most useful of all CSS selector types. It’s declared using a dot (.) followed by a custom name that must exist as a class attribute in your HTML.

For example you might have the following HTML:

<div class="module module-other">
Code language: HTML, XML (xml)

In your CSS you can target the above element using the class selector as follows:

.module {
  margin: 0 auto;

.module-other {
  max-width: 900px;
Code language: CSS (css)

The HTML included two different class names in the class attribute, so I was able to target the same element using separate class selectors.

The class selector is the basis for CSS methodologies like OOCSS, BEM, and SMACSS. This selector type is also the foundation for popular CSS frameworks like Bootstrap and Tailwind.

You can create atomic styles using the class selector. These types of styles, often called single-purpose utility classes, are reusable and can serve as building blocks for complex CSS projects. The number of class names you can include in a single HTML class attribute is more or less unlimited. Thus, the class selector serves a powerful foundation for many frameworks today.

The class selector is also powerful because you can reuse a class name multiple times on a single page, making it versatile and flexible.

ID selector

The ID selector is declared using the pound symbol (#) followed by a custom name. This selector will match an HTML element that uses the same name as a value for the id attribute.

Your HTML would look like this:

<div id="module">
Code language: HTML, XML (xml)

And the following CSS would target the above element:

#module {
  width: 80%;
Code language: CSS (css)

Use of the ID selector is strongly discouraged for multiple reasons:

  • The ID selector can only target a single element and therefore cannot be reused
  • The ID selector has very high specificity, so if you want to override it elsewhere in your stylesheet, it will be difficult, making your stylesheet harder to maintain

While most developers today discourage use of the ID selector, the id attribute is encouraged in HTML for defining in-page anchors as well as for JavaScript hooks. Use class names or other selectors instead of the ID selector, to ensure CSS is as maintainable as possible.

Attribute selector

The attribute selector is a fairly underused and underappreciated CSS selector type. You write this selector using square brackets ([]) and the value inside the brackets should match an HTML attribute.

For example, suppose you have the following HTML:

<a href="about.html" title="About">About Us</a>
Code language: HTML, XML (xml)

Now notice the following CSS:

a[title] {
  border-bottom: solid 1px black;
Code language: CSS (css)

This CSS targets all <a> elements that have a title attribute. You can use the attribute selector on its own with no other selector present:

[title] {
  background: yellow;
Code language: CSS (css)

The above targets all elements that have a title attribute, regardless of what element type they are.

Attribute selectors with operators and flags

Usefully, you can declare the attribute selector’s value using a number of different operators and flags. Below are examples of each.

This uses the starts-with operator (^) to target all anchor elements with an href attribute that starts with # (the hash or pound sign):

a[href^="#"] {
  color: lightblue;
Code language: CSS (css)

The following uses the wildcard operator to target all anchor elements with an href attribute that has the word “downloads” anywhere in its value:

a[href*="downloads"] {
  color: lightblue;
Code language: CSS (css)

Next I’ll use the case-insensitivity flag (i) to target all anchor elements with an href attribute that has the word “downloads” anywhere, regardless of capitalization:

a[href*="downloads" i] {
  color: lightblue;
Code language: CSS (css)

This next code uses the case-sensitivity flag (s) to target all anchor elements with an href attribute that has the word “Blog” anywhere in the URL, with strict capitalization:

a[href*="Blog" s] {
  color: lightgren;
Code language: CSS (css)

Finally, the following uses the ends-with operator ($) to target all anchor elements with an href attribute that ends with “.pdf”:

a[href$=".pdf"] {
  color: red;
Code language: CSS (css)

You can use the above snippet to add an icon next to the link, dependent on the file type of the URL.

Descendant combinator

There are a number of different characters that work as combinators that are not technically “selectors” but they work along with selectors. These let you to target specific elements in certain contexts.

The descendant combinator is the space character that separates two selectors. In the following code, the selector targets all anchor elements that are children of paragraph elements.

p a {
  line-height: 1.4;
Code language: CSS (css)

Children targeted are immediate children and any grandchildren, no matter how deeply nested.

Child combinator

You declare a child combinator using the greater-than symbol (<). It’s similar to the descendant combinator but allows you to select only the direct child elements of the element to the left of the combinator.

p > a {
  line-height: 1.4;
Code language: CSS (css)

The above will target only anchor elements that are immediate children of paragraphs. Any anchor elements that are nested inside other elements that are inside the <p> will not be selected.

General sibling combinator

The general sibling combinator is defined using a tilde (~). This allows you to select all sibling elements that appear after the elements defined by the selector before the tilde. It will not select sibling elements that are prior to elements defined by the selector before the tilde.

For example, the following will select all <div> elements that follow .module and share the same parent as it (i.e. they are siblings):

.module ~ div {
  border: solid 1px;
Code language: CSS (css)

This means in the following HTML, only the latter two <div> elements will qualify for the styles, even though the first <div> is also technically a “sibling”:

  <!-- not selected... -->

<section class="module">
  <!-- content... -->

<!-- below divs both selected... -->
Code language: HTML, XML (xml)

Adjacent sibling combinator

The adjacent sibling combinator is defined using the plus sign (+). With this combinator, you can select an element that immediately follows the first element. For example, one common use case for this CSS selector type is indenting paragraphs:

p + p {
  text-indent: 1em;
Code language: CSS (css)

Although most paragraphs on the web are double spaced with no indenting, many blogs will use the method common in print publications (that is, indenting paragraphs that follow paragraphs). This is achieved using the CSS above via the adjacent sibling combinator. Thus, the above CSS will only apply the indent to paragraphs that immediately follow another paragraph. The first paragraph inside the parent will not be selected.

Pseudo-class selector types

There are a whole slew of pseudo-classes available to style different elements and parts of a web page. A pseudo-class selects based on a state rather than the existence of an element. Pseudo-classes are defined using a single-colon, followed by the pseudo-class name.

If the psuedo-class is placed directly following another selector (e.g. a:link) it will style the specified state only on the targeted selector. If there’s a space between the selector and the pseudo-class (a :link), it becomes two selectors separated by a descendant combinator.

Below is a list and brief description of the most common pseudo-classes:

  • :link – Styles links that are not visited.
  • :visited – Targets links that have been visited, with some limitations due to privacy
  • :target – Styles a part of a document based on a document fragment in the URL, similar to how Wikipedia styles footnotes
  • :root – Targets the root element of a document, usually the <html> element
  • :nth-child – Allows you to use math-like notation or keywords (odd and even) to select specific elements, commonly used for things like zebra stripes with CSS
  • :first-child – Targets the first child element of a parent
  • :last-child – Targets the last child element of a parent
  • :nth-of-type – Allows you to use math-like notation and keywords (odd and even) to match a specific set of elements among a group
  • :hover – Styles elements that the user hovers their mouse over
  • :active – Targets the element that’s currently ‘activated’, usually by means of a click
  • :focus – Matches the element that currently has focus, usually by means of the keyboard or a mouse click
  • :disabled – Matches any disabled elements (e.g. form inputs, radio buttons, etc.)
  • :checked – Selects radio buttons and checkboxes that are in the “selected” or “checked” state
  • :required – Targets form elements that have the required attribute set

There are also some functional pseudo-classes that allow you to pass in a list of selectors. Based on the list provided, this type of pseudo-class will either style or avoid styling the elements passed in.

  • :is() – Matches any selectors in a comma-separated list passed into the function, but with potentially high specificity
  • :not() – Matches any element that’s not in the list passed in
  • :where() – Slightly improves on :is() by matching any elements in the list without affecting specificity
  • :has() – Helps to target a parent or previous sibling element, depending on the selector provided

There are a number of other pseudo-classes, many added to the specification in more recent years with weaker browser support. You can see a comprehensive list on MDN.

Pseudo-element selector types

Pseudo-elements are declared using a double colon (::) followed by the pseudo-element name. As the word “pseudo” suggests, these selectors target what more or less amount to imaginary elements on the page.

The most common pseudo-elements are briefly described below:

  • ::before – Used in conjunction with the content property, adds a pseudo-element inside an element, but before the targeted element’s content
  • ::after – Same as previous, but adds a pseudo-element after the targeted element’s content
  • ::first-letter – Styles the first letter of the first line of the targeted block-level element that’s not preceded by other content (like an image)
  • ::first-line – Styles the first line of the targeted block-level element, the size of which is defined by various factors (width of the element, width of the viewport, font size, etc.)
  • ::placeholder – Allows you to style the placeholder text that appears inside a form input or textarea element, set using the placeholder attribute in HTML
  • ::selection – Styles any text on the page that’s selected, usually done by the user by clicking and dragging the mouse
  • ::marker – This targets the “marker box” for HTML list items, meaning the area where the “bullet” appears, allowing you to add a custom marker, number, or bullet

Pseudo-elements should be used with care, particularly ::before and ::after. These can inject text content into the page that’s not accessible to screen readers. Thus, these selectors should only be used for decorative elements that aren’t critical to the main content.

It should also be noted that some pseudo-elements previously were supported using the single-colon syntax (e.g. :before). These will still work in most browsers for legacy reasons but it’s recommended to always use the double-colon syntax to differentiate them from pseudo-classes.

There are some other pseudo-elements that are lesser-known with weaker browser support that are listed on MDN.

Combining CSS selector types

As many of the code examples in this article have shown, CSS selector types can be combined to create all sorts of possibilities when targeting elements.

For example, there are simple selectors that include a single entity (e.g. article). There are compound selectors that combine multiple selectors but don’t use a combinator (e.g. input[type=email]). There are complex selectors that use combinators that separate two or more selectors (e.g. article > p + span). Some of these are referred to as relative selectors because a portion of the selector is implied (e.g. + article, which uses a combinator with nothing in front).

Finally, one of the most common ways to combine selectors is using one or more commas (,). Note the following example:

article, section, div {
  margin-top: 1em;
Code language: CSS (css)

The above is called a selector list. This will target any of the elements represented by one or more of the listed selectors separated by commas.

In the above example, even if a page has no <section> elements, it will still target the <article> and <div> elements. This is a common technique to avoid repeating CSS for multiple selector rules. This allows you to use one set of properties for a single CSS rule.

Wrapping up CSS selector types

Learning the different CSS selector types takes some time and experience. Remember to do your best to keep selectors simple. The more selectors you include in a combined selector, the harder it will be to maintain your CSS. Class selectors are always the safest to use to help with this.

I hope this review of the different selector types has helped you and given you some powerful tools to use for your next CSS project.

Let us know if you have any questions on how these things work or if any of the above advice is unclear in any way.

Don’t forget to join our crash course on speeding up your WordPress site. Learn more below:


Layout and presentation by Karol K.

Yay! 🎉 You made it to the end of the article!
Louis Lazaris

Inline Feedbacks
View all comments

Or start the conversation in our Facebook group for WordPress professionals. Find answers, share tips, and get help from other WordPress experts. Join now (it’s free)!

Would love your thoughts, please comment.x