skip to Main Content

A tree structure is simulated with a sequence of sibling divs with varying left indent:

.l2 { padding-left: 1em; }
.l3 { padding-left: 2em; }
...

<div class="a"><div class="l1">A1</div></div>
<div class="a"><div class="l2">A2</div></div>
...

Using just CSS, is it possible to mark only siblings with lower level than the hovered item, but not those beyond the next item of the same level?

The snippet below illustrates the idea, but over-selects beyond the current nesting level. Couldn’t figure out how to not select items that have an item of the same level as the hovered item closer than the one being hovered that is not being hovered over.

Explicitly:

  • Hover over A1 should light up the border for all items marked A, but not B.
  • Hover over second A2 should light up only the border of items A3, but not B.
  • Hover over B1 lights up borders for B2 and B3.
  • Hover over B2 lights up the border of B3, and so on.

Is this possible with CSS only? Modern CSS is just fine, no backward compatibility needed.

Note that no nested structure is possible as this is part of a rather large virtual list and it is not known in advance how many sub-levels there are, so the only solution is to render the sibling divs and mark their indent. The actual implementation has many more levels, but it’s not a problem to generate appropriate CSS selector combinations if a solution exits.

Also no js is allowed; only CSS if possible.

* { 
font-family: sans-serif;
border-left: 1px solid transparent; 
--l1-border: 1px solid purple; 
--l2-border: 1px solid blue;
}

.a { width: 150px; padding: 0 3px; }
.a:hover { background-color: #eee; }

.a:has(.l1):has(+ .a .l2):hover ~ .a .l2 {
  border-left: var(--l1-border);
}
.a:has(.l1):has(+ .a .l2):hover ~ .a .l3 {
  margin-left: 0;
  padding-left: 2em;
  border-left: var(--l1-border);
}

.a:has(.l2):has(+ .a .l3):hover ~ .a .l3 {
  border-left: var(--l2-border);
}

.l2 { padding-left: 1em; }
.l3 { padding-left: 1em; margin-left: 1em; }
<div class="a"><div class="l1">A1</div></div>
<div class="a"><div class="l2">A2</div></div>
<div class="a"><div class="l2">A2</div></div>
<div class="a"><div class="l3">A3</div></div>
<div class="a"><div class="l3">A3</div></div>
<div class="a"><div class="l2">A2</div></div>
<div class="a"><div class="l1">B1</div></div>
<div class="a"><div class="l2">B2</div></div>
<div class="a"><div class="l3">B3</div></div>

2

Answers


  1. Interesting task! Are you happy with your current approach in which you manually specify styles for each level? So far I have come up with an idea to add one more hover that targets next siblings of the same level and removes the border for all elements after them. Let me know what you think about this and maybe I will be able to find another solution.

    * {
      font-family: sans-serif;
      border-left: 1px solid transparent;
      --l1-border: 1px solid purple;
      --l2-border: 1px solid blue;
    }
    
    .a {
      width: 150px;
      padding: 0 3px;
    }
    
    .a:hover {
      background-color: #eee;
    }
    
    .a:has(.l1):has(+ .a .l2):hover~.a .l2 {
      border-left: var(--l1-border);
    }
    
    .a:has(.l1):has(+ .a .l2):hover~.a .l3 {
      margin-left: 0;
      padding-left: 2em;
      border-left: var(--l1-border);
    }
    
    .a:has(.l1):has(+ .a .l2):hover~.a:has(.l1)~.a * {
      border-left: none;
    }
    
    .a:has(.l2):has(+ .a .l3):hover~.a .l3 {
      border-left: var(--l2-border);
    }
    
    .a:has(.l2):has(+ .a .l3):hover~.a:has(.l2)~.a * {
      border-left: none;
    }
    
    .l2 {
      padding-left: 1em;
    }
    
    .l3 {
      padding-left: 1em;
      margin-left: 1em;
    }
    <div class="a">
      <div class="l1">A1</div>
    </div>
    <div class="a">
      <div class="l2">A2</div>
    </div>
    <div class="a">
      <div class="l2">A2</div>
    </div>
    <div class="a">
      <div class="l3">A3</div>
    </div>
    <div class="a">
      <div class="l3">A3</div>
    </div>
    <div class="a">
      <div class="l2">A2</div>
    </div>
    <div class="a">
      <div class="l1">B1</div>
    </div>
    <div class="a">
      <div class="l2">B2</div>
    </div>
    <div class="a">
      <div class="l3">B3</div>
    </div>
    Login or Signup to reply.
  2. This can be done with combinations of :not and :has by setting the border for all those following the hovered element and then unsetting it for those at a ‘higher’ level or following one at a higher level further down.

    <style>
      * {
        font-family: sans-serif;
        border-left: 1px solid transparent;
        --l1-border: 1px solid purple;
        --l2-border: 1px solid blue;
      }
      
      .a {
        width: 150px;
        padding: 0 3px;
      }
      
      .a:hover {
        background-color: #eee;
      }
      
      .l2 {
        padding-left: 1em;
      }
      
      .l3 {
        padding-left: 1em;
        margin-left: 1em;
      }
      
      .a:has(.l1:hover)~.a:not(:has(.l1)) {
        rbackground: lime;
        border-left: var(--l1-border);
      }
      
      .a:has(.l1:hover)~.a:has(.l1)~* {
        rbackground: transparent;
        border-left: none;
      }
      
      .a:has(.l2:hover)~.a:not(:has(.l2)) {
        border-left: var(--l2-border);
      }
      
      .a:has(.l2:hover)~.a:has(.l1),
      .a:has(.l2:hover)~.a:has(.l1)~*,
      .a:has(.l2:hover)~.a:has(.l2)~* {
        border-left: none;
      }
    </style>
    <div class="a">
      <div class="l1">A1</div>
    </div>
    <div class="a">
      <div class="l2">A2</div>
    </div>
    <div class="a">
      <div class="l2">A2</div>
    </div>
    <div class="a">
      <div class="l3">A3</div>
    </div>
    <div class="a">
      <div class="l3">A3</div>
    </div>
    <div class="a">
      <div class="l2">A2</div>
    </div>
    <div class="a">
      <div class="l1">B1</div>
    </div>
    <div class="a">
      <div class="l2">B2</div>
    </div>
    <div class="a">
      <div class="l3">B3</div>
    </div>

    But it rapidly has the need to generate long selectors and an upper bound of the number of levels would need to be known as currently in CSS it is not possible to use variables or calculations in selectors.

    i.e. it is likely to be impractical in a real-life situation.

    A fun exercise but I don’t recommend it’s use in reality!

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search