Skip to content

rightxt/focus-trap-vue

Repository files navigation

@rightxt/focus-trap-vue

License NPM version

This library provides a Vue-component wrapper around 'focus-trap'.

Before getting started, it is recommended to read the documentation to understand what a 'focus-trap' is.

Requirements

  • Vue: ^3.5
  • focus-trap: ^7.6.5
  • Node: >=18

The library never bundles 'vue' and 'focus-trap'.

Installation

# npm
npm i @rightxt/focus-trap-vue

# pnpm
pnpm add @rightxt/focus-trap-vue

# yarn
yarn add @rightxt/focus-trap-vue

Build variants

The package publishes several artifacts.

Variant Import/Include Suitable for You must provide
Core ESM import plugin from '@rightxt/focus-trap-vue' Vite / Webpack / Rollup Install 'focus-trap' (peer)
IIFE <script src="rxt-focus-trap-vue.iife.js">window.RxtFocusTrapVue Static pages w/o bundler Load before: UMD 'tabbable', UMD 'focus-trap', 'vue'

As a plugin:

import { createApp } from 'vue';
import Plugin from '@rightxt/focus-trap-vue';
import App from './App.vue';

const app = createApp(App);
app.use(Plugin);
app.mount('#app');

Component registration:

import { createApp } from 'vue';
import { FocusTrap, FocusTrapExclude } from '@rightxt/focus-trap-vue';
import App from './App.vue';

const app = createApp(App);
app.component('FocusTrap', FocusTrap);
app.component('FocusTrapExclude', FocusTrapExclude);
app.mount('#app');

API

Package exports

default:

  • plugin { install(app) }

Named:

  • FocusTrap — the main trap component
  • FocusTrapExclude — excludes a node/subtree from inertness
  • TypeScript types
import type {
  ActivateOptions,
  CreateOptions,
  CreateOptionsProps,
  DeactivateOptions,
  ExclusionsApi,
  FocusableElement,
  FocusTarget,
  FocusTargetOrFalse,
  FocusTargetValueOrFalse,
  FocusTrapI,
  FocusTrapState,
  FocusTrapTabbableOptions,
  InertEntry,
  KeyboardEventToBoolean,
  MouseEventToBoolean,
  PauseOptions,
  PropsOptions,
  RevertRecord,
  UnpauseOptions,
  VoidFunction,
  FocusTrapVue,
} from '@rightxt/focus-trap-vue';
import Plugin, { FocusTrap, FocusTrapExclude } from '@rightxt/focus-trap-vue';

Library components

FocusTrap

The component's content will be wrapped with 'focus-trap'.

Prop Type Default Description
v-model:active boolean false Activates/deactivates the trap
v-model:paused boolean false Pauses/unpauses the trap
createOptions CreateOptions null Options proxied to 'focus-trap' (focus-trap)
renderless boolean false If true, does not create a wrapper and wraps exactly one child
tag string 'div' Root element tag when renderless = false

Slots:

  • default — content within which focus is trapped

Events:

  • update:active(boolean) — trap activation state change
  • update:paused(boolean) — trap pause state change
  • created — component created
  • destroyed — component destroyed
  • error(where, error?) — an exception occurred inside the component
  • inner(handlerName) — events re-emitting internal 'focus-trap' events

Expose methods:

  • activate(options?) — activate the trap (focus-trap)
  • deactivate(options?) — deactivate the trap (focus-trap)
  • pause(options?) — pause an active trap (focus-trap)
  • unpause(options?) — unpause an active trap (focus-trap)
  • update() — force the component's DOM to update and reinitialize the trap
  • getState() — get the internal 'focus-trap' state as an object with two properties: { active, paused }
  • rootElement — a ref to the trap's root DOM element

Example:

<script setup lang="ts">
import { ref } from 'vue';
import { FocusTrap } from '@rightxt/focus-trap-vue';
const active = ref<boolean>(false);
const paused = ref<boolean>(false);
</script>

<template>
  <FocusTrap v-model:active="active" v-model:paused="paused">...</FocusTrap>
</template>

FocusTrapExclude

Marks a node/subtree inside the trap that should not become inert while the trap is active.

This component is only used inside FocusTrap!

Example:

<script setup lang="ts">
import { ref } from 'vue';
import { FocusTrap, FocusTrapExclude } from '@rightxt/focus-trap-vue';
const active = ref<boolean>(false);
const paused = ref<boolean>(false);
</script>

<template>
  <FocusTrap v-model:active="active" v-model:paused="paused">
    ...
    <FocusTrapExclude>
      <p>Here is <a href="#">something</a> (excluded group).</p>
      <p>Here is <a href="#">something</a> (excluded group).</p>
    </FocusTrapExclude>
    ...
  </FocusTrap>
</template>

Demo

FAQ

Can I use it without the plugin?

Yes. Import it as a component.

Why does v-model:paused sometimes revert to true?

If unpause() didn't take effect (the trap is not at the top of the stack), the component resyncs the model back so it reflects the trap's actual state. This matches 'focus-trap' behavior.

Why isn't the library a lightweight wrapper around 'focus-trap' like some alternatives, and what are its key features?

Because the library aims to ease developer experience by providing extra functionality for convenience.

The FocusTrap component creates an additional root HTML element by default. This ensures the trap works correctly even when there are temporarily no focusable elements inside — you don't have to track this manually.

If you set renderless=true, the component does not create a wrapper and wraps exactly one child. In this mode, the responsibility for having a proper container and focusable elements is on the developer.

You can control the trap declaratively (v-model:active, v-model:paused) or imperatively via expose methods (activate, deactivate, pause, unpause).

The additional FocusTrapExclude component lets you exclude specific elements or subtrees from the 'inertness' mode when the trap is active.

About

This library provides a Vue-component wrapper around 'focus-trap'.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors