FezVrasta/popper.js

View on GitHub
website/pages/docs/FloatingFocusManager.mdx

Summary

Maintainability
Test Coverage
<PageCard>

# FloatingFocusManager

Provides flexible modal or non-modal focus management for a
floating element. Modal behavior is the default — focus is fully
trapped inside the floating element while it is rendered.

```js
import {FloatingFocusManager} from '@floating-ui/react';
```

This is necessary to ensure that focus is properly managed for
keyboard interaction.

<ShowFor packages={['react-dom']}>

<PackageLimited>@floating-ui/react only</PackageLimited>

</ShowFor>

</PageCard>

This component should only be rendered when the floating element
is open and directly wrap it.

```js /context/1,3
function App() {
  const {context} = useFloating();

  return (
    <>
      {/* reference element */}
      {isOpen && (
        <FloatingFocusManager context={context}>
          {/* floating element */}
        </FloatingFocusManager>
      )}
    </>
  );
}
```

## Props

```ts
interface FloatingFocusManagerProps {
  context: FloatingContext;
  disabled?: boolean;
  initialFocus?:
    | number
    | React.MutableRefObject<HTMLElement | null>;
  returnFocus?: boolean;
  guards?: boolean;
  modal?: boolean;
  visuallyHiddenDismiss?: boolean | string;
  closeOnFocusOut?: boolean;
  order?: Array<'reference' | 'floating' | 'content'>;
}
```

### `context{:.keyword}`

<Required />

The `context{:.const}` object returned from `useFloating(){:js}`.

```js
<FloatingFocusManager context={context}>
  {/* floating element */}
</FloatingFocusManager>
```

### `disabled{:.keyword}`

default: `false{:js}`

Disables all focus management entirely. Useful to delay focus
management until after a transition completes or some other
conditional state.

```js
<FloatingFocusManager context={context} disabled>
  {/* floating element */}
</FloatingFocusManager>
```

### `initialFocus{:.keyword}`

default: `0{:js}`

Which element to initially focus. Can be either a number
(tabbable index as specified by the `order{:.key}`) or a ref.

```js
<FloatingFocusManager
  context={context}
  initialFocus={elementRef}
>
  {/* floating element */}
</FloatingFocusManager>
```

#### Negative number

You can set this to a negative number to ignore the initial
focus. This is required to prevent conflicts if your floating
element has no tabbable content and is instead controlled by the
`useListNavigation(){:js}` Hook which has its own focus
management.

```js
<FloatingFocusManager context={context} initialFocus={-1}>
  {/* floating element */}
</FloatingFocusManager>
```

If there is no list navigation, then `modal{:.keyword}`
behavior's focus trap will no longer work. To maintain the trap,
you can place the initial focus on the floating element:

```js /refs/
const {context, refs} = useFloating();

return (
  <FloatingFocusManager
    context={context}
    initialFocus={refs.floating}
  >
    {/* floating element */}
  </FloatingFocusManager>
);
```

### `returnFocus{:.keyword}`

default: `true{:js}`

Determines if focus should be returned to the reference element
(or if that is not available, the previously focused element).
This prop is ignored if the floating element lost focus.

```js
<FloatingFocusManager context={context} returnFocus={false}>
  {/* floating element */}
</FloatingFocusManager>
```

### `guards{:.keyword}`

default: `true{:js}`

Determines if the focus guards are rendered. If not, focus can
escape into the address bar/console/browser UI, like in native
dialogs.

```js
<FloatingFocusManager context={context} guards={false}>
  {/* floating element */}
</FloatingFocusManager>
```

### `modal{:.keyword}`

default: `true{:js}`

Determines if focus is "modal", meaning focus is fully trapped
inside the floating element and outside content cannot be
accessed. This includes screen reader virtual cursors.

```js
<FloatingFocusManager context={context} modal={false}>
  {/* floating element */}
</FloatingFocusManager>
```

#### Non-modal behavior

The floating element should be rendered after the reference
element in the React tree. It can be portalled however.

#### Modal behavior

Ensure you have an explicit "close" button. This can be either
visible to all users, or visually-hidden so it is only available
to assistive tech (see
[`visuallyHiddenDismiss`](/docs/FloatingFocusManager#visuallyhiddendismiss)).
Touch-based screen readers often will not have an `esc` key
available to dismiss the element, so an explicit close button is
required, otherwise the user will be trapped in the floating
element if they don't want to select anything (thus needing to
reload the page).

#### Comboboxes

When the reference element has a `combobox{:.string}`
`role{:.keyword}` present, the focus manager behavior changes.

The listbox part (floating element) of a combobox (input +
listbox popup) can be portalled and accessible to touch screen
readers when using modal focus management, but DOM focus does not
become modal. Screen reader virtual cursors do become modal,
allowing a touch screen reader to immediately access the
portalled listbox items.

### `visuallyHiddenDismiss{:.keyword}`

default: `false{:js}`

If your focus management is modal and there is no explicit close
button available, you can use this prop to render a
visually-hidden dismiss button at the start and end of the
floating element. This allows touch-based screen readers to
escape the floating element due to lack of an `esc` key.

```js
<FloatingFocusManager context={context} visuallyHiddenDismiss>
  {/* floating element */}
</FloatingFocusManager>
```

You can pass a string which will be announced by the screen
reader, e.g. for languages other than English. The default string
when using `true{:js}` is `Dismiss{:.string}`.

```js
<FloatingFocusManager
  context={context}
  visuallyHiddenDismiss="Dismiss popup"
>
  {/* floating element */}
</FloatingFocusManager>
```

### `closeOnFocusOut{:.keyword}`

default: `true{:js}`

Determines whether `focusout{:.string}` event listeners that
control whether the floating element should be closed if the
focus moves outside of it are attached to the reference and
floating elements. This affects non-modal focus management.

```js
<FloatingFocusManager context={context} closeOnFocusOut={false}>
  {/* floating element */}
</FloatingFocusManager>
```

### `order{:.keyword}`

default: `['content']{:js}`

The order in which focus cycles.

```js
<FloatingFocusManager
  context={context}
  // Initially focuses the floating element. Subsequent tabs
  // will cycle through the tabbable contents of the floating
  // element.
  order={['floating', 'content']}
  // Keeps focus on the reference element. Subsequent tabs
  // will cycle through the tabbable contents of the floating
  // element.
  order={['reference', 'content']}
>
  {/* floating element */}
</FloatingFocusManager>
```

## Troubleshooting

### Page scrolls to top when opening the floating element

This can happen if you're placing the `autoFocus{:.keyword}` prop
on an element inside the floating element. This is because the
floating element is initially rendered at the top-left of the
page, and the browser scrolls to it when it receives focus.

Instead, use the `initialFocus{:.keyword}` prop on
`<FloatingFocusManager>{:js}` to focus the desired element, which
waits for the position to be ready first.

### Usage with `<FloatingPortal>{:js}`

Ensure the focus manager is rendered as a child (can be a nested
descendant) of `<FloatingPortal>{:js}`.