CSS checkboxes & radio buttons when input is inside label?
Asked Answered
D

6

41

I've searched extensively on here for an answer to this but haven't quite come across what I'm looking for. Found this CSS - How to Style a Selected Radio Buttons Label? but the answer involved changing the markup, and that's something I want to avoid which I'll explain below.

I'm trying to add custom css3 checkboxes and radio buttons to my XenForo forum theme, and all the tutorials I've read on this have the label after the input:

<input type="checkbox" id="c1" name="cc" />
<label for="c1">Check Box 1</label>

However in XenForo's markup the input is inside the label:

<label for="ctrl_enable_rte">
<input type="checkbox" name="enable_rte" value="1" id="ctrl_enable_rte" {xen:checked "{$visitor.enable_rte}"} />
{xen:phrase use_rich_text_editor_to_create_and_edit_messages}
</label>

I looked into why this is, but didn't find anything related to custom css checkboxes/radio buttons when the markup is this way. I want to avoid having to change the markup, because I'm dealing with a forum system here, and that would involve editing a bunch of core templates... which I'd have to then update the markup on each time the forum software put out an update (presuming the templates changed).

I've tried to create CSS rules that would work with this type of markup, but thus far I've been unsuccessful. I'm not too experienced with CSS, don't have every selector memorized, and pseudo classes are still pretty foreign to me, so I was hoping someone here could shed some light on if this will be possible, and if so, how I might go about it. I already have my sprite ready to go, I just need to figure the CSS out.

My main issue is I can't figure out how to select the label (only the labels involved with these inputs of course). Usually something like

input[type="checkbox"] + label

is used, but when the label isn't after the input and instead outside/before it... how do I select it?

Danidania answered 9/11, 2012 at 22:29 Comment(3)
Instead of selecting the label for the checked input (which I believe cannot be done in this case with CSS only as a "parent" selector does not exist), there may be workarounds with CSS. What specifically did you want to happen?Avie
@WesleyMurch I'm attempting to customize all of the checkboxes/radio buttons by adding a sprite image with the background-position defined for each different state (checked, hover, focus, active, disabled). I'm not sure what I could select to achieve this, all the tutorials regarding custom css3 checkboxes and the like had the label after the input and the CSS reflected that specifically. Sorry if I'm not answering your question accurately or with enough detail.Danidania
Your response is clear but your restrictions are not completely. So aside from the requirement to keep inputs inside the label, are you able to edit the HTML? For example could you add an empty <span> after each checkbox, or wrap the adjacent content in one? If not, I'm afraid you are stuck using javascript (and that's not really a bad thing).Avie
A
18

If you can edit the markup to wrap the actual label text, for example:

<label>
    <input type="checkbox">
    <span>One</span>
</label>
<label>
    <input type="checkbox">
    <span>Two</span>
</label>
<label>
    <input type="checkbox" disabled>
    <span>Three</span>
</label>

You can pull off some tricks with CSS:

label {
    position:relative;   
    cursor:pointer;
}
label [type="checkbox"] {
    display:none; /* use your custom sprites instead as background on the span */
}
[type="checkbox"] + span {
    display:inline-block;
    padding:1em;
}
:checked + span {
    background:#0f0;
}
[type="checkbox"][disabled] + span {
    background:#f00;  
}​

Demo:

label {
  position: relative;
  cursor: pointer;
}

label [type="checkbox"] {
  display: none;
}

[type="checkbox"]+span {
  display: inline-block;
  padding: 1em;
}

:checked+span {
  background: #0f0;
  display: inline-block;
}

[type="checkbox"][disabled]+span {
  background: #f00;
}
<label>
    <input type="checkbox">
    <span>One</span>
</label>
<label>
    <input type="checkbox">
    <span>Two</span>
</label>
<label>
    <input type="checkbox" disabled>
    <span>Three</span>
</label>

http://jsfiddle.net/dMhDM/1/

Keep in mind that this will fail if the browser doesn't support :checked.

This is basically the same as the other solution, but the stying is done on the span rather than the label.

Avie answered 9/11, 2012 at 22:56 Comment(5)
While this would involve editing the HTML which I wanted to avoid, it's a method that might be possible with some template mods using regex which would a lot better and seamless during forum updates. The only downside being I'd still have to modify 73 templates and any new templates from future updates or addons. Perhaps I should give up on the pure CSS solution and instead look into making JS work perfectly in all instances. Thank you for taking the time to respond and write up an example, really appreciate it. I'm sure it will help others who aren't in as particular of a situation as myself.Danidania
Or you could simply modify whatever it is that {xen:phrase ...} outputs to wrap it in a span. I'd go with js personally, but my company supports IE7 as a rule - I'm jealous of those who get to actually use CSS3 on a day to day basis.Avie
Ah yeah, didn't think of that. Definitely an alternate possibility though it might cause some funkiness in some areas. Definitely seems like JS is the only way to go here... and it would add in the extra browser compatibility that CSS would lack. I just need to figure out a good JS solution that works for my situation. Thanks again.Danidania
Simple js solution would be adding a class to the wrapper label when the checkbox is checked or unchecked.Avie
I know this is old, but, if you're following this example, consider using a visually-hidden on the input as display: none removes it from the DOM flow, therefore disabling keyboard controls and making it less accessible.Divinity
G
5

IMHO best way to do this without messing up with html by PURE CSS is

 input[type="checkbox"] {
     position: relative;
     cursor: pointer;
     padding: 0;
     margin-right: 10px;
}
 input[type="checkbox"]:before {
     content: '';
     margin-right: 10px;
     display: inline-block;
     margin-top: -2px;
     width: 20px;
     height: 20px;
     background: #fcfcfc;
     border: 1px solid #aaa;
     border-radius: 2px;
}
 input[type="checkbox"]:hover:before {
     background: #f5821f;
}
 input[type="checkbox"]:focus:before {
     box-shadow: 0 0 0 3px rgba(0, 0, 0, 0.12);
}
 input[type="checkbox"]:checked:before {
     background: #f5821f;
     border-color: #f5821f;
}
 input[type="checkbox"]:disabled {
     color: #b8b8b8;
     cursor: auto;
}
 input[type="checkbox"]:disabled:before {
     box-shadow: none;
     background: #ddd;
}
 input[type="checkbox"]:checked:after {
     content: '';
     position: absolute;
     left: 5px;
     top: 8px;
     background: white;
     width: 2px;
     height: 2px;
     box-shadow: 2px 0 0 white, 4px 0 0 white, 4px -2px 0 white, 4px -4px 0 white, 4px -6px 0 white, 4px -8px 0 white;
     transform: rotate(45deg);
}
Girlfriend answered 31/12, 2018 at 18:19 Comment(2)
brilliant! jsfiddle.net/ozv467na this doesn't require any special html label <-> input hierarchyBi
This does not work in Firefox. input[type="checkbox"] has no children in Firefox, so ::before and ::after cannot exist. This works in Chrome, but is based on the point that the W3 specs leave it open whether a self-closing tag can have children.Etherify
B
4

Unfortunately CSS doesn't allow the styling of parent elements based on child elements which would be the easy way example:

div.a < div { border: solid 3px red; }

The opposite of selecting a child based on parent:

div.a > div { border: solid 3px red; }

What you are wanting to do can be achieved using jQuery.

Check out this post.

Bundestag answered 9/11, 2012 at 22:53 Comment(1)
Interesting, I'll look into this. After reviewing other answers it seems JS will indeed be the key here and as Wesley Murch said I might be able to pull it off by simply adding a class to the wrapper label. Thanks!Danidania
V
1

Very interesting question. Truth be told, I'm pretty sure that's just not possible with CSS alone.

If there's some sort of pattern to the label's for attribute (ctrl_enable_rte), there's hope for you. For example, if all checkbox labels end with rte, you could use

label[for$=rte] { ... }

If there is no such pattern, and the checkbox IDs are chosen arbitrarily, you'll have to resort to JavaScript.

Vow answered 9/11, 2012 at 22:53 Comment(2)
Interesting, I'll have to look into this and see if it's a possible solution, thanks! Edit: Seems "ctrl_" is used everywhere checkbox inputs are, however it's also used on labels that don't have checkboxes. Guess this won't work after all and JS is looking more and more like the only outlet here.Danidania
I don’t see how this could be used to style the label depending on the checked state of a control. The state is dynamix, and the for attribute is static.Plaintiff
U
1

I had this problem recently and you can use the :has property in css.

label {
cursor: pointer;
}

label > input {
  display: none;
}

label:has(#ctrl_enable_rte:checked) {
  color: red;
  border: 1px solid red;
}
<label for="ctrl_enable_rte"><input type="checkbox" name="enable_rte" value="1" id="ctrl_enable_rte" />
    Test
 </label>

    
Underpart answered 23/10, 2023 at 15:20 Comment(0)
L
0

I was also facing the same issue and have figured out the solution to this issue.

So, we can select the parent label to our input according to the question is by using :has() CSS pseudo-class.

label:has(input[type="checkbox"]) {
  font-weight: bold;
}
<label>
  <input type="checkbox" />
  Label
</label>

This is a very new selector in CSS. So, please, check https://caniuse.com/css-has

Lycanthropy answered 20/12, 2022 at 5:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.