Focus Trapping in Full-Screen Grid Mode

Permalink to "Focus Trapping in Full-Screen Grid Mode"

Many grids offer an “expand” or “full screen” button that enlarges the grid to fill the viewport. Visually it is a different mode; programmatically it is often just a CSS class. Screen reader users can still browse the page behind; keyboard users tab out of the grid into hidden controls; and there is no announced way back. Treating full-screen mode as a modal dialog — trapped focus, named, with a visible exit — makes it predictable for everyone.

This guide belongs to keyboard focus trapping and navigation.

Spec reference

Permalink to "Spec reference"
  • ARIA dialog (modal) — focus contained; content outside inert.
  • SC 2.1.2 No Keyboard Trap (A) — a standard, documented exit (Escape and a button).
  • SC 2.4.3 (A) — focus order stays within the visible mode.
  • SC 4.1.2 (A) — the mode has a name and the expand button reports state.
Entering and leaving full-screen grid mode Flow: the user activates Expand grid; the grid moves into a modal dialog filling the viewport; focus goes to the grid's current cell; Escape or Exit returns the grid, restores scroll and focuses the expand button. Entering and leaving full-screen grid modeExpand grid buttonactivateDialog opens"Invoices, full screen, dialog"Focuscurrent grid cellExit (Escape or button)dialog closesRestorefocus expand button, scroll back
The mode is a dialog, not just a CSS class.

When to use — and when not to

Permalink to "When to use — and when not to"

Use full-screen mode for wide, dense grids where users need every pixel for a focused task. Make it a choice, not a forced state.

Do not use the browser Fullscreen API alone; it changes the visual viewport but not focus containment, and Escape exits the browser mode without your app knowing. The misapplication: a CSS class that positions the grid fixed over the page while leaving page controls focusable behind it.

CSS-only expansion versus a modal mode Comparison: CSS-only expansion leaves the page interactive and gives no announced exit; a modal dialog mode contains focus and announces entry and exit. CSS-only expansion versus a modal mode✗ position: fixed overlayTab reaches hidden page controlsScreen reader browses page behindNo announced exitScroll position lost✓ Modal dialogFocus containedBackground inertExit button + EscapeRestored on exit

Annotated code example

Permalink to "Annotated code example"
<button type="button" id="expand" aria-haspopup="dialog">Expand grid</button>
<dialog id="grid-full" aria-labelledby="grid-full-title" class="full">
  <header>
    <h2 id="grid-full-title">Invoices — full screen</h2>
    <button type="button" id="exit-full">Exit full screen</button>
  </header>
  <div id="grid-slot"></div>                                <!-- grid moved here -->
</dialog>
let savedScroll;
expand.onclick = () => {
  savedScroll = window.scrollY;
  gridSlot.append(grid);                                    // move, do not clone: keeps state
  gridFull.showModal();                                     // traps focus, page inert
  grid.querySelector('[tabindex="0"]').focus();             // current cell
};
gridFull.addEventListener('cancel', e => {
  if (grid.contains(document.activeElement) && isEditing()) { e.preventDefault(); cancelEdit(); }   // Escape cancels the edit first
});
gridFull.addEventListener('close', () => {
  originalSlot.append(grid); window.scrollTo(0, savedScroll); expand.focus();
});
exitFull.onclick = () => gridFull.close();

Keyboard & AT behaviour

Permalink to "Keyboard & AT behaviour"
Key In full-screen mode Announcement
Enter on Expand grid Mode opens “Invoices — full screen, dialog” + cell
Tab Moves between Exit and grid —
Escape while editing a cell Cancels the edit only “Edit cancelled”
Escape otherwise Exits the mode “Expand grid, button”
Arrow keys Grid navigation Cells
Full-screen approaches compared Matrix of approaches — CSS class, browser Fullscreen API, modal dialog, modal dialog plus Fullscreen API — against focus containment, inert background, Escape handling and announcements. Full-screen approaches comparedFocus containedBackground inertEscape handledAnnouncedCSS classNoNoNoNoFullscreen APINoVisuallyBrowser exitsNoModal dialogYesYesYesYesDialog + Fullscreen APIYesYesDouble EscapeYes
A modal dialog provides the semantics; the Fullscreen API is optional on top.

Step-by-step implementation

Permalink to "Step-by-step implementation"
  1. Move the grid into a dialog. Moving the same element preserves selection, scroll and editing state; cloning loses them.
  2. Name the dialog. “Invoices — full screen” with a heading, so screen reader users hear where they are.
  3. Focus the current cell. Keep the user’s position in the grid rather than jumping to the first cell.
  4. Resolve Escape conflicts. Escape first cancels an active cell edit or closes a popover; only then does it exit the mode.
  5. Restore on exit. Move the grid back, restore page scroll and focus the expand button.

Integration context

Permalink to "Integration context"

Dialog choices are compared in native dialog versus custom focus traps. Restoring focus is covered in restoring focus after closing complex modals. Escape handling inside cells is part of inline editing and form controls. Nested dialogs from full-screen mode follow nested dialogs and stacked focus traps.

Gotchas

Permalink to "Gotchas"

Fullscreen API Escape. If you also use requestFullscreen, the first Escape exits browser full screen and may never reach your handler; listen for fullscreenchange and close the dialog too.

Portals. Menus and popovers that portal to body will render under the modal or become inert; portal them into the dialog.

Virtualization. The viewport size changes; recompute virtual rows after the move.

Edge cases

Permalink to "Edge cases"
  • Deep links. If full-screen mode has a URL, the browser Back button should exit it.
  • Zoom. At high zoom, full-screen mode is often the only usable view; keep the header compact.
  • Multiple grids. Only one grid should be expanded at a time.

Worked example

Permalink to "Worked example"

An analytics grid’s “Expand” button added a fixed-position class. NVDA users heard the page heading and navigation behind the grid when they pressed H, and keyboard users tabbed into invisible page controls. The team moved the grid into a native modal dialog with a heading and Exit button, made Escape cancel cell edits before exiting, and restored focus and scroll on close. Screen reader users heard a clear “full screen, dialog” on entry and returned exactly where they had been.

Common mistakes

Permalink to "Common mistakes"
  • CSS-only full screen.
  • Escape exiting while a cell edit is active.
  • Cloning the grid and losing state.
  • Focus lost on exit.

Testing checklist

Permalink to "Testing checklist"

Testing it with a screen reader

Permalink to "Testing it with a screen reader"

Activate “Expand grid” with NVDA and listen for the dialog name and the focused cell. Press H in browse mode and confirm that only the dialog heading is found. Start editing a cell, press Escape and confirm only the edit is cancelled; press Escape again to exit and hear the expand button announced. In VoiceOver, check that the rotor’s landmarks and headings lists contain only dialog content while the mode is open.

Keyboard shortcut for the mode

Permalink to "Keyboard shortcut for the mode"

Power users appreciate a shortcut to toggle full-screen mode, but choose it carefully. F11 belongs to the browser, and single-character keys conflict with screen reader quick navigation. A modifier combination such as Ctrl+Shift+F, scoped to when focus is in the grid, is safer; expose it with aria-keyshortcuts on the expand button and list it in the grid’s keyboard help. Make sure the same shortcut exits the mode, so users can toggle without hunting for the Exit button.

Toolbars in full-screen mode

Permalink to "Toolbars in full-screen mode"

Grids usually come with a toolbar — filters, column chooser, export. If the toolbar stays on the page when the grid expands, users lose access to it in full-screen mode. Move the toolbar into the dialog header along with the grid, or provide the same commands in the dialog. Keep the tab order simple: dialog heading, Exit button, toolbar, grid. Users who exit full screen should find the toolbar back in its original place with the same state.

Design system notes

Permalink to "Design system notes"

Offer full-screen mode as a grid feature built on the dialog service, so focus, Escape precedence and restoration come for free.

FAQ

Permalink to "FAQ"
Should full-screen mode use the Fullscreen API?

Optionally, for extra space; the dialog provides the semantics.

What should the expand button announce?

A name like “Expand grid” and aria-haspopup=“dialog”.

Can users still reach page navigation?

Not while the mode is open; they exit first.

Where does focus go on entry?

To the grid’s current cell.

Is Escape enough as an exit?

Provide a visible button too; not everyone knows Escape.

What about mobile?

Full-screen mode is useful on phones; the same dialog approach applies.

Permalink to "Related"

← Back to Keyboard Focus Trapping & Navigation for Data Interfaces