Для начала разметим чекбокс:

<label>
  <input type="checkbox" checked /> Чекбокс
</label>

К сожалению, сам чекбокс стилизовать невозможно, поэтому мы спрячем настоящий чекбокс, добавим вспомогательный span и стилизуем его. Для этого воспользуемся псевдоклассом :checked, указывающим на состояние чекбокса, и селектором +, позволяющим выбрать соседний элемент:

/* html */
<label>
  <input class="checkbox" type="checkbox" />
  <span class="fake-checkbox"></span> Чекбокс
</label>
/* css */
label {
  display: block;
  cursor: pointer;
}
​
.checkbox { 
  display: none;
}
​
.fake-checkbox { 
  display: inline-block;
  width: 20px;
  height: 20px;
  border: 2px solid #3c7e5e;
  /* Выровняем по тексту */
  vertical-align: middle;
  margin-top: -2px;
  margin-right: 4px;
}
​
/* ".checkbox:checked" срабатывает
когда checkbox отмечен, а 
"+ .fake-checkbox" выбирает следующий 
элемент с классом fake-checkbox */
.checkbox:checked + .fake-checkbox {
  background: #3c7e5e;
}

Улучшим чекбокс, заменив закрашивание на настоящую галочку. Для этого воспользуемся псевдоэлементом ::before, который позволяет вставить дополнительный элемент перед указанным:

/* css */
​
/* ... */
​
/* Вместо самого элемента, выберем 
псевдоэлемент с помощью "::before" */
.checkbox:checked + .fake-checkbox::before {
  content: "✓";
  font-size: 35px;
  color: #3c7e5e;
  /* Выровняем галочку */
  display: block;
  margin-top: -10px;
}

Если галка в вашем шрифте не подходит, можно подключить для неё другой шрифт:

/* css */
​
.checkbox:checked + .fake-checkbox::before {
  /* ... */
  font-family: Bureausans;
}

Если галка нестандартная, можно подставить картинку:

/* css */
​
/* ... */
​
.fake-checkbox::before {
  content: '';
  display: block;
  width: 100%;
  height: 100%;
  background: url("circle.png") no-repeat center / contain;
}
​
.checkbox:checked + .fake-checkbox::before {
  background-image: url("filled-circle.png");
}

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

/* css */
​
/* ... */
​
.fake-checkbox {
  position: relative;
}
​
​/* Добавим второй элемент и 
разместим их друг над другом */
.fake-checkbox::before,
.fake-checkbox::after {
  content: '';
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  background: url("circle.png") no-repeat center / contain;
}
​
/* Второму элементу поменяем картику
и спрячем его прозрачностью */
.fake-checkbox::after {
  background-image: url("filled-circle.png");
  opacity: 0;
}
​
/* Для отмеченного чекбокса поменяем 
прозрачность элементов на противоположные */
.checkbox:checked + .fake-checkbox::before {
  opacity: 0;
}
​
.checkbox:checked + .fake-checkbox::after {
  opacity: 1;
}
P. S. Это был совет о веб‑разработке. Хотите знать всё о коде, тестах, фронтенд‑разработке, цеэсэсе, яваскрипте, рельсах и джейде? Присылайте вопросы.
Отправить
Поделиться
Запинить

Комментарии

Руст,

Сафари при активации чекбокса выделяет и текст. Можно ли это победить?

25 окт 2018

Александр, такое свойство есть и обычного элемента. Обычно это не проблема, потому что чекбокс редко кликается дважды кем‑то кроме самого разработчика при проверке.

Тем не менее можно воспользоваться user‑select: none, но в этом случае вы запретите выделение полностью.

25 окт 2018

Спан избыточен, псевдоэлементы могут быть у лейбла.
Ставить слэш в одиночных тегах — моветон, XHTML давно умер.

25 окт 2018

А дальше для всего вот этого надо решать кучу неинтересных задач.

  • Скрытый чекбокс недоступен с клавиатуры.
  • Скрытый чекбокс не читается скринридером.
  • Галочка уезжает из квадрата, если пользователь запретил загрузку шрифтов и/или выставил себе отличный от дефолтного интерлиньяж.
  • PNG‑картинка расплывается на HiDPI‑мониторе (если верстальщик не положил заранее картинку двойного размера).
25 окт 2018

У предложенного решения неприятный побочный эффект — такой чекбокс недоступен с клавиатуры. Это важно не только для пользователей с ограниченными возможностями. Но и в больших формах, админках, где удобно скакать по инпутам табом. Хороший (в смысле плохой) пример — чекбоксы в админке Битрикса. Это боль.

Чтобы кастомный чекбокс был доступен с клавиатуры, надо как‑то оставить штатный чекбокс в DOM и добавить стиль на кастомный чекбокс с помощью :focus. Небезупречный пример реализации: https://codepen.io/Ollyak/pen/wYRaOw

25 окт 2018

Присоединяюсь к Юрию и Дмитрию — получившийся чекбокс недоступен с клавиатуры и для скринридеров.
Как скрыть чекбокс правильно, можно почитать в справке к ally.js Родни Рейма: https://allyjs.io/tutorials/hiding-elements.html

28 окт 2018

Чекбокс с клавиатуры будет доступен, но только не нужно его скрывать через display:none.
Достаточно добавить прозрачность, абсолютно спозиционировать над фэйковым боксом и отключить пользовательские события pointer-events: none. Только не выносите его на северный полюс как в примере у Димы ↑ — left: -100000px.
Это кстати ещё необходимо чтобы показывались браузерные ошибки валидации.

Евгений Закурдаев, без элемента нельзя будет стилизовать состояния инпута.

30 окт 2018

Рекомендуем похожие советы