Custom Code Custom Toggle Button

I have created a very simple toggle button by using a very simple HTML markup →

I wish to create toggles that are similar to the ones found here →, but using my own HTML and CSS instead. I find it challenging to read and comprehend someone else’s code.

In the skew one, I could not know which logic should I use to replicate the button type.

You can use transform with the skew option to create the skew.

For the on and off you can use :before and :after on the label to create 2 elements next to each other with one hidden out of sight using overflow:hidden on the label.

Then use the checkbox hack method and just slide them from side to side as required. You can use data attributes for the on and off text.

1 Like

Got it will try and update with the code. Thanks.

sir, I didn’t use data attributes, yet it works, but please suggest any scope of improvement →


<form  id="radio-buttons" action="#">
  <legend>radio buttons</legend>
   <input type="radio" id="rd1" name="r" value="radio 1" hidden>
   <label for="rd1" class="rd"></label>
   <input type="radio" id="rd2" name="r" value="radio 2" hidden>
   <label for="rd2" class="rd"></label>
   <input type="radio" id="rd3" name="r" value="radio 3" hidden>
   <label for="rd3" class="rd"></label>
 <input type="reset">


body {
    font: normal 1em / 1.5  sans-serif;
#radio-buttons {
   font-family: arial,helvetica, sans-serif; 
#radio-buttons fieldset {
   border: 0; 
#radio-buttons label {
   position: relative;
   display: block;
   width: 2.5em;
   height: 1.5em;
   border: 1px solid #999;
   border-radius: 0.75em;
   margin: 0.25em;
   background-color: #ccc;
   transition: 0.5s ease-in-out;
   cursor: pointer;
#radio-buttons label::before {
   top: 0.125em; 
   left: 0.125em;
   width: 0.5em;
   height: 0.5em;
   line-height: 0.5em;
   border-radius: 0.5em;
   font-size: 2em;
   font-weight: normal;
   content: '\02022';
   text-align: center; 
   color: transparent;
   background-color: #fff;  
   transition: 0.5s ease-in-out;
#radio-buttons input:checked + label  {
   background-color: #2196f3;
#radio-buttons input:checked + label::before  {
   transform: translateX( 0.5em );
   color: #2196f3;
#radio-buttons label::after {
   right: -4em;
   width: 3em;
   padding-left: 1em;
#radio-buttons label:nth-of-type(1)::after {
   content: 'one';
#radio-buttons label:nth-of-type(2)::after {
   content: 'two';
#radio-buttons label:nth-of-type(3)::after {  
   width: 9em;
   right: -10em;
   content: 'longer content';
#radio-buttons label:hover::after,
#radio-buttons label:active::after {
  font-weight: bold;
  color: #2196f3;
1 Like

The div should be a span as the label is an inline element and can only hold other inline elements.

The input should be hidden visually and not display:none (nor by using the hidden attribute) otherwise keyboard tabbing doesn’t work and will fail accessibility criteria.

You should also apply a focus clue so that when tabbing works the keyboard user can find it.

.wtoggle__input {
    position: absolute;
    clip: rect(1px, 1px, 1px, 1px);
    padding: 0;
    border: 0;
    height: 1px;
    width: 1px;
    overflow: hidden;         
  outline:1px solid red;/* style to suit.*/

I’m not keen on generated content being used for meaningful text so I would put some meaningful label text in there and hide it visually again. I also prefer to keep the input and label separate which means you don’t need an extra element.

This is a quick demo and I used a clip method instead of skew (just because I can).

The html is now clean and accessible.

<input class="toggle-input" type="checkbox" id="toggle-1">
<label class="toggle-label" for="toggle-1"> Off / On</label>

Of course you should factor in the disabled states etc.

You can tab to the element with the tab key and then change the selection using the spacebar as per the specs.

1 Like

Can i ask you something sir? Is this not a good HHTML markup/structure:

  <label for="stoggle" class="wtoggle skew">
    <input class="wtoggle__input" type="checkbox" id="stoggle">
    <div class="wtoggle__fill"></div>

As mentioned the div is invalid in that position so would need to be a span.

I don’t like that structure as you effectively limit what styling can be applied to the label element when the input is checked. That’s why you had to add an extra div to add more styling.

If the input is before the label then you can style the label using :checked + label. (There are some modern methods using :has but there is not good support yet. )

Historically there was also an accessibility bug with inputs nested inside labels but that isn’t the case these days but I still prefer the separate structure.

1 Like

Sir, As far as I remember you also created a Pen with your HTML against my pen with that semi-circular toggle. Is that part of the post deleted or I am under the assumption that you created such a pen?

I couldn’t find one but here’s yours quickly adapted to show focus to accommodate tabbing and spacebar activation and also removing the inner (invalid) div.

1 Like

Isn’t that looking ugly? I am just curious to know why it was added. Thanks!

Because without it you alienate all keyboard users and those users that have a disability with using the mouse. It’s the same as if you hid the mouse cursor. Have you tried using the mouse when the cursor is hidden? :slight_smile:

Without focus there is virtually no way to activate that button for a keyboard users. It is one of the first accessibility failures in the accessibility guidelines.

There’s a good article here that explains it all.

The border I added was just to show it in action but you can style it to be a different visual clue such as box shadow or background color or something that has a contrast that can easily be seen.

It can be combined with :focus-visible for modern browers so that only keyboard users can see it.

I have updated the codepen with an example.

The main thing to remember is that you remove :slight_smile: focus styles at your peril :slight_smile:

1 Like

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.