Используйте область заголовка рядом с элементами управления окном, чтобы сделать ваше PWA более похожим на приложение.
Если вы помните мою статью «Сделайте ваше PWA похожим на приложение» , вы, возможно, помните, как я упоминал настройку заголовка приложения как способ сделать его более похожим на приложение. Вот пример того, как это может выглядеть на примере приложения «Подкасты» для macOS.

Конечно, у вас может возникнуть соблазн возразить, сказав, что Podcasts — это платформенно-зависимое приложение для macOS, которое не запускается в браузере и, следовательно, может делать всё, что захочет, не подчиняясь его правилам. Это правда, но хорошая новость заключается в том, что функция Window Controls Overlay, о которой и идёт речь в этой статье, вскоре позволит вам создавать аналогичные пользовательские интерфейсы для ваших PWA.
Компоненты наложения элементов управления окнами
Наложение элементов управления окнами состоит из четырех подфункций:
- Значение
"window-controls-overlay"
для поля"display_override"
в манифесте веб-приложения. - Переменные среды CSS
titlebar-area-x
,titlebar-area-y
,titlebar-area-width
иtitlebar-area-height
. - Стандартизация ранее запатентованного свойства CSS
-webkit-app-region
в качестве свойстваapp-region
для определения перетаскиваемых областей в веб-контенте. - Механизм запроса и обхода области элементов управления окном через член
windowControlsOverlay
объектаwindow.navigator
.
Что такое наложение элементов управления окнами
Область заголовка окна — это пространство слева или справа от элементов управления окном (то есть кнопок сворачивания, разворачивания, закрытия и т. д.), которое часто содержит заголовок приложения. Наложение элементов управления окном позволяет прогрессивным веб-приложениям (PWA) создавать ощущение, более похожее на приложение, заменяя существующую полноэкранную строку заголовка на небольшое наложение с элементами управления окном. Это позволяет разработчикам размещать собственный контент в области, которая ранее управлялась браузером.
Текущий статус
Шаг | Статус |
---|---|
1. Создайте пояснитель | Полный |
2. Создайте первоначальный проект спецификации | Полный |
3. Собирайте отзывы и дорабатывайте дизайн | В ходе выполнения |
4. Испытание происхождения | Полный |
5. Запуск | Полная версия (в Chromium 104) |
Как использовать наложение элементов управления окнами
Добавление window-controls-overlay
в манифест веб-приложения
Прогрессивное веб-приложение может включить наложение элементов управления окна, добавив "window-controls-overlay"
в качестве основного члена "display_override"
в манифест веб-приложения:
{
"display_override": ["window-controls-overlay"]
}
Наложение элементов управления окна будет видно только при выполнении всех следующих условий:
- Приложение открывается не в браузере, а в отдельном окне PWA.
- Манифест включает
"display_override": ["window-controls-overlay"]
. (В дальнейшем допускаются другие значения.) - PWA работает на базе настольной операционной системы.
- Текущее происхождение совпадает с происхождением, для которого был установлен PWA.
Результатом этого является пустая область заголовка с обычными элементами управления окном слева или справа, в зависимости от операционной системы.

Перемещение содержимого в строку заголовка
Теперь, когда в заголовке страницы есть место, можно что-нибудь туда переместить. Для этой статьи я создал PWA-приложение Wikimedia Featured Content. Полезной функцией этого приложения может быть поиск слов в заголовках статей. HTML-код функции поиска выглядит следующим образом:
<div class="search">
<img src="logo.svg" alt="Wikimedia logo." width="32" height="32" />
<label>
<input type="search" />
Search for words in articles
</label>
</div>
Чтобы переместить этот div
в строку заголовка, понадобится немного CSS:
.search {
/* Make sure the `div` stays there, even when scrolling. */
position: fixed;
/**
* Gradient, because why not. Endless opportunities.
* The gradient ends in `#36c`, which happens to be the app's
* `<meta name="theme-color" content="#36c">`.
*/
background-image: linear-gradient(90deg, #36c, #131313, 33%, #36c);
/* Use the environment variable for the left anchoring with a fallback. */
left: env(titlebar-area-x, 0);
/* Use the environment variable for the top anchoring with a fallback. */
top: env(titlebar-area-y, 0);
/* Use the environment variable for setting the width with a fallback. */
width: env(titlebar-area-width, 100%);
/* Use the environment variable for setting the height with a fallback. */
height: env(titlebar-area-height, 33px);
}
Эффект этого кода можно увидеть на скриншоте ниже. Заголовок окна полностью адаптивен. При изменении размера окна PWA заголовок реагирует так, как будто состоит из обычного HTML-контента, что, по сути, и есть.

Определение того, какие части строки заголовка можно перетаскивать
Хотя скриншот выше показывает, что всё готово, это ещё не всё. Окно PWA больше не перетаскивается (за исключением очень небольшой области), поскольку кнопки управления окном не являются областями перетаскивания, а остальная часть заголовка состоит из виджета поиска. Исправьте это с помощью CSS-свойства app-region
со значением drag
. В данном случае вполне допустимо сделать перетаскиваемым всё, кроме элемента input
.
/* The entire search `div` is draggable… */
.search {
-webkit-app-region: drag;
app-region: drag;
}
/* …except for the `input`. */
input {
-webkit-app-region: no-drag;
app-region: no-drag;
}
С этим CSS-кодом пользователь может перетаскивать окно приложения, как обычно, перетаскивая div
, img
или label
. Интерактивным является только элемент input
, поэтому можно ввести поисковый запрос.
Обнаружение особенностей
Поддержку Window Controls Overlay можно обнаружить, проверив наличие windowControlsOverlay
:
if ('windowControlsOverlay' in navigator) {
// Window Controls Overlay is supported.
}
Запрос области элементов управления окна с помощью windowControlsOverlay
В коде пока есть одна проблема: на некоторых платформах элементы управления окном расположены справа, на других — слева. Что ещё хуже, положение трёхточечного меню Chrome также меняется в зависимости от платформы. Это означает, что фоновое изображение с линейным градиентом необходимо динамически адаптировать для перехода от #131313
→ maroon
или maroon
→ #131313
→ maroon
, чтобы оно сочеталось с maroon
фоном заголовка окна, который определяется параметром <meta name="theme-color" content="maroon">
. Этого можно добиться, обратившись к API getTitlebarAreaRect()
к свойству navigator.windowControlsOverlay
.
if ('windowControlsOverlay' in navigator) {
const { x } = navigator.windowControlsOverlay.getTitlebarAreaRect();
// Window controls are on the right (like on Windows).
// Chrome menu is left of the window controls.
// [ windowControlsOverlay___________________ […] [_] [■] [X] ]
if (x === 0) {
div.classList.add('search-controls-right');
}
// Window controls are on the left (like on macOS).
// Chrome menu is right of the window controls overlay.
// [ [X] [_] [■] ___________________windowControlsOverlay [⋮] ]
else {
div.classList.add('search-controls-left');
}
} else {
// When running in a non-supporting browser tab.
div.classList.add('search-controls-right');
}
Вместо того, чтобы напрямую указывать фоновое изображение в правилах CSS класса .search
(как раньше), измененный код теперь использует два класса, которые код выше устанавливает динамически.
/* For macOS: */
.search-controls-left {
background-image: linear-gradient(90deg, #36c, 45%, #131313, 90%, #36c);
}
/* For Windows: */
.search-controls-right {
background-image: linear-gradient(90deg, #36c, #131313, 33%, #36c);
}
Определение видимости наложения элементов управления окна
Наложение элементов управления окнами будет отображаться в области заголовка не во всех случаях. Хотя оно, естественно, отсутствует в браузерах, не поддерживающих функцию наложения элементов управления окнами, оно также не будет отображаться при запуске рассматриваемого PWA во вкладке. Чтобы определить эту ситуацию, можно запросить свойство visible
объекта windowControlsOverlay
:
if (navigator.windowControlsOverlay.visible) {
// The window controls overlay is visible in the title bar area.
}
В качестве альтернативы вы также можете использовать медиа-запрос display-mode
в JavaScript и/или CSS:
// Create the query list.
const mediaQueryList = window.matchMedia('(display-mode: window-controls-overlay)');
// Define a callback function for the event listener.
function handleDisplayModeChange(mql) {
// React on display mode changes.
}
// Run the display mode change handler once.
handleDisplayChange(mediaQueryList);
// Add the callback function as a listener to the query list.
mediaQueryList.addEventListener('change', handleDisplayModeChange);
@media (display-mode: window-controls-overlay) {
/* React on display mode changes. */
}
Получение уведомлений об изменениях геометрии
Запрос области наложения элементов управления окна с помощью getTitlebarAreaRect()
может быть достаточным для разовых задач, таких как установка корректного фонового изображения в зависимости от расположения элементов управления окна, но в других случаях требуется более детальное управление. Например, это может быть адаптация наложения элементов управления окна в зависимости от доступного пространства и добавление шутки прямо в наложение элементов управления окна, когда места достаточно.

Вы можете получать уведомления об изменениях геометрии, подписавшись на событие navigator.windowControlsOverlay.ongeometrychange
или настроив прослушиватель для события geometrychange
. Это событие сработает только тогда, когда наложение элементов управления окном будет видимым, то есть когда значение navigator.windowControlsOverlay.visible
равно true
.
const debounce = (func, wait) => {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
};
if ('windowControlsOverlay' in navigator) {
navigator.windowControlsOverlay.ongeometrychange = debounce((e) => {
span.hidden = e.titlebarAreaRect.width < 800;
}, 250);
}
Вместо назначения функции ongeometrychange
вы также можете добавить прослушиватель событий к windowControlsOverlay
, как показано ниже. Подробнее о разнице между ними можно прочитать на MDN .
navigator.windowControlsOverlay.addEventListener(
'geometrychange',
debounce((e) => {
span.hidden = e.titlebarAreaRect.width < 800;
}, 250),
);
Совместимость при запуске во вкладке и в неподдерживаемых браузерах
Следует рассмотреть два возможных случая:
- Случай, когда приложение работает в браузере, который поддерживает наложение элементов управления окнами, но при этом приложение используется во вкладке браузера.
- Случай, когда приложение работает в браузере, который не поддерживает наложение элементов управления окнами.
В обоих случаях по умолчанию HTML-код, созданный для наложения элементов управления окнами, будет отображаться в строке, как обычное HTML-содержимое, а резервные значения переменных env()
будут задействованы для позиционирования. В поддерживающих браузерах вы также можете запретить отображение HTML-кода, предназначенного для наложения элементов управления окнами, проверив свойство visible
наложения и, если оно возвращает значение false
, скрыв это HTML-содержимое.

Напоминаем, что неподдерживаемые браузеры либо вообще не учитывают свойство манифеста веб-приложения "display_override"
, либо не распознают "window-controls-overlay"
и, таким образом, используют следующее возможное значение в соответствии с цепочкой отката, например, "standalone"
.

Соображения относительно пользовательского интерфейса
Хотя это может показаться заманчивым, не рекомендуется создавать классическое выпадающее меню в области наложения элементов управления окна. Это нарушит правила дизайна macOS — платформы, на которой пользователи ожидают увидеть панели меню (как системные, так и пользовательские) в верхней части экрана.
Если ваше приложение поддерживает полноэкранный режим, тщательно подумайте, имеет ли смысл размещать элементы управления окнами в полноэкранном режиме. Возможно, вам потребуется изменить компоновку макета при срабатывании события onfullscreenchange
.
Демо
Я создал демо-версию , с которой вы можете поиграть в различных браузерах (с поддержкой и без), как в установленном, так и в демонтированном состоянии. Чтобы опробовать наложение элементов управления окнами, вам необходимо установить приложение. Ниже представлены два скриншота того, что вас ждёт. Исходный код приложения доступен на Glitch.

Функция поиска в оверлее элементов управления окна полностью функциональна:

Соображения безопасности
Команда Chromium разработала и реализовала API наложения элементов управления окнами, используя основные принципы, определенные в документе «Управление доступом к мощным функциям веб-платформы» , включая пользовательский контроль, прозрачность и эргономику.
Спуфинг
Предоставление сайтам частичного контроля над заголовком страницы открывает разработчикам возможность подделывать контент в ранее доверенной области, контролируемой браузером. В настоящее время в браузерах Chromium автономный режим включает заголовок, который при первом запуске отображает заголовок веб-страницы слева, а исходный адрес страницы справа (за которым следуют кнопка «Настройки и прочее» и элементы управления окном). Через несколько секунд текст исходного адреса исчезает. Если в браузере настроен язык с письмом справа налево (RTL), эта структура переворачивается так, что исходный адрес оказывается слева. Это открывает наложение элементов управления окном для подделки исходного адреса, если между исходным адресом и правым краем наложения недостаточно отступов. Например, к исходному адресу «evil.ltd» можно добавить доверенный сайт «google.com», что создаст у пользователей впечатление, что источник заслуживает доверия. Планируется сохранить этот текст исходного адреса, чтобы пользователи знали, откуда пришло приложение, и могли убедиться, что оно соответствует их ожиданиям. Для браузеров с настройкой RTL справа от текста источника должно быть достаточно отступов, чтобы не допустить добавления вредоносным веб-сайтом небезопасного источника к доверенному источнику.
Дактилоскопирование
Включение наложения элементов управления окнами и перетаскиваемых областей не представляет серьёзных проблем с конфиденциальностью, за исключением обнаружения функций. Однако из-за различий в размерах и расположении кнопок управления окнами в разных операционных системах метод navigator. windowControlsOverlay. getTitlebarAreaRect()
возвращает DOMRect
, положение и размеры которого раскрывают информацию об операционной системе, в которой работает браузер. В настоящее время разработчики уже могут определить операционную систему по строке пользовательского агента, но из-за проблем с идентификацией ведутся дискуссии о заморозке строки UA и унификации версий ОС. В сообществе разработчиков браузеров продолжаются попытки понять, как часто меняется размер наложения элементов управления окнами на разных платформах, поскольку в настоящее время предполагается, что они достаточно стабильны между версиями ОС и, следовательно, не будут полезны для отслеживания младших версий ОС. Хотя это потенциальная проблема с идентификацией, она применима только к установленным PWA, использующим функцию настраиваемой строки заголовка, и не применима к общему использованию браузера. Кроме того, API navigator. windowControlsOverlay
не будет доступен для встроенных в PWA iframe-ов.
Навигация
Переход к другой исходной точке внутри PWA приведёт к возврату к обычной строке заголовка отдельного окна, даже если оно соответствует вышеуказанным критериям и запущено с наложением элементов управления окна. Это сделано для того, чтобы компенсировать чёрную полосу, появляющуюся при переходе к другой исходной точке. После возврата к исходной исходной точке наложение элементов управления окна будет снова использоваться.

Обратная связь
Команда Chromium хочет узнать о вашем опыте работы с API Window Controls Overlay.
Расскажите нам о дизайне API
Есть ли что-то в API, что работает не так, как вы ожидали? Или отсутствуют методы или свойства, необходимые для реализации вашей идеи? Есть вопросы или комментарии по модели безопасности? Отправьте запрос на спецификацию в соответствующий репозиторий GitHub или добавьте свои замечания к существующему запросу.
Сообщить о проблеме с реализацией
Нашли ошибку в реализации Chromium? Или реализация отличается от спецификации? Сообщите об ошибке на сайте new.crbug.com . Опишите её как можно подробнее, предоставьте простые инструкции по воспроизведению ошибки и введите UI>Browser>WebAppInstalls
в поле « Компоненты ».
Показать поддержку API
Планируете ли вы использовать API наложения элементов управления окнами? Ваша публичная поддержка помогает команде Chromium расставлять приоритеты в отношении функций и показывает другим разработчикам браузеров, насколько важна их поддержка.
Отправьте твит @ChromiumDev с хэштегом #WindowControlsOverlay
и расскажите, где и как вы его используете.
Полезные ссылки
- Объяснитель
- Проект спецификации
- Ошибка хрома
- Запись статуса платформы Chrome
- Обзор TAG
- Связанные документы Microsoft Edge
Благодарности
Наложение элементов управления окнами было реализовано и описано Амандой Бейкер из команды Microsoft Edge. Рецензентами статьи выступили Джо Медли и Кеннет Роде Кристиансен . Изображение предоставлено Sigmund на Unsplash .