Как создать пользовательский переключатель с помощью CSS

В этой статье мы расскажем, как с помощью CSS создать пользовательский переключатель.

Недавно мы создали компонент Radio Switch. Чтобы обеспечить его доступность необходимо учитывать некоторые моменты.

Компонент, который мы создадим в этом руководстве, основан на платформе CodyHouse.

Давайте сделаем это!

Основная идея заключается в следующем: мы создаем список, состоящий из двух переключателей (два доступных варианта). Затем скрываем эти элементы ввода и применяем стиль к видимым label.

Вот структура HTML:

<ul class="radio-switch">
  <li class="radio-switch__item">
    <input type="radio" class="radio-switch__input sr-only" id="radio1" name="radioSwitch" checked>
    <label for="radio1" class="radio-switch__label">Monthly</label>
  </li>

  <li class="radio-switch__item">
    <input type="radio" class="radio-switch__input sr-only" id="radio2" name="radioSwitch">
    <label for="radio2" class="radio-switch__label">Yearly</label>
  </li>
</ul>

Мы применили класс sr-only к обоим элементам ввода, чтобы визуально скрыть их. Но они все еще доступны для программ чтения с экрана. Вы можете узнать больше об этом классе из документации к платформе.

Теперь добавим стили к label, чтобы они были выровнены и имели одинаковую ширину. А также к элементу .radio-switch:

:root {
  // стили
  --radio-switch-width: 186px;
  --radio-switch-height: 46px;
  --radio-switch-padding: 3px;
  --radio-switch-radius: 50em;

  // анимация
  --radio-switch-animation-duration: 0.3s;
}

.radio-switch {
  display: inline-flex;
  padding: var(--radio-switch-padding);
  border-radius: var(--radio-switch-radius);
  border: 1px solid var(--color-contrast-low);
}

.radio-switch__item {
  height: calc(var(--radio-switch-height) - 2*var(--radio-switch-padding));
  width: calc(var(--radio-switch-width)*0.5 - var(--radio-switch-padding));
}

.radio-switch__label {
  display: block;
  line-height: calc(var(--radio-switch-height) - 2*var(--radio-switch-padding));
  text-align: center;
  border-radius: var(--radio-switch-radius);
}

После этого создадим маркер, чтобы использовать его в качестве элемента фона, перемещающегося с одной стороны переключателя на другую. Можно добавить этот маркер во второй элемент списка:

<ul class="radio-switch">
  <li class="radio-switch__item">
    <!-- input + label --> 
  </li>

  <li class="radio-switch__item">
    <!-- input + label --> 
    <div class="radio-switch__marker" aria-hidden="true"></div>
  </li>
</ul>

Мы добавили aria-hidden true, чтобы скрыть элемент от программ чтения с экрана. Теперь стилизуем его:

.radio-switch__item {
  position: relative;
}

.radio-switch__marker {
  position: absolute;
  top: 0;
  left: -100%;
  height: 100%;
  width: 100%;
  background-color: var(--color-primary);
  border-radius: var(--radio-switch-radius); 
  transition: transform var(--radio-switch-animation-duration);

  .radio-switch__input:checked ~ & { // перемещаем маркер с одной стороны на другую
    transform: translateX(100%);
  }
}

Нужно, чтобы пользователи, перемещающиеся по странице с помощью клавиатуры, видели, что элемент переключателя находится в фокусе ввода. Для этого можно использовать псевдокласс :focus-within:

.radio-switch {
  // ...

  &:focus-within {
    box-shadow: 0 0 0 3px var(--color-contrast-lower);
  }
}

Когда переключатель выделен фокусом ввода (класс .radio-switch), к переключателю добавляется тень, которая сигнализирует о его выделении фокусом ввода.

Примечание: на момент написания данной статьи псевдокласс :focus-within поддерживается не во всех современных браузерах.

Чтобы исправить это, можно реализовать другой эффект а затем переписать его для браузеров, которые поддерживают :focus-within :

.radio-switch {
  // ...

  &:focus-within {
    box-shadow: 0 0 0 3px var(--color-contrast-lower);
  }
}

.radio-switch__label {
  //..

  .radio-switch__input:focus ~ & { 
// эффект выделения фокусом ввода в браузерах, не поддерживающих :focus-within
    background-color: lightness(var(--color-primary), 0.6);
  }

  :not(*):focus-within, // trick to detect :focus-within support -> https://css-tricks.com/using-feature-detection-conditionals-and-groups-with-selectors/
  .radio-switch__input:focus ~ & { 
    // переключение эффекта фокуса для браузеров, поддерживающих :focus-within
    background-color: transparent;
  }
}

Пользовательский переключатель готов к использованию.

Вадим Дворниковавтор-переводчик статьи «How to create a custom radio switch in CSS»

Дайте знать, что вы думаете по этой теме материала в комментариях. Мы очень благодарим вас за ваши комментарии, подписки, отклики, дизлайки, лайки!