iOS 6 switch style checkboxes with pure CSS

I recently found myself looking at the Tools switch in Espresso:

Not because I was going to use it (I rarely do), but because I started wondering what would be the best way to replicate this effect in CSS. I set on to create something that adhered to the following rules:

  1. It should be keyboard accessible
  2. It should work in as many browsers as possible and degrade gracefully to a plain checkbox in the rest
  3. It shouldn’t depend on pseudo-elements in replaced elements (such as checkboxes), since that’s non-standard so not very dependable
  4. It shouldn’t require any extra HTML elements
  5. It shouldn’t use JS, unless perhaps to generate HTML that could be written by hand if the author wishes to do so.

Why you may ask? Some of them are good practices in general, and the rest make it easier to reuse the component (and they made it more challenging too!).

The best idea I came up with was to use a radial gradient for the knob and animate its background-position. All that on a checkbox. After a lot of tweaking, I settled on something that looked decent (although not as good as the Espresso one) in the browser I was using (Chrome) and went ahead to test it in others. The result was disappointing: I had forgotten that not all browsers allow that kind of customization on checkboxes. And who can blame them? This is what happens when you’re wandering in Undefined Behavior Land. They are not violating any spec, because there is no spec mandating or forbidding checkboxes from being stylable with CSS and to what extent, so every browser does its thing there.

Here you can see my failed attempt, which only works as intended in Chrome:

I realized I had to lift one of the restrictions if I wanted to solve this, so I picked the 4th (no extra HTML elements), as it was the least important one. I could have done it as a pseudoelements on <label>s, but I decided to use a <div> instead, for maximum flexibility. The <div> is added through script in the Dabblet below, but it could be added by hand instead.

To get around the limitation of pseudo-elements not being animatable in current and older versions of WebKit, I animate the padding of the <div> instead.

And then I thought, why not make iOS-style switches? Even more challenging! I turned on my iPhone and tried to replicate the look. Adding the ON/OFF text was very painful, as it needs to both animate and be styled differently for “ON” and “OFF”. Eventually, I ended up doing it with text-indent in such a way that it depends on the knob’s position, so that when the knob animates, the text moves too.

Another challenge with this was the different backgrounds. Changing the background color upon :checked was not enough, since it needs to slide as well, not just abruptly change or fade in. I ended up doing it with a gradient and animating its background-position. Naturally, this makes it not look as good in IE9.

So, without further ado, here is the final result:

Yes, I know there are other efforts on the web to replicate this effect with pure CSS, but none of them seems to come as close to the original, without images and with such minimal HTML.

Why bother, you may ask? Well, it was a fun pastime during SXSW breaks or sessions that turned out to be less interesting than expected or in the plane on the way home. Besides, I think that it could be useful in some cases, perhaps if the styling is tweaked to not resemble iOS too obviously or maybe in iOS app mockups or something.


Credits to Ryan Seddon for paving the way for custom form elements through CSS, a couple years ago

  • Wow that’s impressive! I might suggest, in regards to keyboard accessibility, to add a :focus state. Currently you can tab thru and switch switches with keyboard, but there’s no indication of which input you’re going to act upon. Thanks for writing this up!

    • Done, thanks!

    • Why? They are iOS type checkboxes and they are already awesome. There is no tabbing functionality in iOS 🙂

        • You are mixing 2 irrelevant concepts. WAI ARIA has not much to do with iOS visual foundation and/or visual Web CSS.

          Per ARIA, a blind user would not give a damn whether the checkbox looks focused or not. Neither a user with disability to press the tab on keyboard to focus, or tap a smartphone screen.

          For Apple, ergonomics is a huge concern. I wonder why they did not read your wiki link 🙂

        • O’Ryan

          there is no iOS focus state because touching it both focuses and activates it. However when you are building things for the web, it should be accessible. If a user with a disability is tabbing through and there is no indication of focus, how will they know which element they are focused on?

          Giving it a focus state allows a user on a desktop to orient themselves and understand where they are in the flow of any given page or process.

          You are correct, on a smartphone a focus state may not be that helpful, but on a desktop it should be considered. And because this is a css example, people will inevitably use this for some desktop web interface.

        • yes, but the previous commenter had mixed 2 concepts.

          Also, which disability are you talking about when you say user is “tabbing through” ? The user can definitely see and operate a keyboard, when “tabbing through”, right?

        • Juanca

          A blind user can’t see the screen while he or she is tabbing through. That kind of disability.

  • Selim

    The checkboxes don’t show up at all in IE9. So not really degrading gracefully…
    They do look good in Chrome!

    • Dabblet doesn’t fully work in IE9, not the checkboxes. It should take you to the full page version, but that’s currently having issues, so it says “not found”.

  • I hate to bitch about such a tiny detail, but I’m gonna do it anyway:

    When switching, the word “off” jumps in only after the slide completes more than half of the transition, it doesn’t slide in wíth the button. Using Chrome 25 / Canary 27

    • Good catch, hadn’t noticed it!! I’ll look into how or whether it could be fixed.

  • David Murdoch

    I made some older iOS checkboxes with pure CSS a while back:!).aspx

    Here is a JSfiddle for it:

    • jp_c

      Thanks to you and Lea (and the others w/ their variations) been messing around with custom “checkboxes” the past week.

      Personally not too concerned whether using JS or not – I’m good either way.

  • I’m just a humble beginner … I did something similar, but without such nice animation:

  • Very Good

  • Daniel Chatfield

    I was considering using something like this but what put me off was too many users were trying to drag it rather then click it and getting stuck in UX testing.

    • Brandon Capecci

      Agreed. If he’s going to use this style, not having the buttons be draggable is a pretty poor experience.

      • He? Who’s he?

        I’m sure a small amount of javascript could alleviate this problem. But this is a proof of concept, nothing more.

  • I was trying to mimic a design of a form created in Photoshop and it ended pretty well except that the checkboxes and the radio buttons where not accessible using keyboard.
    My method (you can find it here: was using pseudo-elements and labels and I set the display of the input field to none. I followed the example you wrote and fount that setting the opacity to 0 and taking the input out of the flow by using position absolute fixes that issue. Thanks for your contribution

  • The behavior is a bit strange in IE10. If you remove “width” from transition-property, it’s ok but then it’s broken on chrome.

  • Rudie Dirkx

    Overriding checkbox design is always bad, because people know what ‘their’ checkboxes look like and every website having its own checkbox look only adds confusion. If you’re on ios, you’ll want ios checkboxes (AND radiobuttons etc) and if you’re not on ios you don’t want ios checkboxes.

    • It was a proof of concept, not a style recommendation.

    • “people know what their’ checkboxes look like” ?? Really?

      People want more than “right tick” marks. you are seriously questioning evolution of design

      • I’m not questioning the evolution of design =) I’m questioning the user friendliness of a million different designs for 1 form element. If I apparently don’t know what people want, maybe you don’t either?

        • level

          better not change the color or styling of menus on your sites then, people might get confused.

    • SolrWind

      Except she DIDN’T override the checkbox design. What she did was create a completely new kind of web-UI control. The only similarity that this shares with a checkbox is the fact that each state (checked/unchecked; on/off) is mutually exclusive of the other. That’s all. Not all UI controls that represent an on/off state are checkboxes. In fact, the same kind of logic can be done using radio buttons, and yet no one confuses a radio button with a checkbox. Whatever Jakob Nielsen may have said doesn’t apply here.

    • jp_c

      This was not about making a checkbox look different. It was about using the underlying functionality in a different way.

      @SolrWind – come one now radio buttons are round checkboxes or is it that checkboxes are square radio buttons ? Wait – I’m confused…:))

  • wow. thanks!

  • I’ve been working on something similar for a while (well, without the iOS-ness):

    They recently also got integrated into Foundation 4 as the Switch component.

  • Impressively good. Do these perform well on touch screens?

  • Pingback: Weekly Design News – Resources, Tutorials and Freebies (N.176) | Wordpress Webdesigner()

  • Romu

    Even though ios looking stuff is tiring, works great here, android chrome latest. Somehow, even the small one are touch friendly despite their size.

  • Nice! I love the way you challenge yourself with these types of things.

    Minor nit-pick: The blue background slides past the button when it’s animating to ‘On’ (also back the other way, but not so noticeable). I was able to fix it by setting the background x-positions to -2.5em and -0.5em for ‘Off’ and ‘On’ respectively. To hide some of the colour bleed, I made the borders opaque. However there’s still a slight blue bleed on left of the button when it’s ‘Off’.

  • boennemann

    I’ve recently done something similiar. CSSUISwitch is an almost 1:1 recreation of the UISwitch, but with CSS only. No images & native checkbox.

  • antoniobrandao

    To be perfect the circle just needed to be draggable. Quite a nice work here, but in my opinion this kind of switch only makes sense when one can drag it. It’s because there is a natural instinct to do it, everyone tries to.

  • Nicholas

    The three iframes just contain the text “Not found, sorry! :(” for me.

  • Pingback: Debugable » Weekly Design News – Resources, Tutorials and Freebies (N.176)()

  • maxw3st

    Very nice. Looks handy.

  • this is veryuseful – i liks iOS switch 🙂

  • Just amazing Lea! Thanks for sharing this… I will use it in my next project 😀

  • Pingback: iOS 6 switch style checkboxes with pure CSS | Lea Verou | Mintao()

  • Pingback: Collective #55 - HOST4WORK()

  • netgoblin

    where’s “pure css” if you use javascript?!

    • Did you even glance at the JavaScript? Did you even read the blog post?
      The JS is only adding a div per switch. It’s not needed, you could just add the extra div manually.

    • You just went full retard.

  • William George

    I wanted to make this without images too, in order to make it easier for the developer. However i also wanted to make them draggable as it is more intuitive for the user. Therefore i had to user javascript, if you would like to take a look at the results:

    Keep up the great work!


    • None of the toggles seem to be using complex image patterns. I believe they can be simply implemented with CSS 3 grads.

  • .

    I’ve done something similar, I was looking for a “draggable” switch though, just like it works in iOS. It ended up being webkit only, but since it was used in a controlled scenario, everything was peachy.
    { ::webkit-slider-thumb is the magic word }

    • Yes, that would work nicely for WebKit-only. However, I can only think of very few scenarios where WebKit-only is a good approach, namely browser extensions and bundled WebKit views in native apps. Going WebKit-only on mobile is a bad idea.

      • .

        It’s really a bummer because of how much I like that pseudo-element and the “free” sliding-interaction that comes with it. If you have a client that only uses devices with a webkit-browser (namely safari) it can work quite well as an intranet-accessible application within the company. They can use the (responsive) app on an iPad, iPod or workstation or even hand over a device to their clients for them to fill out.
        Another scenario could be a museum, where you’d have a form to be filled out on a big touchscreen. Then you could provide several values to the answer-slider, with different text. You’d be free to style it however it fits the need.
        I used the toggler/switch in different variations, the multi-value was one of them. I changed the dabbler to show that scenario.

  • abidibo

    You rock

  • Pingback: Weekly Design News – Resources, Tutorials and Freebies (N.176)()

  • Pingback: Le meilleur du web #54: liens, ressources, tutoriels et inspiration « Design Spartan : Art digital, digital painting, webdesign, illustration et inspiration…()

  • Pingback: Today’s Readings | Aaron T. Grogg()

  • margin: -0.1em on div:before (line:43) breaks it while scale:

    But looks like line-height: 1.7 !important will fixed it:

  • Pingback: Gameleon – WordPress Arcade Theme | ThemeToucan()

  • Johnny Loke

    This is brilliant. What if I would like to change the text inside the switch, which css I should look into?

    • Glad you like it! Lines 39 and 71, the content property.

  • Looked for this a ways back, and I couldn’t find a solution that didn’t use pseudo-elements. Since pseudo-elements can’t be read by JS, there’s no way to maintain the state of the application in the context of the form items.

    Am super excited to move back to this solution over drop down menus since I can hook into the checked/unchecked value now.

  • It gets a bit wonky if you use *{ box-sizing: border box; }. Just make sure you have box-sizing: content-box; on the .switch div

  • If used jQuery Mobile you’ll need to use a data-role=”none” attribute on the checkbox to disable jQuery Mobile from adding all the crazy extra divs and classes it would add around the label and checkbox otherwise.
    Great example though 🙂

  • Pingback: Tweet Parade (no.12 Mar 2013) | gonzoblog()

  • Too much for too little, still not interested.

  • James Gentes

    Thanks so much for this!

    FYI – I had to make a slight change to get the colors working right in Opera (for iPad support):

    background-image : -webkit-linear-gradient(rgba(0, 0, 0, .1), transparent), -webkit-linear-gradient(0deg, #3D99F5 50%, transparent 50%);

    Can be seen at

    • I applaud your efforts for taking time to research your information before you write.

  • Hi – the buttons look like the background’s position is more “0 50%” on a Galaxy S4. I am hoping to find a fix, but if you can think of one, give me a shout.

    • We fixed it by adding a solid background colour to the switch div and moving the background position to 0 0:

      .checkboxHolder input[type=”checkbox”].ios-switch:checked + div {
      background-position: 0 0;
      background: #00689A;

      then inverted the order of the background gradient it has, putting the -webkit rule last:

      background-image: linear-gradient(rgba(0,0,0,.1), transparent), linear-gradient(90deg, #00689a 50%, transparent 50%);

      background-image: -webkit-linear-gradient(rgba(0,0,0,.1),transparent), -webkit-linear-gradient(0deg, #00689a 50%, transparent 50%);

      Hope this works for you too!

  • Thanks, I like that OS.

  • it is an operating system that I found quite adequate and good quality.

  • I like that OS.I like that OS.

  • I was scrambling to hunt out analysis knowledge that i’ll grasp merely for a hunt project on this subject once I finally found your article. thanks for making this knowledge so clear and attention-grabbing.

    • Reading this text gave American state several things to admit.

  • I appreciate however you’ve got given browseers like American state easy to browse information.

  • Marvin H.

    This fails the W3C HTML validator – you can’t have a div tag inside a label tag. :-

    • Oh, boo. Well, it could be a span or anything really.

      • Marvin H.

        Yep that’s what I did – changed the div tag to a span tag and all was beautiful in the world again. 🙂

  • DanOwen

    There was an okay approach on Sitepoint (Here). Did a variation on it:

  • Arvind Srinivasan

    I can’t seem to get this to work when I try to change the checkbox state using JS. The underlying checkbox state gets changed but the overlay control doesn’t change. I’ve even tried doing a .trigger() on the different elements. Any suggestiong?

  • Pingback: Gameleon - WordPress Arcade Theme Free Download - Wp Free | Wp Free()

  • Pingback: Gameleon - WordPress Arcade Theme -Free download - Wp Free | Wp Free()

  • Pingback: Gameleon – WordPress Arcade Theme (Entertainment) | Best WP Themes()

  • Pingback: Gameleon — WordPress Arcade Theme (Entertainment) | Envato news()

  • Pingback: dev/null » Blog Archive » iOS 6 switch style checkboxes with pure CSS | Lea Verou()

  • Pingback: Gameleon – WordPress Arcade Theme (Entertainment) | WP Fire()

  • Justin

    I know this was a proof more than a functional thing, but since the toggle doesn’t truly switch the state of the checkbox, what is your recommended solution for selecting which ones have been toggled? I wasn’t looking for a javascript solution 😉

    • Justin

      Wooooow, nvm I don’t know what I was thinking….

  • Tommy Korner

    Can’t get it to work?? copied the css as provided above; added the following to my page: (1) question; (2) in a table: question@Html.CheckBoxFor(Model => Model.Question1), new { @class = “ios-switch” }. Please help

  • Tommy Korner

    Cant get this to work. copied the css as provided; added the following to my page:


    Question 1

    2) in a table…

    Question 1

    @Html.CheckBoxFor(Model => Model.Question1, new { @class = “ios-switch” })

    3) simply as used in the example:
    Question 1

    None of the options above work (the only thing that does work is the default checkbox is “hidden” with opacity of ‘0’)

    • It’s really hard to debug like this. Do you have a testcase online?

  • Pingback: Checkbox-uri sau switch-uri pentru aplicatiile web - UX Designer()

  • Pingback: Gameleon – Wordpress arcade theme | Blog()

  • aldwin collantes

    thank you for this amazing guide, i’m using it right now. but i noticed that the background color for the ON switch does not appear on safari, is there any way around that?

    however, when i looked at the dabblet full page and include this line from the code:

    the color appeared, do i really need this js?

    • PrefixFree just adds prefixes. You can add the prefixes manually if you want to avoid it.

  • Bruno

    Hello! How do I get this to work with HTML5 data-attributes. Where data-type=checkbox and data-value=true etc.

  • Martin

    How can I set the state via JS / jQuery ?

  • Pingback: Gameleon – WordPress Arcade Theme | themesafe()

  • Doogie

    Your rock! Thank you so much for this great code. I learned quite a bit about CSS!

  • Sydney_o9

    Awesome post! A problem for this though would be the translation of the content ‘on’ and ‘off’ elements in different languages (since the text in embedded in CSS)

  • Pingback: Star Themes | Gameleon – WordPress Arcade Theme()

  • Pingback: Gameleon - WordPress Arcade Theme | 9Downloads()

  • Pingback: Gameleon – WordPress Arcade Theme Free Download Nulled Torrent | Crunchy Themes()

  • Pingback: Numérotation automatique en CSS | Codex sphinxial()

  • Ricardo H

    Amazing!! Just i was looking for… An switch-like checkbox approach using only CSS and JS. Thanks!!

  • Peter

    Definitely impressive, however using text inside css is a bad idea if you want to offer a multilingual website.

  • Pingback: Mobile Switch | Precambria()

  • Pingback: Gameleon – WordPress Arcade Theme | Website Templates()

  • how to get the checked event?

  • Dumindu Chathuranga

    thanks !

  • great help, Thanks

  • John Graybosch

    Instead of setting the vertical align to be middle, set it to -0.333 em. That seems to be exactly centered, in this case.

  • peter vkekem

    Just want to share that I’ve used your 2nd example, and got rid of the javascript (and some css stuff). I’m just using the input and label elements now. Have a look at

  • kishor

    nice tutorial very useful i also read a tutorial to create iphone like toggle button without plugin if want to create your own toggle button you can view this tutorial

  • Thanks so much for this!

  • Pingback: domeni()

  • Pingback: Table Padding Css Html5 Templates Torrent | Supreme Tablet Deals()