~/hackweb.dev
CSS Pseudo-classes & Pseudo-elements
Quiz
...

CSS Pseudo-classes & Pseudo-elements

beginner · updated Tue Sep 08 2026Contribute

Master states, structural selectors, and content generation with pseudo-classes.

CSS Pseudo-classes & Pseudo-elements

Pseudo-classes target elements in specific states. Pseudo-elements target specific parts of elements.

Pseudo-classes

/* Unvisited link */
a:link {
  color: blue;
}

/* Visited link */
a:visited {
  color: purple;
}

/* Hover state */
a:hover {
  color: red;
}

/* Active (being clicked) */
a:active {
  color: orange;
}

/* Focus (keyboard navigation) */
a:focus {
  outline: 2px solid blue;
}

Order: L-V-H-A (Link, Visited, Hover, Active)

Input States

/* Focus state */
input:focus {
  border-color: #3498db;
  outline: none;
}

/* Disabled */
input:disabled {
  background-color: #eee;
  cursor: not-allowed;
}

/* Enabled */
input:enabled {
  background-color: white;
}

/* Required */
input:required {
  border-left: 3px solid red;
}

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

/* Valid */
input:valid {
  border-color: green;
}

/* Invalid */
input:invalid {
  border-color: red;
}

/* Checked */
input:checked {
  accent-color: blue;
}

/* Placeholder shown */
input:placeholder-shown {
  color: gray;
}

Structural Pseudo-classes

/* First child */
li:first-child {
  font-weight: bold;
}

/* Last child */
li:last-child {
  border-bottom: none;
}

/* nth-child (position) */
li:nth-child(2) {
  color: red;
}

/* nth-child (formula) */
li:nth-child(odd) {
  background-color: #f5f5f5;
}

li:nth-child(3n) {
  font-size: 1.2em;
}

li:nth-child(3n+1) {
  color: blue;
}

/* First of type */
p:first-of-type {
  font-size: 1.5em;
}

/* Last of type */
p:last-of-type {
  margin-bottom: 0;
}

/* Only child */
.item:only-child {
  margin: 0 auto;
}

/* Only of type */
p:only-of-type {
  font-style: italic;
}

/* Empty element */
div:empty {
  display: none;
}

/* Root element */
:root {
  font-size: 16px;
}

/* Not selector */
p:not(.special) {
  color: gray;
}

/* Is selector (matches any) */
:is(h1, h2, h3) {
  line-height: 1.2;
}

/* Where selector (same as is, lower specificity) */
:where(h1, h2, h3) {
  margin-bottom: 0.5rem;
}

/* Has selector (contains) */
div:has(p) {
  padding: 1rem;
}

Child Pseudo-classes

/* Any child */
li:nth-child(n) {
  /* All items */
}

/* First 3 items */
li:nth-child(-n+3) {
  font-weight: bold;
}

/* After first 3 items */
li:nth-child(n+4) {
  color: gray;
}

/* Last 3 items */
li:nth-last-child(-n+3) {
  border-bottom: none;
}

/* Every other item */
li:nth-child(even) {
  background-color: #f5f5f5;
}

/* Every third item */
li:nth-child(3n) {
  margin-left: 20px;
}

Pseudo-elements

Content Generation

/* Before content */
h1::before {
  content: "★ ";
  color: gold;
}

/* After content */
h1::after {
  content: " ★";
  color: gold;
}

/* With quotes */
blockquote::before {
  content: open-quote;
}

blockquote::after {
  content: close-quote;
}

/* Empty content (for styling) */
.icon::before {
  content: "";
  display: inline-block;
  width: 20px;
  height: 20px;
  background-image: url('icon.png');
}

Text Pseudo-elements

/* First line */
p::first-line {
  font-weight: bold;
  color: #333;
}

/* First letter */
p::first-letter {
  font-size: 3em;
  float: left;
  line-height: 1;
  margin-right: 0.1em;
}

/* Selection */
::selection {
  background-color: yellow;
  color: black;
}

/* Placeholder */
input::placeholder {
  color: gray;
  font-style: italic;
}

Tooltip Example

.tooltip {
  position: relative;
  display: inline-block;
  cursor: help;
}

.tooltip::after {
  content: attr(data-tooltip);
  position: absolute;
  bottom: 125%;
  left: 50%;
  transform: translateX(-50%);
  background-color: #333;
  color: white;
  padding: 5px 10px;
  border-radius: 5px;
  white-space: nowrap;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.3s;
}

.tooltip:hover::after {
  opacity: 1;
  visibility: visible;
}
<span class="tooltip" data-tooltip="This is a tooltip">
  Hover me
</span>

Quote Marks

q::before {
  content: open-quote;
}

q::after {
  content: close-quote;
}

blockquote {
  font-style: italic;
  border-left: 4px solid #3498db;
  padding-left: 1rem;
  margin: 1rem 0;
}

blockquote::before {
  content: open-quote;
  font-size: 2em;
  color: #ccc;
}

blockquote::after {
  content: close-quote;
  font-size: 2em;
  color: #ccc;
}

Complete Example: List Styling

/* Custom list markers */
ul.custom-list {
  list-style: none;
  padding-left: 0;
}

ul.custom-list li {
  position: relative;
  padding-left: 1.5rem;
  margin-bottom: 0.5rem;
}

ul.custom-list li::before {
  content: "✓";
  position: absolute;
  left: 0;
  color: #27ae60;
  font-weight: bold;
}

/* Numbered list with custom format */
ol.custom-list {
  list-style: none;
  padding-left: 0;
  counter-reset: item;
}

ol.custom-list li {
  counter-increment: item;
  padding-left: 2rem;
  margin-bottom: 0.5rem;
}

ol.custom-list li::before {
  content: counter(item) ".";
  position: absolute;
  left: 0;
  font-weight: bold;
  color: #3498db;
}

Complete Example: Accordion

.accordion-item {
  border: 1px solid #ddd;
  margin-bottom: 0.5rem;
}

.accordion-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 1rem;
  cursor: pointer;
  background-color: #f5f5f5;
}

.accordion-header::after {
  content: "+";
  font-size: 1.5rem;
  font-weight: bold;
  transition: transform 0.3s;
}

.accordion-item.active .accordion-header::after {
  transform: rotate(45deg);
}

.accordion-content {
  max-height: 0;
  overflow: hidden;
  transition: max-height 0.3s ease;
}

.accordion-item.active .accordion-content {
  max-height: 200px;
}

Complete Example: Custom Checkbox

.checkbox-wrapper {
  display: flex;
  align-items: center;
  cursor: pointer;
}

.checkbox-wrapper input {
  display: none;
}

.checkbox-mark {
  width: 20px;
  height: 20px;
  border: 2px solid #ccc;
  border-radius: 4px;
  margin-right: 10px;
  position: relative;
  transition: background-color 0.3s, border-color 0.3s;
}

.checkbox-wrapper input:checked + .checkbox-mark {
  background-color: #3498db;
  border-color: #3498db;
}

.checkbox-wrapper input:checked + .checkbox-mark::after {
  content: "✓";
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  color: white;
  font-size: 14px;
}
<label class="checkbox-wrapper">
  <input type="checkbox" />
  <span class="checkbox-mark"></span>
  <span>Accept terms</span>
</label>

Best Practices

  1. Use pseudo-classes for states — Hover, focus, active
  2. Use structural pseudo-classes — Instead of extra classes
  3. Use pseudo-elements for content — Decorative content only
  4. Test keyboard navigation — Focus states are important
  5. Don’t use for essential content — Screen readers may not see it

Common Mistakes

  1. Wrong order for link states — L-V-H-A
  2. Using for essential content — Pseudo-elements aren’t accessible
  3. Not providing focus styles — Keyboard users can’t navigate
  4. Overusing :not() — Can become complex
  5. Forgetting ::before/::after need content — Even if empty