Skip to main content

Types of Selectors

1. Element Selector (Type Selector)

Targets all elements of a specific type.
p { }           /* All paragraphs */
header { }      /* All headers */
div { }         /* All divs */

2. Class Selector - Most Used! ⭐

Targets elements with a specific class attribute.
.header { }              /* Elements with class="header" */
.product-card { }        /* Elements with class="product-card" */
Class selectors are the most commonly used in modern CSS because they’re reusable and have good specificity.

3. ID Selector

Targets a single element with a specific ID.
#main-nav { }   /* Element with id="main-nav" */
Avoid using ID selectors for styling - they have high specificity and aren’t reusable. Use classes instead.

4. Universal Selector

Targets all elements.
* {
  box-sizing: border-box;
}

Attribute Selectors

Target elements based on their attributes.
/* Has attribute */
[data-active] { }              /* Has data-active attribute */

/* Exact match */
[type="text"] { }              /* type equals "text" */

/* Starts with */
[href^="https"] { }            /* href starts with "https" */

/* Ends with */
[href$=".pdf"] { }             /* href ends with ".pdf" */

/* Contains */
[href*="google"] { }           /* href contains "google" */

Real Example: Cart Counter

.header__nav-link--cart[data-cart-count]:not([data-cart-count="0"])::after {
  content: attr(data-cart-count);
  background-color: var(--color-error);
  /* Shows cart count badge */
}

Pseudo-Classes (States)

Target elements in specific states.

Interactive States

:hover          /* Mouse is over element */
:focus          /* Element has focus */
:active         /* Being clicked */
:visited        /* Link was visited */
:focus-visible  /* Focus via keyboard only */

Example from Header Navigation

.header__nav-link {
  color: var(--color-gray-600);
  transition: background-color var(--transition-fast);
}

.header__nav-link:hover {
  background-color: rgba(0, 0, 0, 0.05);
}

Structural Pseudo-Classes

:first-child    /* First child of parent */
:last-child     /* Last child of parent */
:nth-child(n)   /* Nth child */
:nth-child(odd) /* 1st, 3rd, 5th... */
:nth-child(even)/* 2nd, 4th, 6th... */
:not(selector)  /* Negation */
/* Stripe table rows */
table tr:nth-child(odd) {
  background-color: var(--color-gray-100);
}

table tr:nth-child(even) {
  background-color: var(--color-white);
}

Pseudo-Elements (Parts)

Create and style parts of elements.
::before        /* Content before element */
::after         /* Content after element */
::first-letter  /* First letter */
::first-line    /* First line */
::placeholder   /* Input placeholder text */

Example: Cart Counter Badge

.header__nav-link--cart::after {
  content: attr(data-cart-count);
  position: absolute;
  top: -4px;
  right: -8px;
  
  min-width: 18px;
  height: 18px;
  padding: 0 var(--spacing-xs);
  
  background-color: var(--color-error);
  color: var(--color-white);
  font-size: var(--font-size-xs);
  border-radius: var(--border-radius-full);
  
  display: flex;
  align-items: center;
  justify-content: center;
}

Placeholder Styling

.header__search-input::placeholder {
  color: var(--color-gray-400);
}

Combinators

Combine selectors to target specific relationships.
/* Descendant - any level */
.header .nav-link { }       /* nav-link inside header (any depth) */

/* Child - direct child only */
.header > .nav { }          /* nav that's a direct child of header */

/* Adjacent sibling - immediately after */
.title + .subtitle { }      /* subtitle immediately after title */

/* General sibling - any following */
.title ~ .paragraph { }     /* any paragraph after title */

Real Example: Image Zoom on Card Hover

/* When card is hovered, scale the image inside */
.product-card:hover .product-card__image {
  transform: scale(1.05);
}

Specificity

When multiple rules target the same element, specificity determines which wins.

Specificity Values (from lowest to highest):

  • Element: 0,0,0,1 (p, div, header)
  • Class: 0,0,1,0 (.class)
  • ID: 0,1,0,0 (#id)
  • Inline: 1,0,0,0 (style=”…”)
  • !important: Wins always (avoid!)

Calculating Specificity

p {
  color: blue;
}

Best Practices

Use classes for styling - they’re reusable and have good specificity
Keep specificity low to make styles easier to override
Avoid nesting selectors too deeply (max 2-3 levels)
Avoid ID selectors for styling - use for JS/links only
Never use !important unless absolutely necessary

Grouping Selectors

Apply the same styles to multiple selectors.
/* Same styles for multiple elements */
input:focus,
textarea:focus,
button:focus-visible,
a:focus-visible {
  outline: 2px solid var(--color-secondary);
  outline-offset: 2px;
}

Hidden Elements (Accessibility)

Two ways to hide elements with different accessibility impacts:
/* Completely hidden - screen readers won't read */
.hide-mobile {
  display: none !important;
}

Practice Example

Let’s build navigation link styles using various selectors:
/* Base link styles */
.header__nav-link {
  display: flex;
  align-items: center;
  gap: var(--spacing-xs);
  padding: var(--spacing-xs) var(--spacing-sm);
  font-size: var(--font-size-sm);
  color: var(--color-gray-600);
  border-radius: var(--border-radius-sm);
  transition: background-color var(--transition-fast);
}

/* Hover state (pseudo-class) */
.header__nav-link:hover {
  background-color: rgba(0, 0, 0, 0.05);
}

/* Cart link modifier (class) */
.header__nav-link--cart {
  position: relative;
}

/* Cart counter badge (attribute + pseudo-element) */
.header__nav-link--cart[data-cart-count]:not([data-cart-count="0"])::after {
  content: attr(data-cart-count);
  position: absolute;
  top: -4px;
  right: -8px;
  background-color: var(--color-error);
  color: var(--color-white);
  /* ... more styles ... */
}

Next Steps

BEM Methodology

Learn to write maintainable selector names

Box Model

Control spacing and sizing

Build docs developers (and LLMs) love