CSS Selectors and Specificity: Targeting HTML Elements - johnverz22/webdev1-lessons GitHub Wiki

CSS (Cascading Style Sheets) uses selectors to target HTML elements for styling. Specificity is the mechanism that determines which styles are applied when multiple rules could affect the same element. Understanding both concepts is crucial for writing maintainable and predictable CSS.


Basic Selectors

Universal Selector

Targets all elements on the page.

* {
  margin: 0;
  padding: 0;
}

Type/Element Selector

Targets elements by their HTML tag name.

p {
  color: blue;
}

Class Selector

Targets elements with a specific class attribute.

.note {
  font-style: italic;
}

ID Selector

Targets a unique element with a specific ID.

#header {
  background-color: #333;
}

Grouping Selector

Applies the same styles to multiple selectors.

h1, h2, h3 {
  font-family: Arial, sans-serif;
}

Combinators

Descendant Combinator (space)

Targets elements that are descendants of another element.

article p {
  font-size: 16px;
}

Child Combinator (>)

Targets direct children of an element.

ul > li {
  list-style: square;
}

Adjacent Sibling Combinator (+)

Targets an element that directly follows another.

h2 + p {
  font-weight: bold;
}

General Sibling Combinator (~)

Targets elements that are siblings of another element.

h2 ~ p {
  color: gray;
}

Pseudo-classes

Pseudo-classes select elements based on their state or position.

Link States

a:link { color: blue; }
a:visited { color: purple; }
a:hover { text-decoration: underline; }
a:active { color: red; }

Form States

input:focus { border-color: blue; }
input:disabled { background-color: #eee; }
input:checked { border: 2px solid green; }

Structural Pseudo-classes

li:first-child { font-weight: bold; }
li:last-child { border-bottom: none; }
li:nth-child(2n) { background-color: #f0f0f0; } /* Even items */
li:nth-child(2n+1) { background-color: #fff; } /* Odd items */
p:only-child { font-style: italic; }

Other Common Pseudo-classes

div:empty { display: none; }
.error:not(.warning) { color: red; }
:root { --main-color: blue; }

Pseudo-elements

Pseudo-elements create "virtual" elements for styling specific parts of an element.

p::first-line { font-variant: small-caps; }
p::first-letter { font-size: 2em; }
p::before { content: "→ "; }
p::after { content: " ←"; }
input::placeholder { color: #999; }
p::selection { background: yellow; }

Attribute Selectors

Target elements based on their attributes.

Basic Attribute Selectors

[title] { cursor: help; } /* Elements with title attribute */
[type="checkbox"] { margin-right: 5px; }
[lang="fr"] { font-style: italic; }

Pattern Matching Attribute Selectors

[class^="icon-"] { /* Class starts with "icon-" */
  background-repeat: no-repeat;
}

[href$=".pdf"] { /* href ends with ".pdf" */
  background-image: url('pdf-icon.png');
}

[data-info*="user"] { /* data-info contains "user" */
  border: 1px solid blue;
}

[lang|="en"] { /* lang is "en" or begins with "en-" */
  color: blue;
}

[title~="flower"] { /* title contains the word "flower" */
  color: pink;
}

Understanding Specificity

Specificity determines which CSS rule is applied when multiple rules could style the same element. When multiple declarations conflict, the one with the highest specificity wins.

Key principles:

  1. Specificity is hierarchical
  2. Specificity is cumulative within its level
  3. A more specific selector always overrides a less specific one, regardless of order
  4. Equal specificity follows source order (last rule wins)

Specificity Calculation

Specificity is typically represented as four values: (a, b, c, d)

  • a: Inline styles
  • b: Number of ID selectors
  • c: Number of class selectors, attribute selectors, and pseudo-classes
  • d: Number of element selectors and pseudo-elements

Some developers visualize specificity as a four-digit number (e.g., 1,0,2,1), but remember it's not strictly decimal - 1,0,0,0 beats 0,9,9,9.

Examples:

Selector Specificity (a,b,c,d) As Number
* (0,0,0,0) 0
li (0,0,0,1) 1
li:first-line (0,0,0,2) 2
ul li (0,0,0,2) 2
.active (0,0,1,0) 10
[type="text"] (0,0,1,0) 10
:hover (0,0,1,0) 10
.nav .active (0,0,2,0) 20
#nav (0,1,0,0) 100
style="color: red" (1,0,0,0) 1000

Specificity Hierarchy

From lowest to highest specificity:

  1. Universal selector (*)
  2. Element/type selectors (h1, div) and pseudo-elements (::before)
  3. Class selectors (.example), attribute selectors ([type="radio"]), and pseudo-classes (:hover)
  4. ID selectors (#example)
  5. Inline styles (style="color: red")
  6. !important (overrides everything)

The !important Exception

The !important declaration overrides normal specificity rules, making a property take precedence regardless of the selector's specificity.

p { color: red !important; } /* Will override even inline styles */

Warning: !important breaks the natural cascading behavior of CSS and should be used sparingly.

Best Practices

For Maintainable Specificity:

  1. Avoid ID selectors when possible (they have high specificity and are not reusable)
  2. Don't use !important (except as a last resort or in utility classes)
  3. Use classes instead of elements for more specific styling
  4. Keep selectors as simple as possible
  5. Limit nesting to 2-3 levels max
  6. Use specificity deliberately - don't rely on accidental specificity
  7. Follow a naming convention like BEM to manage specificity through class names
  8. Consider a CSS methodology like SMACSS, ITCSS, or Atomic CSS to organize styles
  9. Use modern CSS features like custom properties and :where() to manage specificity
  10. Document complex specificity decisions in comments

Debugging Specificity Issues

When styles aren't applying as expected:

  1. Inspect the element using browser developer tools
  2. Check the Styles panel to see which rules are being applied/overridden
  3. Look for crossed-out properties - these are being overridden
  4. Check the order of CSS files being loaded
  5. Use browser extensions like SelectorGadget or Specificity Visualizer
  6. Temporarily add !important to test if specificity is the issue
  7. Use more specific selectors strategically to override problematic rules
  8. Consider refactoring overly complex selectors

Practical Examples

Example 1: Specificity Override Chain

/* Specificity: 0,0,0,1 */
p {
  color: black;
}

/* Specificity: 0,0,1,1 */
.content p {
  color: blue;
}

/* Specificity: 0,0,2,1 */
.article .content p {
  color: green;
}

/* Specificity: 0,1,1,1 */
#main .content p {
  color: red;
}

A paragraph with all these selectors would be colored red.

Example 2: Equal Specificity - Last Rule Wins

.alert {
  background-color: yellow;
}

.alert {
  background-color: orange; /* This wins if same specificity */
}

Example 3: The Cascade and Inheritance

body {
  font-family: Arial, sans-serif; /* Inherited by all elements */
  color: #333; /* Inherited by all elements */
}

a {
  color: blue; /* Overrides inherited color for links */
}

.button {
  font-family: Roboto, sans-serif; /* Overrides inherited font */
}

Browser Support

Most CSS selectors are well-supported in modern browsers. Some considerations:

  • CSS Selectors Level 4 (like :has()) have variable browser support
  • Older browsers (IE) may not support all pseudo-classes and combinators
  • Mobile browsers generally have good selector support
  • Always check caniuse.com for specific selector support
  • Fallbacks may be needed for certain selectors in legacy projects