How to align checkboxes and their labels consistently cross-browsers

Viewed 1161937

This is one of the minor CSS problems that plague me constantly.

How do folks around Stack Overflow vertically align checkboxes and their labels consistently cross-browser?

Whenever I align them correctly in Safari (usually using vertical-align: baseline on the input), they're completely off in Firefox and IE.

Fix it in Firefox, and Safari and IE are inevitably messed up. I waste time on this every time I code a form.

Here's the standard code that I work with:

<form>
    <div>
        <label><input type="checkbox" /> Label text</label>
    </div>
</form>

I usually use Eric Meyer's reset, so form elements are relatively clean of overrides. Looking forward to any tips or tricks that you have to offer!

40 Answers

Warning! This answer is too old and doesn't work on modern browsers.

I'm not the poster of this answer, but at the time of writing this, this is the most voted answer by far in both positive and negative votes (+1035 -17), and it's still marked as accepted answer (probably because the original poster of the question is the one who wrote this answer).

As already noted many times in the comments, this answer does not work on most browsers anymore (and seems to be failing to do that since 2013).


After over an hour of tweaking, testing, and trying different styles of markup, I think I may have a decent solution. The requirements for this particular project were:

  1. Inputs must be on their own line.
  2. Checkbox inputs need to align vertically with the label text similarly (if not identically) across all browsers.
  3. If the label text wraps, it needs to be indented (so no wrapping down underneath the checkbox).

Before I get into any explanation, I'll just give you the code:

label {
  display: block;
  padding-left: 15px;
  text-indent: -15px;
}
input {
  width: 13px;
  height: 13px;
  padding: 0;
  margin:0;
  vertical-align: bottom;
  position: relative;
  top: -1px;
  *overflow: hidden;
}
<form>
  <div>
    <label><input type="checkbox" /> Label text</label>
  </div>
</form>

Here is the working example in JSFiddle.

This code assumes that you're using a reset like Eric Meyer's that doesn't override form input margins and padding (hence putting margin and padding resets in the input CSS). Obviously in a live environment you'll probably be nesting/overriding stuff to support other input elements, but I wanted to keep things simple.

Things to note:

  • The *overflow declaration is an inline IE hack (the star-property hack). Both IE 6 and 7 will notice it, but Safari and Firefox will properly ignore it. I think it might be valid CSS, but you're still better off with conditional comments; just used it for simplicity.
  • As best I can tell, the only vertical-align statement that was consistent across browsers was vertical-align: bottom. Setting this and then relatively positioning upwards behaved almost identically in Safari, Firefox and IE with only a pixel or two of discrepancy.
  • The major problem in working with alignment is that IE sticks a bunch of mysterious space around input elements. It isn't padding or margin, and it's damned persistent. Setting a width and height on the checkbox and then overflow: hidden for some reason cuts off the extra space and allows IE's positioning to act very similarly to Safari and Firefox.
  • Depending on your text sizing, you'll no doubt need to adjust the relative positioning, width, height, and so forth to get things looking right.

Hope this helps someone else! I haven't tried this specific technique on any projects other than the one I was working on this morning, so definitely pipe up if you find something that works more consistently.


Warning! This answer is too old and doesn't work on modern browsers.

Sometimes vertical-align needs two inline (span, label, input, etc...) elements next to each other to work properly. The following checkboxes are properly vertically centered in IE, Safari, FF, and Chrome, even if the text size is very small or large.

They all float next to each other on the same line, but the nowrap means that the whole label text always stays next to the checkbox.

The downside is the extra meaningless SPAN tags.

.checkboxes label {
  display: inline-block;
  padding-right: 10px;
  white-space: nowrap;
}
.checkboxes input {
  vertical-align: middle;
}
.checkboxes label span {
  vertical-align: middle;
}
<form>
  <div class="checkboxes">
    <label><input type="checkbox"> <span>Label text x</span></label>
    <label><input type="checkbox"> <span>Label text y</span></label>
    <label><input type="checkbox"> <span>Label text z</span></label>
  </div>
</form>

Now, if you had a very long label text that needed to wrap without wrapping under the checkbox, you'd use padding and negative text indent on the label elements:

.checkboxes label {
  display: block;
  padding-right: 10px;
  padding-left: 22px;
  text-indent: -22px;
}
.checkboxes input {
  vertical-align: middle;
}
.checkboxes label span {
  vertical-align: middle;
}
<form>
  <div class="checkboxes">
    <label><input type="checkbox"> <span>Label text x so long that it will probably wrap so let's see how it goes with the proposed CSS (expected: two lines are aligned nicely)</span></label>
    <label><input type="checkbox"> <span>Label text y</span></label>
    <label><input type="checkbox"> <span>Label text z</span></label>
  </div>
</form>

Working off of One Crayon's solution, I have something that works for me and is simpler:

.font2 {font-family:Arial; font-size:32px} /* Sample font */

input[type=checkbox], input[type=radio] {
  vertical-align: middle;
  position: relative;
  bottom: 1px;
}

input[type=radio] { 
  bottom: 2px; 
} 
<label><input type="checkbox" /> Label text</label>
<p class="font2">
  <label><input type="checkbox"/> Label text</label>
</p>

Renders pixel-for-pixel the same in Safari (whose baseline I trust) and both Firefox and IE7 check out as good. It also works for various label font sizes, big and small. Now, for fixing IE's baseline on selects and inputs...


Update: (Third-Party Edit)

The proper bottom position depends on font-family and font-size! I found using bottom: .08em; for checkbox & radio elements is a good general value. I tested it in Chrome/Firefox/IE11 in windows with Arial & Calibri fonts using several small/mid/large font-sizes.

.font2, .font2 input {font-family:Arial; font-size:32px} /* Sample font */

input[type=checkbox], input[type=radio] {
  vertical-align: middle; 
  position: relative;
  bottom: .08em; /* this is a better value for different fonts! */
}
<label><input type="checkbox" /> Label text</label> 

<p class="font2">
  <label><input type="checkbox"/> Label text</label>
</p>

try vertical-align: middle

also your code seems like it should be:

<form>
    <div>
        <input id="blah" type="checkbox"><label for="blah">Label text</label>
    </div>
</form>

Try my solution, I tried it in IE 6, FF2 and Chrome and it renders pixel by pixel in all the three browsers.

* {
  padding: 0px;
  margin: 0px;
}
#wb {
  width: 15px;
  height: 15px;
  float: left;
}
#somelabel {
  float: left;
  padding-left: 3px;
}
<div>
  <input id="wb" type="checkbox" />
  <label for="wb" id="somelabel">Web Browser</label>
</div>

I usually use line height in order to adjust the vertical position of my static text:

label {
  line-height: 18px;
}
input {
  width: 13px;
  height: 18px;
  font-size: 12px;
  line-height: 12px;
}
<form>
  <div>
    <label><input type="checkbox" /> Label text</label>
  </div>
</form>

Hope that helps.

Let's finally take a look at the source of the problem

The checkboxes are rendered using images (one may set custom ones via CSS). Here is an (unchecked) checkbox in FireFox, highlighted with DOM inspector:

it's just a square

And here's the same unstyled checkbox in Chrome:

it's an uncentered square with "margins" on the right and on the bottom

You can see the margin (orange); padding is not present (would be shown green). So what's this pseudo-margin on the right and on the bottom of the checkbox? These are parts of the image used for the checkbox. That's why using just vertical-align: middle doesn't really suffice and that's the source of the cross-browser problems.

So what can we do about this?

One obvious option is – replace the images! Fortunately, one can do this via CSS and replace those with external images, base64 (in-CSS) images, in-CSS svg or just pseudo-elements. It's a robust (cross-browser!) approach, and here's an example of such adjustment stolen from this question:

.checkbox-custom {
  opacity: 0;
  position: absolute;
}
.checkbox-custom,
.checkbox-custom-label {
  display: inline-block;
  vertical-align: middle;
  margin: 5px;
  cursor: pointer;
}
.checkbox-custom + .checkbox-custom-label:before {
  content: '';
  display: inline-block;
  background: #fff;
  border-radius: 5px;
  border: 2px solid #ddd;
  vertical-align: middle;
  width: 10px;
  height: 10px;
  padding: 2px;
  margin-right: 10px;
  text-align: center;
}
.checkbox-custom:checked + .checkbox-custom-label:before {
  width: 1px;
  height: 5px;
  border: solid blue;
  border-width: 0 3px 3px 0;
  transform: rotate(45deg);
  -webkit-transform: rotate(45deg);
  -ms-transform: rotate(45deg);
  border-radius: 0px;
  margin: 0px 15px 5px 5px;
}
<div>
  <input id="checkbox-1" class="checkbox-custom" name="checkbox-1" type="checkbox">
  <label for="checkbox-1" class="checkbox-custom-label">First Choice</label>
</div>
<div>
  <input id="checkbox-2" class="checkbox-custom" name="checkbox-2" type="checkbox">
  <label for="checkbox-2" class="checkbox-custom-label">Second Choice</label>
</div>

You may want to read some more in-depth articles about such styling like some listed here; it's out of scope of this answer.

Ok, still what about no-custom-images-or-pseudo-elements solution?

TL;DR: looks like this won't work, use custom checkbox instead

First, let's notice that if in other browsers those pseudo-margins inside checkbox icon were arbitrary, there were no consistent solution. To build one, we have to explore the anatomy of such images in existing browsers.

So what browsers do have the pseudo-margins in checkboxes? I've checked out Chrome 75, Vivaldi 2.5 (Chromium-based), FireFox 54 (don't ask why such outdated), IE 11, Edge 42, Safari ?? (borrowed one for a minute, forgot to check out the version). Only Chrome and Vivaldi has such pseudo-margins (I suspect all Chromium-based browsers as well, like Opera).

What's the size of those pseudo-margins? To figure this out one can use a zoomed checkbox:

input {
  zoom: 10;
  box-shadow: 0 0 1px inset #999;
}
<input type=checkbox>

my result is ~7% of width/height and hence 0.9-1.0px in absolute units. The accuracy may be questioned, though: try different values of zoom for the checkbox. In my tests in both Chrome and Vivaldi the relative size of the pseudo-margin is very different at zoom values 10, 20 and at values 11-19 (??):

for zoom = 10 it's 7% while for zoom = 11 it's almost twice that value

scale seems to be more consistent:

input {
  transform: scale(10) translate(50%, 50%);
  box-shadow: 0 0 1px inset #999;
}
<input type=checkbox>

so probably ~14% and 2px are the correct values.

Now that we know (?) the size of the pseudo-margin, let's note this is not enough. Are the sizes of the checkbox icons the same for all browsers? Alas! Here's what DOM inspector shows for unstyled checkboxes:

  • FireFox: 13.3px
  • Chromium-based: 12.8px for the whole thing, hence 12.8 (100% - 14%) = 11px for what is visually perceived as checkbox
  • IE 11, Edge: 13px
  • Safari: n/a (these should be compared on the same screen, I believe)

Now before we discuss any solutions or tricks, let's ask: what is a correct alignment? What are we trying to achieve? To certain point it's a matter of taste, but basically I can think of the following "nice" alignments' aspects:

text and checkbox on the same baseline (I deliberately don't adjust checkbox size here):

enter image description here

or have same middle line in terms of lowercase letters:

enter image description here

or same middle line in terms of capital letters (it's easier to see the difference for different font size):

enter image description here

and also we have to decide whether the size of the checkbox should be equal to the height of a lowercase letter, a capital letter or something else (bigger, smaller or between lowercase and capital).

For this discussion let's call an alignment nice if the checkbox is on the same baseline as the text and has the size of a capital letter (a highly arguable choice):

enter image description here

Now what tools do we have to:

  1. adjust checkbox size
  2. recognize Chromium with its pseudo-margined checkbox and set specific styles
  3. adjust checkbox/label vertical alignment

?

  1. Regarding the checkbox size adjustment: there are width, height, size, zoom, scale (have I missed something?). zoom and scale don't allow to set absolute size, so they may help only with adjusting to text size, not set cross-browser size (unless we can write browser-specific rules). size doesn't work with Chrome (did it work with old IE? anyway, it's not that interesting). width and height work in Chrome and other browsers, so we can set a common size, but again, in Chrome it sets the size of the whole image, not the checkbox itself. Note: it is minimum(width, height) which defines a checkbox's size (if width ≠ height, the area outside checkbox square is added to "pseudo-margin").

    An unfortunate thing is, the pseudo-margins in Chrome checkbox are not set to zero for any width and heights, as far as I can see.

  2. I'm afraid there's no reliable CSS-only method these days.

  3. Let's consider vertical alignment. vertical-align can't give consistent results when set to middle or baseline because of the Chrome's pseudo-margin, the only real option to get the same "coordinate system" for all the browsers is to align label and input to the top:

    comparing vertical-align: top and bottom

    (on the picture: vertical-align: top, bottom and bottom without box-shadow)

So what result do we get from this?

input[type="checkbox"] {
    height: 0.95em;
    width: 0.95em;
}
label, input {
    vertical-align: top;
}
<label><input type="checkbox">label</label>

The snippet above works with Chrome (Chromium-based browsers), but other browsers require a smaller size of the checkbox. It seems to be impossible to adjust both the size and vertical alignment in a way that works around Chromium's checkbox image quirk. My final suggestion is: use custom checkboxes instead – and you'll avoid frustration :)

I think this is the easiest way

input {
    position: relative;
    top: 1px;
}
<form>
    <div>
        <label><input type="checkbox" /> Label text</label>
    </div>
<form>

This works well for me:

fieldset {
  text-align:left;
  border:none
}
fieldset ol, fieldset ul {
  padding:0;
  list-style:none
}
fieldset li {
  padding-bottom:1.5em;
  float:none;
  clear:left
}
label {
  float:left;
  width:7em;
  margin-right:1em
}
fieldset.checkboxes li {
  clear:both;
  padding:.75em
}
fieldset.checkboxes label {
  margin:0 0 0 1em;
  width:20em
}
fieldset.checkboxes input {
  float:left
}
<form>
  <fieldset class="checkboxes">
    <ul>
      <li>
        <input type="checkbox" name="happy" value="yep" id="happy" />
        <label for="happy">Happy?</label>
      </li>
      <li>
        <input type="checkbox" name="hungry" value="yep" id="hungry" />
        <label for="hungry">Hungry?</label>
      </li>
    </ul>
  </fieldset>
</form>

I've never had a problem with doing it like this:

<form>
  <div>
    <input type="checkbox" id="cb" /> <label for="cb">Label text</label>
  </div>
</form>

With an input type checkbox wrapped inside the label and floated to the left like so:

<label for="id" class="checkbox">
    <input type="checkbox" id="id">
    <span>The Label</span>
</label>

this worked for me:

label.checkbox {
    display: block;
}
.checkbox input {
    float: left;
    height: 18px;
    vertical-align: middle;
}
.checkbox span {
    float: left;
    line-height: 18px;
    margin: 0 0 0 20px;
}

Make sure the height of the is identical to the line-height of the (blocklevel) .

I usually leave a checkbox unlabeled and then make its "label" a separate element. It's a pain, but there's so much cross-browser difference between how checkboxes and their labels are displayed (as you've noticed) that this is the only way I can come close to controlling how everything looks.

I also end up doing this in winforms development, for the same reason. I think the fundamental problem with the checkbox control is that it is really two different controls: the box and the label. By using a checkbox, you're leaving it up to the implementers of the control to decide how those two elements are displayed next to each other (and they always get it wrong, where wrong = not what you want).

I really hope someone has a better answer to your question.

Yay thanks! This too has been driving me nuts forever.

In my particular case, this worked for me:

input {
    width: 13px;
    height: 13px;
    padding: 0;
    margin:0;
    vertical-align: top;
    position: relative;
    *top: 1px;
    *overflow: hidden;
}
label {
    display: block;
    padding: 0;
    padding-left: 15px;
    text-indent: -15px;
    border: 0px solid;
    margin-left: 5px;
    vertical-align: top;
}

I am using the reset.css which might explain some of the differences, but this seems to work well for me.

Simple solution:

<label style="display:block">
  <input style="vertical-align:middle" type="checkbox">
  <span  style="vertical-align:middle">Label</span>
</label>

Tested in Chrome, Firefox, IE9+, Safari, iOS 9+

<fieldset class="checks">
    <legend>checks for whatevers</legend>
    <input type="" id="x" />
    <label for="x">Label</label>
    <input type="" id="y" />
    <label for="y">Label</label>
    <input type="" id="z" />
    <label for="z">Label</label>
</fieldset>

You should wrap form controls grouped together in their own fieldsets anyways, here, it plays the wrappa. set input/label do display:block, input float left, label float right, set your widths, control spacing with left/right margins, align label text accordingly.

so

fieldset.checks {
    width:200px
}
.checks input, .checks label {
    display:block;
}
.checks input {
    float:right;
    width:10px;
    margin-right:5px
}
.checks label {
    float:left;
    width:180px;
    margin-left:5px;
    text-align:left;
    text-indent:5px
}

you probably need to set border, outline and line-height on both as well for cross-browser/media solutions.

I found the only way I could line them up was using position absolute for the input. Playing around with input's top variable got the result I wanted.

div {
  clear:    both;
  float:    none;
  position: relative;
}

input {
  left:     5px;
  position: absolute;
  top:      3px;
}

label {
  display:     block;
  margin-left: 20px;
}
label {
    display: inline-block;
    padding-right: 10px;
}
input[type=checkbox] {
    position: relative;
    top: 2px;
}

Simplest solution is

input {
  vertical-align: text-top;
}

try this code

input[type="checkbox"] {
    -moz-appearance: checkbox;
    -webkit-appearance: checkbox;
    margin-left:3px;
    border:0;
    vertical-align: middle;
    top: -1px;
    bottom: 1px;
    *overflow: hidden;
    box-sizing: border-box; /* 1 */
    *height: 13px; /* Removes excess padding in IE 7 */
    *width: 13px;
    background: #fff;
}

input {
    margin: 0;
}

actually does the trick

Related