htmlcssselectorstyling

CSS Sibling Combinator Not Showing Label on Checkbox Check Within <details> Element


The timer label doesn't show up after I checked the countdown, here's the HTML structure and the CSS:

details {
  display: flex;
  /* Use flexbox layout */
  flex-direction: column;
  /* Stack children vertically */
  width: 100%;
  /* Full width of the container */
  font-size: 0.8em;
  /* Smaller font size */
  column-gap: 5px;
  /* Space between flex items */
}

summary {
  margin-bottom: 10px;
}

label {
  display: flex;
  align-items: center;
  gap: 5px;
  margin-bottom: 10px;
}


/* More options section */

#timer {
  width: 4ch;
  text-align: center;
  outline: none;
}

label[for='timer'] {
  display: none;
}

input[name='countdown']:checked~label[for='timer'] {
  display: flex;
}


/* end of options section */
<details>
  <summary>More options...</summary>
  <label for="blip">
    <input type="checkbox" name="blipping" id="blip"> Blipping
  </label>
  <label for="countdown">
<input type="checkbox" name="countdown" id="countdown"> Countdown
</label>
  <label for="timer">
Countdown: <input type="number" name="timer" id="timer" min="5" max="999"> Second
</label>
</details>

I've tried covering the label with a div, but the sibling combinator still didn't detect it, and still gives the same result.

The point of the question is that I want to toggle the "timer" label when user checks/unchecks the "countdown" checkbox with using only CSS.


Solution

  • The problem you're encountering is that the sibling combinator can't work the way you've written the code, given the following HTML (a lightly formatted/tidied copy of your posted HTML):

    <label for="countdown">
      <input type="checkbox" name="countdown" id="countdown"> Countdown
    </label>
    <label for="timer">
      Countdown: <input type="number" name="timer" id="timer" min="5" max="999"> Second
    </label>
    

    The selector you're working with:

    input[name='countdown']:checked~label[for='timer'] {
      display: flex;
    }
    

    requires that the checked "countdown" <input> is a preceding sibling of the <label> element, which it isn't; the <label> is the adjacent sibling of the parent of the "countdown" <input> element.

    So, instead – depending on the browser compatibility of your user-base – you could make use of the :has() selector, and use:

    /*
      here we initially select the <label> element with the "for"
      attribute equal to "countdown", which contains a checked <input>
      element and then select its following sibling <label> element
      (using the same general sibling combinator) that has a "for"
      attribute equal to "timer":
    */
    label[for=countdown]:has(input:checked) ~ label[for=timer] {
      /* I'm using display: initial to update the display, that way
         the browser can decide for itself to display it as block,
         flex-item, grid-item, list-item... according to the CSS
         cascade: */
      display: initial;
    }
    

    details {
      display: flex;
      /* Use flexbox layout */
      flex-direction: column;
      /* Stack children vertically */
      width: 100%;
      /* Full width of the container */
      font-size: 0.8em;
      /* Smaller font size */
      column-gap: 5px;
      /* Space between flex items */
    }
    
    summary {
      margin-bottom: 10px;
    }
    
    label {
      display: flex;
      align-items: center;
      gap: 5px;
      margin-bottom: 10px;
    }
    
    
    /* More options section */
    
    #timer {
      width: 4ch;
      text-align: center;
      outline: none;
    }
    
    label[for='timer'] {
      display: none;
    }
    
    label[for=countdown]:has(input:checked)~label[for=timer] {
      display: initial;
    }
    
    
    /* end of options section */
    <details>
      <summary>More options...</summary>
      <label for="blip">
        <input type="checkbox" name="blipping" id="blip"> Blipping
      </label>
      <label for="countdown">
        <input type="checkbox" name="countdown" id="countdown"> Countdown
      </label>
      <label for="timer">
        Countdown: <input type="number" name="timer" id="timer" min="5" max="999"> Second
      </label>
    </details>

    References: