Стилизация состояний

Choices.js формирует интерфейс вокруг нативных элементов <select> и <input>, заменяя их на кастомную структуру DOM с набором предсказуемых классов состояний. Стилизация этих состояний строится не на глубокой модификации HTML, а на работе с классами, которые библиотека динамически добавляет в ответ на действия пользователя и изменения данных.

Ключевая особенность архитектуры — разделение визуальных состояний и логики. Библиотека не требует переопределения поведения элементов, а предоставляет набор CSS-хуков:

  • .is-focused
  • .is-open
  • .is-disabled
  • .is-active
  • .is-highlighted
  • .is-selected
  • .is-flipped
  • .has-value

Эти классы становятся основой для построения интерфейсных состояний без необходимости вмешиваться в JavaScript-логику.


Фокус — одно из центральных состояний компонента, определяющее визуальную активность поля ввода или контейнера.

В Choices.js фокус обычно отражается через класс .is-focused, который добавляется к корневому контейнеру .choices.

Структурно это выглядит так:

<div class="choices is-focused">
  <div class="choices__inner">
    <input class="choices__input">
  </div>
</div>

Стилизация фокуса должна учитывать не только визуальное выделение, но и контекст взаимодействия пользователя.

.choices.is-focused .choices__inner {
  border-color: #2684ff;
  box-shadow: 0 0 0 3px rgba(38, 132, 255, 0.2);
}

.choices.is-focused .choices__input {
  caret-color: #2684ff;
}

Фокус часто комбинируется с hover-состояниями, поэтому важно избегать конфликтующих визуальных эффектов. В приоритете всегда должен быть фокус, так как он отражает активное управление с клавиатуры.


Состояние открытия выпадающего списка

Открытие списка опций управляется классом .is-open. Он добавляется к корневому контейнеру и активирует отображение dropdown-блока .choices__list--dropdown.

DOM-структура при открытии:

<div class="choices is-open">
  <div class="choices__inner"></div>
  <div class="choices__list choices__list--dropdown is-active"></div>
</div>

Основные аспекты стилизации:

  • управление видимостью списка
  • анимация появления
  • изменение скруглений и теней
  • контроль z-index
.choices__list--dropdown {
  opacity: 0;
  transform: translateY(-6px);
  pointer-events: none;
  transition: all 0.2s ease;
}

.choices.is-open .choices__list--dropdown {
  opacity: 1;
  transform: translateY(0);
  pointer-events: auto;
}

Для более сложных интерфейсов часто применяется изменение скруглений контейнера при открытии:

.choices.is-open .choices__inner {
  border-bottom-left-radius: 0;
  border-bottom-right-radius: 0;
}

Это позволяет визуально «склеить» поле ввода и список опций в единый компонент.


Состояние disabled

Отключённое состояние формируется через .is-disabled. Оно блокирует взаимодействие и изменяет визуальное восприятие элемента.

<div class="choices is-disabled">
  <select disabled></select>
</div>

Основная задача стилизации — передать неактивность без потери читаемости.

.choices.is-disabled .choices__inner {
  background-color: #f5f5f5;
  border-color: #ddd;
  color: #999;
  cursor: not-allowed;
}

.choices.is-disabled .choices__input {
  cursor: not-allowed;
}

Важно учитывать, что disabled состояние распространяется на весь компонент, включая элементы выбора и удаления тегов в multi-select.


Активные элементы и навигация

При работе с выпадающим списком Choices.js использует класс .is-highlighted, который обозначает текущий элемент под курсором или клавиатурной навигацией.

<div class="choices__item choices__item--choice is-highlighted">
  Option 1
</div>

Стилизация этого состояния критична для доступности интерфейса.

.choices__item--choice.is-highlighted {
  background-color: #2684ff;
  color: #fff;
}

При проектировании интерфейса важно избегать слишком слабого контраста, так как highlighted-элемент часто используется при навигации стрелками.


Состояние выбранных элементов

В single-select выбранное значение отображается в .choices__item--selectable, а в multi-select дополнительно формируются теги .choices__item--choice.

Для выбранных элементов применяется класс .is-selected.

<div class="choices__item is-selected">
  Option 2
</div>

Стилизация:

.choices__item.is-selected {
  font-weight: 500;
  background-color: #e6f0ff;
}

В multi-select выбранные элементы отображаются как отдельные блоки:

<div class="choices__list--multiple">
  <div class="choices__item choices__item--selectable">
    Tag 1
  </div>
</div>

Их стилизация часто включает:

  • фон
  • рамки
  • кнопки удаления
.choices__item--selectable {
  background-color: #f1f3f5;
  border-radius: 16px;
  padding: 4px 10px;
}

.choices__item--selectable .choices__button {
  margin-left: 6px;
}

Состояние наличия значения

Класс .has-value используется для определения заполненности поля. Он добавляется к контейнеру, когда пользователь выбрал значение или ввёл текст.

<div class="choices has-value">

Это состояние часто используется для:

  • уменьшения placeholder
  • изменения цвета текста
  • анимации label-подобных элементов
.choices.has-value .choices__placeholder {
  opacity: 0;
}

В более сложных интерфейсах .has-value применяется как триггер для плавающих меток:

.choices.has-value + label {
  transform: translateY(-18px);
  font-size: 12px;
}

Состояние переворота списка

Choices.js автоматически определяет доступное пространство и может добавлять класс .is-flipped, если dropdown отображается вверх.

<div class="choices is-open is-flipped">

Это состояние требует отдельного контроля позиционирования:

.choices.is-flipped .choices__list--dropdown {
  top: auto;
  bottom: 100%;
  transform-origin: bottom;
}

При стилизации важно учитывать, что анимации должны зеркально отражаться:

.choices.is-flipped .choices__list--dropdown {
  transform: translateY(6px);
}

.choices.is-open.is-flipped .choices__list--dropdown {
  transform: translateY(0);
}

Состояние hover и взаимодействие с мышью

Хотя Choices.js не добавляет отдельные классы для hover, он полагается на CSS-псевдокласс :hover внутри структуры элементов.

.choices__item--choice:hover {
  background-color: #f0f6ff;
}

Однако важно учитывать взаимодействие hover и keyboard navigation. При наличии .is-highlighted hover должен уступать приоритет.

.choices__item--choice.is-highlighted,
.choices__item--choice:hover {
  background-color: #2684ff;
  color: #fff;
}

В более строгих интерфейсах hover можно отключать при активной клавиатурной навигации через дополнительный класс состояния:

.choices.is-focused .choices__item--choice:hover {
  background-color: transparent;
}

Состояние ошибки и валидации

Choices.js не предоставляет встроенной системы ошибок, но хорошо интегрируется с внешними валидаторами. Обычно к контейнеру добавляется кастомный класс, например .has-error.

<div class="choices has-error">

Стилизация ошибок строится на визуальном акценте:

.choices.has-error .choices__inner {
  border-color: #e53935;
  box-shadow: 0 0 0 3px rgba(229, 57, 53, 0.15);
}

Дополнительно можно стилизовать сообщения:

.choices__error-message {
  color: #e53935;
  font-size: 12px;
  margin-top: 4px;
}

Комбинации состояний

Реальные интерфейсы редко находятся в одном состоянии. Choices.js позволяет комбинировать классы, и именно это определяет сложность стилизации.

Примеры комбинаций:

  • .is-focused.is-open
  • .is-disabled.has-value
  • .is-flipped.is-open
  • .has-error.is-focused

Приоритеты состояний должны быть заранее определены:

  1. disabled
  2. error
  3. focused
  4. open
  5. hover

Пример приоритетной логики в CSS:

.choices.is-disabled .choices__inner {
  pointer-events: none;
  opacity: 0.6;
}

.choices.has-error .choices__inner {
  border-color: #e53935;
}

.choices.is-focused .choices__inner {
  border-color: #2684ff;
}

Анимация переходов состояний

Плавные переходы между состояниями повышают восприятие интерфейса. Choices.js хорошо работает с CSS transitions, особенно для:

  • dropdown
  • border-color
  • background-color
  • opacity
  • transform
.choices__inner {
  transition: border-color 0.2s ease, box-shadow 0.2s ease;
}

.choices__list--dropdown {
  transition: opacity 0.2s ease, transform 0.2s ease;
}

Важно избегать анимации layout-свойств вроде height, если компонент используется в динамических формах, чтобы не создавать скачков интерфейса.


Состояния элементов множественного выбора

Multi-select добавляет дополнительный слой состояний для тегов:

  • hover по тегу
  • удаление
  • фокус на кнопке удаления
.choices__item--selectable:hover {
  background-color: #e9ecef;
}

.choices__button {
  opacity: 0.6;
  transition: opacity 0.2s ease;
}

.choices__button:hover {
  opacity: 1;
}

При удалении элемента часто используется визуальная обратная связь:

.choices__item--selectable.is-deleting {
  opacity: 0;
  transform: scale(0.9);
  transition: all 0.15s ease;
}