CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-react-aria--focus

React hooks and components for accessible focus management including FocusScope for focus containment, FocusRing for visual focus indicators, and utilities for focus navigation and virtual focus handling.

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

focus-ring.mddocs/

Visual Focus Indicators

Visual focus indicators provide keyboard-only focus rings that respect user input modality. Focus rings are shown only when users are navigating with keyboard, not when using mouse, touch, or other input methods.

Capabilities

FocusRing Component

A utility component that applies CSS classes when an element has keyboard focus, with intelligent detection of interaction modality.

/**
 * A utility component that applies a CSS class when an element has keyboard focus.
 * Focus rings are visible only when the user is interacting with a keyboard,
 * not with a mouse, touch, or other input methods.
 */
function FocusRing(props: FocusRingProps): ReactElement;

interface FocusRingProps {
  /** Child element to apply CSS classes to. */
  children: ReactElement;
  /** CSS class to apply when the element is focused. */
  focusClass?: string;
  /** CSS class to apply when the element has keyboard focus. */
  focusRingClass?: string;
  /**
   * Whether to show the focus ring when something
   * inside the container element has focus (true), or
   * only if the container itself has focus (false).
   * @default false
   */
  within?: boolean;
  /** Whether the element is a text input. */
  isTextInput?: boolean;
  /** Whether the element will be auto focused. */
  autoFocus?: boolean;
}

Usage Examples:

import React from "react";
import { FocusRing } from "@react-aria/focus";

// Basic button with focus ring
function Button({ children, ...props }) {
  return (
    <FocusRing focusRingClass="focus-visible">
      <button className="btn" {...props}>
        {children}
      </button>
    </FocusRing>
  );
}

// Input with different focus classes
function TextInput({ label, ...props }) {
  return (
    <div>
      <label>{label}</label>
      <FocusRing 
        focusClass="input-focused" 
        focusRingClass="input-focus-ring"
        isTextInput
      >
        <input type="text" {...props} />
      </FocusRing>
    </div>
  );
}

// Container with focus-within behavior
function Card({ children }) {
  return (
    <FocusRing 
      focusRingClass="card-focus-ring" 
      within
    >
      <div className="card">
        {children}
      </div>
    </FocusRing>
  );
}

// Auto-focused element
function AutoFocusButton({ children, ...props }) {
  return (
    <FocusRing focusRingClass="focus-visible" autoFocus>
      <button {...props}>
        {children}
      </button>
    </FocusRing>
  );
}

useFocusRing Hook

Determines whether a focus ring should be shown and provides focus state information with modality detection.

/**
 * Determines whether a focus ring should be shown to indicate keyboard focus.
 * Focus rings are visible only when the user is interacting with a keyboard,
 * not with a mouse, touch, or other input methods.
 */
function useFocusRing(props?: AriaFocusRingProps): FocusRingAria;

interface AriaFocusRingProps {
  /**
   * Whether to show the focus ring when something
   * inside the container element has focus (true), or
   * only if the container itself has focus (false).
   * @default false
   */
  within?: boolean;
  /** Whether the element is a text input. */
  isTextInput?: boolean;
  /** Whether the element will be auto focused. */
  autoFocus?: boolean;
}

interface FocusRingAria {
  /** Whether the element is currently focused. */
  isFocused: boolean;
  /** Whether keyboard focus should be visible. */
  isFocusVisible: boolean;
  /** Props to apply to the container element with the focus ring. */
  focusProps: DOMAttributes;
}

Usage Examples:

import React from "react";
import { useFocusRing } from "@react-aria/focus";
import clsx from "clsx";

// Custom button with focus ring logic
function CustomButton({ children, className, ...props }) {
  const { isFocused, isFocusVisible, focusProps } = useFocusRing();
  
  return (
    <button
      {...props}
      {...focusProps}
      className={clsx(className, {
        'is-focused': isFocused,
        'focus-visible': isFocusVisible
      })}
    >
      {children}
    </button>
  );
}

// Input with custom focus ring behavior
function CustomInput({ isTextInput = true, ...props }) {
  const { isFocused, isFocusVisible, focusProps } = useFocusRing({
    isTextInput
  });
  
  return (
    <input
      {...props}
      {...focusProps}
      data-focused={isFocused}
      data-focus-visible={isFocusVisible}
      style={{
        outline: isFocusVisible ? '2px solid blue' : 'none',
        borderColor: isFocused ? '#007acc' : '#ccc'
      }}
    />
  );
}

// Container with focus-within behavior
function FocusWithinContainer({ children }) {
  const { isFocused, isFocusVisible, focusProps } = useFocusRing({
    within: true
  });
  
  return (
    <div
      {...focusProps}
      className={clsx('container', {
        'has-focus-within': isFocused,
        'focus-within-visible': isFocusVisible
      })}
    >
      {children}
    </div>
  );
}

// Conditional focus ring display
function ConditionalFocusRing({ showFocusRing = true, children }) {
  const { isFocused, isFocusVisible, focusProps } = useFocusRing();
  
  return (
    <div
      {...focusProps}
      style={{
        outline: showFocusRing && isFocusVisible ? '2px solid orange' : 'none'
      }}
    >
      Focus state: {isFocused ? 'focused' : 'not focused'}
      <br />
      Focus visible: {isFocusVisible ? 'visible' : 'not visible'}
      {children}
    </div>
  );
}

Focus Ring Behavior

Interaction Modality Detection

The focus ring system automatically detects user interaction modality:

  • Keyboard Navigation: Focus rings are shown when users navigate with Tab, Arrow keys, or other keyboard interactions
  • Mouse/Touch Interaction: Focus rings are hidden when users interact with mouse clicks or touch gestures
  • Programmatic Focus: Focus rings are shown when focus is moved programmatically (e.g., via focus() calls)
  • Auto Focus: Elements with autoFocus={true} show focus rings by default

Text Input Considerations

When isTextInput={true}:

  • Focus rings follow different visibility rules optimized for text input fields
  • Accounts for the fact that text inputs often maintain focus during typing
  • Provides appropriate visual feedback for screen reader users

Focus Within Mode

When within={true}:

  • The focus ring is shown when any descendant element has focus
  • Useful for card components, form groups, or other containers
  • Combines focus-within CSS behavior with modality detection

CSS Integration

The focus ring components work with CSS to provide visual feedback:

/* Example CSS for focus rings */
.focus-visible {
  outline: 2px solid #007acc;
  outline-offset: 2px;
}

.input-focused {
  border-color: #007acc;
}

.input-focus-ring {
  box-shadow: 0 0 0 2px rgba(0, 122, 204, 0.3);
}

.card-focus-ring {
  box-shadow: 0 0 0 2px rgba(0, 122, 204, 0.2);
}

/* Hide default browser focus outlines when using custom focus rings */
button:focus,
input:focus {
  outline: none;
}

Accessibility Considerations

  • Focus rings are essential for keyboard users and screen reader users
  • Never completely hide focus indicators - always provide some visual indication
  • Ensure sufficient color contrast for focus ring colors
  • Test focus ring visibility in both light and dark themes
  • Consider users with visual impairments who may need high contrast focus indicators

Install with Tessl CLI

npx tessl i tessl/npm-react-aria--focus

docs

focus-navigation.md

focus-ring.md

focus-scope.md

index.md

virtual-focus.md

tile.json