4

Problem:

I'm making a nav CSS module. I'm wanting submenus to have a different background color than their parent menu. However, if there is a submenu within a submenu, I need that submenu to have the same background color as it's parent's parent. I know I could do this fairly verbosely if I set a limit on the number of submenus that would be allowed but I would prefer the option to have as many nested menus as needed and have them colored appropriately. Is there some CSS trickery that can do this or am I relegated to JS for this?

I've looked into :nth-child and :nth-of-type but these only apply to sibling elements within a parent node as far as I know.

Code:

ul.nav {
  list-style: none;
}

ul.nav.dropdown {
  height: 0;
  overflow: hidden;
}

ul.nav.dropdown.show {
  height: auto;
}

ul.nav li {
  background-color: #44499f;
}

ul.nav ul li {
  background-color: #b6ff00;
}
<ul class="nav">
  <li><a>Test link</a></li>
  <li><a>Test link</a></li>
  <li><a>Test link</a></li>
  <li>
    <a class="dropdown-toggle">Test dropdown</a>
    <ul class="nav dropdown">
      <li><a>Test link</a></li>
      <li><a>Test link</a></li>
      <li><a>Test link</a></li>
      <li>
        <a class="dropdown-toggle">Test dropdown</a>
        <ul class="nav dropdown">
          <li><a>Test link</a></li>
          <li><a>Test link</a></li>
          <li><a>Test link</a></li>
        </ul>
      </li>
    </ul>
  </li>
</ul>
m4n0
  • 29,823
  • 27
  • 76
  • 89
Tyler Sells
  • 463
  • 4
  • 16
  • 3
    Your requirements aren't entirely clear. A submenu must have a different color than its parent, but the next level submenu must have the same color as its grandparent. That means you only ever use two colors: menu level 1 and menu level 3 must have the same color, so must 2 and 4, and by the previous definition 5 must have the same as 3 (its grandparent) which is the same as 1, 6 the same as 4, and so on. If you meant to continually change colors after the first repeat, well then practically speaking you must limit the number of submenus or you will run out of colors to use. – skyline3000 Sep 16 '19 at 04:33
  • Could you provide an image showing what outcome you are after to avoid any misunderstanding of your requirements please? – Hidden Hobbes Sep 16 '19 at 08:51
  • @skyline3000 i believe your understanding of the question is correct. Using only two colors is an acceptable solution. I will try to update the question as time permits today to better reflect that. – Tyler Sells Sep 16 '19 at 11:43

3 Answers3

6

Here is an idea based on my previous answer where I will rely on a recursive behavior of CSS variables and gradient coloration. The trick is to increment the variable on each level and at the same time move the background and we will alternate between both colors:

:root {
 --x:0;
}

ul.nav {
  list-style: none;
  position: relative;
  border:1px solid;
  background:
    linear-gradient(yellow 50%,pink 0) 0 calc(var(--x)*100%)/100% 200%;
  --y: calc(var(--x) + 1);
}

ul.nav li {
  --x: calc(var(--y));
}
<ul class="nav">
  <li><a>Test link</a></li>
  <li><a>Test link</a></li>
  <li><a>Test link</a></li>
  <li>
    <a class="dropdown-toggle">Test dropdown</a>
    <ul class="nav dropdown">
      <li>
        <a class="dropdown-toggle">Test dropdown</a>
        <ul class="nav dropdown">
          <li><a>Test link</a></li>
          <li><a>Test link</a></li>
          <li>
            <a class="dropdown-toggle">Test dropdown</a>
            <ul class="nav dropdown">
              <li><a>Test link</a></li>
              <li><a>Test link</a></li>
              <li><a>Test link</a></li>
            </ul>
          </li>
        </ul>
      </li>
      <li><a>Test link</a></li>
      <li><a>Test link</a></li>
      <li>
        <a class="dropdown-toggle">Test dropdown</a>
        <ul class="nav dropdown">
          <li><a>Test link</a></li>
          <li><a>Test link</a></li>
          <li><a>Test link</a></li>
        </ul>
      </li>
    </ul>
  </li>
</ul>

You can also do with 3 colors

:root {
 --x:0;
}

ul.nav {
  list-style: none;
  position: relative;
  border:1px solid;
  background:
    linear-gradient(lightblue 33.33%,yellow 33.33% 66.66%,pink 0) 0 calc(var(--x)*100%)/100% 300%;
  --y: calc(var(--x) + 1);
}

ul.nav li {
  --x: calc(var(--y));
}
<ul class="nav">
  <li><a>Test link</a></li>
  <li><a>Test link</a></li>
  <li><a>Test link</a></li>
  <li>
    <a class="dropdown-toggle">Test dropdown</a>
    <ul class="nav dropdown">
      <li>
        <a class="dropdown-toggle">Test dropdown</a>
        <ul class="nav dropdown">
          <li><a>Test link</a></li>
          <li><a>Test link</a></li>
          <li>
            <a class="dropdown-toggle">Test dropdown</a>
            <ul class="nav dropdown">
              <li><a>Test link</a></li>
              <li><a>Test link</a></li>
              <li><a>Test link</a></li>
            </ul>
          </li>
        </ul>
      </li>
      <li><a>Test link</a></li>
      <li><a>Test link</a></li>
      <li>
        <a class="dropdown-toggle">Test dropdown</a>
        <ul class="nav dropdown">
          <li><a>Test link</a></li>
          <li><a>Test link</a></li>
          <li><a>Test link</a></li>
        </ul>
      </li>
    </ul>
  </li>
</ul>

You can easily increase to any number of colors. Simply make the background size to be n*100% with n number of colors and split the color inside the gradient so each one will have the same proportion 100%/n


Related question to understand the calculation behind the background values:

Using percentage values with background-position on a linear gradient

Temani Afif
  • 245,468
  • 26
  • 309
  • 415
  • 1
    This is almost exactly what I was looking for and is most likely to be the answer I accept. However, I think it's worth noting that this isn't supported in Internet Explorer. I did not state that requirement in my question, and I will not fault you for it. – Tyler Sells Sep 16 '19 at 15:00
3

Though my answer is very obvious I've used classnames,

          .oddNav{
            background: red;
          }
          .evenNav{
            background: blue;
          }
        <ul class="nav1 oddNav">
          <li>firstlist</li>
          <li>
            <ul class="nav2 evenNav">
              <li>SecondList</li>
              <li>
                <ul class="nav3 oddNav">
                  <li>ThirdList</li>
                  <li>
                    <ul class="nav4 evenNav">
                      <li>FourthList</li>
                    </ul>
                  </li>
                </ul>
              </li>
            </ul>
          </li>
        </ul>
Ankit
  • 181
  • 2
  • 15
  • 3
    this is a valid approach and what I may end up settling on if no further solutions are presented. Ultimately, If I went with this solution, I would use javascript to apply those classes so that I don't have to keep track of which menu is which while writing the code. – Tyler Sells Sep 16 '19 at 11:47
0

Try like this. Here I have used > hover method for drop-down.

ul.nav {
  list-style: none;
}

ul.nav.dropdown {
  opacity: 0;
  visibility: hidden;
  overflow: hidden;
  display: none;
}

ul.nav.dropdown.show {
  height: auto;
}

ul.nav li {
  background-color: #44499f;
}

ul.nav ul li {
  background-color: #b6ff00;
}

li.drop-down:hover>.nav.dropdown {
  opacity: 1;
  visibility: visible;
  display: block;
}
<ul class="nav">
  <li><a>Test link</a></li>
  <li><a>Test link</a></li>
  <li><a>Test link</a></li>
  <li class="drop-down">
    <a class="dropdown-toggle">Test dropdown</a>
    <ul class="nav dropdown">
      <li><a>Test link</a></li>
      <li><a>Test link</a></li>
      <li><a>Test link</a></li>
      <li class="drop-down">
        <a class="dropdown-toggle">Test dropdown</a>
        <ul class="nav dropdown">
          <li><a>Test link</a></li>
          <li><a>Test link</a></li>
          <li><a>Test link</a></li>
        </ul>
      </li>
    </ul>
  </li>
</ul>
m4n0
  • 29,823
  • 27
  • 76
  • 89
Ranjith v
  • 1,032
  • 5
  • 15
  • Thanks for your input Ranjith. Unfortunately, this is a little different than what I was asking about. I'm already using JS to append the `.show` class to the menus when they should be toggled. I'm really looking more for a way to make the background colors of each submenu different so they don't blend in with their parent menus, but all the way down indefinitely rather than hard-coding for each level of menu. – Tyler Sells Sep 14 '19 at 12:35