MapLibre GL JS использует DOM-контролы, которые поверх WebGL-карты
накладываются как обычные HTML-элементы. Это делает стилизацию контролов
полностью управляемой через CSS, без необходимости вмешиваться в
рендеринг карты. Архитектура контролов построена вокруг единого
контейнера
.maplibregl-ctrl-top-right / top-left / bottom-right / bottom-left,
внутри которого располагаются группы .maplibregl-ctrl-group
и отдельные элементы управления.
Каждый встроенный контрол в MapLibre GL JS реализует интерфейс с
методами onAdd(map) и onRemove(map). При
добавлении на карту создаётся DOM-узел, который автоматически помещается
в один из контейнеров:
.maplibregl-ctrl-top-left.maplibregl-ctrl-top-right.maplibregl-ctrl-bottom-left.maplibregl-ctrl-bottom-rightКонтролы группируются в контейнер
.maplibregl-ctrl-group, который задаёт базовую визуальную
рамку, тень и вертикальное/горизонтальное выравнивание элементов.
Ключевая особенность заключается в том, что визуальный стиль полностью отделён от логики: сам контрол не знает, как он выглядит, он лишь создаёт структуру DOM.
Стилизация начинается с переопределения стандартных классов:
.maplibregl-ctrl — базовый контейнер любого
контрола.maplibregl-ctrl-group — группа кнопок.maplibregl-ctrl button — интерактивные элементы.maplibregl-ctrl-icon — иконки внутри кнопокТиповая структура кнопки:
<div class="maplibregl-ctrl maplibregl-ctrl-group">
<button class="maplibregl-ctrl-icon maplibregl-ctrl-zoom-in"></button>
<button class="maplibregl-ctrl-icon maplibregl-ctrl-zoom-out"></button>
</div>
Именно через эти классы происходит основная стилизация: размеры, фон, hover-состояния, активные состояния и отключение.
Стандартный стиль контролов ориентирован на универсальную светлую тему с полупрозрачным фоном и мягкой тенью. Для полной кастомизации обычно переопределяются следующие свойства:
background-colorbox-shadowborder-radiusbordercolorfill (для SVG-иконок)opacity при hover/activeПример изменения базового контейнера:
.maplibregl-ctrl-group {
background-color: rgba(20, 20, 20, 0.85);
border-radius: 12px;
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.35);
border: 1px solid rgba(255, 255, 255, 0.08);
}
Такой подход позволяет интегрировать карту в тёмные интерфейсы без визуального конфликта.
Кнопки внутри контролов имеют фиксированную модель поведения: квадратный hit-area, центрированная иконка, состояния hover и active.
Основные точки кастомизации:
width, height)padding):hover):active):disabled).maplibregl-ctrl button {
width: 36px;
height: 36px;
transition: background-color 0.15s ease;
}
.maplibregl-ctrl button:hover {
background-color: rgba(255, 255, 255, 0.1);
}
.maplibregl-ctrl button:active {
transform: scale(0.96);
}
Важно учитывать, что трансформации (transform)
применяются к интерактивным элементам без влияния на layout карты.
Иконки контролов в MapLibre GL JS реализованы через SVG или CSS background-image.
Подходы к кастомизации:
.maplibregl-ctrl-zoom-in {
background-image: url('/icons/zoom-in.svg');
background-size: 18px 18px;
}
.maplibregl-ctrl-icon svg {
fill: #ffffff;
stroke: none;
}
В высоконагруженных интерфейсах применяется спрайт:
.maplibregl-ctrl-icon {
background-image: url('/sprites/controls.png');
background-repeat: no-repeat;
}
Такой подход уменьшает количество HTTP-запросов и ускоряет загрузку интерфейса.
Контролы располагаются через flex-контейнеры в углах карты. Каждый контейнер имеет собственную модель выравнивания:
Пример переопределения позиции:
map.addControl(new maplibregl.NavigationControl(), 'bottom-right');
Стилизация контейнеров позволяет изменить поведение группировки:
.maplibregl-ctrl-bottom-right {
display: flex;
flex-direction: row;
gap: 8px;
}
Это превращает стандартную вертикальную колонку в горизонтальную панель инструментов.
Контролы легко адаптируются под системные темы через
prefers-color-scheme:
@media (prefers-color-scheme: dark) {
.maplibregl-ctrl-group {
background-color: rgba(30, 30, 30, 0.9);
border-color: rgba(255, 255, 255, 0.1);
}
.maplibregl-ctrl button {
color: #ffffff;
}
}
В сложных интерфейсах используется токенизация:
:root {
--ctrl-bg: #ffffff;
--ctrl-border: #e5e5e5;
}
.dark-theme {
--ctrl-bg: #1f1f1f;
--ctrl-border: #333;
}
И затем:
.maplibregl-ctrl-group {
background-color: var(--ctrl-bg);
border: 1px solid var(--ctrl-border);
}
Создание собственного контрола требует реализации DOM-структуры вручную. При этом стилизация полностью ложится на разработчика.
Базовая структура:
class CustomControl {
onAdd(map) {
this.container = document.createElement('div');
this.container.className = 'maplibregl-ctrl maplibregl-ctrl-group custom-control';
const button = document.createElement('button');
button.textContent = 'A';
this.container.appendChild(button);
return this.container;
}
onRemove() {
this.container.remove();
}
}
Стилизация:
.custom-control {
padding: 4px;
border-radius: 10px;
}
.custom-control button {
width: 40px;
height: 40px;
font-weight: bold;
}
Ключевой момент: кастомные контролы наследуют только позиционирование, всё остальное полностью контролируется CSS.
Контролы часто требуют отображения состояния:
Используются классы-модификаторы:
.custom-control.is-active {
background-color: rgba(0, 150, 255, 0.2);
}
Или data-атрибуты:
.custom-control[data-state="loading"] {
opacity: 0.5;
pointer-events: none;
}
Такой подход хорошо сочетается с реактивными фреймворками.
При использовании React/Vue/Svelte контролы часто создаются как мост между DOM MapLibre и компонентной системой.
Особенность заключается в том, что контрол живёт вне виртуального DOM. Поэтому стилизация должна быть изолированной:
Пример BEM:
.map-ctrl {}
.map-ctrl__button {}
.map-ctrl--disabled {}
Это предотвращает конфликт со стилями приложения.
На малых экранах контролы требуют адаптации:
@media (max-width: 600px) {
.maplibregl-ctrl button {
width: 30px;
height: 30px;
}
}
Также применяется сворачивание групп:
.maplibregl-ctrl-group.compact {
flex-direction: row;
}
Отдельного внимания требует attribution:
.maplibregl-ctrl-attrib {
font-size: 11px;
opacity: 0.8;
}
Он часто размещается в углу и требует минимальной визуальной нагрузки, чтобы не конкурировать с основным интерфейсом карты.
Поскольку контролы являются частью DOM страницы, возможны конфликты с глобальными стилями приложения. Для предотвращения используются:
.maplibregl-ctrl ....map-container .maplibregl-ctrl button {
all: unset;
}
Этот подход требует осторожности, но обеспечивает полную предсказуемость внешнего вида контролов.