3.2: Selectors & Specificity

Master CSS selectors to target specific HTML elements, classes, and IDs for styling. Understand how specificity and the cascade determine which styles are applied when multiple rules conflict.

1. Basic Selectors

Element Selector

Targets all elements of a specific type.

/* Selects all paragraphs */
p {
  color: #333;
  line-height: 1.6;
}

/* Selects all headings level 2 */
h2 {
  color: #2c3e50;
  font-size: 32px;
}

/* Selects all links */
a {
  color: #42b883;
  text-decoration: none;
}

Use case: Base styling for all elements of a type

Class Selector

Targets elements with a specific class attribute.

/* Selects elements with class="button" */
.button {
  background: #42b883;
  color: white;
  padding: 10px 20px;
  border-radius: 4px;
}

/* Selects elements with class="card" */
.card {
  border: 1px solid #ddd;
  padding: 20px;
  box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}

HTML:

<button class="button">Click me</button>
<div class="card">Card content</div>

Best practice: Use classes for reusable styles

ID Selector

Targets a unique element with a specific ID.

/* Selects element with id="header" */
#header {
  background: #2c3e50;
  padding: 20px;
}

/* Selects element with id="main-content" */
#main-content {
  max-width: 1200px;
  margin: 0 auto;
}

HTML:

<header id="header">Site Header</header>
<main id="main-content">Main content</main>

Warning: IDs should be unique per page. Prefer classes for styling.

Universal Selector

Targets all elements.

/* Selects EVERYTHING */
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

/* Reset all elements in a container */
.container * {
  margin: 0;
}

Performance note: Use sparingly - can be slow on large pages

Grouping Selectors

Apply same styles to multiple selectors.

/* Group related elements */
h1, h2, h3, h4, h5, h6 {
  font-family: 'Georgia', serif;
  color: #2c3e50;
}

/* Multiple classes */
.button, .link, .cta {
  cursor: pointer;
  transition: all 0.3s ease;
}

2. Attribute Selectors

Target elements based on attributes and their values.

Basic Attribute Selectors

/* Has attribute */
[disabled] {
  opacity: 0.5;
  cursor: not-allowed;
}

/* Exact value */
[type="text"] {
  border: 1px solid #ddd;
  padding: 8px;
}

/* Specific class (alternative to .class) */
[class="button"] {
  /* styles */
}

HTML:

<button disabled>Disabled</button>
<input type="text" placeholder="Name">

Advanced Attribute Selectors

/* Starts with */
[href^="https"] {
  color: green; /* Secure links */
}

[href^="http://"] {
  color: orange; /* Non-secure links */
}

/* Ends with */
[href$=".pdf"] {
  background: url('pdf-icon.png') no-repeat left center;
  padding-left: 20px;
}

[href$=".jpg"],
[href$=".png"] {
  /* Image links */
}

/* Contains */
[href*="example.com"] {
  font-weight: bold; /* Internal links */
}

[class*="col-"] {
  float: left; /* Grid columns */
}

/* Word match (space-separated) */
[class~="active"] {
  background: yellow;
}

/* Starts with word (dash-separated) */
[lang|="en"] {
  /* Matches en, en-US, en-GB */
}

Practical examples:

/* Style external links */
a[href^="http"]:not([href*="yourdomain.com"]) {
  color: blue;
}

a[href^="http"]:not([href*="yourdomain.com"])::after {
  content: " ↗";
}

/* Required form fields */
input[required] {
  border-left: 3px solid red;
}

/* File type icons */
a[href$=".pdf"]::before {
  content: "šŸ“„ ";
}

a[href$=".zip"]::before {
  content: "šŸ“¦ ";
}

3. Combinators

Combinators select elements based on their relationship to other elements.

Descendant Combinator (space)

Selects elements inside another element (any level deep).

/* All paragraphs inside article */
article p {
  line-height: 1.8;
}

/* All links inside navigation */
nav a {
  color: white;
  text-decoration: none;
}

/* Nested example */
.card .header .title {
  font-size: 24px;
}

HTML:

<article>
  <p>This paragraph is styled</p>
  <div>
    <p>This nested paragraph is also styled</p>
  </div>
</article>

Child Combinator (>)

Selects direct children only.

/* Direct children only */
ul > li {
  list-style: square;
}

/* Only direct p children of article */
article > p {
  font-size: 18px;
}

Difference:

<ul>
  <li>Direct child - styled āœ“</li>
  <li>Direct child - styled āœ“
    <ul>
      <li>Nested - NOT styled āœ—</li>
    </ul>
  </li>
</ul>
/* This styles ALL li (descendant) */
ul li { color: red; }

/* This styles only DIRECT li (child) */
ul > li { color: blue; }

Adjacent Sibling Combinator (+)

Selects the immediate next sibling.

/* Paragraph immediately after h2 */
h2 + p {
  font-weight: bold;
  color: #555;
}

/* Image immediately after paragraph */
p + img {
  margin-top: 20px;
}

HTML:

<h2>Heading</h2>
<p>This paragraph is styled (immediately after h2)</p>
<p>This paragraph is NOT styled</p>

General Sibling Combinator (~)

Selects all following siblings.

/* All paragraphs after h2 */
h2 ~ p {
  margin-left: 20px;
}

/* All images after the first image */
img ~ img {
  margin-left: 10px;
}

HTML:

<h2>Heading</h2>
<p>Styled āœ“</p>
<div>Some content</div>
<p>Also styled āœ“</p>
<p>Also styled āœ“</p>

4. Pseudo-Classes

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

Interactive States

/* Link states (must be in this order: LVHA) */
a:link {
  color: blue;
}

a:visited {
  color: purple;
}

a:hover {
  color: red;
  text-decoration: underline;
}

a:active {
  color: orange;
}

/* Focus state (keyboard navigation) */
input:focus,
button:focus {
  outline: 2px solid #42b883;
  outline-offset: 2px;
}

/* Disabled state */
button:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

/* Enabled state */
input:enabled {
  background: white;
}

Mnemonic: LoVe Hate (link, visited, hover, active)

Form Pseudo-Classes

/* Checked checkboxes/radios */
input:checked + label {
  font-weight: bold;
  color: green;
}

/* Valid input */
input:valid {
  border-color: green;
}

/* Invalid input */
input:invalid {
  border-color: red;
}

/* Required fields */
input:required {
  border-left: 3px solid orange;
}

/* Optional fields */
input:optional {
  border-left: 1px solid #ddd;
}

/* In range */
input[type="number"]:in-range {
  border-color: green;
}

/* Out of range */
input[type="number"]:out-of-range {
  border-color: red;
}

/* Placeholder shown */
input:placeholder-shown {
  font-style: italic;
}

Structural Pseudo-Classes

/* First child */
li:first-child {
  font-weight: bold;
}

/* Last child */
li:last-child {
  border-bottom: none;
}

/* Only child */
p:only-child {
  margin: 0;
}

/* nth-child examples */
li:nth-child(2) {
  /* Second item */
}

li:nth-child(odd) {
  /* 1st, 3rd, 5th... */
  background: #f9f9f9;
}

li:nth-child(even) {
  /* 2nd, 4th, 6th... */
  background: white;
}

li:nth-child(3n) {
  /* Every 3rd: 3, 6, 9... */
  color: blue;
}

li:nth-child(3n+1) {
  /* 1, 4, 7, 10... */
  color: red;
}

/* First of type */
p:first-of-type {
  font-size: 1.2em;
}

/* Last of type */
p:last-of-type {
  margin-bottom: 0;
}

/* nth-of-type */
p:nth-of-type(2) {
  /* Second paragraph */
}

Difference: nth-child vs nth-of-type

<div>
  <h2>Title</h2>
  <p>First paragraph</p>
  <p>Second paragraph</p>
</div>
/* Selects second paragraph (3rd child) */
p:nth-child(3) { color: red; }

/* Selects second paragraph (2nd p element) */
p:nth-of-type(2) { color: blue; }

Other Useful Pseudo-Classes

/* Empty elements */
div:empty {
  display: none;
}

/* Not selector */
li:not(.special) {
  color: gray;
}

/* Target (URL fragment) */
:target {
  background: yellow;
}

/* Root element (html) */
:root {
  --primary-color: #42b883;
}

5. Pseudo-Elements

Pseudo-elements create and style parts of elements.

::before and ::after

Insert content before/after an element.

/* Add content before */
.quote::before {
  content: """;
  font-size: 3em;
  color: #ddd;
}

.quote::after {
  content: """;
  font-size: 3em;
  color: #ddd;
}

/* Decorative elements */
.heading::before {
  content: "";
  display: inline-block;
  width: 4px;
  height: 30px;
  background: #42b883;
  margin-right: 10px;
  vertical-align: middle;
}

/* Icon font */
.icon-download::before {
  content: "⬇";
  margin-right: 5px;
}

/* External link indicator */
a[href^="http"]::after {
  content: " ↗";
  font-size: 0.8em;
}

Note: content property is required (can be empty content: "";)

::first-letter

Styles the first letter (drop cap effect).

.article::first-letter {
  font-size: 3em;
  font-weight: bold;
  float: left;
  line-height: 1;
  margin: 5px 10px 0 0;
  color: #42b883;
}

::first-line

Styles the first line of text.

.intro::first-line {
  font-weight: bold;
  font-size: 1.2em;
  color: #2c3e50;
}

::selection

Styles selected text.

::selection {
  background: #42b883;
  color: white;
}

p::selection {
  background: yellow;
  color: black;
}

::placeholder

Styles input placeholder text.

input::placeholder {
  color: #999;
  font-style: italic;
  opacity: 0.7;
}

Practical Examples

Custom list markers:

ul {
  list-style: none;
}

ul li::before {
  content: "āœ“ ";
  color: green;
  font-weight: bold;
  margin-right: 8px;
}

Clearfix (float clearing):

.clearfix::after {
  content: "";
  display: table;
  clear: both;
}

Ribbon/badge:

.new-badge::after {
  content: "NEW";
  position: absolute;
  top: 10px;
  right: -10px;
  background: red;
  color: white;
  padding: 5px 10px;
  font-size: 12px;
  font-weight: bold;
}

6. Specificity Deep Dive

Specificity Calculation

Specificity is calculated as: (inline, IDs, classes/attributes/pseudo-classes, elements/pseudo-elements)

Examples:

/* 0,0,0,1 - One element */
p { }

/* 0,0,1,0 - One class */
.text { }

/* 0,0,1,1 - One class + one element */
p.text { }

/* 0,0,2,0 - Two classes */
.header.active { }

/* 0,1,0,0 - One ID */
#main { }

/* 0,1,1,2 - One ID + one class + two elements */
div#main .content p { }

/* 0,0,1,0 - Attribute selector counts as class */
[type="text"] { }

/* 0,0,1,0 - Pseudo-class counts as class */
a:hover { }

/* 0,0,0,1 - Pseudo-element counts as element */
p::before { }

/* 0,0,2,1 - Attribute + pseudo-class + element */
input[type="text"]:focus { }

Specificity Comparison

Which wins?

/* 0,0,0,1 */
div { color: red; }

/* 0,0,1,0 - WINS */
.text { color: blue; }

/* 0,1,0,0 - WINS */
#intro { color: green; }

/* 0,1,1,1 - WINS (highest) */
div#intro.text { color: purple; }

Specificity Strategies

Increase specificity without changing HTML:

/* Low specificity */
.button { }

/* Higher specificity */
.header .button { }

/* Even higher */
#page .header .button { }

/* Highest (avoid) */
#page #header .button { }

Defensive CSS (high specificity):

/* Hard to override */
body div.container div.content p.text {
  /* Very specific */
}

Better approach (lower specificity):

/* Easy to override */
.content-text {
  /* Use specific class names */
}

() and Specificity

/* Specificity: 0,0,1,1 (class + element) */
p:not(.special) {
  color: gray;
}

/* :not() doesn't add specificity, but its argument does */
/* This has same specificity as p.special */

7. Selector Performance

Fast Selectors

/* Fast - direct class/ID */
.button { }
#header { }

/* Fast - single element */
p { }

/* Fast - child combinator */
.menu > li { }

Slow Selectors

/* Slow - universal selector */
* { }

/* Slow - deep descendant */
html body div.container div.content ul li a { }

/* Slow - attribute with partial match */
[class*="col-"] { }

Best Practices

DO:

  • āœ… Use classes for reusable styles
  • āœ… Keep selectors short (3 levels max)
  • āœ… Use child combinator (>) when possible
  • āœ… Use specific class names

DON'T:

  • āŒ Over-qualify selectors: div.class → .class
  • āŒ Chain too many selectors
  • āŒ Use descendant combinator excessively
  • āŒ Rely on element selectors for layout

Good:

.card { }
.card-title { }
.card-content { }

Bad:

div.container div.row div.col-md-6 div.card h2.title { }

8. Practical Exercises

Exercise 3.2.1: Selector Challenge

Given this HTML:

<div class="container">
  <h1 id="title">Main Title</h1>
  <p class="intro">Introduction</p>
  <ul>
    <li>First</li>
    <li class="active">Second</li>
    <li>Third</li>
  </ul>
  <a href="https://example.com">External</a>
  <a href="/page">Internal</a>
</div>

Write selectors to:

  1. Make the second list item bold
  2. Style external links differently
  3. Add a border to the first paragraph after h1
  4. Style odd list items

Exercise 3.2.2: Pseudo-Elements

Create a custom blockquote with:

  • Large quote marks using ::before and ::after
  • First letter drop cap
  • Custom selection color

Exercise 3.2.3: Specificity Battle

Calculate specificity for:

#header .nav li.active { }
.container .header .nav li { }
div#header nav ul li.active { }

Which one wins?

Exercise 3.2.4: Form Styling

Style a form with:

  • Red border for required fields
  • Green border for valid fields
  • Custom checkbox using ::before
  • Focus states with ::focus

9. Knowledge Check

Question 1: What's the difference between child (>) and descendant (space) combinators?

Show answer Child (>) selects only direct children, while descendant (space) selects all nested elements at any level.

Question 2: What's the specificity of .container #main p.text?

Show answer 0,1,2,1 (1 ID + 2 classes + 1 element) = 121

Question 3: Can ::before and ::after work without the content property?

Show answer No, the content property is required (even if empty: content: "";)

Question 4: What's the difference between and ?

Show answer :first-child selects if element is the first child of its parent. :first-of-type selects the first element of that type among siblings.

Question 5: What's the correct order for link pseudo-classes?

Show answer LVHA: :link, :visited, :hover, :active (mnemonic: LoVe HAte)

Question 6: How does () affect specificity?

Show answer :not() itself has no specificity, but its argument does. p:not(.special) has the same specificity as p.special.

10. Common Mistakes to Avoid

āŒ Wrong Pseudo-Element Syntax

/* Wrong - single colon (old syntax) */
.element:before {
  content: "";
}

/* Correct - double colon (modern) */
.element::before {
  content: "";
}

āŒ Over-Qualifying Selectors

/* Bad - unnecessary element */
div.container { }

/* Good - class is enough */
.container { }

āŒ Forgetting Content Property

/* Won't work */
.element::before {
  display: block;
  width: 20px;
}

/* Correct */
.element::before {
  content: "";
  display: block;
  width: 20px;
}
/* Wrong order - hover won't work after visited */
a:hover { }
a:visited { }

/* Correct order: LVHA */
a:link { }
a:visited { }
a:hover { }
a:active { }

11. Advanced Techniques

Attribute Selector Patterns

/* Language-specific styling */
[lang="zh"] {
  font-family: "Microsoft YaHei", sans-serif;
}

/* Data attributes */
[data-status="success"] {
  color: green;
}

[data-priority="high"] {
  font-weight: bold;
}

/* ARIA attributes */
[aria-hidden="true"] {
  display: none;
}

Complex Selectors

/* First paragraph that's not in a sidebar */
.main p:first-of-type:not(.sidebar p) {
  font-size: 1.2em;
}

/* Links that are not external and not in navigation */
a:not([href^="http"]):not(nav a) {
  color: inherit;
}

/* Checked radio's label */
input[type="radio"]:checked + label {
  font-weight: bold;
}

12. Key Takeaways

āœ… Basic selectors - element, class, ID, attribute, universal āœ… Combinators - descendant (space), child (>), adjacent (+), sibling (~) āœ… Pseudo-classes - , , , , āœ… Pseudo-elements - ::before, ::after, ::first-letter, ::selection āœ… Specificity order - inline > ID > class > element āœ… Link order - LVHA (link, visited, hover, active) āœ… Attribute selectors - attr, attr="value", attr^="value", attr$="value" āœ… Performance - Keep selectors short and specific āœ… Maintainability - Use classes over complex selectors āœ… Content property - Required for ::before and ::after


13. Further Resources

Official Documentation:

Interactive Learning:

Reference:


Next Steps

Excellent! You now have mastery over CSS selectors and can target any element precisely.

In Lesson 3.3: Box Model & Layout Fundamentals, you'll learn how CSS calculates element dimensions, spacing, and the foundation of layout.