Skillversitas

Specificity & Cascade

Specificity is the CSS rule system that determines which style is applied when several rules with different selectors target the same element. Understanding specificity is important for debugging CSS and writing maintainable code.

The specificity hierarchy, from highest: inline styles > ID selectors > class/attribute/pseudo-class selectors > element/pseudo-element selectors > universal selector (*). The more specific a selector, the higher its priority.

The cascade determines the order in which styles are applied: specificity, source order, and importance (!important). When specificity is equal, the rule defined last wins.

!important overrides all specificity, but it should be avoided because it makes debugging difficult and the code fragile.

Code Example

<!DOCTYPE html>
<html lang="en">
<head>
<style>
  body {
    font-family: 'Segoe UI', Arial, sans-serif;
    padding: 30px;
    background: #f5f5f5;
  }

  .demo-box {
    background: white;
    padding: 25px;
    border-radius: 10px;
    margin-bottom: 25px;
    box-shadow: 0 2px 8px rgba(0,0,0,0.06);
  }

  .demo-box h3 {
    margin-top: 0;
    color: #333;
  }

  .demo-box code {
    background: #f0f0f0;
    padding: 2px 6px;
    border-radius: 4px;
    font-size: 0.9em;
    color: #e74c3c;
  }

  /* === SPECIFICITY DEMO === */

  /* Level 1: Element selector (specificity: 0-0-1) */
  p {
    color: #333;
    margin-bottom: 8px;
  }

  /* Level 2: Class selector (specificity: 0-1-0) */
  .text-blue {
    color: #3498db;
  }

  /* Level 3: ID selector (specificity: 1-0-0) */
  #text-highlight {
    color: #e74c3c;
    font-weight: bold;
  }

  /* Level 4: Inline style (specificity: 1-0-0-0) — highest */
  /* In HTML: style="color: purple;" */

  /* Class vs Element */
  .demo-class-element p {
    color: #2ecc71;
  }

  /* ID vs Class */
  .demo-id-class .box {
    border: 2px solid #3498db;
  }

  #demo-id-class .box {
    border: 2px solid #e74c3c;
  }

  /* Cascade Demo - Same specificity, last wins */
  .cascade-demo .item {
    color: blue;
  }

  .cascade-demo .item {
    color: red;
  }

  /* Multiple classes */
  .specificity-a.b {
    font-size: 1.1em;
    color: #9b59b6;
  }

  .specificity-a {
    color: #3498db;
  }

  .specificity-b {
    color: #2ecc71;
  }

  /* Descendant vs Direct Child */
  .descendant-demo p {
    color: #e67e22;
  }

  .direct-demo > p {
    color: #e74c3c;
  }

  /* Attribute selector */
  [type="text"] {
    border: 2px solid #3498db;
  }

  [type="text"][required] {
    border-color: #e74c3c;
  }

  /* Pseudo-class */
  .interactive-box:hover {
    background: #ecf0f1;
  }

  .interactive-box:focus {
    outline: 2px solid #667eea;
  }

  /* !important (avoid!) */
  .important-demo p {
    color: #333 !important;
  }

  .important-demo .override {
    color: #e74c3c;
  }

  /* Practical: Debugging specificity */
  .card {
    background: white;
    border: 1px solid #ddd;
    padding: 15px;
    border-radius: 8px;
    margin: 10px 0;
  }

  .card.important-card {
    border-color: #e74c3c;
  }

  .sidebar .card {
    background: #f8f9fa;
  }

  /* Framework override pattern */
  /* Instead of !important, use more specific selector */
  .app-container .card {
    border-radius: 12px;
    box-shadow: 0 2px 12px rgba(0,0,0,0.08);
  }

  .comparison-table {
    width: 100%;
    border-collapse: collapse;
    margin-top: 15px;
  }

  .comparison-table th,
  .comparison-table td {
    border: 1px solid #ddd;
    padding: 10px 12px;
    text-align: left;
  }

  .comparison-table th {
    background: #f0f0f0;
    font-weight: 600;
  }

  .comparison-table .score {
    font-family: monospace;
    color: #667eea;
  }

  .tip-box {
    background: #fff3cd;
    border: 1px solid #ffc107;
    border-radius: 8px;
    padding: 15px;
    margin-top: 15px;
  }

  .tip-box h4 {
    margin-top: 0;
    color: #856404;
  }

  .tip-box ul {
    margin-bottom: 0;
    padding-left: 20px;
  }
</style>
</head>
<body>
  <div class="demo-box">
    <h3>Hierarchy Specificity</h3>
    <table class="comparison-table">
      <tr>
        <th>Selector</th>
        <th>Specificity</th>
        <th>Example</th>
      </tr>
      <tr>
        <td>Universal (*)</td>
        <td class="score">0-0-0</td>
        <td><code>*</code></td>
      </tr>
      <tr>
        <td>Element</td>
        <td class="score">0-0-1</td>
        <td><code>p, div, h1</code></td>
      </tr>
      <tr>
        <td>Class / Pseudo-class</td>
        <td class="score">0-1-0</td>
        <td><code>.text-blue, :hover</code></td>
      </tr>
      <tr>
        <td>ID</td>
        <td class="score">1-0-0</td>
        <td><code>#header</code></td>
      </tr>
      <tr>
        <td>Inline style</td>
        <td class="score">1-0-0-0</td>
        <td><code>style="color:red"</code></td>
      </tr>
      <tr>
        <td>!important</td>
        <td class="score">Infinity</td>
        <td><code>color: red !important</code></td>
      </tr>
    </table>
  </div>

  <div class="demo-box">
    <h3>Specificity in Action</h3>
    <p style="color: purple;">1. Inline style wins (this is purple because it is inline)</p>
    <p id="text-highlight">2. ID selector wins (red, because ID > class > element)</p>
    <p class="text-blue">3. Class selector wins (blue, because class > element)</p>
    <p>4. Element selector default (dark gray)</p>
  </div>

  <div class="demo-box">
    <h3>Cascade: Last Wins (Same Specificity)</h3>
    <div class="cascade-demo">
      <p class="item">This text should be RED (the second rule wins because it was defined last)</p>
    </div>
  </div>

  <div class="demo-box">
    <h3>Multiple Classes: Specificity Is Added</h3>
    <p class="specificity-a specificity-b">Class A + B: Specificity = 0-2-0 (purple)</p>
    <p class="specificity-a">Class A only: Specificity = 0-1-0 (blue)</p>
    <p class="specificity-b">Class B only: Specificity = 0-1-0 (green)</p>
  </div>

  <div class="demo-box">
    <h3>Descendant vs Direct Child</h3>
    <div class="descendant-demo">
      <p>Descendant: p inside .descendant-demo (orange)</p>
      <div>
        <p>Also a descendant (indirect) - still orange</p>
      </div>
    </div>
    <div class="direct-demo">
      <p>Direct child: p directly inside .direct-demo (red)</p>
      <div>
        <p>Not a direct child (stays default because > only targets direct children)</p>
      </div>
    </div>
  </div>

  <div class="demo-box">
    <h3>Attribute Selector: Specificity Added</h3>
    <div style="display: flex; gap: 10px;">
      <input type="text" placeholder="type=text only (blue)">
      <input type="text" required placeholder="type=text + required (red)">
    </div>
  </div>

  <div class="demo-box">
    <h3>!important (Avoid!)</h3>
    <div class="important-demo">
      <p class="override">This text should be black because of !important on the parent, even though the .override class tries to change it to red</p>
    </div>
    <div class="tip-box">
      <h4>When Is !important Acceptable?</h4>
      <ul>
        <li>Overriding styles from third-party frameworks (e.g., Bootstrap)</li>
        <li>Utility classes for print styles</li>
        <li>When no other feasible way exists</li>
      </ul>
    </div>
  </div>

  <div class="demo-box">
    <h3>Best Practice: Overriding a Framework</h3>
    <div class="card">
      Normal card (default)
    </div>
    <div class="card important-card">
      Card with a red border (extra class)
    </div>
    <div class="app-container" style="margin-top: 10px;">
      <div class="card" style="background: #f8f9fa;">
        Card with an override via a parent selector (.app-container .card) — better than !important
      </div>
    </div>
  </div>
</body>
</html>

Code Explanation

Specificity calculation: Every selector has three numbers (a-b-c): a = ID selectors, b = class/attribute/pseudo-class, c = element/pseudo-element. The higher numbers win. Example: #header .nav a = specificity 1-1-1.

Inline styles: Highest specificity (1-0-0-0) after !important. Often used by JavaScript frameworks but should be avoided in production CSS.

Cascade: When specificity is equal, the rule defined last wins (source order). This is the "C" in CSS (Cascading).

Multiple classes: .a.b has specificity 0-2-0, higher than .a or .b which are each 0-1-0.

Descendant vs Direct Child: > targets only direct children, while a space targets all descendants. Both have the same specificity.

Attribute selector: [type="text"] has the same specificity as a class (0-1-0). [type="text"][required] = 0-2-0.

!important: Overrides all other specificity. It must be used very rarely because it makes CSS hard to maintain.

Everyday Use Cases

Framework overrides: When using Bootstrap or Tailwind and you need to change a particular style, use a more specific selector instead of !important.

State management: An .active class must override the default styles. Make sure its specificity is high enough.

Debugging: When a style is not being applied, check the specificity with the browser dev tools. The "Computed" panel shows which rules are winning.

Component library: Make sure component styles have the right specificity so they can be customized without overriding the entire library.

Conclusion

Specificity is a priority order: inline > ID > class > element. The cascade adds source order and !important to the calculation. Avoid !important and use the right selectors. Use a naming convention such as BEM to keep specificity low and consistent. The browser dev tools are your best friend for debugging specificity issues.