Cascade Layers & Specificity
Master the CSS cascade, specificity calculation, and @layer for predictable styles
What you'll learn
Understand the CSS cascade algorithm and how styles win
Calculate specificity: inline, ID, class, element selectors
Use @layer to control cascade order explicitly
Know when to use (and avoid) !important
Understand CSS reset vs normalize approaches
Apply @layer for framework override patterns
The Cascade Algorithm
CSS resolves conflicting declarations in this order:
- Origin & Importance — user agent < user < author, !important flips priorities
- Context — @layer order determines layer priority
- Specificity — More specific selector wins
- Order — Later declaration wins (same specificity)
/* Both apply to same element — which wins? */
p {
color: red;
} /* specificity: 0-0-1 */
.text {
color: blue;
} /* specificity: 0-1-0 — WINS (higher specificity) */
Specificity Calculation
Calculate as (inline, ID, class/pseudo-class, element/pseudo-element):
| Selector | Specificity |
|---|---|
* | 0-0-0-0 |
p | 0-0-0-1 |
.card | 0-0-1-0 |
#header | 0-1-0-0 |
style="" | 1-0-0-0 |
div.card.active | 0-0-2-1 |
#nav .item a | 0-1-1-1 |
/* Avoid this: overly specific selectors create maintenance problems */
header#main-header nav ul li a.nav-link {
color: blue;
}
/* Prefer: */
.nav-link {
color: blue;
}
Don't Nest Too Deep
Deep nesting in preprocessors (SCSS) or styled-components produces high-specificity selectors. Flat, class-based selectors are easier to override.
@layer for Cascade Control
@layer lets you define explicit cascade layers. Styles in later layers override earlier ones, regardless of specificity.
@layer reset, base, components, utilities;
@layer reset {
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
a {
color: inherit;
}
}
@layer base {
body {
font-family: system-ui;
line-height: 1.6;
}
h1 {
font-size: 2rem;
margin-block: 1rem;
}
}
@layer components {
.card {
padding: 1.5rem;
border-radius: 8px;
}
.btn {
display: inline-flex;
padding: 0.5rem 1rem;
}
}
@layer utilities {
.mt-4 {
margin-top: 1rem;
}
.text-center {
text-align: center;
}
}
!important — When to Avoid
!important breaks the cascade. Use only for:
- Utility overrides (e.g.,
.hidden { display: none !important; }) - Third-party widget isolation
- Overriding inline styles you can't control
/* Avoid this pattern */
.card {
color: red !important;
}
.special {
color: blue !important;
} /* Now you need another !important */
/* Instead — use @layer or specificity */
CSS Reset vs Normalize
| Approach | Philosophy |
|---|---|
| Reset (e.g., Andy Bell) | Remove all default browser styles. Start from zero. |
| Normalize (e.g., Nicolas Gallagher) | Preserve useful defaults, normalize across browsers. |
/* Modern minimal reset */
@layer reset {
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
img,
picture,
video,
canvas,
svg {
display: block;
max-width: 100%;
}
input,
button,
textarea,
select {
font: inherit;
}
h1,
h2,
h3,
h4,
h5,
h6 {
text-wrap: balance;
}
}
@layer for Framework Overrides
Wrap third-party CSS in an @layer to keep your styles on top.
<!-- Framework styles get lower priority -->
<style>
@layer framework {
@import url("framework.css");
}
</style>
<!-- Your styles automatically win -->
<style>
.card {
border: 2px solid #6366f1;
}
</style>
Fix Specificity Issues
Code
html
1<style>2.wrapper .card { border: 2px solid #ccc; }3.special { border-color: #ef4444; }4 5.card-wrapper #my-card { border-color: #6366f1; }6 7</style>8<div class="wrapper card-wrapper">9<div class="card" id="my-card">Normal Card</div>10<div class="card special" id="my-card2">Special Card</div>11</div>The example demonstrates CSS specificity battles: an ID selector beats many class selectors. Preview shows which border color wins on each card.
Key Takeaways
Cascade combines origin, @layer, specificity, and source order
Specificity is (inline, ID, class, element) — keep selectors flat
@layer gives explicit control over cascade priority
!important breaks cascade — reserve for utilities and third-party overrides
Modern CSS reset removes browser defaults with box-sizing and font inheritance