chevron-left chevron-right

[CSS] Jak utworzyć animowany przycisk typu toggle button za pomocą CSS?

Po dłuższym okresie braku aktywności na blogu powracam z nowym wpisem. Dzisiejszy wpis będzie dotyczył utworzenia dwustanowego przycisku, typu toggle button, którego stan będzie można zmienić za pomocą CSS, bez użycia JavaScript.

Kod HTML

Aby móc wykorzystać omawiany przykład przycisku typu toggle musimy przygotować odpowiednią strukturę kodu HTML, która wygląda następująco:

<label class="btn-toggle">
    <input class="btn-toggle__checkbox" type="checkbox"/>
    <span class="btn-toggle__body">
        <span class="btn-toggle__body__switch"></span>
        <span class="btn-toggle__body__track">
            <span class="btn-toggle__body__track__option--view">View</span>
            <span class="btn-toggle__body__track__option--edit">Edit</span>
        </span>
    </span>
</label>

Jak można zauważyć, wykorzystałem elementy <span/> do określenia suwaka i tła pod suwakiem. Jest to związane z tym, że chciałem zachować poprawną strukturę kodu HTML, gdzie wewnątrz elementów liniowych, takich jak <label/> nie powinny się znajdować elementy blokowe (np. takie jak <div/>) tylko elementy liniowe, m.in. <span/>.

Kod CSS

Mając gotowy kod HTML, możemy zabrać się do stylowania tego kodu. Za pomocą poniższego kodu określimy jak ma się zachowywać przycisk w określonych sytuacjach:

.btn-toggle {
    position: relative;
    z-index: 1;
    display: inline-block;
    height: 30px;
    cursor: pointer;
}
 
.btn-toggle__checkbox {
    position: absolute;
    /*
        umieszczamy nasz checkbox poza zasiegiem kursora,
        tj. pod tekstem i tłem suwaka. Dzięki temu użytkownik
        nie będzie go widział, ale dalej będzie w stanie go kliknąć.
     */
    opacity: 0;
    z-index: -1;
    visibility: hidden;
}
 
.btn-toggle__body {
    width: 100px;
    height: 30px;
    background: #fff;
    border: 1px solid #dadde1;
    display: inline-block;
    position: relative;
    z-index: 1;
    border-radius: 50px;
}
 
.btn-toggle__body__switch {
    width: 30px;
    height: 30px;
    display: inline-block;
    position: absolute;
    z-index: 2;
    left: 0;
    top: 50%;
    border-radius: 50%;
    border: 1px solid #fff;
    box-shadow: 0 2px 2px rgba(0, 0, 0, 0.13);
    color: #fff;
    background: #439fd8;
    transition:
        transform cubic-bezier(0.34, 1.61, 0.7, 1) .25s,
        background cubic-bezier(0.34, 1.61, 0.7, 1) .25s;
    transform: translate3d(-1px, -50%, 0);
}
 
.btn-toggle__body__switch:before {
    content: '«';
    display: block;
    line-height: 30px;
    font-size: 20px;
    text-align: center;
}
 
.btn-toggle__body__track {
    position: absolute;
    z-index: 1;
    left: 0;
    top: 0;
    right: 0;
    bottom: 0;
    overflow: hidden;
    border-radius: 50px;
}
 
[class*="btn-toggle__body__track__option"] {
    position: absolute;
    z-index: 1;
    top: 0;
    bottom: 0;
    width: 100px;
    text-align: center;
    line-height: 30px;
    transition:
        transform cubic-bezier(0.34, 1.61, 0.7, 1) .25s,
        background cubic-bezier(0.34, 1.61, 0.7, 1) .25s;
}
 
.btn-toggle__body__track__option--view {
    transform: translate3d(10%, 0, 0);
    background: #fff;
    color: #439fd8;
}
 
.btn-toggle__body__track__option--edit {
    transform: translate3d(-100%, 0, 0);
    background: #439fd8;
    color: #fff;
}
 
.btn-toggle:hover .btn-toggle__body__switch {
    border-color: #b5bbc3;
    transform: scale(1.06) translate3d(-1px, -47%, 0);
}
 
.btn-toggle:active .btn-toggle__body__switch {
    transform: scale(0.94) translate3d(-1px, -53%, 0);
}
 
/*
  gdy checkbox nie jest zaznaczony, ustawiamy mu style które pokażą informację o tym,
  że zmiana stanu przycisku zaznaczy checkbox
*/
.btn-toggle__checkbox:not(:checked) ~ .btn-toggle__body > .btn-toggle__body__switch {
    transform: translate3d(70px, -50%, 0);
    background: #fff;
    color: #439fd8;
    border: 1px solid #ccd0d6;
}
 
.btn-toggle__checkbox:not(:checked) ~ .btn-toggle__body > .btn-toggle__body__switch:before {
    content: '»';
}
 
.btn-toggle:hover .btn-toggle__checkbox:not(:checked) ~ .btn-toggle__body > .btn-toggle__body__switch {
    transform: scale(1.06) translate3d(66px, -47%, 0);
}
 
.btn-toggle:active .btn-toggle__checkbox:not(:checked) ~ .btn-toggle__body > .btn-toggle__body__switch {
    transform: scale(0.94) translate3d(74px, -53%, 0);
}
 
.btn-toggle__checkbox:not(:checked) ~ .btn-toggle__body .btn-toggle__body__track__option--view {
    transform: translate3d(110%, 0, 0);
}
 
.btn-toggle__checkbox:not(:checked) ~ .btn-toggle__body .btn-toggle__body__track__option--edit {
    transform: translate3d(-10%, 0, 0);
}

Powyższy kod CSS jest odpowiedzialny za ukrycie pola typu checkbox przed wzrokiem użytkownika strony. Ponadto, dodaje odpowiedni design i przejścia między stanami zaznaczenia pola typu checkbox. Dzięki temu, użytkownik ma przyjemną kontrolkę do używania na stronie lub w aplikacji internetowej.

Aby poprawić wydajność animacji przejścia wykorzystałem funkcję translate3d, dzięki czemu przeglądarka nie będzie musiała przeliczać ponownie położenia layoutu, tak jakby to miało miejsce w przypadku wykorzystania własności CSS: left, right.

Podsumowanie

Powyższy przykład nie zawiera wszystkich właściwości CSS z vendor prefiksami (tzn. z przedrostkami wymaganymi przez starsze wersje przeglądarek), po to aby kod był bardziej przejrzysty. Dobrym sposobem na uzupełnienie brakującego kodu z vendor prefiksami jest użycie Autoprefixera (można go uruchomić z linii poleceń w terminalu lub za pomocą Grunt.js).

Mam nadzieję, że zaprezentowany przeze mnie sposób na tworzenie efektownie wyglądających przycisków okaże się przydatny. Efekt był testowany na przeglądarkach Mozilla Firefox, Google Chrome oraz Opera.

źródło