Topic 09 / 14

Pseudo-Classes & Pseudo-Elements

~9 min read  //  CSS Series  //  Coding India

State Pseudo-Classes

.btn:hover    { background: #4F46E5; }
.btn:active   { transform: scale(0.98); }   /* while pressed */
a:visited     { color: purple; }

/* focus: never remove outlines — style them */
.btn:focus-visible {
  outline: 2px solid #6366F1;
  outline-offset: 2px;
}

:focus-visible shows the ring for keyboard users but not mouse clicks — accessibility without the “ugly outline” complaint.

Structural Pseudo-Classes

li:first-child       { }
li:last-child        { }
li:nth-child(2)      { }      /* the 2nd */
li:nth-child(odd)    { }      /* zebra striping */
li:nth-child(3n)     { }      /* every 3rd */
li:not(.active)      { }
p:empty              { }

/* classic: borders between items, not after the last */
.menu li:not(:last-child) {
  border-bottom: 1px solid #E5E7EB;
}

Watch out: :nth-child counts all siblings; :nth-of-type counts only same-tag siblings.

Form State Pseudo-Classes

input:focus          { border-color: #6366F1; }
input:disabled       { opacity: 0.5; }
input:checked + label    { font-weight: 700; }   /* style label via checkbox */
input:user-invalid   { border-color: #EF4444; }  /* invalid AFTER interaction */
input::placeholder   { color: #9CA3AF; }

The :checked + sibling pattern enables pure-CSS toggles, tabs, and accordions.

Pseudo-Elements — ::before & ::after

Two free child boxes on any element, requiring content:

.required::after {
  content: " *";
  color: #EF4444;
}

/* decorative shapes — most common use */
.section-title::before {
  content: "";              /* empty but rendered */
  display: inline-block;
  width: 2rem; height: 3px;
  background: #6366F1;
  margin-right: .75rem;
  vertical-align: middle;
}

/* expand a card's clickable area to the whole card */
.card { position: relative; }
.card a::after {
  content: "";
  position: absolute;
  inset: 0;                 /* covers the entire card */
}

Also: ::selection (highlight colour), ::marker (list bullets), ::first-letter (drop caps).

Modern Selectors: :is(), :where(), :has()

/* :is — collapse repetitive lists */
:is(h1, h2, h3) a { color: inherit; }

/* :where — same, but ZERO specificity (easy to override later) */
:where(ul, ol) { padding-left: 1.2rem; }

/* :has — the "parent selector", finally */
.card:has(img)            { padding: 0; }          /* cards that contain an image */
.field:has(input:invalid) label { color: #EF4444; } /* style label by input state */
form:has(:checked) .submit { opacity: 1; }

:has() removes a whole category of JavaScript — styling parents based on children is now pure CSS in every modern browser.