Search code examples
csselementselectorchildren

CSS: styling ALL elements on a page EXCEPT the children of a specific elements?


So let's suppose I have this code. It's probably easiest to just see what is going on in the js fiddle itself, when it's formatted.

Here is some example HTML:

<div>
  <h1>Anything outside of #keep-original should be set color: var(--colour-1) !important</h1>
</div>
<div id='keep-original'>
  <h1>Stuff inside #keep-original should keep original colour</h1>
</div>
<div>
  <h1>Anything outside of #keep-original should be set color: var(--colour-1) !important</h1>
</div>

:root {
  --colour-1: red;
}


/* invalid CSS
*:not(#keep-original *){
  color: var(--colour-1);
}
*/


/* formatting stuff */

* {
  margin: 0px;
  padding: 0px;
  color: white;
}

html {
  height: 100%;
}

body {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  background: #222;
  height: 100%;
}

body>div {
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: space-between;
}

div * {
  display: inline;
}

.c1,
.c1 * {
  color: #ff6500ff;
}

.c2,
.c2 * {
  color: #f0ff00ff;
}

.c3,
.c3 * {
  color: #1fc102ff;
}

.c4,
.c4 * {
  color: #02c1acff;
}

.c5,
.c5 * {
  color: #0f2bfdff;
}

.c6,
.c6 * {
  color: #fc0bbfff;
}
<div>
  <h4>Anything</h4>
  <h3>outside</h3>
  <h2>the</h2>
  <h1>middle</h1>
  <h1>div</h1>
  <h2>should</h2>
  <h3>be</h3>
  <h4>--colour-1</h4>
</div>
<div id='keep-original'>
  <h3 class='c1'>This</h3>
  <h2 class='c2'>text</h2>
  <h1 class='c3'>should</h1>
  <h2 class='c4'>be</h2>
  <h3 class='c5'>unaffected</h3>
  <h1 class='c6'>including
    <p>nested<span> elements<a> like<p> these</p></a></span></p>
  </h1>
</div>
<div>
  <h4>Anything</h4>
  <h3>outside</h3>
  <h2>the</h2>
  <h1>middle</h1>
  <h1>div</h1>
  <h2>should</h2>
  <h3>be</h3>
  <h4>--colour-1</h4>
</div>

Let's assume I want all elements on the page to be a certain colour, EXCEPT for those that are children of a particular element. For example, this obviously sets the colour of all elements:

:root{
    --colour-1: red;
}

*{
     color: var(--colour-1) !important
 }

Logically, I would want to do something like this (i.e. "select all, except children of #keep-original"), but it's not supported by CSS:

*:not(#keep-original *){
    color: var(--colour-1);
}

I realise I could achieve what I want easily in JS by e.g. adding a .keep-original class to all children of #keep-original, then applying the colour style the css selector *:not(.keep-original), but I'm wondering, is it possible to do what I want purely in css? I feel as though this should be something that is quite easy to do, so maybe I've missed something really obvious...


Solution

  • You’re close. :not() doesn't work as well as you'd expect with ID selectors, despite the relevant standards.

    This doesn't work:

    *:not(#keep-original) * {
      color: var(--colour-1);
    }
    

    But this does:

    div:not(#keep-original) * {
      color: var(--colour-1);
    }
    

    For some reason, you have to prefix :not() with something more specific than *.

    I suggest you use a class instead (which is always a good idea anyway):

    <div class='keep-original'>
    

    and then this will work:

    *:not(.keep-original) * {
      color: var(--colour-1);
    }