Механизм внешнего вида в Tom Select построен на переопределении стандартных CSS-классов, которые библиотека генерирует при инициализации компонента. Основная идея заключается в том, что JavaScript отвечает только за поведение и структуру DOM, а визуальная часть полностью переносится в CSS-слой.
Базовая разметка после инициализации включает несколько ключевых узлов:
Каждый из этих узлов получает стабильные классы, которые используются как точки расширения для тем.
Ключевые CSS-классы, которые определяют внешний вид:
.tom-select — корневой контейнер.ts-control — область ввода и выбранных значений.ts-wrapper — обёртка компонента.ts-dropdown — выпадающий список.option — элемент списка.item — выбранный элементИменно эти классы становятся основой для построения любой кастомной темы. Перегрузка внешнего вида происходит через увеличение специфичности или подключение темы после стандартного 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;
}
Поле ввода внутри 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;
}
Иконка раскрытия списка обычно реализуется через псевдоэлементы:
.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);
}
Такой подход исключает дублирование селекторов и уменьшает вероятность конфликтов.
При интеграции в крупные проекты возникает риск пересечения глобальных стилей. Для уменьшения коллизий используются стратегии:
Пример изоляции:
.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;
}
При этом важно избегать чрезмерной вложенности, так как она усложняет поддержку темы.
Типовая организация темы строится по модульному принципу:
Такая структура позволяет расширять систему без нарушения существующих правил каскада.
Для повторного использования тема должна быть независима от глобальных стилей приложения. Это достигается: