CSS Selector "(A or B) and C"?

Learn css selector "(a or b) and c"? with practical examples, diagrams, and best practices. Covers html, css, css-selectors development techniques with visual explanations.

Crafting Complex CSS Selectors: The "(A or B) and C" Challenge

Hero image for CSS Selector "(A or B) and C"?

Learn how to effectively combine CSS selectors to target elements that match multiple conditions, specifically addressing the "(A or B) and C" logic using various CSS techniques.

CSS selectors are powerful tools for styling web pages, but sometimes you need to target elements based on more complex logical conditions than simple chaining. A common challenge is implementing an "(A or B) and C" logic, where an element must satisfy condition C, and either condition A or condition B. This article explores different strategies to achieve this kind of complex selection, leveraging modern CSS capabilities and practical workarounds.

Understanding the Challenge: Logical OR and AND in CSS

CSS selectors inherently support an 'AND' logic when chaining them (e.g., div.class1.class2 means a div that also has class1 and class2). However, a direct 'OR' operator within a single selector chain (like div.(class1 or class2)) does not exist. The comma , acts as a global 'OR' for entire selector blocks (e.g., div.class1, div.class2 means a div with class1 or a div with class2). Our goal is to combine these to achieve a more granular "(A or B) and C" structure.

flowchart TD
    A[Element] --> B{Has C?}
    B -- No --> D[Not Selected]
    B -- Yes --> E{Has A?}
    E -- Yes --> F[Selected]
    E -- No --> G{Has B?}
    G -- Yes --> F[Selected]
    G -- No --> D[Not Selected]

Logical flow for "(A or B) and C" selector

Strategy 1: Using the :is() Pseudo-class (Modern CSS)

The :is() pseudo-class (formerly :matches() or :any()) is the most direct and elegant solution for implementing 'OR' logic within a selector. It takes a comma-separated list of selectors as an argument and matches if the element matches any of them. This allows us to express "(A or B)" directly and then combine it with "and C".

/* Selects elements that have class 'c' AND (class 'a' OR class 'b') */
.c:is(.a, .b) {
  color: blue;
  border: 1px solid blue;
}

/* Example HTML structure */
<div class="a c">This will be blue</div>
<div class="b c">This will also be blue</div>
<div class="a">Not blue (missing c)</div>
<div class="c">Not blue (missing a or b)</div>
<div class="b">Not blue (missing c)</div>
<div>Not blue</div>

Using :is() for (A or B) and C logic

Strategy 2: Combining Multiple Selectors with a Comma (Fallback)

If browser support for :is() is a concern, or for simpler cases, you can achieve the same effect by writing out each 'OR' condition separately and combining them with the 'AND' condition. This effectively translates "(A or B) and C" into "(A and C) or (B and C)".

/* Selects elements that have class 'a' AND class 'c' */
.a.c,
/* OR elements that have class 'b' AND class 'c' */
.b.c {
  background-color: lightgreen;
  padding: 5px;
}

/* Example HTML structure */
<div class="a c">This will be lightgreen</div>
<div class="b c">This will also be lightgreen</div>
<div class="a">Not lightgreen (missing c)</div>
<div class="c">Not lightgreen (missing a or b)</div>

Using comma-separated selectors for (A and C) or (B and C) logic

Strategy 3: Leveraging Parent-Child Relationships or Attributes

Sometimes, the 'A', 'B', and 'C' conditions might relate to different aspects of an element or its ancestors. For instance, if 'C' is a parent element, and 'A' or 'B' are classes on the child. Or if 'A' and 'B' are attributes. The principles remain the same, but the selector syntax adapts.

/* Example: Parent with class 'container-c' AND child with (class 'item-a' OR class 'item-b') */
.container-c :is(.item-a, .item-b) {
  font-weight: bold;
}

/* Example: Element with attribute 'data-c' AND (attribute 'data-a' OR attribute 'data-b') */
[data-c]:is([data-a], [data-b]) {
  text-decoration: underline;
}

/* Example HTML */
<div class="container-c">
  <span class="item-a">Bold text</span>
  <span class="item-b">Also bold</span>
  <span>Not bold</span>
</div>

<p data-c data-a>Underlined text</p>
<p data-c data-b>Also underlined</p>
<p data-a>Not underlined</p>

Combining :is() with descendant selectors and attribute selectors

The flexibility of :is() makes it suitable for a wide range of complex selection scenarios, including those involving descendant selectors, attribute selectors, and other pseudo-classes.