useFocusReturn#
useFocusReturn is the focus management Popover uses. When an overlay opens it
moves focus into the popup; when it closes it returns focus to wherever it was
before. It is not a focus trap: tab order flows out of the popup into the rest
of the page, which is what a non-modal overlay wants.
Demo#
Close it (Escape or a button) and focus returns to the trigger.
import { useDismiss, useFocusReturn } from 'hono-preact-ui';
import { useRef, useState } from 'preact/hooks';
// When the panel opens, useFocusReturn moves focus to its first button; when it
// closes, focus returns to the trigger. Paired with useDismiss so Escape closes
// it (useFocusReturn is not a focus trap). Styling: .docs-focusreturn* in root.css.
export function UseFocusReturnDemo() {
const popupRef = useRef<HTMLDivElement>(null);
const [open, setOpen] = useState(false);
useFocusReturn({ open, popupRef });
useDismiss({
enabled: open,
refs: [popupRef],
onDismiss: () => setOpen(false),
});
return (
<div class="docs-focusreturn">
<button
type="button"
class="docs-focusreturn-trigger"
onClick={() => setOpen(true)}
>
Open (focus moves in)
</button>
{open ? (
<div
ref={popupRef}
class="docs-focusreturn-panel"
role="dialog"
aria-label="Focus panel"
>
<p>Focus jumped to the first button.</p>
<button type="button" onClick={() => setOpen(false)}>
First
</button>
<button type="button" onClick={() => setOpen(false)}>
Second
</button>
</div>
) : null}
<p class="docs-focusreturn-hint">
Close it (Escape or a button) and focus returns to the trigger.
</p>
</div>
);
}
Example#
import { useFocusReturn } from 'hono-preact-ui';
import { useRef } from 'preact/hooks';
function Panel({ open }: { open: boolean }) {
const popupRef = useRef<HTMLDivElement>(null);
useFocusReturn({ open, popupRef });
return open ? (
<div ref={popupRef}>
<button>First</button>
<button>Second</button>
</div>
) : null;
}
Signature#
import { useFocusReturn } from 'hono-preact-ui';
function useFocusReturn(opts: UseFocusReturnOptions): void;
interface UseFocusReturnOptions {
open: boolean;
popupRef: RefObject<HTMLElement>;
initialFocusRef?: RefObject<HTMLElement>; // defaults to first focusable, then popup
}
Options#
| Option | Type | Default | Notes |
|---|---|---|---|
open | boolean | none | Focus moves in when this becomes true and returns when it falls. |
popupRef | RefObject | none | The popup; focus moves into it on open. |
initialFocusRef | RefObject | optional | Element to focus first, instead of the first focusable. |
On open it focuses initialFocusRef, else the first focusable element in the
popup, else the popup itself. On close it returns focus to the element that was
focused when the overlay opened (usually the trigger). It does not trap focus,
so pair it with a dismissal mechanism such as useDismiss.