Summary
Opening the Zoom Menu popover ([data-epdf-i="zoom-menu"], triggered from the toolbar's "Zoom Menu" button) does not move focus into the popover. Its 15 menu items are real, enabled, tabbable <button role="menuitem"> elements, but they're placed at the very end of the DOM (as a late sibling near the end of the [data-epdf] root, not adjacent to the trigger), so keyboard users must Tab through every other unrelated toolbar control to reach them. This fails WCAG 2.1.1 (Keyboard) for keyboard-only users.
Separately — and this may be the more useful clue for a fix — calling .focus() programmatically on a menu item from outside embedpdf is silently reverted. activeElement snaps back to the trigger button synchronously and stays there indefinitely, even when nothing else in the page is doing any focus management. This suggests something internal is fighting external focus assignment into the popover, which is likely also the root cause of #1.
Environment
@embedpdf/react-pdf-viewer: 2.15.0 (latest at time of filing)
- Also present with
@embedpdf/core, @embedpdf/engines, @embedpdf/plugin-document-manager, @embedpdf/plugin-render, @embedpdf/plugin-scroll, @embedpdf/plugin-viewport at the same version
- React 17.0.2
- Reproduced in Chrome (headless, via Puppeteer) with zero surrounding app code — see minimal repro below
Minimal repro
import React from 'react';
import ReactDOM from 'react-dom';
import PDFViewer, { ZoomMode } from '@embedpdf/react-pdf-viewer';
ReactDOM.render(
<div style={{ height: '100vh' }}>
<PDFViewer
config={{
src: 'https://snippet.embedpdf.com/ebook.pdf',
theme: { preference: 'light' },
zoom: { defaultZoomLevel: ZoomMode.FitPage },
}}
/>
</div>,
document.getElementById('root'),
);
Steps (keyboard-only):
- Tab to the "Zoom Menu" button in the toolbar, press Enter to open it.
- Press Tab.
Expected: Focus moves into the popover (ideally straight to the first item, or at minimum reachable within a couple of Tabs).
Actual: Focus walks through 15 unrelated toolbar controls first — Zoom Out, Zoom In, Pan, Pointer, all six mode tabs, Search, Comment, Previous Page, the page-number field, Next Page — landing on the popover's first item ("25%") only on the 16th Tab press.
Steps (programmatic):
const root = document.querySelector('embedpdf-container').shadowRoot;
const menu = root.querySelector('[data-epdf-i="zoom-menu"]');
const firstItem = menu.querySelector('button');
firstItem.focus();
console.log(root.activeElement === firstItem); // expected: true
Actual: root.activeElement remains the trigger button — false — immediately, and still false checked again after a microtask, a 0ms timeout, and a 100ms timeout.
Suggested fix direction
Move focus to the first (or currently-selected) menu item when the popover opens, and either trap Tab within it or ensure DOM/tab order places it adjacent to the trigger. Since external .focus() calls are also being reverted, whatever code is doing that reversion is probably the same code that should instead be performing the correct initial focus-move.
Summary
Opening the Zoom Menu popover (
[data-epdf-i="zoom-menu"], triggered from the toolbar's "Zoom Menu" button) does not move focus into the popover. Its 15 menu items are real, enabled, tabbable<button role="menuitem">elements, but they're placed at the very end of the DOM (as a late sibling near the end of the[data-epdf]root, not adjacent to the trigger), so keyboard users must Tab through every other unrelated toolbar control to reach them. This fails WCAG 2.1.1 (Keyboard) for keyboard-only users.Separately — and this may be the more useful clue for a fix — calling
.focus()programmatically on a menu item from outside embedpdf is silently reverted.activeElementsnaps back to the trigger button synchronously and stays there indefinitely, even when nothing else in the page is doing any focus management. This suggests something internal is fighting external focus assignment into the popover, which is likely also the root cause of #1.Environment
@embedpdf/react-pdf-viewer:2.15.0(latest at time of filing)@embedpdf/core,@embedpdf/engines,@embedpdf/plugin-document-manager,@embedpdf/plugin-render,@embedpdf/plugin-scroll,@embedpdf/plugin-viewportat the same versionMinimal repro
Steps (keyboard-only):
Expected: Focus moves into the popover (ideally straight to the first item, or at minimum reachable within a couple of Tabs).
Actual: Focus walks through 15 unrelated toolbar controls first — Zoom Out, Zoom In, Pan, Pointer, all six mode tabs, Search, Comment, Previous Page, the page-number field, Next Page — landing on the popover's first item ("25%") only on the 16th Tab press.
Steps (programmatic):
Actual:
root.activeElementremains the trigger button —false— immediately, and stillfalsechecked again after a microtask, a 0ms timeout, and a 100ms timeout.Suggested fix direction
Move focus to the first (or currently-selected) menu item when the popover opens, and either trap Tab within it or ensure DOM/tab order places it adjacent to the trigger. Since external
.focus()calls are also being reverted, whatever code is doing that reversion is probably the same code that should instead be performing the correct initial focus-move.