Делаем красивые чекбоксы с анимацией

Делаем красивые чекбоксы с анимацией

Погружаемся в хитрости современного CSS

Мы уже много говорили о базовом CSS, немного о препроцессорах, современных командах и гридах. Теперь посмотрим на вложенности и зависимости — полезный инструмент для веб-разработки в реальном мире. 

Основой нашего кода станет вот этот кусок, в частности — знак плюса:

.checkbox__input:checked + .checkbox__icon .tick {...}

Плюс означает, что стиль применяется к элементам класса .checkbox__icon .tick, которые идут в HTML-коде сразу после отмеченного элемента checkbox__input.

Также в коде будет использоваться такое заклинание: 

.check-group > * + * {...}

Но его так запросто не объяснишь, листайте дальше. 

Что делаем

Допустим, мы делаем интерфейс интернет-пиццерии и хотим добавить конфигуратор начинки. Нам нужно, чтобы пользователь мог поставить галочки возле ингредиентов. При этом важно, чтобы всё выглядело красиво (а не как в стандартном исполнении браузера). 

Классический дизайн чекбоксов

Если мы сделаем это просто на чекбоксах, мы получим мелкие и едва кликабельные штучки. Вот их сейчас мы будем прокачивать, чтобы они были крупными и анимированными: 

Делаем красивые чекбоксы с анимацией

<!DOCTYPE html>
<html lang="en" >
<head>
  <meta charset="UTF-8">
  <title>Начинка для пиццы</title>

</head>
<body>

<h1>
Что добавить в пиццу?
</h1>
<p>
<input type="checkbox" name="in1"> Оливки </input>
</p>
<p>
<input type="checkbox" name="in2"> Пепперони </input>
</p>
<p>
<input type="checkbox" name="in3"> Каперсы </input>
</p>
<p>
<input type="checkbox" name="in4"> Помидоры </input>
</p>
</body>
</html>

Подключаем нормализатор и файл со стилями

Чтобы чекбоксы выглядели одинаково во всех браузерах, начнём с нормализатора CSS. Это специальный набор стилей, который приводит содержимое нашей страницы к единому виду в каждом браузере. 

Подключается нормализатор так:

<link rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/normalize/5.0.0/normalize.min.css">

Заодно сразу подключим файл со стилями. Он пока пустой:

<link rel="stylesheet" href="style.css">

Делаем красивые чекбоксы с анимацией
Из-за нормализатора сломались поля, но это не главная наша проблема

Меняем внешний вид чекбокса

Чтобы сделать анимацию отмечания галочки, мы пойдём на хитрость: отрисуем все элементы вручную с помощью SVG-графики и потом используем её для анимации.

Для этого мы поступим так:

  1. Используем тег <label>, чтобы потом можно было подписать чекбокс, и сразу напишем id элемента, который нужно будет подписать. Этим элементом станет чекбокс. 
  2. Внутри этого тега разместим тег <input>, который создаст нам чекбокс. 
  3. Укажем для чекбокса стиль, чтобы настроить его внешний вид, и id, чтобы подпись поняла, к чему именно она относится. 
  4. Ниже добавим SVG-область, в которой нарисуем сразу рамку и галочку.
  5. Рамка будет видимой сразу, а видимостью галочки мы будем управлять через стили.
  6. Добавим саму подпись чекбокса в теге <span>.

<!DOCTYPE html>
<html lang="ru" >
<head>
  <meta charset="UTF-8">
  <title>Начинка для пиццы</title>
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/5.0.0/normalize.min.css">
  <link rel="stylesheet" href="style.css">

</head>
<body>

<h1>
Что добавить в пиццу?
</h1>

<!-- создаём подпись и указываем, к какому элементу она относится -->
  <label for="myCheckbox01" class="checkbox">

    <!-- размещаем обычный чекбокс и задаём ему нужный класс -->
    <!-- также указываем id, чтобы подпись смогла найти этот инбокс -->
    <input class="checkbox__input" type="checkbox" id="myCheckbox01">
    
    <!-- готовим виртуальное пространство для рисования -->
    <svg class="checkbox__icon" viewBox="0 0 22 22">
      <!-- рисуем красивую рамку чекбокса -->
      <rect width="21" height="21" x=".5" y=".5" fill="#FFF" stroke="#006F94" rx="3" />
      <!-- сразу делаем красивую галочку, но пока делаем её невидимой -->
      <path class="tick" stroke="#6EA340" fill="none" stroke-linecap="round" stroke-width="4" d="M4 10l5 5 9-9" />
    </svg>

    <!-- подписываем наш чекбокс -->
    <span class="checkbox__label">Оливки</span>
  </label>
</body>
</html>
Делаем красивые чекбоксы с анимацией

Видно, что у нас появился новый дизайн чекбокса и галочки, но сломались размеры. Всё дело в том, что мы не настроили стили для этих элементов. Добавим это в файл style.css — читайте комментарии, если что-то будет непонятно:

/* общие настройки для всей страницы */
body {
  /* отступы */
  margin: 2em;
  /* цвет фона */
  background: #f0f0f0;
  /* добавляем адаптивное расположение элементов */
  display: flex;
  /* расстояние между элементами */
  gap: 2rem;
  /* разрешаем увеличивать размеры областей в зависимости от размеров элемента */
  flex-wrap: wrap;
  align-items: flex-start;
}

/* настройки для всего элемента с чекбоксом */
.checkbox {
  /* меняем внешний вид курсора */
  cursor: pointer;
  /* выравниваем элементы по центру */
  display: flex;
  align-items: center;
}

/* отдельные настройки для самого чекбокса */
.checkbox__input {
  /* устанавливаем абсолютное позиционирование */
  position: absolute;
  /* задаём высоту и ширину */
  width: 1.375em;
  height: 1.375em;
  /* делаем чекбокс непрозрачным, чтобы скрыть исходный элемент и заменить его потом нарисованным */
  opacity: 0;
  /* меняем внешний вид курсора */
  cursor: pointer;
}

/* настройки для SVG-иконки */
.checkbox__icon {
  /* размеры совпадают с размерами скрытого чекбокса */
  width: 1.375em;
  height: 1.375em;
  /* убираем ограничение по наименьшей ширине блока */
  flex-shrink: 0;
  /* разрешаем отображать содержимое за пределами блока */
  overflow: visible;
}

/* общие настройки для нового чекбокса и галочки */
.checkbox__icon .tick {
  /* рисовать будем всё отрезками по 20 пикселей */
  stroke-dasharray: 20px;
  /* но сместим следующие отрезки тоже на 20 пикселей, чтобы получить сплошные линии */
  stroke-dashoffset: 20px;
  /* это даст нам плавную анимацию отрисовки галочки */
  transition: stroke-dashoffset 0.2s ease-out;
}

/* настройки для подписи чекбокса */
.checkbox__label {
  /* добавляем отступ слева */
  margin-left: 0.5em;
}
Делаем красивые чекбоксы с анимацией

Стало лучше, появился новый красивый чекбокс, но галочку в него поставить пока нельзя — мы не настроили стили для этого. Исправим это, добавив новые стили в файл:

/* включаем возможность поставить галочку */
.checkbox__input:checked + .checkbox__icon .tick {
  /* убираем смещение для отрезков, чтобы включить анимацию галочки */
  stroke-dashoffset: 0;
}

👉 Обратите внимание на плюс в этой записи. Он означает, что этот стиль применяется к элементам класса .checkbox__icon .tick, которые идут в HTML-коде сразу после отмеченного элемента checkbox__input. Если между элементами с этими классами вставить какой-то другой тег, то ничего не сработает. Такой подход позволяет тонко управлять поведением элементов на странице. 

Делаем красивые чекбоксы с анимацией

Добавляем остальные чекбоксы

Теперь, когда мы научились ставить красивые чекбоксы с анимированными галочками, вернёмся в HTML-код и добавим остальные варианты для пиццы. Делаем это простым копированием и вставкой. Единственное, что нужно не забыть поменять, — числа в id у чекбоксов:

<!DOCTYPE html>
<html lang="ru" >
<head>
  <meta charset="UTF-8">
  <title>Начинка для пиццы</title>
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/5.0.0/normalize.min.css">
  <link rel="stylesheet" href="style.css">

</head>
<body>

<h1>
Что добавить в пиццу?
</h1>

  <!-- создаём подпись и указываем, к какому элементу она относится -->
  <label for="myCheckbox01" class="checkbox">

    <!-- размещаем обычный чекбокс и задаём ему нужный класс -->
    <!-- также указываем id, чтобы подпись смогла найти этот инбокс -->
    <input class="checkbox__input" type="checkbox" id="myCheckbox01">
    
    <!-- готовим виртуальное пространство для рисования -->
    <svg class="checkbox__icon" viewBox="0 0 22 22">
      <!-- рисуем красивую рамку чекбокса -->
      <rect width="21" height="21" x=".5" y=".5" fill="#FFF" stroke="#006F94" rx="3" />
      <!-- сразу делаем красивую галочку, но пока делаем её невидимой -->
      <path class="tick" stroke="#6EA340" fill="none" stroke-linecap="round" stroke-width="4" d="M4 10l5 5 9-9" />
    </svg>

    <!-- подписываем наш чекбокс -->
    <span class="checkbox__label">Оливки</span>
  </label>

  <label for="myCheckbox02" class="checkbox">
    <input class="checkbox__input" type="checkbox" id="myCheckbox02">
    <svg class="checkbox__icon" viewBox="0 0 22 22">
      <rect width="21" height="21" x=".5" y=".5" fill="#FFF" stroke="#006F94" rx="3" />
      <path class="tick" stroke="#6EA340" fill="none" stroke-linecap="round" stroke-width="4" d="M4 10l5 5 9-9" />
    </svg>
    <span class="checkbox__label">Пепперони</span>
  </label>

   <label for="myCheckbox03" class="checkbox">
    <input class="checkbox__input" type="checkbox" id="myCheckbox03">
    <svg class="checkbox__icon" viewBox="0 0 22 22">
      <rect width="21" height="21" x=".5" y=".5" fill="#FFF" stroke="#006F94" rx="3" />
      <path class="tick" stroke="#6EA340" fill="none" stroke-linecap="round" stroke-width="4" d="M4 10l5 5 9-9" />
    </svg>
    <span class="checkbox__label">Каперсы</span>
  </label>

   <label for="myCheckbox04" class="checkbox">
    <input class="checkbox__input" type="checkbox" id="myCheckbox04">
    <svg class="checkbox__icon" viewBox="0 0 22 22">
      <rect width="21" height="21" x=".5" y=".5" fill="#FFF" stroke="#006F94" rx="3" />
      <path class="tick" stroke="#6EA340" fill="none" stroke-linecap="round" stroke-width="4" d="M4 10l5 5 9-9" />
    </svg>
    <span class="checkbox__label">Помидоры</span>
  </label>

</body>
</html>

Делаем красивые чекбоксы с анимацией

Теперь у нас есть все нужные чекбоксы, только они стоят в ряд, а нам хорошо бы их сделать друг под другом. А ещё у нас до сих пор нет автоподсчёта отмеченных галочек.

Объединяем чекбоксы

Самый простой способ что-то объединить в HTML — использовать тег <div>. Объединим наши чекбоксы с помощью него и зададим отдельный класс для оформления:

<!-- общий блок для всех чекбоксов -->
<div class="check-group">
<!-- здесь находится код наших чекбоксов -->
</div>

А теперь пропишем стили в CSS-файле:

.check-group {
  /* делаем группе отдельный фон */
  background: #fff;
  /* ограничиваем ширину блока, чтобы он не расползался */
  max-width: 13rem;
  /* добавляем отступы */
  padding: 1.5rem;
  /* делаем красивое скругление и тень */
  border-radius: 0.5rem;
  box-shadow: 0 1px 3px rgba(0, 0, 10, 0.2);
  /* сразу добавляем два счётчика, они понадобятся для подсчёта выбранных элементов */
  counter-reset: total;
  counter-reset: checked;
}

/* делаем отступы внутри группы */
.check-group > * + * {
  margin-top: 0.75rem;
}
Делаем красивые чекбоксы с анимацией

Самая магия происходит в строке .check-group > * + *, и вот что она означает:

  1. Символ «больше» означает, что правило будет относиться к каким-то элементам, которые лежат внутри тега с классом .check-group.
  2. Звёздочка означает, что это правило относится ко всем элементам, которые лежат внутри.
  3. Плюс означает, что предыдущее правило применяется, только если предыдущий (то есть любой вложенный) и следующий (то есть тоже любой вложенный) элементы идут друг за другом.
  4. Вторая звёздочка означает, что следующий элемент тоже может быть любым.

Если перевести это всё с айтишного на русский, то будет звучать примерно так:

«Отодвинь все вложенные элементы друг от друга на такое-то расстояние. От верха с низом не отодвигай, потому для них это правило не работает».

Ради интереса можете удалить плюс и вторую звёздочку и посмотреть, как изменится внешний вид группы.

Считаем количество отмеченных галочек

Если вы смотрели CSS-код из предыдущего этапа, то обратили внимание на счётчики, которые мы задаём прямо в CSS. Современный CSS получил поддержку таких мини-переменных, которые могут считать количество отображённых элементов. 

Чтобы увеличить значение счётчика на единицу, используют команду  counter-increment. Используем её, чтобы посчитать сначала количество всех чекбоксов:

/* подсчитываем количество всех чекбоксов */
.check-group .checkbox {
  counter-increment: total;
}

А потом считаем количество отмеченных галочек:

/* считаем только отмеченные галочки */
.check-group input[type=checkbox]:checked {
  counter-increment: checked;
}

Последнее, что нам осталось сделать, — вывести на экран итоговое количество выбранных добавок.

Добавляем вывод с результатами

Чтобы вывести строку с результатом, внутри общего блока в самом конце разместим строку:

<div class="check-group__result">Выбрано:</div>

Теперь нам нужно научить станицу правильно заполнять её содержимое. Для этого используем два стиля — просто для блока с этой надписью, чтобы она выглядела солидно, и второй стиль с псевдоклассом :after, который сработает после отрисовки остальных стилей:

/* настраиваем внешний вид строки с результатом */
.check-group__result {
  /* делаем жирный шрифт */
  font-weight: bold;
  /* немного отступаем сверху */
  padding-top: 0.75rem;
  /* рисуем тонкую линию над текстом */
  border-top: 1px solid rgba(0, 0, 0, 0.2);
}

/* обрабатываем результаты */
.check-group__result:after {
  /* добавляем строку со значениями счётчиков */
  content: counter(checked) " / " counter(total);
  /* добавляем отступ слева */
  padding-left: 1ch;
}
Делаем красивые чекбоксы с анимацией

Мы получили анимацию и динамическое управление контентом на странице средствами CSS. Пока что это не слишком функционально, потому что мы не отправляем эти данные никуда на сервер и не берём с клиента деньги за пиццу, но зато CSS. Есть чем гордиться 🙂

Готовый результат можно посмотреть на странице проекта.

<!DOCTYPE html>
<html lang="ru" >
<head>
  <meta charset="UTF-8">
  <title>Начинка для пиццы</title>
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/5.0.0/normalize.min.css">
  <link rel="stylesheet" href="style.css">

</head>
<body>

<h1>
Что добавить в пиццу?
</h1>
<!-- общий блок для всех чекбоксов -->
<div class="check-group">
  <!-- создаём подпись и указываем, к какому элементу она относится -->
  <label for="myCheckbox01" class="checkbox">

    <!-- размещаем обычный чекбокс и задаём ему нужный класс -->
    <!-- также указываем id, чтобы подпись смогла найти этот инбокс -->
    <input class="checkbox__input" type="checkbox" id="myCheckbox01">
    
    <!-- готовим виртуальное пространство для рисования -->
    <svg class="checkbox__icon" viewBox="0 0 22 22">
      <!-- рисуем красивую рамку чекбокса -->
      <rect width="21" height="21" x=".5" y=".5" fill="#FFF" stroke="#006F94" rx="3" />
      <!-- сразу делаем красивую галочку, но пока делаем её невидимой -->
      <path class="tick" stroke="#6EA340" fill="none" stroke-linecap="round" stroke-width="4" d="M4 10l5 5 9-9" />
    </svg>

    <!-- подписываем наш чекбокс -->
    <span class="checkbox__label">Оливки</span>
  </label>

  <label for="myCheckbox02" class="checkbox">
    <input class="checkbox__input" type="checkbox" id="myCheckbox02">
    <svg class="checkbox__icon" viewBox="0 0 22 22">
      <rect width="21" height="21" x=".5" y=".5" fill="#FFF" stroke="#006F94" rx="3" />
      <path class="tick" stroke="#6EA340" fill="none" stroke-linecap="round" stroke-width="4" d="M4 10l5 5 9-9" />
    </svg>
    <span class="checkbox__label">Пепперони</span>
  </label>

   <label for="myCheckbox03" class="checkbox">
    <input class="checkbox__input" type="checkbox" id="myCheckbox03">
    <svg class="checkbox__icon" viewBox="0 0 22 22">
      <rect width="21" height="21" x=".5" y=".5" fill="#FFF" stroke="#006F94" rx="3" />
      <path class="tick" stroke="#6EA340" fill="none" stroke-linecap="round" stroke-width="4" d="M4 10l5 5 9-9" />
    </svg>
    <span class="checkbox__label">Каперсы</span>
  </label>

   <label for="myCheckbox04" class="checkbox">
    <input class="checkbox__input" type="checkbox" id="myCheckbox04">
    <svg class="checkbox__icon" viewBox="0 0 22 22">
      <rect width="21" height="21" x=".5" y=".5" fill="#FFF" stroke="#006F94" rx="3" />
      <path class="tick" stroke="#6EA340" fill="none" stroke-linecap="round" stroke-width="4" d="M4 10l5 5 9-9" />
    </svg>
    <span class="checkbox__label">Помидоры</span>
  </label>

  <!-- считаем и выводим результаты -->
  <div class="check-group__result">Выбрано:</div>
</div>


  
</body>
</html>

/* общие настройки для всей страницы */
body {
  /* отступы */
  margin: 2em;
  /* цвет фона */
  background: #f0f0f0;
  /* добавляем адаптивное расположение элементов */
  display: flex;
  /* расстояние между элементами */
  gap: 2rem;
  /* разрешаем увеличивать размеры областей в зависимости от размеров элемента */
  flex-wrap: wrap;
  align-items: flex-start;
}

/* настройки для всего элемента с чекбоксом */
.checkbox {
  /* меняем внешний вид курсора */
  cursor: pointer;
  /* выравниваем элементы по центру */
  display: flex;
  align-items: center;
}

/* отдельные настройки для самого чекбокса */
.checkbox__input {
  /* устанавливаем абсолютное позиционирование */
  position: absolute;
  /* задаём высоту и ширину */
  width: 1.375em;
  height: 1.375em;
  /* делаем чекбокс непрозрачным, чтобы скрыть исходный элемент и заменить его потом нарисованным */
  opacity: 0;
  /* меняем внешний вид курсора */
  cursor: pointer;
}

/* настройки для SVG-иконки */
.checkbox__icon {
  /* размеры совпадают с размерами скрытого чекбокса */
  width: 1.375em;
  height: 1.375em;
  /* убираем ограничение по наименьшей ширине блока */
  flex-shrink: 0;
  /* разрешаем отображать содержимое за пределами блока */
  overflow: visible;
}

/* общие настройки для нового чекбокса и галочки */
.checkbox__icon .tick {
  /* рисовать будем всё отрезками по 20 пикселей */
  stroke-dasharray: 20px;
  /* но сместим начало всех отрезков тоже на 20 пикселей, чтобы пока галочка не рисовалась */
  stroke-dashoffset: 20px;
  /* это даст нам плавную анимацию отрисовки галочки */
  transition: stroke-dashoffset 0.2s ease-out;
}

/* настройки для подписи чекбокса */
.checkbox__label {
  /* добавляем отступ слева */
  margin-left: 0.5em;
}

/* включаем возможность поставить галочку */
.checkbox__input:checked + .checkbox__icon .tick {
  /* убираем смещение для отрезков, чтобы включить анимацию галочки */
  stroke-dashoffset: 0;
}


.check-group {
  /* делаем группе отдельный фон */
  background: #fff;
  /* ограничиваем ширину блока, чтобы он не расползался */
  max-width: 13rem;
  /* добавляем отступы */
  padding: 1.5rem;
  /* делаем красивое скругление и тень */
  border-radius: 0.5rem;
  box-shadow: 0 1px 3px rgba(0, 0, 10, 0.2);
  /* сразу добавляем два счётчика, они понадобятся для подсчёта выбранных элементов */
  counter-reset: total;
  counter-reset: checked;
}

/* делаем отступы внутри группы */
.check-group > * + * {
  margin-top: 0.75rem;
}

/* подсчитываем количество всех чекбоксов */
.check-group .checkbox {
  counter-increment: total;
}

/* считаем только отмеченные галочки */
.check-group input[type=checkbox]:checked {
  counter-increment: checked;
}

/* настраиваем внешний вид строки с результатом */
.check-group__result {
  /* делаем жирный шрифт */
  font-weight: bold;
  /* немного отступаем сверху */
  padding-top: 0.75rem;
  /* рисуем тонкую линию над текстом */
  border-top: 1px solid rgba(0, 0, 0, 0.2);
}

/* обрабатываем результаты */
.check-group__result:after {
  /* добавляем строку со значениями счётчиков */
  content: counter(checked) " / " counter(total);
  /* добавляем отступ слева */
  padding-left: 1ch;
}

Веб-разработка — это новый чёрный
На базе веб-технологий делают всё — от сложного софта до высокобюджетных игр. Изучите технологии и начните карьеру в ИТ. Старт бесплатно. Попробуйте, вдруг вам понравится.
Изучить
Веб-разработка — это новый чёрный

Текст:

Михаил Полянин

Редактор:

Максим Ильяхов

Художник:

Даня Берковский

Корректор:

Ирина Михеева

Вёрстка:

Кирилл Климентьев

Соцсети:

Алина Грызлова

Получите ИТ-профессию
В «Яндекс Практикуме» можно стать разработчиком, тестировщиком, аналитиком и менеджером цифровых продуктов. Первая часть обучения всегда бесплатная, чтобы попробовать и найти то, что вам по душе. Дальше — программы трудоустройства.
Начать карьеру в ИТ
Получите ИТ-профессию Получите ИТ-профессию Получите ИТ-профессию Получите ИТ-профессию
medium