Labs ICT

CSS Pseudo Classes

CSS pseudo-classes allow you to select elements based on their state or position, not just their type or class. They enable dynamic styling based on user interactions, document structure, and element states.

In this tutorial, we'll explore all major CSS pseudo-classes including dynamic pseudo-classes, structural pseudo-classes, and their practical applications.

Dynamic Pseudo-Classes

Dynamic pseudo-classes respond to user actions or element states, such as hover, focus, active, and more.

:hover Pseudo-Class

/* Hover state */
.button:hover {
  background-color: #0056b3;
  transform: translateY(-2px);
  /* When mouse is over element
  */
}

.link:hover {
  color: #007bff;
  text-decoration: underline;
}

.card:hover {
  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
}

/* Hover with transition */
.smooth-hover {
  transition: all 0.3s ease;
}

.smooth-hover:hover {
  background-color: #007bff;
  color: white;
  transform: scale(1.05);
}

:focus Pseudo-Class

/* Focus state */
.input:focus {
  outline: none;
  border-color: #007bff;
  box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25);
  /* When element has focus
  */
}

.button:focus {
  outline: 2px solid #007bff;
  outline-offset: 2px;
}

.link:focus {
  color: #007bff;
  text-decoration: underline;
}

/* Focus-visible for better accessibility */
.focus-visible:focus-visible {
  outline: 2px solid #007bff;
  outline-offset: 2px;
}

.focus-visible:focus:not(:focus-visible) {
  outline: none;
}

:active Pseudo-Class

/* Active state */
.button:active {
  background-color: #004085;
  transform: translateY(0);
  /* When element is being clicked
  */
}

.link:active {
  color: #0056b3;
}

.tab:active {
  background-color: #e9ecef;
  color: #333;
}

/* Active with transition */
.button:active {
  transition: transform 0.1s ease;
}

.button:active {
  transform: scale(0.98);
}

Combined Dynamic States

/* Hover and focus */
.interactive-element:hover,
.interactive-element:focus {
  background-color: #007bff;
  color: white;
}

/* Focus within hover */
.button:hover:focus {
  background-color: #0056b3;
  /* Different style when both states
  */
}

/* Active with hover */
.button:active:hover {
  background-color: #004085;
  /* Active state takes precedence
  */
}

/* Link states */
.link {
  color: #007bff;
  text-decoration: none;
  transition: color 0.2s ease;
}

.link:hover {
  color: #0056b3;
  text-decoration: underline;
}

.link:active {
  color: #004085;
}

.link:focus {
  outline: 2px solid #007bff;
  outline-offset: 2px;
}

Form Pseudo-Classes

Form-specific pseudo-classes target form elements based on their validation state, content, and user interaction patterns.

:checked and :indeterminate

/* Checked state */
.checkbox:checked + label {
  color: #28a745;
  font-weight: bold;
}

.radio:checked + label {
  color: #007bff;
}

.toggle:checked + .toggle-content {
  display: block;
}

/* Custom checkbox styling */
.checkbox {
  display: none;
}

.checkbox:checked + label::before {
  content: "βœ“";
  color: #28a745;
  font-weight: bold;
}

/* Indeterminate state */
.checkbox:indeterminate + label::before {
  content: "–";
  color: #ffc107;
}

.progress:indeterminate {
  background-color: #ffc107;
}

:disabled and :enabled

/* Disabled state */
.button:disabled {
  background-color: #6c757d;
  color: #fff;
  cursor: not-allowed;
  opacity: 0.6;
}

.input:disabled {
  background-color: #f8f9fa;
  color: #6c757d;
  cursor: not-allowed;
}

/* Enabled state */
.button:enabled {
  cursor: pointer;
}

.input:enabled {
  background-color: white;
  color: #333;
}

/* Form field states */
.form-field:disabled {
  opacity: 0.5;
}

.form-field:disabled label {
  color: #6c757d;
}

:required and :optional

/* Required field indicator */
.input:required {
  border-color: #dc3545;
}

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

/* Optional field styling */
.input:optional {
  border-color: #28a745;
}

.input:optional::after {
  content: " (optional)";
  color: #6c757d;
  font-size: 0.8em;
}

/* Form validation */
.input:required:valid {
  border-color: #28a745;
}

.input:required:invalid {
  border-color: #dc3545;
}

:valid and :invalid

/* Valid state */
.input:valid {
  border-color: #28a745;
  box-shadow: 0 0 0 2px rgba(40, 167, 69, 0.25);
}

.form-group:valid .success-icon {
  display: block;
  color: #28a745;
}

/* Invalid state */
.input:invalid {
  border-color: #dc3545;
  box-shadow: 0 0 0 2px rgba(220, 53, 69, 0.25);
}

.form-group:invalid .error-icon {
  display: block;
  color: #dc3545;
}

/* Validation messages */
.input:valid ~ .valid-message {
  display: block;
  color: #28a745;
  font-size: 0.875rem;
}

.input:invalid ~ .invalid-message {
  display: block;
  color: #dc3545;
  font-size: 0.875rem;
}

:in-range and :out-of-range

/* In-range state */
.range-input:in-range {
  border-color: #28a745;
}

.range-input:in-range ~ .range-status {
  color: #28a745;
}

.range-input:in-range ~ .range-status::before {
  content: "βœ“";
}

/* Out-of-range state */
.range-input:out-of-range {
  border-color: #dc3545;
}

.range-input:out-of-range ~ .range-status {
  color: #dc3545;
}

.range-input:out-of-range ~ .range-status::before {
  content: "βœ—";
}

Structural Pseudo-Classes

Structural pseudo-classes select elements based on their position in the document tree, such as first-child, last-child, and nth-child.

:first-child and :last-child

/* First child */
.list-item:first-child {
  border-top: 2px solid #007bff;
  font-weight: bold;
}

.tab:first-child {
  border-left: 2px solid #007bff;
}

/* Last child */
.list-item:last-child {
  border-bottom: 2px solid #007bff;
}

.tab:last-child {
  border-right: 2px solid #007bff;
}

/* Combined first and last */
.breadcrumb-item:first-child {
  padding-left: 0;
}

.breadcrumb-item:last-child {
  padding-right: 0;
}

/* First and last in different contexts */
.grid-item:first-child {
  grid-column: 1;
}

.grid-item:last-child {
  grid-column: -1;
}

:nth-child() and :nth-last-child()

/* Nth-child patterns */
.list-item:nth-child(odd) {
  background-color: #f8f9fa;
}

.list-item:nth-child(even) {
  background-color: #e9ecef;
}

/* Specific positions */
.list-item:nth-child(3) {
  font-weight: bold;
  color: #007bff;
}

.list-item:nth-child(5) {
  border: 2px solid #28a745;
}

/* Nth-last-child patterns */
.list-item:nth-last-child(2) {
  margin-bottom: 20px;
}

.list-item:nth-last-child(1) {
  border-bottom: 2px solid #dc3545;
}

/* Complex nth-child */
.list-item:nth-child(3n) {
  /* Every 3rd item */
  background-color: #007bff;
  color: white;
}

.list-item:nth-child(3n+1) {
  /* Items 1, 4, 7, 10... */
  background-color: #28a745;
  color: white;
}

/* Responsive grid */
.grid-item:nth-child(2n) {
  grid-column: span 2;
}

.grid-item:nth-child(3n) {
  grid-row: span 2;
}

:only-child

/* Only child */
.single-item:only-child {
  border: 2px solid #007bff;
  background-color: #f8f9fa;
}

.single-image:only-child {
  max-width: 100%;
  height: auto;
}

/* Only child of type */
.paragraph:only-of-type {
  font-size: 1.2rem;
  font-weight: bold;
}

.list-item:only-of-type {
  list-style-type: none;
  margin-left: 0;
}

:empty

/* Empty elements */
.placeholder:empty {
  min-height: 100px;
  background-color: #f8f9fa;
  border: 2px dashed #dee2e6;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #6c757d;
}

.placeholder:empty::before {
  content: "No content available";
}

/* Empty state indicators */
.status-empty:empty::after {
  content: " (Empty)";
  color: #6c757d;
  font-style: italic;
}

/* Empty vs not empty */
.content:empty {
  padding: 2rem;
  text-align: center;
}

.content:not(:empty) {
  padding: 1rem;
}

UI State Pseudo-Classes

UI state pseudo-classes respond to user interface interactions and element states beyond basic hover and focus.

:visited and :link

/* Unvisited links */
.link:link {
  color: #007bff;
  text-decoration: none;
}

/* Visited links */
.link:visited {
  color: #6c757d;
  text-decoration: none;
}

/* Link styling with states */
.nav-link:link {
  color: #333;
  padding: 0.5rem 1rem;
  border-radius: 4px;
  transition: background-color 0.2s ease;
}

.nav-link:hover {
  background-color: #f8f9fa;
}

.nav-link:visited {
  color: #6c757d;
}

.nav-link:visited:hover {
  background-color: #e9ecef;
}

:target

/* Target element */
.section:target {
  background-color: #fff3cd;
  border: 2px solid #ffc107;
  padding: 1rem;
}

/* Smooth scroll to target */
:target {
  scroll-margin-top: 100px;
  animation: highlight 2s ease;
}

@keyframes highlight {
  0% { background-color: #fff3cd; }
  100% { background-color: transparent; }
}

/* Target styling */
.anchor:target {
  color: #007bff;
  font-weight: bold;
}

.section:target h2 {
  color: #28a745;
  border-bottom: 2px solid #28a745;
  padding-bottom: 0.5rem;
}

:default

/* Default form elements */
.button:default {
  background-color: #6c757d;
}

.button:default::after {
  content: " (Default)";
  font-size: 0.8em;
  color: #6c757d;
}

/* Default submit button */
input[type="submit"]:default {
  font-weight: normal;
}

input[type="submit"]:not(:default) {
  font-weight: bold;
}

/* Default option */
select option:default {
  font-weight: bold;
  color: #007bff;
}

Advanced Pseudo-Classes

Advanced pseudo-classes provide more sophisticated selection capabilities for complex UI patterns and states.

:not() Pseudo-Class

/* Not pseudo-class */
.button:not(:disabled) {
  cursor: pointer;
  background-color: #007bff;
}

.list-item:not(:last-child) {
  border-bottom: 1px solid #dee2e6;
  margin-bottom: 0;
}

.input:not(:placeholder-shown) {
  border-color: #28a745;
}

/* Multiple not conditions */
.link:not(:visited):not(:hover) {
  color: #007bff;
}

.form-field:not(:required):not(:invalid) {
  border-color: #28a745;
}

/* Not with structural pseudo-classes */
.list-item:not(:first-child):not(:last-child) {
  margin: 0 10px;
}

.grid-item:not(:nth-child(3n)) {
  background-color: #f8f9fa;
}

:is() and :where() Pseudo-Classes

/* :is() pseudo-class */
article :is(h1, h2, h3) {
  color: #333;
  margin-top: 1rem;
}

/* Equivalent to:
article h1,
article h2,
article h3 {
  color: #333;
  margin-top: 1rem;
}
*/

/* :where() pseudo-class (lower specificity) */
.card :where(.header, .footer) {
  background-color: #f8f9fa;
  padding: 1rem;
}

/* Complex selections */
.form-group :is(input:focus, textarea:focus) {
  border-color: #007bff;
  box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25);
}

/* Nested :is() */
.nav :is(ul, ol) :is(li) :is(a) {
  padding: 0.5rem 1rem;
  text-decoration: none;
}

:has() Pseudo-Class

/* :has() pseudo-class */
.form-group:has(:invalid) {
  border: 2px solid #dc3545;
  padding: 1rem;
}

.card:has(.featured) {
  border: 2px solid #007bff;
  box-shadow: 0 4px 8px rgba(0, 123, 255, 0.2);
}

/* Navigation states */
.nav-item:has(.active) {
  background-color: #007bff;
}

.nav-item:has(.active) a {
  color: white;
}

/* Content-based styling */
.article:has(.video) {
  padding: 0;
}

.article:has(.gallery) {
  padding: 2rem;
}

/* Complex :has() selectors */
.sidebar:has(.widget:last-child) {
  padding-bottom: 0;
}

.container:has(.highlight):hover .content {
  background-color: #fff3cd;
}

:lang() Pseudo-Class

/* Language-based styling */
:lang(en) {
  quotes: '"' '"' '"' "'" "'";
}

:lang(es) {
  quotes: '"' '"' '"' '"' '"';
}

:lang(fr) {
  quotes: 'Β«' 'Β»' '"' '"';
}

/* Language-specific fonts */
:lang(ja) {
  font-family: "Hiragino Sans", sans-serif;
}

:lang(ar) {
  font-family: "Tahoma", sans-serif;
  direction: rtl;
}

/* Language-specific content */
:lang(en) .quote::before {
  content: open-quote;
}

:lang(en) .quote::after {
  content: close-quote;
}

Pseudo-Class Combinations

Combining pseudo-classes enables powerful and precise element selection for complex UI patterns.

State Combinations

/* Hover and focus */
.button:hover:focus {
  background-color: #0056b3;
  transform: scale(1.05);
}

/* Active and hover */
.button:active:hover {
  background-color: #004085;
}

/* Focus and valid */
.input:focus:valid {
  border-color: #28a745;
}

/* Focus and invalid */
.input:focus:invalid {
  border-color: #dc3545;
  animation: shake 0.5s;
}

@keyframes shake {
  0%, 100% { transform: translateX(0); }
  25% { transform: translateX(-5px); }
  75% { transform: translateX(5px); }
}

Structural and State Combinations

/* First child with state */
.list-item:first-child:hover {
  background-color: #007bff;
  color: white;
}

.tab:first-child.active {
  background-color: #28a745;
}

/* Nth-child with state */
.grid-item:nth-child(odd):hover {
  transform: scale(1.05);
}

.form-field:nth-child(even):focus {
  background-color: #f8f9fa;
}

/* Only child with state */
.single-item:only-child:focus {
  border: 2px solid #007bff;
}

/* Last child with state */
.list-item:last-child:active {
  background-color: #dc3545;
}

Complex Combinations

/* Multiple structural pseudo-classes */
.list-item:nth-child(odd):nth-last-child(odd) {
  background-color: #f8f9fa;
}

/* Structural + dynamic */
.nav-item:first-child:hover,
.nav-item:last-child:hover {
  border-radius: 8px;
}

/* Form validation combinations */
.input:required:invalid:focus {
  border-color: #dc3545;
  box-shadow: 0 0 0 3px rgba(220, 53, 69, 0.3);
}

.input:required:valid:focus {
  border-color: #28a745;
  box-shadow: 0 0 0 3px rgba(40, 167, 69, 0.3);
}

/* Link state combinations */
.link:visited:hover {
  color: #0056b3;
  text-decoration: underline;
}

.link:not(:visited):hover {
  color: #007bff;
  text-decoration: underline;
}

Responsive Pseudo-Classes

Some pseudo-classes are particularly useful for creating responsive designs and adapting to different device capabilities.

Media-Responsive Pseudo-Classes

/* Responsive structural selectors */
@media (max-width: 768px) {
  .list-item:nth-child(n+3) {
    display: none;
    /* Show only first 2 items on mobile
    */
  }
  
  .grid-item:nth-child(n+4) {
    grid-column: span 2;
    /* Wider items on mobile
    */
  }
}

@media (min-width: 769px) {
  .list-item:nth-child(n) {
    display: block;
    /* Show all items on desktop
    */
  }
}

/* Responsive hover states */
@media (hover: hover) {
  .card:hover {
    transform: translateY(-4px);
    box-shadow: 0 8px 16px rgba(0, 0, 0, 0.2);
  }
}

@media (hover: none) {
  .card:hover {
    /* No hover effects on touch devices
    */
  }
}

Device Capability Pseudo-Classes

/* Pointer type */
@media (pointer: fine) {
  .button {
    padding: 0.5rem 1rem;
    /* Smaller for mouse/trackpad
    */
  }
}

@media (pointer: coarse) {
  .button {
    padding: 1rem 2rem;
    /* Larger for touch
    */
  }
}

/* Hover capability */
@media (hover: hover) {
  .tooltip-trigger:hover .tooltip {
    display: block;
    /* Show tooltip on hover
    */
  }
}

@media (hover: none) {
  .tooltip-trigger:focus .tooltip {
    display: block;
    /* Show tooltip on focus for touch
    */
  }
}

/* Reduced motion */
@media (prefers-reduced-motion: reduce) {
  .animated-element:hover {
    transform: none;
    /* Disable animations
    */
  }
}

Accessibility Pseudo-Classes

/* Focus-visible for better accessibility */
.focusable:focus-visible {
  outline: 2px solid #007bff;
  outline-offset: 2px;
}

.focusable:focus:not(:focus-visible) {
  outline: none;
}

/* High contrast mode */
@media (prefers-contrast: high) {
  .button:hover {
    border: 2px solid #000;
    /* Stronger borders for high contrast
    */
  }
  
  .link:hover {
    background-color: #000;
    color: #fff;
  }
}

/* Color scheme preference */
@media (prefers-color-scheme: dark) {
  .card {
    background-color: #333;
    color: #fff;
  }
  
  .card:hover {
    background-color: #444;
  }
}

@media (prefers-color-scheme: light) {
  .card {
    background-color: #fff;
    color: #333;
  }
}

Pseudo-Class Best Practices

βœ“ Do:

  • Use pseudo-classes for semantic state styling
  • Consider accessibility when using focus states
  • Test pseudo-classes across different browsers
  • Use :focus-visible for better keyboard navigation
  • Consider performance with complex pseudo-classes
  • Document pseudo-class logic for maintainability
  • Use :not() to avoid overriding styles

βœ— Don't:

  • Rely solely on :hover for touch interactions
  • Forget about focus states for accessibility
  • Overuse complex pseudo-class combinations
  • Ignore browser compatibility for new features
  • Use pseudo-classes without semantic meaning
  • Create states that are confusing to users
  • Forget about performance with many pseudo-classes

Performance Considerations

⚑ Performance Tips:

  • Avoid :nth-child() with complex expressions
  • Limit pseudo-class depth
  • Use classes instead of complex pseudo-classes when possible
  • Avoid :not() with many selectors
  • Test performance with large DOM trees

Browser Compatibility

🌐 Compatibility Notes:

  • Dynamic pseudo-classes: Universal support
  • Structural pseudo-classes: Universal support
  • :is() and :where(): Modern browsers (Chrome 88+, Firefox 78+, Safari 14+)
  • :has(): Modern browsers (Chrome 105+, Firefox 103+, Safari 15.4+)
  • :focus-visible: Modern browsers (Chrome 86+, Firefox 85+, Safari 15.4+)
  • Always provide fallbacks for newer features

Real-World Examples

Interactive Navigation

/* Navigation with pseudo-classes */
.nav {
  display: flex;
  background-color: #f8f9fa;
  padding: 0;
}

.nav-item {
  position: relative;
}

.nav-item > a {
  display: block;
  padding: 1rem 1.5rem;
  color: #333;
  text-decoration: none;
  transition: all 0.2s ease;
}

.nav-item > a:hover {
  background-color: #e9ecef;
  color: #007bff;
}

.nav-item > a:focus {
  outline: 2px solid #007bff;
  outline-offset: -2px;
}

.nav-item > a:focus-visible {
  outline: 2px solid #007bff;
  outline-offset: -2px;
}

/* Active state */
.nav-item.active > a,
.nav-item > a:active {
  background-color: #007bff;
  color: white;
}

/* Dropdown menu */
.nav-item > ul {
  display: none;
  position: absolute;
  top: 100%;
  left: 0;
  background-color: white;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  min-width: 200px;
}

.nav-item:hover > ul {
  display: block;
}

.nav-item > ul > li > a {
  display: block;
  padding: 0.75rem 1rem;
  color: #333;
  text-decoration: none;
  border-bottom: 1px solid #f0f0f0;
}

.nav-item > ul > li > a:hover {
  background-color: #f8f9fa;
}

/* First and last nav items */
.nav-item:first-child > a {
  border-radius: 4px 0 0 4px;
}

.nav-item:last-child > a {
  border-radius: 0 4px 4px 0;
}

.nav-item:only-child > a {
  border-radius: 4px;
}

Form Validation

/* Form validation with pseudo-classes */
.form-group {
  margin-bottom: 1.5rem;
  position: relative;
}

.form-group label {
  display: block;
  margin-bottom: 0.5rem;
  font-weight: 500;
  color: #333;
}

.form-group input {
  width: 100%;
  padding: 0.75rem;
  border: 1px solid #ddd;
  border-radius: 4px;
  font-size: 1rem;
  transition: all 0.2s ease;
}

/* Required fields */
.form-group:has(:required) label::after {
  content: " *";
  color: #dc3545;
}

/* Validation states */
.form-group:has(input:valid) input {
  border-color: #28a745;
  box-shadow: 0 0 0 2px rgba(40, 167, 69, 0.25);
}

.form-group:has(input:invalid) input {
  border-color: #dc3545;
  box-shadow: 0 0 0 2px rgba(220, 53, 69, 0.25);
  animation: shake 0.5s ease-in-out;
}

.form-group:has(input:focus) input {
  border-color: #007bff;
  box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25);
}

/* Validation messages */
.form-group .validation-message {
  display: none;
  font-size: 0.875rem;
  margin-top: 0.5rem;
  padding: 0.5rem;
  border-radius: 4px;
}

.form-group:has(input:valid) .validation-message.success {
  display: block;
  background-color: #d4edda;
  color: #155724;
}

.form-group:has(input:invalid) .validation-message.error {
  display: block;
  background-color: #f8d7da;
  color: #721c24;
}

/* Checkbox styling */
.checkbox-wrapper {
  position: relative;
}

.checkbox-wrapper input[type="checkbox"] {
  position: absolute;
  opacity: 0;
  cursor: pointer;
}

.checkbox-wrapper .checkbox-custom {
  position: relative;
  display: inline-block;
  width: 20px;
  height: 20px;
  border: 2px solid #ddd;
  border-radius: 4px;
  background-color: white;
  transition: all 0.2s ease;
}

.checkbox-wrapper input[type="checkbox"]:checked + .checkbox-custom {
  background-color: #007bff;
  border-color: #007bff;
}

.checkbox-wrapper input[type="checkbox"]:checked + .checkbox-custom::after {
  content: "βœ“";
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  color: white;
  font-weight: bold;
}

.checkbox-wrapper input[type="checkbox"]:focus + .checkbox-custom {
  outline: 2px solid #007bff;
  outline-offset: 2px;
}

Card Gallery

/* Card gallery with pseudo-classes */
.card-gallery {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: 2rem;
  padding: 2rem;
}

.card {
  border: 1px solid #dee2e6;
  border-radius: 8px;
  overflow: hidden;
  transition: all 0.3s ease;
  background-color: white;
}

.card:hover {
  transform: translateY(-4px);
  box-shadow: 0 8px 16px rgba(0, 0, 0, 0.15);
}

.card:focus {
  outline: 2px solid #007bff;
  outline-offset: 2px;
}

/* Card structure */
.card .header {
  padding: 1.5rem;
  background-color: #f8f9fa;
  border-bottom: 1px solid #dee2e6;
}

.card .header h3 {
  margin: 0;
  color: #333;
}

.card .content {
  padding: 1.5rem;
}

.card .content p {
  margin: 0 0 1rem 0;
  line-height: 1.6;
}

.card .content p:last-child {
  margin-bottom: 0;
}

.card .footer {
  padding: 1rem 1.5rem;
  background-color: #f8f9fa;
  border-top: 1px solid #dee2e6;
  display: flex;
  justify-content: space-between;
  align-items: center;
}

/* Featured cards */
.card:nth-child(3n) {
  border: 2px solid #007bff;
  transform: scale(1.02);
}

.card:nth-child(3n):hover {
  transform: scale(1.02) translateY(-4px);
  box-shadow: 0 8px 16px rgba(0, 123, 255, 0.2);
}

/* First and last cards */
.card:first-child {
  grid-column: 1;
}

.card:last-child {
  grid-column: -1;
}

/* Empty state */
.card:empty {
  min-height: 200px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #6c757d;
  font-style: italic;
}

.card:empty::before {
  content: "No content available";
  font-size: 1.1rem;
}

/* Loading state */
.card.loading {
  position: relative;
}

.card.loading::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 20px;
  height: 20px;
  margin: -10px 0 0 -10px;
  border: 2px solid #007bff;
  border-radius: 50%;
  border-top-color: transparent;
  animation: spin 1s linear infinite;
}

@keyframes spin {
  0% { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
}

Complete Example

Comprehensive Pseudo-Class System

/* Pseudo-class system variables */
:root {
  /* Colors */
  --primary-color: #007bff;
  --success-color: #28a745;
  --warning-color: #ffc107;
  --danger-color: #dc3545;
  --info-color: #17a2b8;
  
  /* Spacing */
  --pseudo-gap-sm: 0.5rem;
  --pseudo-gap-md: 1rem;
  --pseudo-gap-lg: 1.5rem;
  
  /* Transitions */
  --pseudo-transition: all 0.2s ease;
}

/* Dynamic pseudo-classes */
.hover:hover {
  transition: var(--pseudo-transition);
}

.hover:hover .hover-content {
  opacity: 1;
  transform: translateY(-2px);
}

.hover .hover-content {
  opacity: 0.7;
  transform: translateY(0);
}

.focus:focus {
  outline: 2px solid var(--primary-color);
  outline-offset: 2px;
}

.focus:focus-visible {
  outline: 2px solid var(--primary-color);
  outline-offset: 2px;
}

.focus:not(:focus-visible) {
  outline: none;
}

.active:active {
  transform: scale(0.98);
  transition: transform 0.1s ease;
}

/* Form pseudo-classes */
.form-field:valid {
  border-color: var(--success-color);
  box-shadow: 0 0 0 2px rgba(40, 167, 69, 0.25);
}

.form-field:invalid {
  border-color: var(--danger-color);
  box-shadow: 0 0 0 2px rgba(220, 53, 69, 0.25);
}

.form-field:focus {
  border-color: var(--primary-color);
  box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25);
}

.form-field:disabled {
  opacity: 0.6;
  cursor: not-allowed;
  background-color: #f8f9fa;
}

.form-field:required::after {
  content: " *";
  color: var(--danger-color);
  margin-left: var(--pseudo-gap-sm);
}

/* Structural pseudo-classes */
.first-child:first-child {
  margin-top: 0;
  border-top: 2px solid var(--primary-color);
}

.last-child:last-child {
  margin-bottom: 0;
  border-bottom: 2px solid var(--primary-color);
}

.nth-odd:nth-child(odd) {
  background-color: #f8f9fa;
}

.nth-even:nth-child(even) {
  background-color: white;
}

.nth-third:nth-child(3n) {
  background-color: var(--info-color);
  color: white;
}

.only-child:only-child {
  border: 2px solid var(--success-color);
  padding: var(--pseudo-gap-md);
}

.empty:empty {
  min-height: 100px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #6c757d;
  font-style: italic;
}

.empty:empty::before {
  content: "No content available";
}

/* UI state pseudo-classes */
.link:link {
  color: var(--primary-color);
  text-decoration: none;
}

.link:visited {
  color: #6c757d;
  text-decoration: none;
}

.link:hover {
  text-decoration: underline;
}

.target:target {
  background-color: #fff3cd;
  border: 2px solid var(--warning-color);
  padding: var(--pseudo-gap-md);
  animation: highlight 2s ease;
}

@keyframes highlight {
  0% { background-color: #fff3cd; }
  100% { background-color: transparent; }
}

.default:default {
  opacity: 0.7;
}

.default:default::after {
  content: " (Default)";
  font-size: 0.8em;
  color: #6c757d;
}

/* Advanced pseudo-classes */
.not-disabled:not(:disabled) {
  cursor: pointer;
  opacity: 1;
}

.is-selector:is(h1, h2, h3, h4, h5, h6) {
  margin-top: var(--pseudo-gap-lg);
  color: #333;
}

.where-selector:where(p, ul, ol) {
  line-height: 1.6;
  margin-bottom: var(--pseudo-gap-md);
}

.has-content:has(.error) {
  border: 2px solid var(--danger-color);
  padding: var(--pseudo-gap-md);
}

.has-content:has(.success) {
  border: 2px solid var(--success-color);
  padding: var(--pseudo-gap-md);
}

/* Responsive pseudo-classes */
@media (hover: hover) {
  .touch-hover:hover {
    transform: scale(1.05);
    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
  }
}

@media (hover: none) {
  .touch-hover:focus {
    transform: scale(1.05);
    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
  }
}

@media (prefers-reduced-motion: reduce) {
  .animated-element {
    transition: none;
  }
  
  .animated-element:hover {
    transform: none;
  }
}

/* Button system */
.btn {
  padding: var(--pseudo-gap-md) var(--pseudo-gap-lg);
  border: none;
  border-radius: 4px;
  font-size: 1rem;
  cursor: pointer;
  transition: var(--pseudo-transition);
  text-decoration: none;
  display: inline-block;
}

.btn:hover {
  transform: translateY(-2px);
  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
}

.btn:focus {
  outline: 2px solid var(--primary-color);
  outline-offset: 2px;
}

.btn:active {
  transform: scale(0.98);
}

.btn:disabled {
  opacity: 0.6;
  cursor: not-allowed;
  transform: none;
}

.btn-primary {
  background-color: var(--primary-color);
  color: white;
}

.btn-secondary {
  background-color: #6c757d;
  color: white;
}

.btn-success {
  background-color: var(--success-color);
  color: white;
}

.btn-danger {
  background-color: var(--danger-color);
  color: white;
}

/* Form system */
.form-group {
  margin-bottom: var(--pseudo-gap-lg);
}

.form-group label {
  display: block;
  margin-bottom: var(--pseudo-gap-sm);
  font-weight: 500;
  color: #333;
}

.form-group input {
  width: 100%;
  padding: calc(var(--pseudo-gap-md) - 2px);
  border: 1px solid #ddd;
  border-radius: 4px;
  font-size: 1rem;
  transition: var(--pseudo-transition);
}

.form-group .validation-message {
  display: none;
  font-size: 0.875rem;
  margin-top: var(--pseudo-gap-sm);
  padding: var(--pseudo-gap-sm);
  border-radius: 4px;
}

.form-group:has(input:valid) .validation-message.success {
  display: block;
  background-color: #d4edda;
  color: #155724;
}

.form-group:has(input:invalid) .validation-message.error {
  display: block;
  background-color: #f8d7da;
  color: #721c24;
}

/* Card system */
.card {
  border: 1px solid #dee2e6;
  border-radius: 8px;
  overflow: hidden;
  transition: var(--pseudo-transition);
  background-color: white;
}

.card:hover {
  transform: translateY(-4px);
  box-shadow: 0 8px 16px rgba(0, 0, 0, 0.15);
}

.card .header {
  padding: var(--pseudo-gap-lg);
  background-color: #f8f9fa;
  border-bottom: 1px solid #dee2e6;
}

.card .content {
  padding: var(--pseudo-gap-lg);
}

.card .footer {
  padding: var(--pseudo-gap-md) var(--pseudo-gap-lg);
  background-color: #f8f9fa;
  border-top: 1px solid #dee2e6;
}

/* Gallery system */
.gallery {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: var(--pseudo-gap-lg);
  padding: var(--pseudo-gap-lg);
}

.gallery .card:nth-child(3n) {
  border: 2px solid var(--primary-color);
}

.gallery .card:nth-child(3n):hover {
  transform: scale(1.02) translateY(-4px);
  box-shadow: 0 8px 16px rgba(0, 123, 255, 0.2);
}

/* Navigation system */
.nav {
  display: flex;
  background-color: #f8f9fa;
  padding: 0;
}

.nav-item {
  position: relative;
}

.nav-item > a {
  display: block;
  padding: var(--pseudo-gap-md) var(--pseudo-gap-lg);
  color: #333;
  text-decoration: none;
  transition: var(--pseudo-transition);
}

.nav-item > a:hover {
  background-color: #e9ecef;
  color: var(--primary-color);
}

.nav-item.active > a {
  background-color: var(--primary-color);
  color: white;
}

.nav-item:first-child > a {
  border-radius: 4px 0 0 4px;
}

.nav-item:last-child > a {
  border-radius: 0 4px 4px 0;
}

/* Accessibility improvements */
@media (prefers-contrast: high) {
  .btn,
  .card,
  .form-group input {
    border: 2px solid #000;
  }
}

@media (prefers-color-scheme: dark) {
  .card {
    background-color: #333;
    color: #fff;
    border-color: #555;
  }
  
  .card:hover {
    background-color: #444;
  }
}

/* Print styles */
@media print {
  .btn:hover,
  .card:hover {
    transform: none;
    box-shadow: none;
  }
  
  .card {
    break-inside: avoid;
    page-break-inside: avoid;
  }
}

Summary

CSS pseudo-classes are powerful tools for creating dynamic, responsive, and accessible user interfaces. They enable state-based styling without JavaScript and provide semantic meaning to user interactions.

Key Takeaways:

  • Dynamic pseudo-classes respond to user interactions
  • Structural pseudo-classes select based on document position
  • Form pseudo-classes handle validation states
  • Advanced pseudo-classes provide powerful selection capabilities
  • Consider accessibility when using focus states
  • Test pseudo-classes across different browsers
  • Use pseudo-classes for semantic state styling

Remember: Pseudo-classes are about states and relationshipsβ€”understanding the user's context and the document structure is key to using them effectively!

πŸ§ͺ Quick Quiz

Which selects first child?