View all posts

Creating a FlipSwitch in Css

As frontend developer, I usually write a lot of stuff that runs in the browser. Anyway, most of the time, I write back-frontend code. It means that usually I do not fight with implementing UI components from scratch, and my CSS skills are usually limited to small modification.

Not so long ago, I was working to a sort of UI Kit for Apiary, and I struggled for a while to implement a simple FlipSwitch.

To clarify, this is a FlipSwitch.

See the Pen Simple flip switch css:checked by goiblas (@goiblas) on CodePen.

Pretty simple, right? I am quite sure that every designer/css ninja will find code this a newbie task, but it may give some problems to backend developers and so on.

Given that…

Let’s make a FlipSwitch in CSS3 from scratch!

Skeleton

We will make one slightly different than the one proposed, but the general concepts should be almost the same.

If we think well, a FlipSwitch is no more than a well-styled checkbox, so it is going to be our starting point.

<input type="checkbox"/>

That will render something like this:

Ok, now let’s make the things nicer. At first we will need some additional HTML code. At first we will wrap the checkbox itself into a div and add some stuff that we will use later.

<div class="switch">
    <input type="checkbox" class="switch-checkbox" id="switch"/>
    <label class="switch-label" for="switch">
        <span class="switch-inner"></span>
        <span class="switch-switch"></span>
    </label>
</div>

So, given that we’re going to replace the checkbox with our implementation, the first thing is to hide the current one, so:

.switch
    .switch-checkbox
        display none

Now, let’s give to the switch some basic styles to understand what is going on.

.switch
  display inline-block
  background-color #49B7FF
  width 70px
  height 34px
  
  .switch-checkbox
    display none

That should give us something like this.

FUCKING IMAGE

Q: Why inline-block instead of inline?

A: Basically inline tells to the selected element: Hey please, do not lay on all the current line, but use just the necessary space.. It turns out that, when the current element hasn’t any visible content (that’s our case, since we hid the original checkbox), it won’t show, even if we specify width and height properties. On the other side, inline-block says: Hey please, do not lay on all the current line, but use just the necessary space (inline). But inside this element, behave as a block, so get all the space, regardless of content presence.

Now let’s go further and let’s style the switch-switch element, that is going to be our clickable button.

.switch-switch
  display block
  background #FFFFFF
  width 28px
  height 28px
  border 1px solid #FFFFFF
  border-radius 40px

This will make our switch to appear with a circle, that is going to be the part in movement for our component.

The hard part

Now the hard part comes. Our aim is now to move the circle on left and right based on checkbox state, and the best way is to act directly using right or left properties, that work only when the position is fixed or absolute, Usually, using the position: absolute is a bad practice but, given that we are in an isolated container with fixed width and height, we can safely rely that our element will always stay in a well defined container (if the container is decorated with position: relative) without damaging the rest of the page. Given that, let’s switch to an absolute position method, specifying the top and left properties:

.switch
  position relative
  display inline-block
  background-color #49B7FF
  width 70px
  height 34px

.switch-switch
  display block
  position absolute
  background #FFFFFF
  top 3px
  left 4px
  width 28px
  height 28px
  border 1px solid #FFFFFF
  border-radius 40px

And then we will have the element fixed on the left.

Now, we want to move the circle when we change the checkbox. At first, thanks to the for attribute on top of the switch-label element, every click on that element will move the checkbox state from selected to unselected, and viceversa.

Given that, we want to move the circle about a certain number of pixels, and we can do that using the left property. In order to do that, we’re going to use a bit odd css selector:

.switch-checkbox:checked 
  &+ .switch-label 
     .switch-switch
        left 38px

That, literally, means: Hey, take the switch-switch element contained into a switch-label element, which must be next to a checked switch-checkbox and move the left property in accord.

So, we now have got a moving button and it is setting the checked value in the right way.

Even more hard part

Ok, now let’s go into the hardest part.

.switch-inner
  display block
  width 200%
  margin-left -100%

.switch-inner:before,
.switch-inner:after
  float left
  width 50%
  line-height 34px
  color white
  box-sizing border-box

.switch-inner:before
  content "On"
  padding-left 10px
  background-color #49B7FF
  color #FFFFFF

.switch-inner:after
  content "Off"
  padding-right 10px
  background-color #8A97B1
  color #FFFFFF
  text-align right

.switch-checkbox
    &:checked
      &+ .switch-label 
         .switch-switch
            left inherit
            right 4px 
            
      &+ .switch-label
         .switch-inner
            margin-left 0        

Ok, at the first lines, we’re telling that swith-inner element will have the double of parent’s width (and so it will overflow). Given that, we’re equally distributing the width between the before and after part, and then we set up two different background colors. This will basically give us the two background colors that will slide between right and left, depending on the state. We’re then setting the content to ‘On’ and ‘Off’.

Futhermore, as you can see, we’re using the margin-left as moving property.

Next step: let’s hide the overflowing part in order to display one part per time.

.switch-label
  display block
  overflow hidden
  cursor pointer

Closing the loop

The switch is now done, but it’s ugly. Let’s add some fanciness to the thing. At first, let’s add some animation for the properties we’re moving when clicking the checkbox: margin and left.

.switch-inner
  transition margin 0.2s ease-in 0s

.switch-switch
  transition all 0.2s ease-in 0s

Using styl variables

You will surely notice that, if we change the width of our flipswitch, the toy brokes. For example, the inner circle will stay in a totally wrong place. This is because we decided to use absolute positioning withing the switch. A partial solution to this is to use stylus variables. It turns out that if we don’t hard code some width

$switchWidth = 100px
$circleWidth = 28px

And replace the right expression in the checked state with something like

.switch-switch
  right $switchWidth - $circleWidth - 4px

The switch will behave correctly for each width

News:

People from Webucator found my blog post and they thought it was be a good idea to create a video from that. So, they made it, and I published it. You might be interested to their courses!