1. css
  2. /modern pseudo-classes

Modern CSS Pseudo-classes

Modern CSS Pseudo-classes

CSS has introduced several powerful new pseudo-classes that enhance selector capabilities and make complex selections more manageable.

Browser Support

Modern pseudo-classes are supported in recent browser versions:

  • Chrome 105+
  • Firefox 103+
  • Safari 15.4+
  • Edge 105+

Parent Selector (:has)

The :has() pseudo-class allows selecting elements based on their children or subsequent elements:

/* Select parent with specific child */
.container:has(.child) {
  display: flex;
}

/* Select parent with specific direct child */
.container:has(> .direct-child) {
  gap: 1rem;
}

/* Select based on subsequent elements */
h2:has(+ p) {
  margin-bottom: 0.5rem;
}

/* Complex conditions */
.card:has(img):has(.title) {
  padding: 1rem;
}

Common Use Cases for :has()

/* Style forms based on input state */
.form-group:has(:invalid) {
  border-color: red;
}

/* Responsive layouts */
.container:has(> :nth-child(4)) {
  grid-template-columns: repeat(2, 1fr);
}

/* Interactive components */
.accordion:has(input:checked) .content {
  display: block;
}

Specificity-reducing Pseudo-classes

:where()

Zero-specificity selector matching:

/* Zero specificity, easily overridable */
:where(.header, .footer) a {
  color: blue;
}

/* Traditional high specificity */
.header a, .footer a {
  color: red; /* This wins over :where() */
}

:is()

Specificity-preserving selector matching:

/* Equivalent to .header a, .footer a */
:is(.header, .footer) a {
  color: blue;
}

/* Simplified complex selectors */
:is(h1, h2, h3):is(.title, .heading) {
  font-family: sans-serif;
}

State Pseudo-classes

:focus-visible

Intelligent focus styles:

.button:focus-visible {
  outline: 2px solid blue;
  outline-offset: 2px;
}

/* Progressive enhancement */
.button:focus {
  /* Fallback focus styles */
  outline: 1px solid gray;
}

.button:focus-visible {
  /* Modern focus styles */
  outline: 2px solid blue;
  box-shadow: 0 0 0 4px rgba(0, 0, 255, 0.2);
}

:focus-within

Style containers with focused elements:

.form-group:focus-within {
  background: #f5f5f5;
}

.search-box:focus-within {
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

Form Pseudo-classes

:user-invalid and :user-valid

Style form elements based on user interaction:

input:user-invalid {
  border-color: red;
  background: #fff5f5;
}

input:user-valid {
  border-color: green;
  background: #f5fff5;
}

/* Progressive enhancement */
input:invalid {
  border-color: #ff8888;
}

input:user-invalid {
  border-color: red;
  animation: shake 0.3s;
}

Practical Examples

Interactive Card Component

.card {
  padding: 1rem;
  border: 1px solid #ddd;
  
  /* Style when card has image */
  &:has(img) {
    padding-top: 0;
  }
  
  /* Style when card has both image and heading */
  &:has(img + h2) {
    text-align: center;
  }
  
  /* Style when card is empty */
  &:not(:has(*)) {
    display: none;
  }
}

Advanced Form Styling

.form {
  /* Style form groups with invalid inputs */
  .form-group:has(:invalid) {
    border-left: 3px solid red;
  }
  
  /* Style labels of required inputs */
  label:has(+ :required)::after {
    content: "*";
    color: red;
  }
  
  /* Style based on input type */
  :is([type="text"], [type="email"]):user-invalid {
    background: #fff5f5;
  }
}

Dynamic Navigation

.nav {
  /* Style when has more than 5 items */
  &:has(> :nth-child(6)) {
    display: flex;
    flex-wrap: wrap;
  }
  
  /* Style when has dropdown */
  &:has(.dropdown) {
    position: relative;
  }
  
  /* Style items with dropdowns */
  .nav-item:has(> .dropdown) {
    padding-right: 1.5em;
  }
}

Best Practices

1. Progressive Enhancement

/* ✓ Good - provide fallbacks */
.button {
  /* Base styles */
  border: 1px solid #ddd;
  
  /* Enhanced styles with :has() */
  &:has(svg) {
    display: flex;
    align-items: center;
  }
}

/* ✗ Bad - no fallback */
.button:has(svg) {
  display: flex;
}

2. Specificity Management

/* ✓ Good - use :where() for low specificity */
:where(.header, .footer) a {
  color: inherit;
}

/* ✗ Bad - unnecessarily high specificity */
.header a, .footer a {
  color: inherit;
}

3. Semantic Usage

/* ✓ Good - meaningful relationships */
article:has(h1 + p) {
  max-width: 60ch;
}

/* ✗ Bad - arbitrary relationships */
div:has(span) {
  margin: 10px;
}

Common Use Cases

  1. Responsive Layouts: Adapt layouts based on content
  2. Form Validation: Style forms based on input states
  3. Interactive Components: Create dynamic UI components
  4. Content Management: Style based on content presence
  5. Accessibility: Enhance focus and interaction states

Tips and Tricks

1. Combining Pseudo-classes

/* Multiple conditions */
.container:has(img):has(.title):not(:has(.subtitle)) {
  grid-template: "image title" auto / auto 1fr;
}

2. State Management

/* Track multiple states */
.form-group:has(:focus):has(:valid) {
  background: #f0fff4;
}

3. Layout Patterns

/* Responsive grid */
.grid:has(> :nth-child(4)) {
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
}

Modern CSS pseudo-classes provide powerful tools for creating more dynamic and maintainable stylesheets. They enable complex selections that were previously impossible or required JavaScript, making CSS more capable and reducing the need for additional scripting.