shrewdbeans
shrewdbeans

Reputation: 12549

How to use the CSS :not selector for classes further up the DOM tree

I have widget that has some styling applied to it. But then if a certain class is applied to the body that styling gets overridden. For example, let's say the body gets a class of .blue-theme

.widget { background: lime }
body.blue-theme .widget { background: blue }

This override will obviously apply to all widgets. Now let's say there's a type of page called body.test, where the same rules apply except for one widget, that has a class of .joe. This .joe widget can appear on other pages - but only if it appears on the body.blue-theme.test page does it ignore the overriding styles. I can achieve by overriding it again like this:

.widget { background: lime }
body.blue-theme .widget { background: blue }
body.blue-theme.test .widget.joe { background: lime } // revert to original styling

However in my case, I have many styles applied to the widget and it's child elements, so much so that it would be a huge headache if I need to make a change and have to change the styling in two places.

Is there some way, perhaps with clever use of selectors, whereby I can do this without that extra third line in my example? For example; saying that I want to override any widget in body.blue-theme, except for .widget.joe if the body also has a class of .test.

I am using LESS CSS, so a technique using that would be fine.

Upvotes: 1

Views: 302

Answers (2)

BoltClock
BoltClock

Reputation: 724342

If :not() allowed combinators, this would be trivial:

.widget { background: lime }
body.blue-theme .widget:not(body.test .joe) { background: blue }

But it doesn't in CSS, and there is no equivalent for :not(body.test .joe) that can be expressed as a single valid selector.

In your case, since body.blue-theme.test .widget.joe is more specific than body.blue-theme .widget, it is sufficient to move that selector into your first ruleset. It will override the second ruleset for those particular elements:

.widget, body.blue-theme.test .widget.joe { background: lime }
body.blue-theme .widget { background: blue }

Alternatively you could have two separate selectors in the overriding rule, one for all widgets in body.blue-theme:not(.test), and one for all widgets except .joe in body.blue-theme.test:

.widget { background: lime }

body.blue-theme:not(.test) .widget,
body.blue-theme.test       .widget:not(.joe) { background: blue }

Either way, you will require at least two selectors in one of the rulesets. Personally, I find this method needlessly verbose compared to just combining the first and third rulesets together.

Upvotes: 1

Arbel
Arbel

Reputation: 30999

This will do the job:

body.blue-theme.test .widget:not(.joe) { background: "blue" }

Upvotes: 2

Related Questions