Skip to main content

Command Palette

Search for a command to run...

Mastering CSS Selectors: How to Precisely Target Elements

Updated
•11 min read•View as Markdown

How selectors work

A selector is the part of a CSS rule that targets specific HTML elements you want to style. Without selectors, CSS wouldn't know which elements you want to apply styles to.

Each CSS rule has:

  • Selector — finds elements to apply styles to

  • Declaration block — the styles themselves, inside {}

Example:

.my-css-rule {
  color: red;
  font-size: 1.2em;
}
  • .my-css-rule is the selector.

  • color: red; font-size: 1.2em; are the declarations.

Simple Selectors

Universal Selector (*)

The universal selector targets every single element on a page.

Example:

* {
  color: blue;
}

This would make every HTML element’s text blue — headings, paragraphs, buttons, etc.

When to use:

  • Resetting default browser styles (common in CSS resets).

  • Applying a very general style across all elements (but use carefully, as it can cause performance issues on large pages).


Type Selector (Element Selector)

The type selector targets elements by their HTML tag name.

Example:

section {
  padding: 2em;
}

This rule will add 2em of padding to every <section> element on the page.

You can use type selectors for any HTML tag: <div>, <h1>, <a>, <ul>, <input>, etc.


Class Selector (.)

The class selector targets elements based on their class attribute.

Example HTML:

<div class="highlight"></div>
<button class="highlight"></button>

CSS:

.highlight {
  background-color: yellow;
}

Both the <div> and <button> will get a yellow background.

Important notes:

  • A class name is prefixed with a dot (.) in CSS.

  • An element can have multiple classes, and you can style them individually or together.

  • Class names must not start with a number (e.g., .1class is invalid).

Example of multiple classes:

<div class="highlight important box"></div>

In this case, you can target any of the classes individually or combined with compound selectors.


ID Selector (#)

The ID selector targets a single, unique element by its id attribute.

Example HTML:

<div id="main-banner"></div>

CSS:

#main-banner {
  background: blue;
}

This applies the style only to the element with id="main-banner".

Important notes:

  • An ID is prefixed with a hash (#) in CSS.

  • IDs must be unique on a page — only one element should have a particular ID.

  • Avoid styling IDs for reusable components, because IDs cannot be repeated easily like classes.


Attribute Selector ([attribute])

The attribute selector allows you to target elements based on the presence (or value) of an attribute.

Example:
Target all elements that have a data-type="primary" attribute:

[data-type="primary"] {
  color: red;
}

HTML:

<div data-type="primary">Primary item</div>
<div data-type="secondary">Secondary item</div>

Only the first <div> will be red.


More powerful variations:

  • Any element with a certain attribute, regardless of value:

      [data-type] {
        color: green;
      }
    
  • Attribute value starts with a string (^=):

      [href^="https://"] {
        color: green;
      }
    
  • Attribute value contains a string (*=):

      [href*="example"] {
        color: blue;
      }
    

Attribute value ends with a string ($=):

[href$=".pdf"] {
  text-decoration: underline;
}

Grouping Selectors

  • You can target multiple selectors at once by separating them with commas.
h1, h2, p {
  margin-bottom: 1em;
}

This rule applies to all <h1>, <h2>, and <p> elements.


Summary Table of Simple Selectors

Selector TypeSymbolExampleTarget
Universal** {}Every element
Type(none)p {}All <p> elements
Class..menu {}Elements with class="menu"
ID##header {}The element with id="header"
Attribute[][href] {}Elements with a href attribute

Advanced Targeting with Pseudo-classes and Pseudo-elements

After mastering simple selectors, CSS offers even more precision and flexibility with pseudo-classes and pseudo-elements.
These powerful tools let you style elements based on their state or specific parts of an element, without needing to change your HTML structure.

Let’s dive into both:


Pseudo-classes (:)

A pseudo-class allows you to apply styles to an element based on its state, its position in the document, or user interaction — without changing the DOM.

You recognize a pseudo-class by a single colon : followed by the pseudo-class name.

Common Examples:

1. :hover

Triggered when the user hovers their mouse pointer over an element.

button:hover {
  background-color: lightblue;
}

The button's background becomes light blue when the user hovers over it.


2. :nth-child()

Targets elements based on their position inside a parent.

li:nth-child(odd) {
  background-color: #f9f9f9;
}

Every odd <li> (1st, 3rd, 5th, etc.) will have a different background color.

You can also specify exact numbers:

li:nth-child(3) {
  font-weight: bold;
}

Only the third list item becomes bold.


3. :first-child and :last-child

Style the first or last child of a parent.

p:first-child {
  font-size: 1.5em;
}
p:last-child {
  color: darkgreen;
}

The first paragraph will be bigger, and the last one will be green.


4. :checked

Targets form elements (like checkboxes or radio buttons) that are checked.

input[type="checkbox"]:checked {
  outline: 2px solid green;
}

Checked checkboxes will have a green outline.


Quick tip:

Pseudo-classes don't create new elements; they simply style existing elements based on their state.


Pseudo-elements (::)

Pseudo-elements allow you to style specific parts of an element or even inject new content into the page, using CSS only.

They use two colons (::) to distinguish them from pseudo-classes (though browsers still support the old single colon syntax for compatibility).

Common Examples:

1. ::before and ::after

Insert content before or after an element’s real content.

h2::before {
  content: "★ ";
  color: gold;
}

A gold star will appear before every <h2> heading.

h2::after {
  content: " ✔";
  color: green;
}

A green checkmark will appear after every <h2>.

You can use these to create decorative effects, labels, icons, and more — without altering your HTML!


2. ::first-letter

Targets the first letter of a text block.

p::first-letter {
  font-size: 2em;
  color: crimson;
}

Makes the first letter of each paragraph large and crimson (great for stylistic designs like drop caps).


3. ::first-line

Targets the first line of text inside a block.

p::first-line {
  font-weight: bold;
}

The first line of each paragraph becomes bold.


4. ::selection

Styles the portion of an element that the user has selected (highlighted with the mouse or keyboard).

::selection {
  background: black;
  color: white;
}

When you highlight text on the page, the background becomes black and the text turns white.


Summary Table of Pseudo Selectors

TypeSyntax ExampleWhat it Targets
Pseudo-classa:hoverElement in a specific state
Pseudo-classp:first-childElement in a specific position
Pseudo-elementp::first-letterThe first letter inside a paragraph
Pseudo-elementh1::beforeInserted content before an element

Building Power with Compound and Complex CSS Selectors

Once you're comfortable with simple selectors, pseudo-classes, and pseudo-elements, CSS unlocks even more power:
You can combine selectors to precisely target specific elements based on multiple conditions or relationships.

Let’s break it down carefully:


Compound Selectors

A compound selector happens when you combine multiple simple selectors without any space between them.

This makes the selector more specific, requiring an element to match all conditions at once.

Syntax:
[type][class][attribute]:pseudo-class::pseudo-element

Examples:


1. Type + Class

button.primary {
  background-color: blue;
}

This only targets <button> elements with the class primary.
Other elements with primary (like <div class="primary">) are not affected.


2. Class + Attribute

.card[data-active="true"] {
  border: 2px solid green;
}

This targets elements with a class of card and an attribute data-active="true".


3. ID + Pseudo-class

#submit-button:hover {
  background-color: darkred;
}

Targets only the element with id="submit-button" when hovered.


Real-world example:

a.button.primary[download]:hover::after {
  content: " ⬇️";
}

This targets a link (<a>) that:

  • has a class of button

  • has a class of primary

  • has a download attribute

  • is being hovered
    and adds a download emoji after it.


Complex Selectors (Combinators)

A complex selector combines two or more selectors using combinators to define relationships between elements.

Important: In complex selectors, the space or symbol between selectors matters — it defines how elements are related.

There are four main combinators:


1. Descendant Combinator (Space " ")

Selects an element that is inside another, at any depth.

section p {
  color: gray;
}

Targets any <p> that is inside a <section>, even deeply nested.

Think: Parent ➡️ Grandchild ➡️ Great-grandchild, etc.


2. Child Combinator (>)

Selects only direct children of an element — not deeper levels.

section > p {
  font-size: 1.2em;
}

Targets only <p> elements that are immediate/direct children of a <section>.
(NOT grandchildren or great-grandchildren.)


3. Adjacent Sibling Combinator (+)

Selects an element that is the very next sibling of a specified element.

h1 + p {
  margin-top: 0;
}

Targets the first <p> directly following an <h1>, and removes its top margin.

Only one step right — no skipping!


4. General Sibling Combinator (~)

Selects all siblings after the specified element, not just the next one.

h1 ~ p {
  color: blue;
}

Targets every <p> that appears after an <h1>, regardless of how many other elements come in between.


Summary Table of Combinators

CombinatorSyntaxRelationship It SelectsExample Use
SpaceA BB is a descendant of A (any depth)nav ul (any <ul> in <nav>)
> (Child)A > BB is a direct child of Asection > h2
+ (Adjacent Sibling)A + BB is immediately after Ah1 + p
~ (General Sibling)A ~ BB is any sibling after Ah1 ~ p

Final Tips: Compound vs Complex

ConceptExample SyntaxHow it Works
Compoundbutton.primary[disabled]Multiple conditions on the same element
Complex (Combinators)ul > li:first-childRelationship between different elements

Remember:

  • Compound = One element matching multiple rules.

  • Complex = Relationship between multiple elements.

Mastering Modern Advanced CSS Selectors

CSS has evolved — we now have supercharged selectors that make writing powerful, flexible styles even easier!

Here’s a detailed, easy-to-understand breakdown:


1. The :not() Pseudo-Class

The :not() pseudo-class excludes elements that match a certain selector.

Syntax:
element:not(selector)

It selects everything that doesn't match the condition.


Example:

button:not(.primary) {
  background-color: gray;
}

Targets all <button> elements except those with the class primary.


Multiple exclusions inside :not()

button:not(.primary, .secondary) {
  opacity: 0.5;
}

Targets buttons that are neither .primary nor .secondary.

Tip: You can list multiple selectors inside :not() (comma-separated) — super handy for large projects!


2. The :is() Pseudo-Class

The :is() pseudo-class groups multiple selectors together for shorter, easier CSS.

Syntax:
:is(selector, selector, selector)

It matches if any of the selectors inside match.


Example:

:is(h1, h2, h3) {
  font-family: 'Poppins', sans-serif;
}

Targets all <h1>, <h2>, and <h3> headings at once!


Combine with parent selectors:

main :is(h1, h2, h3) {
  color: darkblue;
}

All headings inside <main> turn dark blue.


Bonus:
When combined with combinators, :is() helps reduce repetition and makes selectors easier to maintain.


3. The :where() Pseudo-Class

The :where() pseudo-class is similar to :is(), but assigns zero specificity to itself.

Syntax:
:where(selector, selector)

It matches elements just like :is() — but without affecting specificity calculations.


Why use :where()?

  • Helps write global, low-priority rules.

  • Avoids overriding more specific styles accidentally.

  • Useful for theme resets and utility classes.


Example:

:where(h1, h2, h3, p) {
  margin: 0;
}

Removes margins from headings and paragraphs without fighting with more specific styles later.


4. The :has() Pseudo-Class

The :has() pseudo-class is often called the "parent selector" —
it allows you to select an element based on its child elements or descendants!

Syntax:
element:has(child-selector)


Example:

article:has(h2) {
  border: 1px solid black;
}

Adds a border only to <article> elements that contain an <h2>!


Real-world usage:

Highlight <div>s that contain a form:

div:has(form) {
  background-color: #f9f9f9;
  padding: 1em;
}

Dynamic styling based on whether a child exists!


⚡ Important:
:has() is very powerful but can be more performance heavy, so use it thoughtfully in large pages.


Visual Summary of Advanced Selectors

SelectorPurposeExample
:not()Exclude elementsa:not(.external)
:is()Group matching elements (normal specificity):is(h1, h2, h3)
:where()Group elements with zero specificity:where(h1, p, li)
:has()Select elements containing other elementsdiv:has(img)

Final Tips for Mastery:

  • Use :not() to refine what you don't want selected.

  • Use :is() to group selectors cleanly and reduce repetition.

  • Use :where() when you need global resets or lightweight styling.

  • Use :has() to select based on content, not just structure.

More from this blog

HTML & CSS

19 posts