35

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?

Community
  • 1
  • 1
Bob Loblaw
  • 351
  • 1
  • 3
  • 4
  • 1
    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? – Wesley Murch Nov 09 '12 at 22:36
  • @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. – Bob Loblaw Nov 09 '12 at 22:43
  • 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 `` 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). – Wesley Murch Nov 09 '12 at 22:44

4 Answers4

15

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: 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.

Wesley Murch
  • 95,417
  • 36
  • 177
  • 220
  • 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. – Bob Loblaw Nov 10 '12 at 00:34
  • 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. – Wesley Murch Nov 10 '12 at 00:37
  • 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. – Bob Loblaw Nov 10 '12 at 00:42
  • Simple js solution would be adding a class to the wrapper label when the checkbox is checked or unchecked. – Wesley Murch Nov 10 '12 at 00:44
  • 2
    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. – 3stacks Mar 31 '17 at 03:00
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);
}
  • 1
    brilliant! http://jsfiddle.net/ozv467na/ this doesn't require any special html label input hierarchy – max4ever Oct 29 '19 at 13:31
  • 2
    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. – orithena Jul 07 '20 at 09:36
3

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.

Community
  • 1
  • 1
agit
  • 141
  • 13
  • 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! – Bob Loblaw Nov 10 '12 at 01:06
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.

Madara's Ghost
  • 158,961
  • 49
  • 244
  • 292
  • 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. – Bob Loblaw Nov 09 '12 at 23:09
  • 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. – Jukka K. Korpela Nov 10 '12 at 06:34