Skip to main content

Accessibility

Dockview ships a baseline of accessibility in core: WAI-ARIA roles and labels, roving-tabindex navigation within the tab strip, screen-reader announcements of layout changes, and keyboard-only focus indicators. These come with the dockview package and the framework packages. No configuration is required for the semantics below; the options on this page tune the announcements and the opt-in keymap.

ARIA roles and labels

The tab headers are marked up as a WAI-ARIA Tabs widget, and groups and floating windows carry landmark roles, so assistive technology can describe the layout:

  • The tab strip is a role="tablist" (with aria-orientation), each tab a role="tab" carrying aria-selected and aria-controls. Each tab has an explicit accessible name set from its panel title (falling back to the panel id), kept in sync with setTitle, so a screen reader reads the title cleanly rather than absorbing the close button's label.
  • Panel content is a role="tabpanel" labelled by its tab (aria-labelledby).
  • Each group is a role="region" labelled by its active panel's title.
  • A floating group is a non-modal role="dialog" named from its active panel, so the grid behind it stays reachable.
  • Close buttons expose an accessible name that includes the panel title (e.g. "Close Orders").

These roles are reactive: activating a tab, floating a group or calling setTitle updates the relevant attributes.

Keyboard navigation

Within the tab strip, Dockview uses a roving tabindex: only one tab is a tab stop, and the arrow keys move between tabs once the strip has focus. Home / End jump to the first / last tab, and Enter / Space activate the focused tab. This within-strip navigation is always on.

Broader keyboard operability (switching tabs, cycling groups with F6, jumping focus to the tab strip, and keyboard-driven docking) is enabled with the keyboardNavigation option and is documented in full on the Keyboard page, including the rebindable keymap.

const options = {
keyboardNavigation: true,
};

Focus indicators

Keyboard focus is drawn with a :focus-visible outline, so the focus ring appears when you tab to a control but is suppressed on plain mouse clicks. It is themeable through the theme's CSS custom properties, and honours :has(:focus-visible) so the ring still shows when a child control (such as a tab's close button) is focused by keyboard.

Screen-reader announcements

Dockview narrates layout changes (panels opening/closing, groups floating, docking, popping out, maximising/restoring) through visually-hidden ARIA live regions (a polite region for routine status and an assertive one for cancellations). This is on by default.

Each window gets its own live region: the main document plus every popout window. Announcements are routed to the region of the window that currently has focus, so a screen-reader user working inside a popped-out group hears them in that window rather than the opener.

The announcement behaviour is configured with the following options:

Opt out entirely with:

const options = {
announcements: false,
};

Localising and overriding announcements

getAnnouncement runs per event and lets you override or localise the spoken string. Return a string to use it, null / '' to suppress that announcement, or undefined to fall through to the default:

const options = {
getAnnouncement: (event) => {
if (event.kind === 'maximize') {
return 'Panel maximised';
}
return undefined; // keep the default
},
};

For full translations, provide a messages catalog. Each entry is a function that builds the string from the relevant context (e.g. the panel title); supply any subset; unset entries keep the English defaults:

const options = {
messages: {
panelOpened: (title) => `${title} ouvert`,
panelClosed: (title) => `${title} fermé`,
groupMaximized: (title) => `${title} agrandi`,
},
};

Routing to your own announcer

If your app already manages a screen-reader live region, route Dockview's announcements to it with announcer instead of using the built-in regions. getAnnouncement (localisation) still applies first, and each event carries the resolved message and a politeness of 'polite' or 'assertive':

const options = {
announcer: (event) =>
myLiveRegion.announce(event.message, event.politeness),
};

See also

  • Focus navigation: DockviewApi methods for moving focus between panels and groups
  • Keyboard: the opt-in keymap, F6 group cycling and keyboard-driven docking