cyberangles guide

CSS Selectors Explained: From Basic to Advanced

Cascading Style Sheets (CSS) is the backbone of web design, responsible for styling and layout. At the heart of CSS lies **selectors**—powerful tools that target HTML elements to apply styles. Whether you’re styling a single paragraph or building complex, responsive layouts, mastering selectors is essential for writing clean, efficient, and maintainable CSS. This guide will take you from the fundamentals of CSS selectors to advanced techniques, with practical examples and best practices. By the end, you’ll be able to precisely target elements, optimize specificity, and write CSS that’s both powerful and easy to debug.

Table of Contents

  1. Basic Selectors
    • Universal Selector
    • Type Selector
    • Class Selector
    • ID Selector
    • Attribute Selector
  2. Combinators
    • Descendant Combinator
    • Child Combinator
    • Adjacent Sibling Combinator
    • General Sibling Combinator
  3. Pseudo-Classes
    • State Pseudo-Classes
    • Structural Pseudo-Classes
    • Negation Pseudo-Class
  4. Pseudo-Elements
    • ::before and ::after
    • ::first-line and ::first-letter
    • ::selection
  5. Advanced Selectors & Specificity
    • Complex Attribute Selectors
    • Combining Selectors
    • Specificity: How CSS Decides Which Rule Wins
  6. Best Practices
    • Classes vs. IDs
    • Avoid Over-Specificity
    • Semantic Selectors
    • Performance Considerations
  7. Reference Table

Basic Selectors

Basic selectors target elements directly based on their type, class, ID, attributes, or a universal match. They form the foundation of CSS styling.

1.1 Universal Selector

Targets all elements on a page. Useful for resetting margins/padding or applying global styles, but use sparingly (see performance notes).

Syntax: *

Example:

/* Reset default margins/padding for all elements */
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

Use Case: Global resets or applying styles to every element (e.g., font-family).

1.2 Type Selector

Targets elements by their HTML tag name (e.g., <p>, <div>, <h1>).

Syntax: tagname

Example:

/* Style all <p> tags */
p {
  color: #333;
  font-size: 16px;
  line-height: 1.5;
}

/* Style all <button> tags */
button {
  background: #007bff;
  color: white;
  padding: 8px 16px;
}

Use Case: Styling semantic elements consistently (e.g., all headings or lists).

1.3 Class Selector

Targets elements with a specific class attribute. Classes are reusable and can be applied to multiple elements.

Syntax: .classname

Example:

<!-- HTML -->
<div class="alert">This is a warning!</div>
<p class="alert">Another warning here.</p>
/* CSS */
.alert {
  background: #fff3cd;
  color: #856404;
  padding: 12px;
  border-left: 4px solid #ffc107;
}

Use Case: Grouping elements with shared styles (e.g., buttons, cards, alerts).

1.4 ID Selector

Targets a single element with a unique id attribute (IDs must be unique per page).

Syntax: #idname

Example:

<!-- HTML -->
<header id="main-header">Welcome to My Site</header>
/* CSS */
#main-header {
  background: #2d3436;
  color: white;
  padding: 20px;
  text-align: center;
}

Note: Avoid overusing IDs—they have high specificity (see specificity) and can’t be reused. Prefer classes for most styling.

1.5 Attribute Selector

Targets elements with a specific attribute or attribute value.

Syntax: [attribute], [attribute="value"], or variations (e.g., [attribute^="value"] for “starts with”).

Common Variations:

SelectorDescriptionExample
[attr]Elements with attr attribute (any value)[disabled] { opacity: 0.5; }
[attr="val"]Elements where attr equals val (exact)[type="email"] { border: 1px solid blue; }
[attr^="val"]attr starts with val[href^="https"] { color: #28a745; }
[attr$="val"]attr ends with val[src$=".pdf"] { background: #f8d7da; }
[attr*="val"]attr contains val (any position)[class*="btn-"] { padding: 8px 16px; }

Example:

/* Style links pointing to external sites */
a[href^="http"] {
  background: url("external-icon.png") no-repeat right;
  padding-right: 18px;
}

/* Style input fields with placeholder text */
input[placeholder] {
  border: 1px solid #ced4da;
}

Use Case: Styling form inputs, links, or media elements based on their attributes (e.g., type, href, src).

Combinators

Combinators let you target elements based on their relationship to other elements (e.g., parent-child, siblings).

2.1 Descendant Combinator

Targets an element that is a descendant (child, grandchild, etc.) of another element.

Syntax: ancestor descendant (space-separated)

Example:

<!-- HTML -->
<nav class="main-nav">
  <ul>
    <li><a href="/">Home</a></li>
    <li><a href="/about">About</a></li>
  </ul>
</nav>
/* CSS: Target all <a> tags inside .main-nav (even nested ones) */
.main-nav a {
  color: #2d3436;
  text-decoration: none;
}

Note: The <a> tags here are descendants of .main-nav (via <ul> and <li>).

2.2 Child Combinator

Targets an element that is a direct child of another element (no nested levels).

Syntax: parent > child (greater-than symbol)

Example:

<!-- HTML -->
<div class="card">
  <h3>Card Title</h3> <!-- Direct child -->
  <div class="card-body">
    <p>Card content</p> <!-- Grandchild (not a direct child) -->
  </div>
</div>
/* CSS: Target only direct children of .card */
.card > h3 {
  color: #007bff;
  margin-bottom: 12px;
}

Result: Only <h3> (direct child) is styled, not the <p> (grandchild).

2.3 Adjacent Sibling Combinator

Targets an element that is the immediately next sibling of another element (shares the same parent).

Syntax: element + sibling (plus symbol)

Example:

<!-- HTML -->
<h2>Introduction</h2>
<p>First paragraph.</p> <!-- Adjacent sibling of h2 -->
<p>Second paragraph.</p> <!-- Not adjacent -->
/* CSS: Style the first <p> after an <h2> */
h2 + p {
  font-weight: bold;
  color: #28a745;
}

Result: Only the first <p> (immediately after <h2>) is bold and green.

2.4 General Sibling Combinator

Targets all siblings of an element that appear after it (shares the same parent).

Syntax: element ~ sibling (tilde symbol)

Example:

<!-- HTML -->
<input type="checkbox" id="toggle">
<label for="toggle">Show details</label>
<p class="details">Hidden content</p>
<p class="details">More hidden content</p>
/* CSS: Show .details when checkbox is checked */
#toggle:checked ~ .details {
  display: block;
}

.details {
  display: none;
}

Result: Both .details paragraphs (siblings of the checkbox) are shown when the checkbox is checked.

Pseudo-Classes

Pseudo-classes (:) target elements based on state (e.g., hover, active) or position (e.g., first child, even/odd rows).

3.1 State Pseudo-Classes

Target elements in a specific state (e.g., user interaction).

Pseudo-ClassDescriptionExample
:hoverElement is hovered over (mouse only)button:hover { transform: scale(1.05); }
:activeElement is being clicked/activatedbutton:active { background: #0056b3; }
:focusElement has keyboard focus (e.g., input)input:focus { border: 2px solid #007bff; }
:visitedLink has been visited by the usera:visited { color: #6610f2; }
:disabledElement is disabled (e.g., input)input:disabled { opacity: 0.5; cursor: not-allowed; }

Example:

/* Style a button in different states */
.btn {
  padding: 10px 20px;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}

.btn:hover {
  background: #0056b3; /* Darken on hover */
}

.btn:active {
  transform: translateY(2px); /* Slight press effect */
}

.btn:focus {
  outline: 3px solid rgba(0, 123, 255, 0.3); /* Focus ring for accessibility */
}

3.2 Structural Pseudo-Classes

Target elements based on their position in the DOM.

Pseudo-ClassDescriptionExample
:first-childFirst child of its parentul li:first-child { font-weight: bold; }
:last-childLast child of its parentul li:last-child { margin-right: 0; }
:nth-child(n)nth child (n = number, even, odd, or formula like 2n+1)tr:nth-child(even) { background: #f8f9fa; }
:only-childOnly child of its parentdiv:only-child { margin: 0 auto; }
:first-of-typeFirst element of its type among siblingsarticle p:first-of-type { font-size: 18px; }

Example:

<!-- HTML -->
<ul class="todo-list">
  <li>Buy groceries</li>
  <li>Finish CSS project</li>
  <li>Call mom</li>
  <li>Read book</li>
</ul>
/* CSS: Style odd/even list items */
.todo-list li:nth-child(odd) {
  background: #f8f9fa;
}

.todo-list li:nth-child(even) {
  background: #e9ecef;
}

/* Style the first and last items */
.todo-list li:first-child {
  border-top-left-radius: 4px;
  border-top-right-radius: 4px;
}

.todo-list li:last-child {
  border-bottom-left-radius: 4px;
  border-bottom-right-radius: 4px;
}

3.3 Negation Pseudo-Class (:not())

Targets elements that do not match a given selector.

Syntax: :not(selector)

Example:

/* Style all buttons except .secondary */
button:not(.secondary) {
  background: #007bff;
}

/* Style all inputs except disabled ones */
input:not([disabled]) {
  cursor: text;
}

Use Case: Excluding specific elements from a style rule (e.g., “all buttons except the cancel button”).

Pseudo-Elements

Pseudo-elements (::) target specific parts of an element (e.g., the first line of text, or adding content before/after an element).

4.1 ::before and ::after

Insert content before or after an element’s content. Requires the content property (use content: "" for empty content).

Syntax: element::before or element::after

Example:

<!-- HTML -->
<p class="quote">CSS is awesome!</p>
/* CSS: Add quotes around the text */
.quote::before {
  content: '"';
  font-size: 24px;
  color: #6c757d;
}

.quote::after {
  content: '"';
  font-size: 24px;
  color: #6c757d;
}

Result: "CSS is awesome!"

Use Case: Adding icons, decorative elements, or tooltips without cluttering HTML.

4.2 ::first-line and ::first-letter

Target the first line or first letter of text in a block-level element.

Example:

/* Style the first line of a paragraph */
.intro::first-line {
  font-weight: bold;
  color: #007bff;
}

/* Style the first letter of a heading */
h1::first-letter {
  font-size: 2em;
  color: #dc3545;
}

Note: Only works on block-level elements (e.g., <p>, <h1>).

4.3 ::selection

Targets text selected by the user (e.g., highlighted with the mouse).

Example:

/* Customize selected text color */
::selection {
  background: #ffc107;
  color: #2d3436;
}

Use Case: Improving user experience with branded selection colors.

Advanced Selectors & Specificity

5.1 Complex Selector Combinations

Combine basic selectors, combinators, and pseudo-classes for precise targeting.

Example:

/* Target the 3rd <li> in a .nav list that is not active */
.nav li:nth-child(3):not(.active) a {
  color: #6c757d;
}

/* Target disabled inputs inside a .form-group */
.form-group input[disabled] {
  background: #e9ecef;
}

5.2 Specificity: How CSS Decides Which Rule Wins

When multiple CSS rules target the same element, specificity determines which rule is applied. Think of it as a “score” for selectors: higher scores override lower ones.

Specificity Calculation (from highest to lowest):

Selector TypeScore ContributionExampleScore (0,0,0,0)
Inline styles1,0,0,0<div style="color: red">(1,0,0,0)
ID selectors0,1,0,0#header(0,1,0,0)
Class/pseudo-class/attribute0,0,1,0.btn, :hover, [type](0,0,1,0)
Type selectors0,0,0,1div, p(0,0,0,1)
Universal selector (*)0,0,0,0*(0,0,0,0)

Example:

/* Score: (0,0,1,0) */
.alert { color: red; }

/* Score: (0,1,0,0) → higher than .alert → wins */
#critical { color: blue; }

/* Score: (0,0,2,0) → higher than (0,0,1,0) → wins over .alert */
.alert.error { color: purple; }

Tip: Use !important to force a rule (e.g., .alert { color: red !important; }), but avoid it—overuse leads to unmaintainable code.

Best Practices

  1. Prefer Classes Over IDs: IDs are unique and hard to reuse. Classes are flexible and promote reusability.
  2. Avoid Over-Specificity: Don’t chain selectors unnecessarily (e.g., div.container .row .col can often be simplified to .col).
  3. Use Semantic Selectors: Target elements like <nav> or <article> instead of generic <div>s for better readability.
  4. Limit Universal Selectors: * is slow on large pages—use resets like Normalize.css instead.
  5. Test for Accessibility: Ensure state pseudo-classes (e.g., :focus) are visible for keyboard users.

Reference Table

SelectorSyntaxDescriptionExample
Universal*All elements* { margin: 0; }
TypetagElements by tag namep { color: #333; }
Class.classElements with class class.btn { padding: 8px; }
ID#idElement with ID id (unique)#logo { width: 100px; }
Attribute[attr]Elements with attribute attr[disabled] { opacity: 0.5; }
Descendantancestor descendantDescendant of ancestor.nav a { color: blue; }
Childparent > childDirect child of parentul > li { list-style: none; }
Adjacent SiblingA + BB is immediately after Ah2 + p { font-weight: bold; }
General SiblingA ~ BAll B after Ainput ~ label { color: #666; }
:hoverelement:hoverElement on hoverbutton:hover { transform: scale(1.05); }
:nth-child(n)element:nth-child(n)nth child of parentli:nth-child(odd) { background: #f8f9fa; }
::beforeelement::beforeAdd content before element.quote::before { content: '"'; }

Conclusion

Mastering CSS selectors is key to writing efficient, maintainable styles. From basic classes to advanced specificity rules, selectors let you target elements with precision. Practice combining selectors and testing specificity to build robust layouts.

Happy styling! 🚀