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.
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
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.
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>
);
}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>
);
}The focus ring system automatically detects user interaction modality:
focus() calls)autoFocus={true} show focus rings by defaultWhen isTextInput={true}:
When within={true}:
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;
}Install with Tessl CLI
npx tessl i tessl/npm-react-aria--focus