Мы сделали для ProcessWire новую админку на нативных веб-компонентах. jQuery в ней не грузится вовсе, пока на экране не появится модуль, которому он нужен: тогда старый стек подгружается для этого экрана, и модуль работает как раньше. Lighthouse на рабочих экранах — 98 против 69 у стандартной темы.
Админка ProcessWire собрана из полей, а поля — из модулей, которые годами писались под jQuery, jQuery UI и inputfields.js. Поэтому стандартная тема грузит этот стек на каждом экране, даже там, где он никому не нужен. Просто убрать его нельзя: сломается всё, что написано не тобой.
Bridge — обычная тема админки, AdminThemeBridge, которая решает это иначе. Рамку и основные экраны она рисует сама, на веб-компонентах и небольшом рантайме на TypeScript, а старый стек грузит только там, где он действительно нужен. Код открыт: github.com/theoretic/bridge, модуль для установки — github.com/theoretic/AdminThemeBridge. Вопросы и отчёты об ошибках — в теме на форуме ProcessWire.
Мост, а не замена
Название говорит, что это такое: мост между админкой эпохи jQuery и современным вебом. Всё, что можно сделать нативно, уже нативно. Чужие модули остаются на своём берегу и работают так же, как работали.
Нативные экраны. Большинство полей ядра — текст, списки, чекбоксы, даты, ссылки на страницы, файлы, картинки с кадрированием, повторители, языковые вкладки — и экраны ядра: дерево страниц, редактор, Lister, модули, поля, шаблоны, пользователи, логи. Редактор сохраняет страницу без перезагрузки; Shift+клик по кнопке сохраняет по-старому.
Старый стек по требованию. Если на экране есть поле, процесс или скрипт сайта, написанный под jQuery, Bridge подгружает jQuery, jQuery UI, inputfields.js, WireTabs и остальное — в том же порядке, с теми же глобальными переменными и событиями, что и ядро. Сервер узнаёт об этом, когда рисует поле; браузер подстраховывает, если поле пришло позже, по AJAX. Свёрнутое поле тянет стек, только когда его открывают.
Ядро не трогаем. Папка wire/ остаётся нетронутой копией оригинала. Id и классы в DOM, AJAX-адреса, имена классов модулей и хукаемые методы — те же, что в ядре, поэтому модули и хуки находят то, что ищут. Обновление ProcessWire — по-прежнему замена wire/.
Редактор страницы

Сколько это весит
Честно о цифрах
Lighthouse 12, десктопный профиль, под залогиненным пользователем, с холодным кэшем, медиана трёх прогонов. Все темы стояли на одной и той же копии реального сайта; «было» — стандартная тема Uikit. AdminThemeBoss, на котором этот сайт жил до Bridge, набирает 81. Нагляднее всего разница во времени до отрисовки основного содержимого (LCP): 0,9 секунды против 3,6.
Мерили на локальной машине. На настоящем сервере абсолютные цифры будут выше — смысл в разнице между темами. И это экраны без старых полей: экран, где есть поле эпохи jQuery, загрузит стек для этого поля, как и должен. Bridge не делает старые модули легче — он перестаёт брать за них плату там, где их нет.
Совместимость проверяется, а не обещается
«Все старые модули работают» — громкое обещание, поэтому его проверяет отдельный набор тестов. Он открывает каждый экран админки дважды, под Uikit и под Bridge, и сравнивает то, что видят модули.
На копиях девяти наших реальных сайтов это 1 434 экрана, и ни один не отличается из-за темы. Под проверку попал 41 сторонний модуль из 45, установленных на этих сайтах; у оставшихся четырёх экраны админки падают и под Uikit — на копиях они не работают сами по себе. Поведение интерфейса отдельно проверяется в Chromium и Firefox, и любая ошибка на странице или в консоли валит проверку.
Чего не проверяли: Pro-модули — ProFields, FormBuilder, ListerPro. Ни на одном из наших сайтов их нет.
Старые редакторы в тёмной схеме

Первая боевая установка — этот сайт
17 октября 2026 года Bridge встал на atis.pro, и админка этого сайта работает на нём до сих пор. Установка пошла не так тремя разными способами — и ни в одном из них Bridge не был виноват напрямую.
Память. Установка упала на нехватке памяти. Виноватым оказался другой наш модуль, StaticPages 0.6.3: его файл настроек обращался к самому модулю, а настройки читаются при каждой пересборке кэша модулей — так пересборка запускала саму себя. Исправлено
Тема без настройки. Падение пришлось на момент, когда модуль уже был записан в базу, а его сведения в кэш — ещё нет. Без них ProcessWire загрузил тему слишком рано, когда ещё не было известно, какая страница открыта, и админка показалась в Times New Roman и без меню. Кнопка обновления модулей не помогала: в 3.0.274 она ничего не делает, пока не изменится какой-нибудь файл модуля.
Чужая настройка. У AdminThemeBoss была включена «активировать для всех пользователей», и на каждом запросе она возвращала всем тему Uikit.
На следующий день, на testfly.me, та же настройка Boss сохранила пользователя ещё на старте, и это уронило хук самого сайта. Теперь тема знает об этом сама: с 0.8.1 она замечает пропавший кэш и пересобирает его, с 0.8.2 не даёт себя выбрать, пока у Boss включена эта настройка. Остальное собрано в чек-лист установки на живой сайт.
Первые отзывы
19 октября, через пять дней после того, как был написан план, Bridge вышел публичной бетой 0.8.2 с анонсом на форуме ProcessWire. Первые сообщения о проблемах пришли в первые же сутки — в теме и в личку. Каждое мы воспроизводили на чистой установке с тем самым модулем, чинили и закрывали тестом.
Самое интересное прислал автор ProcessShowcase: его модуль под Bridge выглядел сломанным. Модуль пользуется UIkit напрямую — сетками, карточками, UIkit.notification(), — потому что стандартная тема грузит UIkit на каждой странице. Bridge его не грузил.
Решение в 0.9.5 устроено так же, как мост для jQuery: слой UIkit подгружается только там, где нужен, — если в разметке есть UIkit, если скрипт модуля обращается к UIkit или модуль сам добавил его файлы. Это таблица стилей UIkit без базовых правил, перекрашенная в цвета темы для обеих схем (32 КБ после gzip), в отдельном каскадном слое, так что перекрасить саму админку она не может. Экраны ядра её не грузят никогда. А вызов UIkit.modal.confirm(), сделанный раньше, чем слой загрузился, не теряется: слой догружается и выполняет его.
Для авторов модулей
// run your Inputfield natively, without the legacy stack:
// implement BridgeComponent, or add the class to the theme's map
$wire->addHookAfter('AdminThemeBridge::nativeMap', function(HookEvent $e) {
$map = $e->return;
$map['InputfieldMyMarkup'] = array('kind' => 'wrapper');
$e->return = $map;
});
// a module built on UIkit asks for the layer itself (0.9.5)
if(method_exists($adminTheme, 'needUikit')) $adminTheme->needUikit('MyModule'); Если ничего из этого не делать, ничего и не меняется: модуль получает старый стек, как сегодня. Нативным можно объявить только то, чему не нужны ни jQuery, ни inputfields.js. Интерфейс BridgeComponent объявляется и без установленной темы. Подробности — в контракте компонентов, docs/contract.md в репозитории.