CSS selectors and specificity calculation rules

Alex Chang Feb 2026
1 tab
/* Specificity: 0,0,0,1 - Element selector */
p {
  color: black;
}

/* Specificity: 0,0,1,0 - Class selector */
.text {
  color: blue;
}

/* Specificity: 0,1,0,0 - ID selector */
#main-text {
  color: green;
}

/* Specificity: 0,0,1,1 - Class + Element */
p.highlight {
  color: yellow;
}

/* Specificity: 0,1,1,1 - ID + Class + Element */
#main p.highlight {
  color: orange;
}

/* Specificity: 1,0,0,0 - Inline style (written in HTML) */
/* <p style="color: red;">Highest specificity</p> */

/* !important overrides everything (avoid when possible) */
.important-text {
  color: purple !important; /* Wins over inline styles */
}

/* Attribute selectors - Specificity: 0,0,1,0 */
[data-theme="dark"] {
  background: #222;
}

[type="submit"] {
  background: blue;
}

[class*="btn"] { /* Contains "btn" */
  padding: 0.5rem 1rem;
}

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

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

[lang|="en"] { /* Exact or with hyphen */
  quotes: '"' '"';
}

/* Pseudo-class selectors - Specificity: 0,0,1,0 */
a:hover {
  text-decoration: underline;
}

input:focus {
  outline: 2px solid blue;
}

li:first-child {
  font-weight: bold;
}

li:last-child {
  border-bottom: none;
}

li:nth-child(odd) {
  background: #f5f5f5;
}

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

p:not(.exclude) {
  margin-bottom: 1rem;
}

/* Pseudo-element selectors - Specificity: 0,0,0,1 */
p::before {
  content: "→ ";
}

p::after {
  content: " ←";
}

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

input::placeholder {
  color: #999;
  opacity: 1;
}

/* Modern pseudo-classes */
:is(h1, h2, h3) {
  /* Specificity = highest selector (h1) = 0,0,0,1 */
  font-family: 'Heading Font', sans-serif;
}

:is(.btn, #submit-btn) {
  /* Specificity = highest (#submit-btn) = 0,1,0,0 */
  padding: 0.5rem 1rem;
}

:where(h1, h2, h3) {
  /* Specificity = 0,0,0,0 - Zero specificity! */
  margin-top: 0;
}

/* :has() - parent selector */
.card:has(img) {
  display: grid;
  grid-template-columns: 200px 1fr;
}

form:has(input:invalid) {
  border-color: red;
}

/* Combinators */
/* Descendant - Specificity: 0,0,0,2 */
div p {
  color: blue;
}

/* Child - Specificity: 0,0,0,2 */
ul > li {
  list-style: none;
}

/* Adjacent sibling - Specificity: 0,0,0,2 */
h2 + p {
  font-size: 1.2rem;
}

/* General sibling - Specificity: 0,0,0,2 */
h2 ~ p {
  color: gray;
}

/* State pseudo-classes */
input:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

input:checked + label {
  font-weight: bold;
}

option:checked {
  background: lightblue;
}

:target {
  background: yellow; /* Element targeted by URL fragment */
}

/* Form validation pseudo-classes */
input:valid {
  border-color: green;
}

input:invalid {
  border-color: red;
}

input:required {
  border-left: 3px solid blue;
}

input:optional {
  border-left: 3px solid gray;
}

input:in-range {
  border-color: green;
}

input:out-of-range {
  border-color: red;
}

/* Structural pseudo-classes */
:root {
  --main-color: blue; /* Root element */
}

:empty {
  display: none; /* Elements with no children */
}

tr:nth-child(even) {
  background: #f9f9f9;
}

li:only-child {
  list-style: none;
}

/* Specificity comparison examples */
/* Specificity: 0,0,1,0 - Wins */
.btn { color: blue; }

/* Specificity: 0,0,0,1 - Loses */
button { color: red; }

/* Specificity: 0,1,0,0 - Wins */
#submit { color: green; }

/* Specificity: 0,0,2,1 - Loses */
button.btn.primary { color: yellow; }

/* Best practice: avoid overly specific selectors */
/* Bad - too specific */
body div.container ul.nav li.nav-item a.nav-link {
  color: blue;
}

/* Good - just enough specificity */
.nav-link {
  color: blue;
}

/* Using :where() for zero-specificity base styles */
:where(h1, h2, h3, h4, h5, h6) {
  margin: 0;
  font-weight: normal;
}

/* Easy to override later */
h1 {
  font-size: 2rem; /* 0,0,0,1 beats :where's 0,0,0,0 */
}

/* Custom data attribute selectors */
[data-size="small"] { font-size: 0.875rem; }
[data-size="medium"] { font-size: 1rem; }
[data-size="large"] { font-size: 1.25rem; }

[data-state~="active"] { /* Word in space-separated list */
  background: blue;
}
1 file · css Explain with highlit

CSS specificity determines which styles apply when multiple rules target the same element. I calculate specificity as (inline, IDs, classes/attributes/pseudo-classes, elements/pseudo-elements). Inline styles have highest specificity at 1000. ID selectors score 100, class selectors 10, and element selectors 1. The !important flag overrides specificity but creates maintenance issues. Combining selectors increases specificity strategically. The :where() pseudo-class has zero specificity for flexible base styles. Using :is() takes the highest specificity from its argument list. Attribute selectors like [data-state="active"] have same specificity as classes. Understanding specificity prevents override wars and reduces !important usage.