Skip to Content
UtilitiesFocus Trap

Focus Trap

Purpose

Focus Trap provides shared utility behavior for design-system usage.

Import

import { FocusTrap } from "@tesseract-nexus/tesserix-ui"

API reference

import * as React from "react" export interface FocusTrapProps { /** * The children to render */ children: React.ReactNode /** * Whether the focus trap is active */ enabled?: boolean /** * Whether to restore focus to the previously focused element on unmount */ restoreFocus?: boolean } /** * Component that traps focus within its children */ export function FocusTrap({ children, enabled = true, restoreFocus = true }: FocusTrapProps) { const containerRef = React.useRef<HTMLDivElement>(null) const previouslyFocusedElement = React.useRef<HTMLElement | null>(null) React.useEffect(() => { if (!enabled) return // Store the currently focused element previouslyFocusedElement.current = document.activeElement as HTMLElement const container = containerRef.current if (!container) return // Focus first focusable element const focusableElements = getFocusableElements(container) if (focusableElements.length > 0) { focusableElements[0].focus() } const handleKeyDown = (e: KeyboardEvent) => { if (e.key !== "Tab") return const focusableElements = getFocusableElements(container) if (focusableElements.length === 0) return const firstElement = focusableElements[0] const lastElement = focusableElements[focusableElements.length - 1] // If shift + tab on first element, focus last if (e.shiftKey && document.activeElement === firstElement) { e.preventDefault() lastElement.focus() } // If tab on last element, focus first else if (!e.shiftKey && document.activeElement === lastElement) { e.preventDefault() firstElement.focus() } } document.addEventListener("keydown", handleKeyDown) return () => { document.removeEventListener("keydown", handleKeyDown) // Restore focus to previously focused element if (restoreFocus && previouslyFocusedElement.current) { previouslyFocusedElement.current.focus() } } }, [enabled, restoreFocus]) return <div ref={containerRef}>{children}</div> } function getFocusableElements(container: HTMLElement): HTMLElement[] { const selector = 'button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"]):not([disabled])' return Array.from(container.querySelectorAll(selector)) as HTMLElement[] }

Do / Don’t

Do

// Use shared utility in app architecture for consistency

Don’t

// Avoid duplicating equivalent utility behavior in feature code

Token / Theming Mapping

  • Utility UI should inherit system tokens: —primary, —foreground, —muted, —radius, —ring.
  • Theming/loading utilities should rely on provider config and CSS variables.

Interaction Test Checklist

  1. Functional behavior contract verification.
  2. Integration verification in at least one consuming screen.
  3. Accessibility verification for focus/visibility semantics where relevant.
  4. Error/fallback behavior verification.