Use CSS media queries to detect and adapt to different display modes:
/* CSS display mode detection */
@media (display-mode: browser) {
/* Regular browser tab */
body::before { content: "Browser mode"; }
}
@media (display-mode: standalone) {
/* Installed PWA */
body { background: var(--app-bg); }
.browser-chrome { display: none; }
}
@media (display-mode: minimal-ui) {
/* Minimal browser UI */
.minimal-ui-hint { display: block; }
}
@media (display-mode: fullscreen) {
/* Full screen */
body { cursor: none; }
}
/* JavaScript display mode detection */
const displayMode = window.matchMedia('(display-mode: standalone)').matches
? 'standalone'
: window.matchMedia('(display-mode: minimal-ui)').matches
? 'minimal-ui'
: window.matchMedia('(display-mode: fullscreen)').matches
? 'fullscreen'
: 'browser';
console.log('Current display mode:', displayMode);
The Window Controls Overlay API lets you customize the title bar area of installed desktop PWAs.
// manifest.json - Enable overlay
{
"display_override": ["window-controls-overlay"],
"display": "standalone"
}
// CSS for the title bar area
#title-bar {
position: fixed;
top: 0;
left: 0;
right: 0;
height: 40px;
display: flex;
align-items: center;
padding-left: env(titlebar-area-x, 0);
padding-top: env(titlebar-area-y, 0);
width: env(titlebar-area-width, 100%);
height: env(titlebar-area-height, 40px);
-webkit-app-region: drag;
background: var(--theme-color);
}
#title-bar button {
-webkit-app-region: no-drag;
}
// JavaScript API
if ('windowControlsOverlay' in navigator) {
const overlay = navigator.windowControlsOverlay;
// Check if overlay is visible
console.log('Overlay visible:', overlay.visible);
// Listen for geometry changes
overlay.addEventListener('geometrychange', (event) => {
const { x, y, width, height } = event.titlebarAreaRect;
console.log('Title bar area:', { x, y, width, height });
});
}
Open and manage multiple windows from your PWA:
// Open a new window
async function openDocumentWindow(docId) {
const windowRef = window.open(
`/document?id=${docId}`,
'doc-window',
'width=800,height=600'
);
// Communicate between windows
windowRef.addEventListener('message', (event) => {
if (event.data.type === 'DOCUMENT_SAVED') {
refreshDocumentList();
}
});
}
// Post message to opener window
window.opener.postMessage({
type: 'DOCUMENT_SAVED',
docId: 123
}, '*');
// Get all windows (from Service Worker)
self.addEventListener('message', (event) => {
self.clients.matchAll({ type: 'window' }).then(clients => {
clients.forEach(client => {
client.postMessage({ type: 'UPDATE_AVAILABLE' });
});
});
});
// Keep screen on (Wake Lock)
async function keepScreenOn() {
if ('wakeLock' in navigator) {
try {
const wakeLock = await navigator.wakeLock.request('screen');
console.log('Wake lock acquired');
wakeLock.addEventListener('release', () => {
console.log('Wake lock released');
});
} catch (err) {
console.error('Wake lock failed:', err);
}
}
}
// Detect screen orientation
screen.orientation.addEventListener('change', () => {
console.log('Orientation:', screen.orientation.type);
});