Skip to content

Accessibility in standalone PWAs

Published

A standalone PWA excludes user-agent navigation controls, although the user agent may retain other UI such as a status bar. The app therefore has to supply operable navigation inside its own document, and it has to give each view a descriptive title. Structural landmarks and a visible focus indicator belong there too.

The display-mode media feature reports which mode the app is shown in. browser is the ordinary tab; standalone, minimal-ui and fullscreen are the installed-app modes, and other supported values exist. MDN recommends minimal-ui when an app does not provide its own navigation elements because that mode can retain browser controls for moving between pages.

Display mode does not change WCAG requirements. WCAG 2.2 requires web pages to have titles that describe their topic or purpose, and requires a visible focus indicator in a keyboard-operable interface. WAI’s page-structure guidance recommends meaningful elements, headings, and labeled regions to support navigation and orientation.

window.matchMedia('(display-mode: standalone)').matches is true in a standalone display context. When the query does not match, the document remains usable in its browser context; the Home link stays available in either mode.

Give each route a descriptive <title> and use a logical heading hierarchy. Choose meaningful semantic elements and labeled regions that fit the page’s structure. In a standalone window, include links or controls that reach the app’s home and previous views; do not make browser chrome the sole recovery path.

Keep native link and button behavior, and preserve a visible keyboard focus indicator.

The first example provides orientation inside the document. The second detects the display mode and adds a back control only when the app is running standalone.

Provide navigation landmarks and visible focus

Section titled “Provide navigation landmarks and visible focus”

This fragment provides a title, a heading, a labeled navigation region, and a visible keyboard focus indicator.

<title>Account settings</title>
<a class="skip-link" href="#main">Skip to content</a>
<nav aria-label="App navigation">
<a href="/">Home</a>
</nav>
<main id="main" tabindex="-1">
<h1>Account settings</h1>
</main>
<style>
:focus-visible { outline: 3px solid #075fcc; outline-offset: 3px; }
.skip-link { position: absolute; left: -10000px; }
.skip-link:focus { left: 1rem; top: 1rem; }
</style>

The home link works in every display mode. The skip link lets keyboard users jump past the repeated navigation.

Detect standalone mode with a browser fallback

Section titled “Detect standalone mode with a browser fallback”

This fragment reveals a back control in standalone mode. When matchMedia is absent or the query does not match, it leaves the ordinary home link available instead of hiding navigation.

<nav aria-label="App navigation">
<a href="/">Home</a>
<button id="back" type="button" hidden>Back</button>
</nav>
<script type="module">
const back = document.querySelector('#back');
if (!('matchMedia' in window)) {
back.hidden = true;
} else if (window.matchMedia('(display-mode: standalone)').matches) {
back.hidden = false;
back.addEventListener('click', () => {
if (history.length > 1) history.back();
else location.assign('/');
});
} else {
back.hidden = true;
}
</script>

The back button appears only in standalone mode; everywhere else users still have the home link.

After a single-page navigation, update document.title to describe the new view. The WAI guidance for Page Titled notes that distinct SPA views should update the title when their content changes.