How to style the parent label of a checked radio input

A possibility

At my time of posting, I am not exactly sure what the desired layout should be, but there is one specific problem in the attempted CSS that needs to be addressed.

The adjacent siblings selector:

... separates two selectors and matches the second element only if it immediately follows the first element.

If the <input> is a child of the <label>, it isn't adjacent, so while:

label.radio input[type="radio"]:checked + label

is looking for a label immediately following a :checked input inside a label with the class .radio, nothing like that exists.

To alter the styling of the label in this case, would require a selector that affected the parent, which currently isn't possible.

So, to select the label of the :checked input, we need the label to be adjacent, not the parent.

We can use the for="id" attribute:

A <label> can be associated with a control either by placing the control element inside the <label> element, or by using the for attribute.

As I said, I'm not exactly sure what the desired layout should be, but here's an example using the for attribute, that doesn't look too bad.

div {
  display: inline-block;
  position: relative;
}
label {
  background: #fcb608;
  padding: 2px 10px 2px 1.5em;
  border: 1px solid transparent; /* keeps layout from jumping */
}
input {
  position: absolute;
}
input[type="radio"]:checked + label {
  background: #000;
  border-color: green;
  color: white;
}
<div>
  <input id="id1" type="radio" name="ad_caroserie" value="0">
  <label for="id1" class="radio">Berlina</label>
</div>
<div>
  <input id="id2" type="radio" name="ad_caroserie" value="1">
  <label for="id2" class="radio">Break</label>
</div>
<div>
  <input id="id3" type="radio" name="ad_caroserie" value="2">
  <label for="id3" class="radio">Cabrio</label>
</div>

With <input> as a child of <label>

Using a small JavaScript handler listening for changes to the <form>.

  • If a change is detected, the triggered function checks if an <input type="radio"> was changed, and if so, if it has a <label> as its parentElement.
  • If that's true, it checks to see if there's an identically named <input type="radio"> that's a child of a <label> element with the class .checked.
  • If there is, it removes the class from the <label> before applying the same class to the <label> parent of the <input> target that triggered the whole thing.

let form = document.querySelector( "form" );

form.addEventListener( "change", ( evt ) => {
  let trg = evt.target,
      trg_par = trg.parentElement;
  
  if ( trg.type === "radio" && trg_par &&
       trg_par.tagName.toLowerCase() === "label" ) {
    
    let prior = form.querySelector( 'label.checked input[name="' +
                                    trg.name + '"]' );
    
    if ( prior ) {
      prior.parentElement.classList.remove( "checked" );
    }
    
    trg_par.classList.add( "checked" );
    
  }
}, false );
label {
  background: #fcb608;
  padding: 2px 10px 2px 0;
  border: 1px solid transparent; /* keeps layout from jumping */
}
label.checked {
  background: #000;
  border-color: green;
  color: white;
}
<form>
  <label class="radio"><input type="radio" name="ad_caroserie" value="0">Berlina</label>
  <label class="radio"><input type="radio" name="ad_caroserie" value="1">Break</label>
  <label class="radio"><input type="radio" name="ad_caroserie" value="2">Cabrio</label>
</form>

Without JavaScript things get difficult (per my original explanation of why it's best to use the for attribute in this case).

We can use the appearance property (with prefixes and reasonable support) to effectively hide the user-agent radio GUI, then use the remaining faceless element to build a fake background for the <label>.

This is very hacky and a great deal less dynamic than the default, since some absolute positioning and specific dimensions are required to pull it off.
It kind of works (in most browsers), but is tricky to enforce sitewide.

Something to play around with though :-)

input {
  position: absolute;
  -webkit-appearance: none;
  -moz-appearance: none;
  appearance: none;
  width: 5em;
  height: 1.5em;
  z-index: -1;
  background: #fcb608;
  border: 1px solid transparent;
  margin: -.1em -.8em;
  outline: 0;
}
label {
  display: inline-block;
  width: 5em;
  color: white;
  text-shadow: 1px 1px 0px black;
}
input[type="radio"]:checked {
  background: #000;
  border-color: green;
}
<label class="radio"><input type="radio" name="ad_caroserie" value="0">Berlina</label>
<label class="radio"><input type="radio" name="ad_caroserie" value="1">Break</label>
<label class="radio"><input type="radio" name="ad_caroserie" value="2">Cabrio</label>


Just use jQuery with a new css class "selected" something like this:

on start:

$("input[name='ad_caroserie']:checked").parent().addClass("selected");

and onchange:

$('input[type=radio][name=ad_caroserie]').change(function() {
  $("input[name='ad_caroserie']").parent().removeClass("selected");
  $("input[name='ad_caroserie']:checked").parent().addClass("selected");
  // console.log($("input[name='ad_caroserie']:checked").val());
});