chevron-left chevron-right

Piotr Nalepa Frontend, architecture and AI

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

This version
blog.piotrnalepa.pl/2015/04/01/css-jak-utworzyc-animowany-przycisk-typu-toggle-button-za-pomoca-css
Published
Author
, Staff Software Engineer
Category
CSS
Feedback
Write to me · Post on X

This post is 11 years old. Browsers and tools may have changed since then, so check the code against current documentation before you use it.

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.

Section 1Kod HTML

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

Example 1HTML
<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/>.

Section 2Kod 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:

Example 2CSS
.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.

Section 3Podsumowanie

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

Share
Feedback

About the author

Staff Software Engineer

I've been writing this blog since 2009 and building for the web professionally for 14 years. Today I lead the frontend of the Cosmos platform at Bishop Fox, where I've been since day one of the project. Before that I worked at OLX, KUDO, Auto1 and eZ Systems. Here I write about what I've learned in practice: JavaScript, TypeScript, React, CSS and frontend architecture. I've also spoken at conferences across Poland and Europe. Got a question or an idea for a post? Let me know!