257

This should be simple, but I'm having trouble finding the search terms for it.
Let's say I have this:

<div class="a c">Foo</div>
<div class="b c">Bar</div>

In CSS, how can I create a selector that matches something that matches "(.a or .b) and .c"?

I know I could do this:

.a.c,.b.c {
  /* CSS stuff */
}

But, assuming I'm going to have to do this sort of logic a lot, with a variety of logical combinations, is there a better syntax?

0

5 Answers 5

199

is there a better syntax?

No. CSS' or operator (,) does not permit groupings. It's essentially the lowest-precedence logical operator in selectors, so you must use .a.c,.b.c.

2
  • 1
    Is there a technical reason why they decided not to implement nested logic? Does this speed up the implementations? Would this break their earlier syntactic choices?
    – Arnaud
    Commented Aug 14, 2022 at 17:19
  • 3
    This is no longer true currently. See the other answers. Commented Mar 28, 2023 at 10:14
76

For those reading this >= 2021:

I found success using the :is() selector:

*:is(.a, .b).c{...}
4
  • 3
    Thank you. I wonder if the ":is()" selector existed 9 years ago when I asked the question?
    – Josh
    Commented Jan 29, 2021 at 19:16
  • 2
    I doubt it! @Josh --> I left this answer because this post is the #1 result on Google for me when searching how to combine classes in a css selector. Helpful answer for those viewing presently – though the previous answers show perhaps more of an artifact of tech's evolution. Commented Jan 29, 2021 at 20:10
  • 3
    @Josh is() is what replaced :matches() from Metalcoder's answer from 2014. See developer.mozilla.org/en-US/docs/Web/CSS/:is for more info.
    – TylerH
    Commented Mar 11, 2021 at 14:37
  • Curious what I'm doing wrong? This: *:is(.foo, .bar) ~ .baz { margin-top: 10px; } ...compiles into this: *:is(.foo):is(.bar) ~ .baz { margin-top: -10px; } ...but the compilation is throwing: ":is(.foo)" is not a valid pseudo-class. ":is(.bar)" is not a valid pseudo-class. Commented Mar 22, 2022 at 22:34
51

Not yet, but there is the experimental :is() (formerly :matches()) pseudo-class selector that does just that:

:is(.a, .b).c {
  /* style properties go here */
}

You can find more info on it here and here. Currently, most browsers support its initial version :any(), which works the same way, but will be replaced by :is(). We just have to wait a little more before using this everywhere (I surely will).

6
  • 2
    It is unreasonable to blame IE for not implementing something they simply never thought of and was not in any draft to begin with. :-moz-any() and :-webkit-any() showed up long before Mozilla suggested it for Selectors 4 (leading up to its current incarnation as :matches()).
    – BoltClock
    Commented May 20, 2015 at 16:25
  • 2
    It is equally unreasonable to expect any vendor to implement a feature from an unstable draft regardless of how long the draft has been made available (this is to account for the fact that Selectors 4 remains unstable and largely unimplemented more than three years after its FPWD).
    – BoltClock
    Commented May 20, 2015 at 16:29
  • 1
    @BoltClock, they were blaming IE6 for a whole decade for things that didn't exist on 2001. People just want to blame someone.
    – GetFree
    Commented Aug 17, 2015 at 18:14
  • One word on experimental ones is that you don't want to start relying on them unless it officially implemented. While it could seem like a great solution - the possibility of it disappearing what personally prevents me from implementing it in my projects. Even to keep track of one experimental pseudo class does not make me feel safe. Most likely it is going to be used in more than one place in the project and in case and if you have more than one where you use it - you somehow need to keep track of it all. You can surely have fallbacks and still pull it off but that does not sound clean Commented Aug 12, 2016 at 19:34
  • 5
    Not experimental anymore. :is() is now implemented in all major browsers since February 2021 (MDN) - though it's probably way too soon to use it for web apps (without a fallback). Commented Jul 28, 2021 at 7:13
14

If you have this:

<div class="a x">Foo</div>
<div class="b x">Bar</div>
<div class="c x">Baz</div>

And you only want to select the elements which have .x and (.a or .b), you could write:

.x:not(.c) { ... }

but that's convenient only when you have three "sub-classes" and you want to select two of them.

Selecting only one sub-class (for instance .a): .a.x

Selecting two sub-classes (for instance .a and .b): .x:not(.c)

Selecting all three sub-classes: .x

8
  • 8
    Any logical clause of the form a or b or c or d is the same as not(not(a) and not(b) and not(c) and not(d)) so 'or' is actually just a convenience function. In theory it should be possible to get by without it in all cases. In the OP's case (.a or .b) and .c -> :not(:not(.a):not(.b)).c
    – Max Murphy
    Commented Aug 24, 2014 at 3:35
  • 2
    @MaxMurphy Have you tested this? Commented Aug 24, 2014 at 12:28
  • 5
    As of five o'clock this morning, yes! I have js that spits out first order logic, with selectors at the leaves, giving results such as :not(:not([data-status="ACT"]):not([data-status="ISS"]):not([data-status="COR"]))[data-month="08"]. The code is not clean enough for public perusal, yet, otherwise I'd post it. I tested on chrome, safari and a low end android phone.
    – Max Murphy
    Commented Aug 24, 2014 at 13:06
  • 2
    @MaxMurphy You have a great point, but I think you've invoked a rather tortured definition of "convenience function." Anything a digital computer can do can be decomposed into NAND gates, but I wouldn't call the rest of software engineering "a bunch of convenience functions." Differences of degree quickly become de facto differences of kind when constructs can be combined.
    – Sarah G
    Commented May 18, 2015 at 7:02
  • 1
    @MaxMurphy I had a hard time figuring out that you were just joking. :( The negation CSS pseudo-class, :not(X), is a functional notation taking a simple selector X as an argument. It matches an element that is not represented by the argument. X must not contain another negation selector. (from Mozilla docs, emphasis mine) Commented Sep 20, 2016 at 13:57
5

No. Standard CSS does not provide the kind of thing you're looking for.

However, you might want to look into LESS and SASS.

These are two projects which aim to extend default CSS syntax by introducing additional features, including variables, nested rules, and other enhancements.

They allow you to write much more structured CSS code, and either of them will almost certainly solve your particular use case.

Of course, none of the browsers support their extended syntax (especially since the two projects each have different syntax and features), but what they do is provide a "compiler" which converts your LESS or SASS code into standard CSS, which you can then deploy on your site.

1
  • At that point, I might as well just use PHP printing "Content-type: text/css"
    – Josh
    Commented Sep 22, 2011 at 15:53

Not the answer you're looking for? Browse other questions tagged or ask your own question.