Phoundry UI

Focus Trap

Constrains Tab/Shift+Tab focus cycling within a container element. Useful for dialogs, sidebars, and any overlay that should capture keyboard focus.

import { createFocusTrap } from 'phoundry-ui';

Interactive Focus Trap

Click to open a focus-trapped panel.

Show code
<script lang="ts">
  import { createFocusTrap } from 'phoundry-ui';
  import { Button, TextInput } from 'phoundry-ui';

  let trapActive = $state(false);
  const trap = createFocusTrap({
    escapeDeactivates: true,
    onEscape: () => (trapActive = false),
  });
</script>

<Button onclick={() => (trapActive = true)}>Activate Trap</Button>

{#if trapActive}
  <div use:trap.action class="p-4 border rounded-lg space-y-3">
    <TextInput placeholder="First input (auto-focused)" />
    <TextInput placeholder="Second input" />
    <div class="flex gap-2">
      <Button variant="primary">Confirm</Button>
      <Button onclick={() => (trapActive = false)}>Cancel</Button>
    </div>
    <p class="text-xs text-txt-tertiary">
      Tab cycles within this panel. Press Escape to exit.
    </p>
  </div>
{/if}

FocusTrapOptions

PropTypeDefaultDescription
autoFocus booleantrueAuto-focus the first focusable element when the trap activates.
returnFocusOnDeactivate booleantrueReturn focus to the previously focused element on deactivation.
escapeDeactivates booleanfalseAllow Escape key to deactivate the trap.
onEscape () => voidCalled when Escape is pressed (if escapeDeactivates is true).

Return Value

PropTypeDefaultDescription
action ActionSvelte action: use:trap.action or use:trap.action={{ enabled: boolean }}. When enabled is false, Tab capture is off (e.g. stacked modals not on top) without restoring focus.
activate() () => voidManually activate the trap (called automatically by the action when enabled).
deactivate(options?) { restoreFocus?: boolean }Manually deactivate. Pass restoreFocus: false to pause without moving focus (stacked overlays). Defaults to the trap’s returnFocusOnDeactivate when omitted.
active boolean (readonly)Whether the trap is currently active.

FocusTrapActionParams (use:trap.action parameter)

PropTypeDefaultDescription
enabled booleantrueWhen false, the action does not activate Tab capture until set true again (see action update).

Usage tips

  • Used internally by Modal and the modal manager overlay — you only need this directly for custom dialogs or sidebar panels.
  • The action activates the trap when applied (if enabled is not false) and cleans up on destroy.
  • Focus returns to the previously focused element when the trap deactivates (unless returnFocusOnDeactivate is false).
  • Only visible, non-disabled focusable elements participate in the cycle.
  • Set escapeDeactivates: true and provide an onEscape callback to handle closing.