📌 Why Specificity Matters
CSS Specificity is a set of rules the browser uses to decide which style rule applies to an element when multiple rules could match.
When two or more CSS rules target the same element, the one with higher specificity wins (i.e., is applied).
🔢 Specificity Values (Weights)
CSS assigns points to selectors based on how specific they are. The specificity is calculated as a four-part number like a,b,c,d where:
Selector Type | Value (a,b,c,d) |
|---|---|
Inline styles | (1,0,0,0) |
ID selectors ( | (0,1,0,0) |
Class / attribute / pseudo-class | (0,0,1,0) |
Element / pseudo-element | (0,0,0,1) |
Universal selector | (0,0,0,0) |
📝 The higher value from left to right wins.
📚 Examples of Specificity
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>CSS Specificity Example</title>
<link rel="stylesheet" href="styles.css" />
</head>
<body>
<!-- Inline style has the highest specificity -->
<h1 id="title" class="title" style="color: red">Hello, Specificity!</h1>
</body>
</html>/* Inline style - specificity (1,0,0,0) */
<h1 style="color: red">Hello</h1># ID selector - (0,1,0,0)
#title {
color: green;
}# Class selector - (0,0,1,0)
.title {
color: blue;
}# Element selector - (0,0,0,1)
h1 {
color: purple;
}<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>CSS Specificity Anatomy</title>
<style>
.infographic-specificity {
--ink: #1a1a1a;
--ink-soft: #5c5c5c;
--paper: #ffffff;
--paper-alt: #f0ece4;
--accent: #2563eb;
--accent-soft:#e3ebfd;
--line: #ddd6c9;
box-sizing: border-box;
margin: 0 auto;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
background: var(--paper);
color: var(--ink);
border-radius: 14px;
overflow: hidden;
}
.infographic-specificity[data-theme="dark"] {
--ink: #e8e6e1;
--ink-soft: #9a968c;
--paper: #121212;
--paper-alt: #1e1e26;
--accent: #5b8def;
--accent-soft:#202942;
--line: #2c313d;
}
.ha-inner { padding: 0.5rem; }
.ha-layout { display: flex; flex-direction: column; gap: 1.25rem; }
/* Output at the top */
.ha-browser {
border: 1px solid var(--line);
border-radius: 10px;
overflow: hidden;
background: var(--paper);
}
.ha-browser-bar {
background: var(--paper-alt);
padding: 0.5rem 0.7rem;
display: flex;
align-items: center;
gap: 0.5rem;
border-bottom: 1px solid var(--line);
}
.ha-dot { width: 9px; height: 9px; border-radius: 50%; background: var(--line); }
.ha-tab {
background: var(--paper);
border: 1px solid var(--line);
border-bottom: none;
border-radius: 6px 6px 0 0;
font-size: 0.72rem;
color: var(--ink-soft);
padding: 0.25rem 0.6rem;
}
.ha-urlrow {
background: var(--paper-alt);
padding: 0.35rem 0.7rem 0.6rem;
font-size: 0.72rem;
color: var(--ink-soft);
font-family: "SFMono-Regular", Consolas, monospace;
}
.ha-screen {
background: #ffffff;
color: #111;
padding: 1.4rem 1.4rem 1.2rem;
text-align: center;
}
.ha-out-h1 {
font-family: Georgia, "Times New Roman", serif;
font-weight: 700;
font-size: 1.6rem;
margin: 0 0 0.5rem;
transition: color 0.25s ease;
}
.ha-winner-note {
font-size: 0.78rem;
color: var(--ink-soft);
font-weight: 700;
}
.ha-winner-note b { transition: color 0.25s ease; }
/* Rule stack */
.ha-rules {
display: flex;
flex-direction: column;
gap: 0.6rem;
}
.ha-rule {
display: flex;
align-items: center;
gap: 0.8rem;
background: var(--paper-alt);
border: 1px solid var(--line);
border-radius: 10px;
padding: 0.7rem 0.9rem;
opacity: 1;
transition: opacity 0.2s ease, box-shadow 0.2s ease;
}
.ha-rule.ha-off { opacity: 0.35; }
.ha-rule.ha-winning { box-shadow: 0 0 0 2px var(--rule-color); }
.ha-rule-toggle {
width: 38px;
height: 22px;
border-radius: 999px;
border: none;
background: var(--line);
position: relative;
cursor: pointer;
flex-shrink: 0;
transition: background 0.2s ease;
}
.ha-rule-toggle::after {
content: "";
position: absolute;
top: 2px;
left: 2px;
width: 18px;
height: 18px;
border-radius: 50%;
background: #fff;
transition: transform 0.2s ease;
}
.ha-rule.ha-off .ha-rule-toggle { background: #b7b0a1; }
.ha-rule:not(.ha-off) .ha-rule-toggle { background: var(--rule-color); }
.ha-rule:not(.ha-off) .ha-rule-toggle::after { transform: translateX(16px); }
.ha-rule-main { flex: 1; min-width: 0; }
.ha-rule-sel {
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace;
font-size: 0.82rem;
font-weight: 700;
color: var(--rule-color);
}
.ha-rule-label {
font-size: 0.72rem;
color: var(--ink-soft);
margin-top: 0.1rem;
}
.ha-weight {
display: flex;
gap: 3px;
flex-shrink: 0;
}
.ha-weight span {
width: 20px;
height: 20px;
border-radius: 4px;
background: var(--line);
color: var(--ink-soft);
font-size: 0.68rem;
font-weight: 800;
display: flex;
align-items: center;
justify-content: center;
}
.ha-weight span.ha-fill {
background: var(--rule-color);
color: #fff;
}
.ha-rule[data-rule="inline"] { --rule-color: #dc2626; }
.ha-rule[data-rule="id"] { --rule-color: #16a34a; }
.ha-rule[data-rule="class"] { --rule-color: #2563eb; }
.ha-rule[data-rule="element"] { --rule-color: #7c3aed; }
</style>
</head>
<body>
<div class="infographic-specificity" id="ha-root">
<div class="ha-inner">
<div class="ha-layout">
<div class="ha-browser">
<div class="ha-browser-bar">
<span class="ha-dot"></span><span class="ha-dot"></span><span class="ha-dot"></span>
<div class="ha-tab">🌐 127.0.0.1:5500/index.html</div>
</div>
<div class="ha-urlrow">127.0.0.1:5500/index.html</div>
<div class="ha-screen">
<h1 class="ha-out-h1" id="out-h1">Hello, Specificity!</h1>
<div class="ha-winner-note">Winning rule: <b id="winner-label">inline style</b></div>
</div>
</div>
<div class="ha-rules" id="ha-rules">
<div class="ha-rule" data-rule="inline" data-score="1000">
<button class="ha-rule-toggle" aria-label="toggle inline style"></button>
<div class="ha-rule-main">
<div class="ha-rule-sel">style="color: red"</div>
<div class="ha-rule-label">Inline style</div>
</div>
<div class="ha-weight">
<span class="ha-fill">1</span><span>0</span><span>0</span><span>0</span>
</div>
</div>
<div class="ha-rule" data-rule="id" data-score="100">
<button class="ha-rule-toggle" aria-label="toggle id selector"></button>
<div class="ha-rule-main">
<div class="ha-rule-sel">#title { color: green; }</div>
<div class="ha-rule-label">ID selector</div>
</div>
<div class="ha-weight">
<span>0</span><span class="ha-fill">1</span><span>0</span><span>0</span>
</div>
</div>
<div class="ha-rule" data-rule="class" data-score="10">
<button class="ha-rule-toggle" aria-label="toggle class selector"></button>
<div class="ha-rule-main">
<div class="ha-rule-sel">.title { color: blue; }</div>
<div class="ha-rule-label">Class selector</div>
</div>
<div class="ha-weight">
<span>0</span><span>0</span><span class="ha-fill">1</span><span>0</span>
</div>
</div>
<div class="ha-rule" data-rule="element" data-score="1">
<button class="ha-rule-toggle" aria-label="toggle element selector"></button>
<div class="ha-rule-main">
<div class="ha-rule-sel">h1 { color: purple; }</div>
<div class="ha-rule-label">Element selector</div>
</div>
<div class="ha-weight">
<span>0</span><span>0</span><span>0</span><span class="ha-fill">1</span>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
(function () {
const wrapper = document.getElementById('ha-root');
const bodyAttr = document.body.getAttribute('data-darkmode');
let dark;
if (bodyAttr === 'true') {
dark = true;
} else if (bodyAttr === 'false') {
dark = false;
} else {
const match = document.cookie.match(/(?:^|;\s*)darkMode=([^;]+)/);
dark = match ? match[1] === 'true' : false;
}
if (dark) wrapper.setAttribute('data-theme', 'dark');
})();
(function () {
const rules = Array.from(document.querySelectorAll('.ha-rule'));
const outH1 = document.getElementById('out-h1');
const winnerLabel = document.getElementById('winner-label');
const colors = { inline: '#dc2626', id: '#16a34a', class: '#2563eb', element: '#7c3aed' };
const names = { inline: 'inline style', id: 'ID selector', class: 'class selector', element: 'element selector' };
const state = { inline: true, id: true, class: true, element: true };
function recompute() {
let winner = null;
let bestScore = -1;
rules.forEach(r => {
const rule = r.getAttribute('data-rule');
const score = parseInt(r.getAttribute('data-score'), 10);
r.classList.toggle('ha-off', !state[rule]);
r.classList.remove('ha-winning');
if (state[rule] && score > bestScore) {
bestScore = score;
winner = rule;
}
});
if (winner) {
document.querySelector('.ha-rule[data-rule="' + winner + '"]').classList.add('ha-winning');
outH1.style.color = colors[winner];
winnerLabel.textContent = names[winner];
winnerLabel.style.color = colors[winner];
} else {
outH1.style.color = '#111';
winnerLabel.textContent = 'none — default text color';
winnerLabel.style.color = '#111';
}
}
rules.forEach(r => {
const toggle = r.querySelector('.ha-rule-toggle');
const rule = r.getAttribute('data-rule');
toggle.addEventListener('click', () => {
state[rule] = !state[rule];
recompute();
});
});
recompute();
})();
</script>
</body>
</html>.demo {
font-family: system-ui, sans-serif;
padding: 16px;
}
.demo h1 {
color: #2563eb;
margin: 0 0 8px;
}
.demo p {
color: #4b5563;
margin: 0;
}document.querySelector('.demo h1')?.addEventListener('click', () => {
alert('Hello from live preview!');
});🎯 If all four of these applied to <h1 id="title" class="title">, the final color would be:
Inline wins > ID > Class > Element
✅ So it would be red (from inline style)
⚖️ Comparing Specificities
<div class="infographic-specstack">
<style>
.infographic-specstack {
--ink: #1a1a1a;
--ink-soft: #5c5c5c;
--paper: #faf8f4;
--paper-alt: #f0ece4;
--accent: #2563eb;
--accent-soft:#e3ebfd;
--win: #166534;
--win-soft: #dcf2e3;
--line: #ddd6c9;
--code-bg: #1a1a1a;
--code-text: #f0ece4;
--display-font: 'Newsreader', serif;
--body-font: 'Inter', sans-serif;
--mono-font: 'IBM Plex Mono', monospace;
color: var(--ink);
font-family: var(--body-font);
max-width: 760px;
margin: 0 auto;
line-height: 1.5;
box-sizing: border-box;
background: transparent;
}
.infographic-specstack[data-theme="dark"] {
--ink: #e8e6e1;
--ink-soft: #9a968c;
--paper: #121212;
--paper-alt: #1e1e26;
--accent: #5b8def;
--accent-soft:#202942;
--win: #4ade80;
--win-soft: #1a3326;
--line: #2c313d;
--code-bg: #1a1a22;
--code-text: #e8e3d8;
}
.infographic-specstack *, .infographic-specstack *::before, .infographic-specstack *::after {
box-sizing: border-box;
}
.infographic-specstack h1 {
font-family: var(--display-font);
font-size: clamp(1.5rem, 5vw, 2rem);
margin: 0 0 0.3rem;
font-weight: 600;
color: var(--ink);
}
.infographic-specstack .eyebrow {
font-size: 0.78rem;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--accent);
font-weight: 600;
margin-bottom: 0.4rem;
}
.infographic-specstack .subhead {
color: var(--ink-soft);
font-size: 0.93rem;
margin: 0 0 1.75rem;
text-align: center;
}
.infographic-specstack code { font-family: var(--mono-font); font-size: 0.85em; }
/* ---- Target element preview ---- */
.ss-preview-wrap {
border: 1px solid var(--line);
background: var(--paper-alt);
padding: 2rem 1.5rem;
text-align: center;
margin-bottom: 1.75rem;
transition: background 0.2s ease, border-color 0.2s ease;
}
.ss-preview-wrap .ss-target-tag {
font-size: 0.75rem;
color: var(--ink-soft);
text-transform: uppercase;
letter-spacing: 0.05em;
margin-bottom: 0.6rem;
}
#ss-target {
display: inline-block;
padding: 0.6rem 1.5rem;
font-size: 1.05rem;
border: 2px solid #888;
transition: color 0.2s, background 0.2s, border-color 0.2s;
}
#ss-target-html {
font-family: var(--mono-font);
font-size: 0.78rem;
color: var(--ink-soft);
margin-top: 0.75rem;
}
/* ---- Stack of competing rules, each a clickable card ---- */
.ss-stack { display: flex; flex-direction: column; gap: 0.5rem; margin-bottom: 1.5rem; }
.ss-rule {
display: grid;
grid-template-columns: auto 1fr auto auto;
align-items: center;
gap: 0.75rem;
border: 1px solid var(--line);
background: var(--paper);
padding: 0.7rem 0.9rem;
cursor: pointer;
opacity: 0.45;
transition: opacity 0.2s, border-color 0.2s, background 0.2s;
}
@media (max-width: 560px) {
.ss-rule { grid-template-columns: auto 1fr; grid-template-areas: "check sel" "blank score" "blank declared"; row-gap: 0.3rem; }
.ss-rule .ss-score { grid-area: score; justify-self: start; }
.ss-rule .ss-declared { grid-area: declared; justify-self: start; }
}
.ss-rule.is-on { opacity: 1; }
.ss-rule.is-winner { border-color: var(--win); background: var(--win-soft); }
.ss-rule input { accent-color: var(--accent); margin: 0; }
.ss-rule .ss-sel { font-family: var(--mono-font); font-size: 0.88rem; }
.ss-rule .ss-score {
font-family: var(--mono-font);
font-size: 0.78rem;
color: var(--ink-soft);
white-space: nowrap;
}
.ss-rule.is-winner .ss-score { color: var(--win); font-weight: 700; }
.ss-rule .ss-declared {
font-size: 0.7rem;
color: var(--ink-soft);
white-space: nowrap;
}
.ss-rule .ss-winner-pill {
display: none;
font-size: 0.68rem;
font-weight: 700;
color: var(--win);
text-transform: uppercase;
letter-spacing: 0.04em;
}
.ss-rule.is-winner .ss-winner-pill { display: inline; }
.ss-explain {
border-left: 3px solid var(--accent);
background: var(--accent-soft);
padding: 0.9rem 1.1rem;
font-size: 0.88rem;
min-height: 3.2em;
color: var(--ink);
transition: background 0.2s ease;
}
.ss-explain strong { color: var(--accent); }
/* ---- Code panel — intentionally a fixed dark editor palette in both
themes, the way a code editor pane stays dark regardless of the page
around it. Only the bg/text base swaps via tokens for a slightly
deeper black in dark mode; the muted/empty grays stay fixed since
they're calibrated against that fixed dark backdrop. ---- */
.ss-code {
font-family: var(--mono-font);
font-size: 0.85rem;
background: var(--code-bg);
color: var(--code-text);
padding: 0.9rem 1.1rem;
margin-bottom: 1.75rem;
white-space: pre;
overflow-x: auto;
line-height: 1.65;
min-height: 2.4em;
transition: background 0.2s ease, color 0.2s ease;
}
.ss-code .ss-line { display: block; color: #8a8a8a; }
.ss-code .ss-line.is-winning { color: var(--code-text); font-weight: 600; }
.ss-code .ss-line.is-winning::before { content: "▸ "; color: var(--win); }
.ss-code .ss-line:not(.is-winning)::before { content: " "; }
.ss-code .ss-empty { color: #6b6b6b; font-style: italic; }
@media (prefers-reduced-motion: reduce) {
.infographic-specstack *, .infographic-specstack *::before, .infographic-specstack *::after {
animation-duration: 0.001ms !important;
transition-duration: 0.001ms !important;
}
}
</style>
<p class="subhead">Every rule below targets the same button. Toggle rules on/off and watch which one actually wins — and why.</p>
<div class="ss-code" id="ss-code"></div>
<div class="ss-preview-wrap">
<div class="ss-target-tag">Rendered result</div>
<button id="ss-target">Submit</button>
<div id="ss-target-html"><button id="submit" class="btn primary" style="">Submit</button></div>
</div>
<div class="ss-stack" id="ss-stack">
<label class="ss-rule" data-rule="el" data-inline="0" data-id="0" data-cls="0" data-el="1" data-color="#ffffff" data-bg="#1a1a1a" data-selector="button" data-decl="color: white; background: black;">
<input type="checkbox" checked>
<span class="ss-sel">button { color: white; background: black; }</span>
<span class="ss-score">(0,0,0,1)</span>
<span class="ss-declared">declared 1st</span>
</label>
<label class="ss-rule" data-rule="class" data-inline="0" data-id="0" data-cls="1" data-el="0" data-color="#1f8a4c" data-bg="#ffffff" data-selector=".btn" data-decl="color: green;">
<input type="checkbox">
<span class="ss-sel">.btn { color: green; }</span>
<span class="ss-score">(0,0,1,0)</span>
<span class="ss-declared">declared 2nd</span>
</label>
<label class="ss-rule" data-rule="class2" data-inline="0" data-id="0" data-cls="2" data-el="0" data-color="#ffffff" data-bg="#2563eb" data-selector=".btn.primary" data-decl="color: white; background: blue;">
<input type="checkbox">
<span class="ss-sel">.btn.primary { color: white; background: blue; }</span>
<span class="ss-score">(0,0,2,0)</span>
<span class="ss-declared">declared 3rd</span>
</label>
<label class="ss-rule" data-rule="id" data-inline="0" data-id="1" data-cls="0" data-el="0" data-color="#ffffff" data-bg="#b91c1c" data-selector="#submit" data-decl="color: white; background: crimson;">
<input type="checkbox">
<span class="ss-sel">#submit { color: white; background: crimson; }</span>
<span class="ss-score">(0,1,0,0)</span>
<span class="ss-declared">declared 4th</span>
</label>
<label class="ss-rule" data-rule="inline" data-inline="1" data-id="0" data-cls="0" data-el="0" data-color="#1a1a1a" data-bg="#fbbf24" data-selector="" data-decl="color: black; background: gold;">
<input type="checkbox">
<span class="ss-sel">style="color: black; background: gold;"</span>
<span class="ss-score">(1,0,0,0)</span>
<span class="ss-declared">written on the tag</span>
</label>
</div>
<div class="ss-explain" id="ss-explain">
The <code>element</code> rule scores (0,0,0,1) — it's the only active rule, so it wins by default.
</div>
</div>
<script>
(function () {
var wrapper = document.currentScript.closest('.infographic-specstack')
|| document.querySelector('.infographic-specstack');
var bodyAttr = document.body.getAttribute('data-darkmode');
var dark;
if (bodyAttr === 'true') {
dark = true;
} else if (bodyAttr === 'false') {
dark = false;
} else {
var match = document.cookie.match(/(?:^|;\s*)darkMode=([^;]+)/);
dark = match ? match[1] === 'true' : false;
}
if (dark) wrapper.setAttribute('data-theme', 'dark');
})();
(function() {
var scripts = document.getElementsByTagName('script');
var thisScript = scripts[scripts.length - 1];
var root = thisScript.closest ? thisScript.closest('.infographic-specstack')
: document.querySelector('.infographic-specstack');
if (!root) root = document.querySelector('.infographic-specstack');
var target = root.querySelector('#ss-target');
var explain = root.querySelector('#ss-explain');
var codeEl = root.querySelector('#ss-code');
var ruleEls = root.querySelectorAll('.ss-rule');
var labels = {
el: 'element', class: 'class', class2: 'second class', id: 'ID', inline: 'inline style'
};
function escapeHtml(str) {
return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
}
function render() {
var active = [];
ruleEls.forEach(function(rule) {
var checked = rule.querySelector('input').checked;
rule.classList.toggle('is-on', checked);
rule.classList.remove('is-winner');
if (checked) {
active.push({
el: rule,
inline: Number(rule.dataset.inline),
id: Number(rule.dataset.id),
cls: Number(rule.dataset.cls),
tag: Number(rule.dataset.el),
color: rule.dataset.color,
bg: rule.dataset.bg,
rule: rule.dataset.rule,
selector: rule.dataset.selector,
decl: rule.dataset.decl
});
}
});
if (active.length === 0) {
target.style.color = '';
target.style.background = '';
target.style.borderColor = '#888';
explain.innerHTML = 'No rules active — the button falls back to the browser default.';
codeEl.innerHTML = '<span class="ss-empty">/* no rules applied */</span>';
return;
}
var winnerInfo = active.slice().sort(function(a, b) {
if (a.inline !== b.inline) return b.inline - a.inline;
if (a.id !== b.id) return b.id - a.id;
if (a.cls !== b.cls) return b.cls - a.cls;
if (a.tag !== b.tag) return b.tag - a.tag;
return 0;
})[0];
winnerInfo.el.classList.add('is-winner');
target.style.color = winnerInfo.color;
target.style.background = winnerInfo.bg;
target.style.borderColor = winnerInfo.bg === '#ffffff' ? '#888' : winnerInfo.bg;
var scoreStr = '(' + winnerInfo.inline + ',' + winnerInfo.id + ',' + winnerInfo.cls + ',' + winnerInfo.tag + ')';
explain.innerHTML = 'The <strong>' + labels[winnerInfo.rule] + '</strong> rule scores ' + scoreStr + ' — higher than every other active rule — so it wins.';
// build one combined code block, lines in declared (markup) order
var html = active.map(function(item) {
var isWinner = item.rule === winnerInfo.rule;
var lineText;
if (item.rule === 'inline') {
lineText = '<button style="' + item.decl + '">';
} else {
lineText = item.selector + ' { ' + item.decl + ' }';
}
var cls = 'ss-line' + (isWinner ? ' is-winning' : '');
return '<span class="' + cls + '">' + escapeHtml(lineText) + '</span>';
}).join('\n');
codeEl.innerHTML = html;
}
ruleEls.forEach(function(rule) {
rule.querySelector('input').addEventListener('change', render);
});
render();
})();
</script>
</div>.demo {
font-family: system-ui, sans-serif;
padding: 16px;
}
.demo h1 {
color: #2563eb;
margin: 0 0 8px;
}
.demo p {
color: #4b5563;
margin: 0;
}document.querySelector('.demo h1')?.addEventListener('click', () => {
alert('Hello from live preview!');
});💡 Tips to Handle Specificity
Avoid overusing
!important; use it only when absolutely necessary.Prefer lower specificity for reusability.
When two selectors have equal specificity, the later one wins (source order).
Use developer tools to debug specificity issues.