CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-floating-ui--react

React library for creating accessible floating UI elements like tooltips, popovers, and dropdowns with advanced positioning

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

list-navigation.mddocs/

List Navigation & Composite Widgets

System for creating accessible lists, menus, and composite widgets with keyboard navigation, virtual focus, type-ahead search, and proper ARIA support.

Capabilities

useListNavigation Hook

Provides arrow key navigation for lists of items with real or virtual focus management.

/**
 * Arrow key navigation for lists with real or virtual focus
 * @param context - Floating UI context
 * @param props - List navigation configuration
 * @returns Element props for list navigation handling
 */
function useListNavigation(
  context: FloatingRootContext,
  props: UseListNavigationProps
): ElementProps;

interface UseListNavigationProps {
  listRef: React.MutableRefObject<Array<HTMLElement | null>>;
  activeIndex: number | null;
  onNavigate?: (activeIndex: number | null) => void;
  enabled?: boolean;
  orientation?: 'vertical' | 'horizontal' | 'both';
  loop?: boolean;
  nested?: boolean;
  focusItemOnOpen?: boolean | 'auto';
  focusItemOnHover?: boolean;
  cols?: number;
  disabledIndices?: Array<number>;
  allowEscape?: boolean;
  itemSizes?: Array<number>;
  dense?: boolean;
  rtl?: boolean;
  virtual?: boolean;
  virtualItemRef?: React.MutableRefObject<HTMLElement | null>;
}

Usage Examples:

import { 
  useListNavigation, 
  useFloating, 
  useClick, 
  useInteractions,
  FloatingList,
  useListItem
} from '@floating-ui/react';
import { useState, useRef } from 'react';

// Basic list navigation
function Menu() {
  const [isOpen, setIsOpen] = useState(false);
  const [activeIndex, setActiveIndex] = useState<number | null>(null);
  const listRef = useRef<Array<HTMLElement | null>>([]);

  const { refs, floatingStyles, context } = useFloating({
    open: isOpen,
    onOpenChange: setIsOpen,
  });

  const click = useClick(context);
  const listNavigation = useListNavigation(context, {
    listRef,
    activeIndex,
    onNavigate: setActiveIndex,
  });
  
  const { getReferenceProps, getFloatingProps, getItemProps } = useInteractions([
    click,
    listNavigation,
  ]);

  const items = ['Item 1', 'Item 2', 'Item 3'];

  return (
    <>
      <button ref={refs.setReference} {...getReferenceProps()}>
        Menu
      </button>
      {isOpen && (
        <FloatingList elementsRef={listRef} labelsRef={undefined}>
          <div
            ref={refs.setFloating}
            style={floatingStyles}
            {...getFloatingProps()}
          >
            {items.map((item, index) => (
              <MenuItem
                key={item}
                label={item}
                active={activeIndex === index}
                {...getItemProps({
                  onClick() {
                    setIsOpen(false);
                  },
                })}
              />
            ))}
          </div>
        </FloatingList>
      )}
    </>
  );
}

function MenuItem(props: {
  label: string;
  active: boolean;
  [key: string]: any;
}) {
  const { label, active, ...rest } = props;
  const { ref, index } = useListItem({ label });

  return (
    <div
      ref={ref}
      role="menuitem"
      tabIndex={active ? 0 : -1}
      style={{
        background: active ? 'lightblue' : 'white',
        padding: '8px',
      }}
      {...rest}
    >
      {label}
    </div>
  );
}

// Grid navigation
function GridMenu() {
  const [activeIndex, setActiveIndex] = useState(0);
  const listRef = useRef<Array<HTMLElement | null>>([]);

  const { refs, context } = useFloating({
    open: true,
    onOpenChange: () => {},
  });

  const listNavigation = useListNavigation(context, {
    listRef,
    activeIndex,
    onNavigate: setActiveIndex,
    orientation: 'both',
    cols: 3,
    loop: true,
  });

  const { getFloatingProps, getItemProps } = useInteractions([listNavigation]);

  const items = Array.from({ length: 9 }, (_, i) => `Item ${i + 1}`);

  return (
    <div
      ref={refs.setFloating}
      style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)' }}
      {...getFloatingProps()}
    >
      {items.map((item, index) => (
        <div
          key={item}
          ref={(el) => { listRef.current[index] = el; }}
          tabIndex={activeIndex === index ? 0 : -1}
          style={{
            background: activeIndex === index ? 'lightblue' : 'white',
            padding: '8px',
            border: '1px solid gray',
          }}
          {...getItemProps()}
        >
          {item}
        </div>
      ))}
    </div>
  );
}

useTypeahead Hook

Provides type-to-search functionality for focusing items in lists by typing their labels.

/**
 * Type-to-search functionality for focusing items
 * @param context - Floating UI context
 * @param props - Typeahead configuration
 * @returns Element props for typeahead handling
 */
function useTypeahead(
  context: FloatingRootContext,
  props: UseTypeaheadProps
): ElementProps;

interface UseTypeaheadProps {
  listRef: React.MutableRefObject<Array<HTMLElement | null>>;
  activeIndex: number | null;
  onMatch?: (index: number) => void;
  onTypingChange?: (typing: boolean) => void;
  enabled?: boolean;
  findMatch?: (
    list: Array<string | null>,
    typedString: string
  ) => string | null | undefined;
  resetMs?: number;
  ignoreKeys?: Array<string>;
  selectedIndex?: number | null;
}

Usage Example:

import { useTypeahead, useListNavigation } from '@floating-ui/react';

function SearchableMenu() {
  const [activeIndex, setActiveIndex] = useState<number | null>(null);
  const [typing, setTyping] = useState(false);
  const listRef = useRef<Array<HTMLElement | null>>([]);
  const labelRef = useRef<Array<string | null>>([]);

  const { refs, context } = useFloating({
    open: true,
    onOpenChange: () => {},
  });

  const listNavigation = useListNavigation(context, {
    listRef,
    activeIndex,
    onNavigate: setActiveIndex,
  });

  const typeahead = useTypeahead(context, {
    listRef: labelRef,
    activeIndex,
    onMatch: setActiveIndex,
    onTypingChange: setTyping,
  });

  const { getFloatingProps, getItemProps } = useInteractions([
    listNavigation,
    typeahead,
  ]);

  const items = ['Apple', 'Banana', 'Cherry', 'Date', 'Elderberry'];

  return (
    <div ref={refs.setFloating} {...getFloatingProps()}>
      {typing && <div>Typing...</div>}
      {items.map((item, index) => (
        <div
          key={item}
          ref={(el) => {
            listRef.current[index] = el;
            labelRef.current[index] = item;
          }}
          tabIndex={activeIndex === index ? 0 : -1}
          style={{
            background: activeIndex === index ? 'lightblue' : 'white',
            padding: '8px',
          }}
          {...getItemProps()}
        >
          {item}
        </div>
      ))}
    </div>
  );
}

FloatingList Component

Context provider for managing lists of floating items with element and label references.

/**
 * Context provider for managing floating lists
 * @param props - List context configuration
 * @returns Context provider for floating lists
 */
interface FloatingListProps {
  children: React.ReactNode;
  elementsRef: React.MutableRefObject<Array<HTMLElement | null>>;
  labelsRef?: React.MutableRefObject<Array<string | null>>;
}

declare const FloatingList: React.FC<FloatingListProps>;

useListItem Hook

Registers an item within a FloatingList context and provides ref management.

/**
 * Registers item in floating list context
 * @param props - List item configuration
 * @returns Ref and index for the list item
 */
function useListItem(props?: UseListItemProps): {
  ref: React.RefCallback<HTMLElement>;
  index: number;
};

interface UseListItemProps {
  label?: string | null;
}

Usage Example:

import { FloatingList, useListItem } from '@floating-ui/react';

function ListWithItems() {
  const elementsRef = useRef<Array<HTMLElement | null>>([]);
  const labelsRef = useRef<Array<string | null>>([]);

  return (
    <FloatingList elementsRef={elementsRef} labelsRef={labelsRef}>
      <Item label="First item" />
      <Item label="Second item" />
      <Item label="Third item" />
    </FloatingList>
  );
}

function Item({ label }: { label: string }) {
  const { ref, index } = useListItem({ label });

  return (
    <div ref={ref} data-index={index}>
      {label}
    </div>
  );
}

Composite Component

Creates a single tab stop with arrow key navigation for composite widgets per WAI-ARIA guidelines.

/**
 * Single tab stop with arrow key navigation for composite widgets
 * @param props - Composite widget configuration
 * @returns Composite widget container
 */
interface CompositeProps {
  children: React.ReactNode;
  role?: string;
  orientation?: 'horizontal' | 'vertical' | 'both';
  loop?: boolean;
  cols?: number;
  disabledIndices?: Array<number>;
  activeIndex?: number;
  onNavigate?: (activeIndex: number) => void;
  itemSizes?: Array<number>;
  dense?: boolean;
  render?: React.ComponentPropsWithoutRef<any>;
}

declare const Composite: React.FC<CompositeProps>;

CompositeItem Component

Individual item within a Composite widget with proper focus management.

/**
 * Individual item within Composite widget
 * @param props - Composite item configuration
 * @returns Composite item element
 */
interface CompositeItemProps {
  children: React.ReactNode;
  role?: string;
  render?: React.ComponentPropsWithoutRef<any>;
}

declare const CompositeItem: React.FC<CompositeItemProps>;

Usage Example:

import { Composite, CompositeItem } from '@floating-ui/react';
import { useState } from 'react';

function Toolbar() {
  const [activeIndex, setActiveIndex] = useState(0);

  return (
    <Composite
      role="toolbar"
      orientation="horizontal"
      activeIndex={activeIndex}
      onNavigate={setActiveIndex}
    >
      <CompositeItem role="button">
        <button>Cut</button>
      </CompositeItem>
      <CompositeItem role="button">
        <button>Copy</button>
      </CompositeItem>
      <CompositeItem role="button">
        <button>Paste</button>
      </CompositeItem>
    </Composite>
  );
}

Navigation Patterns

Menu Navigation

  • Vertical orientation for dropdown menus
  • Loop navigation for continuous scrolling
  • Focus on open for immediate keyboard access

Grid Navigation

  • Both orientation for 2D navigation
  • Cols property to define grid structure
  • Dense layout for optimized keyboard flow

Toolbar Navigation

  • Horizontal orientation for tool selection
  • Single tab stop pattern via Composite
  • Arrow key navigation between tools

Accessibility Features

ARIA Compliance

  • role="menu" for menu lists
  • role="menuitem" for menu items
  • role="toolbar" for composite toolbars
  • aria-activedescendant for virtual focus

Keyboard Support

  • Arrow keys: Navigate between items
  • Home/End: Jump to first/last item
  • Page Up/Down: Navigate by page (if supported)
  • Enter/Space: Activate focused item
  • Escape: Close list (if dismissible)

Focus Management

  • Virtual focus: Maintains single tab stop with aria-activedescendant
  • Real focus: Moves actual DOM focus between items
  • Focus restoration: Returns to appropriate element on close

docs

core-positioning.md

focus-management.md

index.md

interaction-hooks.md

layout-components.md

list-navigation.md

positioning-middleware.md

transitions.md

tree-context.md

tile.json