5

I'm trying to theme some buttons according to their ancestors (not parents, especialy), so... I have the following HTML structure

<body class="theme-a"> <section class="container"> <form class="theme-b"> <div class="button-group"> <button type="button">Button B1</button> <button type="button">Button B2</button> </div> <div class="button-group"> <button type="button">Button B3</button> <button type="button">Button B4</button> </div> </form> <form> <div class="button-group"> <button type="button">Button A1</button> <button type="button">Button A2</button> </div> <div class="button-group"> <button type="button">Button A3</button> <button type="button">Button A4</button> </div> </form> </section> </body> 

Well, as you can see, there are two themes .theme-a and .theme-b

The CSS code, looks like:

.theme-a { background: #999; } .theme-b { background: #555; } .theme-a button { background: #222; } .theme-b button { background: #69C; } 

The problem is: if you switch the theme classes (A with B and B with A), you'll notice that the button on A theme (which has a closer ancestor with the theme class, keeps the styling of the far ancestor, the blue background rather than black one).

How can I achieve a proper specificity in a way that the button properties are set according to the closest ancestor?

Here's the link from JSfiddle: http://jsfiddle.net/XVaQT/1/

I hope that I explained in a clear way :)

Thanks ​

2
  • 1
    The problem with specificity is that it has no notion of closest or furthest ancestor or sibling. Even combinators don't make a difference in specificity; E F is equally specific to E > F. Commented Nov 15, 2012 at 16:06
  • @BoltClock The direct descendat rule is out of discussion :) As I said, the problem is related to ancestors, but not just with parents Commented Nov 15, 2012 at 16:27

3 Answers 3

1

Thanfully, with CSS you can combine multiple selectors to specify the same styles for lots of elements, I updated your jsfiddle with a working example, just change the classes theme-a and theme-b, as you said in your question, to see it working: http://jsfiddle.net/cchana/XVaQT/3/

All I have done is add a second selector where you were just looking for a button that is the descendant of an element with the class theme-a:

.theme-a button { background: #222; } 

It now also looks for a button that is the descendant of an element with the class theme-b that is itself a descendant of an element with the class theme-a:

.theme-a button, .theme-a .theme-b button { background: #222; } 

There should be no need to add a !important to your background value as it will override the styles defined for .theme-b button thanks to this selector being more specific.

Sign up to request clarification or add additional context in comments.

3 Comments

What if I have a 3 or 4 themes nesting (actually, I'm working on a 4 themes thing)? Imagine a theme for body, one for a section and one for the header of that section. I'll have to write every combination of themes? It won't be nice at all!
I agree it can get messy, but unfortunately, CSS only goes one way! You can select the child(ren) of a parent, but not the parent(s) of a child. To help make it easier to maintain, maybe LESS would be a good option, allowing you to nest/re-use styles.
I know that you can't target the parent of a child. I'm also using Sass/SCSS for my styles, but I'm trying to find an elegant solution to this issue, hopefuly :)
0

Here's a 2023 solution using :has that styles a button based off the .theme-* class on the closest ancestor, and allows unlimited nesting of .theme-* classes on ancestors.

.theme-a:not(:has([class*='theme-'])) button { /* theme-a button styles */ } 

The selector is saying: "Give me all .theme-a elements, but not ones with a nested theme-* class inside them." (Specifically, not ones with a child element with a class attribute that contains theme-.)

1 Comment

Be aware that as of August 2023, Firefox does not yet support this selector developer.mozilla.org/en-US/docs/Web/CSS/… it’s reasonably well covered, but not above 90%/95% caniuse.com/css-has
0

There's now a way to directly address this in Chrome and Edge, and hopefully the other browsers will follow soon. CSS scoping takes the proximity of the matching selector into account, allowing the declarations from the nearest matching ancestor to take precedence ahead of the order in which the CSS rules appear.

So in this example, for the first set of buttons, both the button rules match, but instead of the later rule using .theme-b winning the cascade, the .theme-a rule wins because the .theme-a element is a nearer ancestor of the buttons.

.theme-a { background: #999; } .theme-b { background: #555; } @scope(.theme-a) { button { background: #222; } } @scope(.theme-b) { button { background: #69C; } }
<body class="theme-b"> <section class="container"> <form class="theme-a"> <div class="button-group"> <button type="button">Button B1</button> <button type="button">Button B2</button> </div> <div class="button-group"> <button type="button">Button B3</button> <button type="button">Button B4</button> </div> </form> <form> <div class="button-group"> <button type="button">Button A1</button> <button type="button">Button A2</button> </div> <div class="button-group"> <button type="button">Button A3</button> <button type="button">Button A4</button> </div> </form> </section> </body>

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.