How to create a toggle button with input=checkbox without using the <label> element?

Asked

Viewed 1,483 times

5

Is to create/transform a <input type="checkbox"> on a toggle button without needing the element <label> as in these examples:

An example I made based on the code of Andreas Storm it was this:

body {
    background: #eee;
}

.toggle {
    margin-bottom: 20px;
}

.toggle > input {
    display: none;
}

.toggle > label {
    position: relative;
    display: block;
    height: 28px;
    width: 52px;
    background-color: #f7f7f7;
    border: 1px #a2e3e6 solid;
    border-radius: 100px;
    cursor: pointer;
    transition: all 0.3s ease;
}
.toggle > label:after {
    position: absolute;
    left: 1px;
    top: 1px;
    display: block;
    width: 26px;
    height: 26px;
    border-radius: 100px;
    background: #fff;
    box-shadow: 0px 3px 3px rgba(0,0,0,0.05);
    content: '';
    transition: all 0.3s ease;
}
.toggle > label:active:after {
    transform: scale(1.15, 0.85);
}
.toggle > input:checked ~ label {
    background-color: #4cda64;
    border-color: #4cda64;
}
.toggle > input:checked ~ label:after {
    left: 25px;
}
.toggle > input:disabled ~ label {
    background-color: #d5d5d5;
    pointer-events: none;
}
.toggle > input:disabled ~ label:after {
    background-color: rgba(255, 255, 255, 0.3);
}
<div class="toggle">
    <input type="checkbox" id="foo">
    <label for="foo"></label>
</div>

<div class="toggle">
    <input type="checkbox" id="bar" checked>
    <label for="bar"></label>
</div>

<div class="toggle">
    <input type="checkbox" id="baz" disabled>
    <label for="baz"></label>
</div>

It would be possible to create something like this without label and without any other element (and without Javascript too)?

2 answers

6


It is possible using two pseudo elements, the ::before and the ::after, will also need to use properties -webkit-appearance: and -moz-appearance: (and in the future appearance:).

In old Msedge (Edgehtml) and Internet Explorer this will not work, already in recent versions of Firefox, Webkit (Safari, iOS and Android) and Chrome browsers (and Chromium-based browsers, including current Msedge)

Should stay like this:

/*Adiciona border-box para o input e para os pseudo-elementos*/
input[type=checkbox], input[type=checkbox]::after, input[type=checkbox]::before {
    box-sizing: border-box;
}

/*Estiliza e remove a aparencia padrão do elemento*/
input[type=checkbox] {
    outline: none;
    position: relative;
    z-index: 1;
    margin: 2px;
    padding: 0;
    cursor: pointer;
    width: 48px;
    height: 24px;
    overflow: hidden;
    -webkit-appearance: none;
    -moz-appearance: none;
    appearance: none;
}

/*cria os elementos before e after*/
input[type=checkbox]::before, input[type=checkbox]::after {
    content: "";
    position: absolute;
    top: 0;
    left: 0;
    z-index: 2;

    /*efeito animado*/
    transition: left .15s cubic-bezier(.25, .8, .25, .1),
                transform .15s ease-in;
}

/*Cor padrão de quando o elemento não esta selecionado*/
input[type=checkbox]::before {
    background-color: #ccc;
    width: 100%;
    height: 100%;
    border-radius: 28px;
}

/*estiliza para parecer um botão toggle*/
input[type=checkbox]::after {
    margin: 2px 0 0 2px;
    background: #fff;
    width: 20px;
    height: 20px;
    border-radius: 100%;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.4);
}

/*troca a cor se estiver com a propriedade checked no html ou o usuário "checar"*/
input[type=checkbox]:checked::before {
    background-color: #75a940;
}

/*muda a posição do botão toggle se estiver checado*/
input[type=checkbox]:checked::after {
    left: 24px;
}

/*Efeito opicional de quando preciona o botão*/
input[type=checkbox]:active::after {
    transform: scale(1.15, 0.85);
}

/*Se o input tiver com o atributo disabled a cor é alterada*/
input[type=checkbox]:disabled::before {
     background-color: #b1b4b7 !important;
}

/*Se o input tiver com o atributo disabled a cor é alterada*/
input[type=checkbox]:disabled::after {
     background-color: #dcd8d8 !important;
}

/*OUTRAS CORES = Cores alternativas*/

input[type=checkbox].red:checked::before {
     background-color: #fd4554;
}

input[type=checkbox].blue:checked::before {
     background-color: #0f90dc;
}

input[type=checkbox].red:checked::before {
     background-color: #fd4554;
}

input[type=checkbox].purple:checked::before {
     background-color: #9e3eff;
}
<input type="checkbox">
<input type="checkbox" class="red">
<input type="checkbox" class="blue">
<input type="checkbox" class="purple">
<input type="checkbox" class="purple" disabled>

<hr>

<input type="checkbox" checked>
<input type="checkbox" class="red" checked>
<input type="checkbox" class="blue" checked>
<input type="checkbox" class="purple" checked>
<input type="checkbox" class="purple" checked disabled>

1

I have a similar option to Guilherme, but I give a all:unset in the checkbox first to clear all formatting default of user-agent

Then I stylize myself input the way you want it and use only the ::after. It worked in Chrome and Firefox, I believe that in Safari tb will work. In IE and Edge is not as expected.

OBS: From the point of view of accessibility, because it is an item of form without label, it would be interesting to assign a aria-label=" " in the ckeckbox saying what this btn is about. If you are interested here is the W3C recommendation on the subject https://www.w3.org/WAI/tutorials/forms/labels/

  .btn {
    all: unset;
    position: relative;
    display: inline-block;
    width: 60px;
    height: 30px;
    background-color: #f00;
    border-radius: 15px;
    cursor: pointer;
    border: 1px solid #ccc;
    box-shadow: inset 0 0 5px 0 rgba(0,0,0,0.75);
    transition: background-color 100ms ease-in-out;
  }
  .btn:checked {
    background-color: #090;
  }
  .btn::after {
    content: "";
    position: absolute;
    height: 20px;
    width: 20px;
    box-sizing: border-box;
    border: 1px solid #666;
    border-radius: 50%;
    box-shadow: 0 0 8px rgba(0,0,0,0.5);
    background-image: radial-gradient(#fff 0%, #999 100%);
    top: 50%;
    left: 5px;
    transform: translateY(-50%) scale(1, 1);
    transition: left 200ms ease-in-out, transform 150ms ease-in-out;
  }
  .btn:checked::after {
    left: calc(100% - 25px);
  }
  .btn:active::after {
    transform: translateY(-50%) scale(1.15, 0.85);
  }
  <input class="btn" type="checkbox" aria-label="btn seleciona opção x">

  • Better apply all: unset; the class .btn, because in the case of the specific example would affect all radios, but only radios with the btn class would have complete effects, so other radios without the class would be bugged. Or exchange the class . btn for the widget type selector [type="checkbox"] on all selectors.

  • @Guilhermenascimento yes you’re right, as I was doing in a test environment I didn’t even notice it, but it already includes everything in the same class and I removed the global checkbox selector

Browser other questions tagged

You are not signed in. Login or sign up in order to post.