Создание собственной темы

Механизм внешнего вида в Tom Select построен на переопределении стандартных CSS-классов, которые библиотека генерирует при инициализации компонента. Основная идея заключается в том, что JavaScript отвечает только за поведение и структуру DOM, а визуальная часть полностью переносится в CSS-слой.

Базовая разметка после инициализации включает несколько ключевых узлов:

  • контейнер селекта
  • поле ввода
  • контейнер выбранных элементов
  • выпадающий список опций
  • элементы списка

Каждый из этих узлов получает стабильные классы, которые используются как точки расширения для тем.

Базовые классы и точки переопределения

Ключевые CSS-классы, которые определяют внешний вид:

  • .tom-select — корневой контейнер
  • .ts-control — область ввода и выбранных значений
  • .ts-wrapper — обёртка компонента
  • .ts-dropdown — выпадающий список
  • .option — элемент списка
  • .item — выбранный элемент

Именно эти классы становятся основой для построения любой кастомной темы. Перегрузка внешнего вида происходит через увеличение специфичности или подключение темы после стандартного CSS библиотеки.

Стратегии создания собственной темы

Существует несколько подходов к построению темы:

  1. Полное переопределение стандартных стилей
  2. Наследование с точечным изменением переменных и классов
  3. Создание изолированной темы с префиксами
  4. Использование CSS-переменных как уровня конфигурации

Наиболее масштабируемым считается подход с CSS-переменными, так как он позволяет менять визуальный стиль без изменения структуры селекторов.

Использование CSS-переменных

Создание темы удобно начинать с определения набора переменных:

:root {
  --ts-bg: #ffffff;
  --ts-border: #d0d7de;
  --ts-text: #1f2328;
  --ts-primary: #2f81f7;
  --ts-radius: 8px;
  --ts-shadow: 0 6px 24px rgba(0, 0, 0, 0.12);
}

Далее переменные связываются с ключевыми элементами:

.ts-wrapper {
  font-family: system-ui, sans-serif;
}

.ts-control {
  background: var(--ts-bg);
  border: 1px solid var(--ts-border);
  border-radius: var(--ts-radius);
  color: var(--ts-text);
  padding: 8px 10px;
  transition: border-color 0.2s ease, box-shadow 0.2s ease;
}

Такой подход позволяет централизованно управлять визуальной системой.

Темизация состояний компонента

Tom Select активно использует состояния, которые отражаются через классы:

  • .focus
  • .dropdown-active
  • .disabled
  • .loading
  • .error (при кастомных расширениях)

Пример стилизации состояния фокуса:

.ts-wrapper.focus .ts-control {
  border-color: var(--ts-primary);
  box-shadow: 0 0 0 3px rgba(47, 129, 247, 0.2);
}

Состояние активного dropdown:

.ts-wrapper.dropdown-active .ts-dropdown {
  opacity: 1;
  transform: translateY(0);
  pointer-events: auto;
}

Оформление выпадающего списка

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

.ts-dropdown {
  background: var(--ts-bg);
  border: 1px solid var(--ts-border);
  border-radius: var(--ts-radius);
  box-shadow: var(--ts-shadow);
  margin-top: 6px;
  overflow: hidden;
  opacity: 0;
  transform: translateY(-6px);
  transition: opacity 0.15s ease, transform 0.15s ease;
}

Элементы списка:

.ts-dropdown .option {
  padding: 10px 12px;
  cursor: pointer;
  color: var(--ts-text);
  transition: background 0.15s ease;
}

.ts-dropdown .option:hover {
  background: rgba(47, 129, 247, 0.08);
}

Выделенный элемент:

.ts-dropdown .option.active {
  background: rgba(47, 129, 247, 0.12);
  color: var(--ts-primary);
}

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

Мультивыбор формирует набор “чипов” внутри control-блока:

.ts-control .item {
  display: inline-flex;
  align-items: center;
  background: rgba(47, 129, 247, 0.12);
  color: var(--ts-primary);
  border-radius: 999px;
  padding: 4px 8px;
  margin: 2px;
  font-size: 13px;
}

Кнопка удаления элемента:

.ts-control .item .remove {
  margin-left: 6px;
  cursor: pointer;
  opacity: 0.6;
}

.ts-control .item .remove:hover {
  opacity: 1;
}

Кастомизация input-поля

Поле ввода внутри control требует отдельного внимания из-за взаимодействия с flex-layout:

.ts-control input {
  border: none;
  outline: none;
  background: transparent;
  color: var(--ts-text);
  min-width: 60px;
  flex: 1;
}

При необходимости можно скрывать input в режиме только выбора:

.ts-wrapper.single .ts-control input {
  display: none;
}

Изменение индикаторов и caret

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

.ts-wrapper::after {
  content: "";
  position: absolute;
  right: 10px;
  top: 50%;
  width: 8px;
  height: 8px;
  border-right: 2px solid var(--ts-text);
  border-bottom: 2px solid var(--ts-text);
  transform: translateY(-50%) rotate(45deg);
  pointer-events: none;
}

Состояние раскрытия:

.ts-wrapper.dropdown-active::after {
  transform: translateY(-50%) rotate(-135deg);
}

Тёмная тема как слой поверх базовой

Тёмная тема чаще всего реализуется через переопределение переменных:

[data-theme="dark"] {
  --ts-bg: #161b22;
  --ts-border: #30363d;
  --ts-text: #e6edf3;
  --ts-primary: #58a6ff;
  --ts-shadow: 0 6px 24px rgba(0, 0, 0, 0.5);
}

Такой подход исключает дублирование селекторов и уменьшает вероятность конфликтов.

Изоляция темы и предотвращение конфликтов

При интеграции в крупные проекты возникает риск пересечения глобальных стилей. Для уменьшения коллизий используются стратегии:

  • повышение специфичности через контейнер
  • использование namespace-класса
  • изоляция через BEM-подобные модификаторы
  • ограничение влияния через родительский селектор

Пример изоляции:

.form-scope .ts-wrapper {
  font-size: 14px;
}

Анимации и визуальная динамика

Плавность интерфейса достигается через минимальные трансформации:

.ts-dropdown {
  transition: opacity 0.15s ease, transform 0.15s ease;
}

Анимация появления элементов:

.ts-dropdown .option {
  transition: background 0.12s ease, padding-left 0.12s ease;
}

При необходимости создаётся эффект “въезда”:

.ts-wrapper.focus .ts-control {
  transform: scale(1.01);
}

Адаптивность и мобильное поведение

На мобильных устройствах важна оптимизация размеров и кликабельности:

@media (max-width: 600px) {
  .ts-control {
    padding: 12px;
  }

  .ts-dropdown .option {
    padding: 14px;
    font-size: 15px;
  }
}

Также часто увеличивается зона клика у элементов удаления:

.ts-control .item .remove {
  padding: 4px;
}

Переопределение глубоко вложенных элементов

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

.ts-wrapper .ts-dropdown-content .option.disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

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

Структура масштабируемой темы

Типовая организация темы строится по модульному принципу:

  • variables.css — набор переменных
  • base.css — базовые стили компонентов
  • dropdown.css — стили списка
  • control.css — стили поля ввода
  • states.css — состояния
  • theme-dark.css — тёмная тема

Такая структура позволяет расширять систему без нарушения существующих правил каскада.

Переиспользование темы между проектами

Для повторного использования тема должна быть независима от глобальных стилей приложения. Это достигается:

  • отказом от глобальных теговых селекторов
  • использованием только классов Tom Select и namespace-обёрток
  • вынесением всех цветов в переменные
  • минимизацией зависимости от внешних шрифтов и reset-стилей