Chapter 1 of ?
css 8 min read

CSS3 Mastery — Chapter 2: CSS Selectors, Combinators, Specificity & Cascading

Chapter 2: CSS Selectors, Combinators, Specificity & Cascading

CSS selectors target HTML elements to apply styles. Mastering simple selectors, attribute selectors, combinators, specificity calculations, cascading rules, and !important is the key to writing scalable, conflict-free stylesheets.

2.1 Simple CSS Selectors

Simple selectors target elements by tag name, class name, unique ID, or universal patterns:

Selector Type Syntax Description Example
Universal * Matches every element in the HTML document. * { box-sizing: border-box; }
Element / Type element Matches all HTML elements with the specified tag name. p { line-height: 1.6; }
Class .class Matches all elements with a specific class attribute. .btn { border-radius: 8px; }
ID #id Matches a single unique element with a specific id attribute. #navbar { position: fixed; }
Grouping sel1, sel2 Applies the same style declarations to multiple comma-separated selectors. h1, h2, h3 { font-family: sans-serif; }
<!DOCTYPE html>
<html>
<head>
  <style>
    body { font-family: system-ui, sans-serif; background: #0f172a; color: #f8fafc; padding: 24px; }

    /* Universal Selector — applies to every element */
    * { box-sizing: border-box; }

    /* Element / Type Selector */
    p { color: #94a3b8; line-height: 1.7; }

    /* Class Selector */
    .highlight-box {
      background: #1e293b;
      border: 1px solid #6366f1;
      border-radius: 10px;
      padding: 16px;
      margin: 12px 0;
    }

    /* ID Selector */
    #main-title { color: #818cf8; font-size: 1.6rem; margin-top: 0; }

    /* Grouping Selector — h2 and h3 share the same font style */
    h2, h3 { color: #38bdf8; font-family: 'Segoe UI', sans-serif; }
  </style>
</head>
<body>
  <h1 id="main-title">Simple Selectors Demo</h1>
  <h2>Class &amp; Element Selector</h2>
  <div class="highlight-box">
    <h3>Highlight Box (class selector)</h3>
    <p>This paragraph uses the element selector <code>p { color: #94a3b8 }</code>.</p>
  </div>
  <div class="highlight-box">
    <p>Another .highlight-box — same class, same styles applied!</p>
  </div>
</body>
</html>

2.2 CSS Attribute Selectors

Attribute selectors target HTML elements based on the presence, value, or substring match of their attributes:

Presence Selector: [attribute]

Selects elements that possess the specified attribute, regardless of its value.

<!DOCTYPE html>
<html>
<head>
  <style>
    body { font-family: system-ui, sans-serif; background: #0f172a; color: #f8fafc; padding: 24px; }
    label { display: block; font-size: 13px; color: #94a3b8; margin-bottom: 4px; }
    input {
      display: block; width: 100%; padding: 8px 12px;
      background: #1e293b; color: #f8fafc;
      border: 1px solid #334155; border-radius: 6px;
      margin-bottom: 6px; font-family: inherit;
    }
    /* Style any input that has a required attribute */
    input[required] { border-left: 4px solid #ef4444; }
    .note { font-size: 11px; color: #64748b; margin: 0 0 16px; }
  </style>
</head>
<body>
  <h3 style="color:#6366f1;margin-top:0;">[attribute] Presence Selector</h3>
  <label>Optional Field (no required attr — normal border):</label>
  <input type="text" placeholder="Optional input..." />
  <p class="note">No [required] attribute → default left border</p>
  <label>Required Field (has required attr — red left border):</label>
  <input type="text" placeholder="Required input..." required />
  <p class="note">input[required] matched → 4px red left border applied!</p>
</body>
</html>
Exact Value Selector: [attribute="value"]

Selects elements whose attribute value matches the string exactly.

<!DOCTYPE html>
<html>
<head>
  <style>
    body { font-family: system-ui, sans-serif; background: #0f172a; color: #f8fafc; padding: 24px; }
    label { display: block; font-size: 13px; color: #94a3b8; margin-bottom: 4px; }
    input {
      display: block; width: 100%; padding: 8px 12px;
      border-radius: 6px; margin-bottom: 6px; font-family: inherit;
      background: #1e293b; color: #f8fafc; border: 1px solid #334155;
    }
    /* Select text inputs specifically */
    input[type="text"] {
      background-color: #0f172a;
      color: #ffffff;
      border: 2px solid #6366f1;
    }
    .note { font-size: 11px; color: #64748b; margin: 0 0 14px; }
  </style>
</head>
<body>
  <h3 style="color:#3b82f6;margin-top:0;">[attr="value"] Exact Match Selector</h3>
  <label>type="text" (targeted by CSS rule):</label>
  <input type="text" value="Text input — dark bg + indigo border" />
  <p class="note">input[type="text"] matches → styled with dark bg and indigo border</p>
  <label>type="number" (not targeted):</label>
  <input type="number" value="42" />
  <p class="note">input[type="number"] does not match "text" → no special styling</p>
  <label>type="email" (not targeted):</label>
  <input type="email" value="user@example.com" />
  <p class="note">input[type="email"] does not match "text" → no special styling</p>
</body>
</html>
Substring Matchers: ^=, $=, *=

Matches starts with (^=), ends with ($=), or contains substring (*=).

<!DOCTYPE html>
<html>
<head>
  <style>
    body { font-family: system-ui, sans-serif; background: #0f172a; color: #f8fafc; padding: 24px; }
    a {
      display: block; margin-bottom: 10px; padding: 10px 14px;
      border-radius: 6px; background: #1e293b; text-decoration: none; color: #94a3b8;
    }
    /* Links starting with https:// — green (secure) */
    a[href^="https://"] { color: #10b981; border-left: 3px solid #10b981; }
    /* Links ending in .pdf — append PDF label */
    a[href$=".pdf"]::after { content: " 📄 PDF"; font-size: 11px; opacity: 0.8; }
    /* Any element whose data-role contains 'card' */
    [data-role*="card"] {
      border: 2px solid #6366f1;
      box-shadow: 0 4px 20px rgba(99,102,241,0.35);
      padding: 14px; border-radius: 8px; margin-top: 16px; background: #1e293b;
    }
  </style>
</head>
<body>
  <h3 style="color:#10b981;margin-top:0;">Substring Attribute Selectors</h3>
  <a href="https://example.com">https://example.com (^= starts with https://)</a>
  <a href="http://example.com">http://example.com (no match — no style)</a>
  <a href="annual-report-2024.pdf">annual-report-2024.pdf ($= ends with .pdf)</a>
  <div data-role="product-card">
    <strong>data-role="product-card"</strong>
    <p style="margin:4px 0 0;font-size:13px;color:#a5b4fc;">*= contains "card" → indigo border + glow!</p>
  </div>
</body>
</html>

2.3 CSS Combinators

Combinators define structural relationships between two or more selectors:

Combinator Syntax Relationship Example Description
Descendant A B (space) Descendant Selects all <p> elements inside .card at any nesting depth.
Child A > B Direct Child Selects all <li> elements that are direct children of <ul>.
Adjacent Sibling A + B Next Immediate Sibling Selects <p> only if it is placed immediately after an <h2>.
General Sibling A ~ B Subsequent Sibling Selects all <p> elements that follow an <h2> under the same parent.
<!DOCTYPE html>
<html>
<head>
  <style>
    body { font-family: system-ui, sans-serif; background: #0f172a; color: #f8fafc; padding: 24px; }
    .section { background: #1e293b; border-radius: 10px; padding: 16px; margin-bottom: 16px; }

    /* Descendant combinator: any <p> inside .card (any depth) */
    .card p { color: #a5b4fc; font-style: italic; }

    /* Child combinator: only DIRECT <li> children of <ul> */
    ul > li { color: #34d399; list-style: none; padding-left: 0; }
    ul > li::before { content: "✓ "; }

    /* Adjacent sibling: <p> immediately after <h3> */
    h3 + p { color: #f59e0b; font-weight: 600; }

    /* General sibling: ALL <p> that follow <h3> at same level */
    h3 ~ p { border-left: 2px solid #6366f1; padding-left: 10px; }
  </style>
</head>
<body>
  <div class="section">
    <strong style="color:#818cf8;">Descendant (.card p) &amp; Child (ul > li)</strong>
    <div class="card">
      <p>Descendant: this &lt;p&gt; is inside .card → indigo italic!</p>
      <ul>
        <li>Direct child &lt;li&gt; of ul → green checkmark!</li>
        <li>Another direct child li → also green!</li>
      </ul>
    </div>
  </div>
  <div class="section">
    <strong style="color:#818cf8;">Adjacent (h3 + p) &amp; General Sibling (h3 ~ p)</strong>
    <h3>Heading</h3>
    <p>Adjacent sibling: immediately after h3 → amber bold!</p>
    <p>General sibling: follows h3 at same level → indigo left border!</p>
    <p>Also a general sibling → also gets indigo left border!</p>
  </div>
</body>
</html>

Modern CSS Feature: :has() (The Relational Parent Selector)

Traditionally, CSS selectors could only target elements downwards or sideways. With :has(), you can style a parent element based on its child contents or states!

<!DOCTYPE html>
<html>
<head>
  <style>
    body { font-family: system-ui, sans-serif; background: #0f172a; color: #f8fafc; padding: 24px; }
    .card {
      background: #1e293b; border: 2px solid #334155;
      border-radius: 12px; padding: 16px; margin-bottom: 16px;
    }
    /* Style a card container if it contains an image */
    .card:has(img) { border-color: #6366f1; }

    .form-group {
      background: #1e293b; border: 1px solid #334155;
      border-radius: 8px; padding: 14px; margin-bottom: 12px;
      transition: all 0.2s;
    }
    /* Style a form group if the input inside is currently focused */
    .form-group:has(input:focus) {
      background: rgba(99, 102, 241, 0.1);
      border-color: #6366f1;
    }
    input { background: transparent; border: none; color: #f8fafc; outline: none; width: 100%; font-family: inherit; padding: 4px 0; }
    label { font-size: 12px; color: #94a3b8; display: block; margin-bottom: 4px; }
    img { width: 100%; border-radius: 6px; height: 80px; object-fit: cover; }
  </style>
</head>
<body>
  <h3 style="color:#6366f1;margin-top:0;">:has() — Parent Selector Demo</h3>
  <div class="card">
    <p style="margin:0;font-size:13px;color:#94a3b8;">Card with no image — normal grey border</p>
  </div>
  <div class="card">
    <img src="https://picsum.photos/seed/css3/400/80" alt="Demo image" />
    <p style="margin:6px 0 0;font-size:13px;color:#a5b4fc;">.card:has(img) → indigo border applied!</p>
  </div>
  <div class="form-group">
    <label>Click inside the input field to see the parent highlight ↓</label>
    <input type="text" placeholder="Focus me — entire box highlights!" />
  </div>
  <p style="font-size:12px;color:#64748b;">.form-group:has(input:focus) → parent highlights when child input is focused!</p>
</body>
</html>

2.4 CSS Specificity Calculation & Live Calculator

When multiple conflicting CSS rules target the same element, the browser calculates a 4-tuple Specificity Weight (Inline, ID, Class/Attr/Pseudo, Element) to determine which rule wins:

1, 0, 0, 0
Inline (style="")
0, 1, 0, 0
ID Selectors (#nav)
0, 0, 1, 0
Class / Attr / Pseudo
0, 0, 0, 1
Element / Pseudo-elem
Interactive Specificity Calculator

Type any CSS selector below to calculate its exact (Inline, ID, Class, Element) specificity score live!

Specificity Weight Score
(0, 1, 2, 3)

2.5 Cascading Rules & The !important Exception

The Cascade is the algorithm browsers use to resolve conflicts when multiple styles apply to the same element. It resolves styles in the following order:

  1. Origin & Importance: User Agent default browser styles < Author normal styles < Author !important styles < User Agent !important.
  2. Specificity Score: Higher specificity weight tuple wins.
  3. Source Order: When origin and specificity are identical, the rule defined last in the stylesheet wins.

Using !important Responsibly

Adding !important to a CSS declaration forces it to override all normal specificity rules. However, overusing it leads to specificity wars and unmaintainable code!

<!DOCTYPE html>
<html>
<head>
  <style>
    body { font-family: system-ui, sans-serif; background: #0f172a; color: #f8fafc; padding: 24px; }
    button {
      padding: 10px 22px; border: none; border-radius: 8px;
      cursor: pointer; font-size: 14px; font-weight: 600; color: #fff;
      display: block; margin: 8px 0;
    }
    .note { font-size: 12px; color: #64748b; margin: 0 0 16px; }

    /* High specificity class rule */
    .button-primary { background-color: #4f46e5; }

    /* !important overrides EVERYTHING — even higher-specificity selectors */
    .alert-btn { background-color: #ef4444 !important; }

    /* This very specific rule still CANNOT beat !important */
    body .button-primary.alert-btn { background-color: #10b981; }
  </style>
</head>
<body>
  <h3 style="color:#f59e0b;margin-top:0;">!important Override Demo</h3>
  <button class="button-primary">.button-primary → indigo (normal rule)</button>
  <p class="note">background: #4f46e5 from .button-primary</p>
  <button class="alert-btn">.alert-btn → red (!important wins)</button>
  <p class="note">background: #ef4444 !important — overrides everything</p>
  <button class="button-primary alert-btn">Both classes — body .button-primary.alert-btn exists but STILL red!</button>
  <p class="note">Even "body .button-primary.alert-btn" (high specificity) cannot beat !important!</p>
</body>
</html>

2.6 Interactive Sandbox — Selectors & Specificity

Test combinators, attribute selectors, and specificity overrides live in the interactive code playground below:

Live Playground — Chapter 2 Sandbox
Done with this chapter?
Mark it complete to track your progress and unlock your certificate.
Next Up

Learner Reviews

Write a Review
Share your experience to help other learners.
Your Rating *