CDK Keyboard Navigation Manager

Keyboard Navigation Manager

Overview API Examples

The Keyboard Navigation feature provides a comprehensive system for managing focus and enabling accessible keyboard navigation within a group of interactive elements. It allows users to navigate through a list, grid, or collection of items using arrow keys, and optionally, type-ahead functionality. This system is composed of two main directives: LmnKeyboardNavigationManagerDirective which orchestrates the navigation logic, and LmnKeyboardNavigationItemDirective which marks individual elements as navigable items within a managed group.

Technical Overview

This feature relies on the interplay between a manager directive and one or more item directives.

LmnKeyboardNavigationManagerDirective ([lmnKeyboardNavigationManager]): This directive is applied to a container element that groups several navigable items. It listens for keyboard events (primarily arrow keys and alphanumeric keys for type-ahead) on its host element or a specified scopeElement. Based on its configuration (direction, wrapping, navigation mode), it determines the next item to activate and focus. It manages the tabindex of its child items, ensuring only the currently "active" item is in the tab order (in 'arrows' mode) or all items are tabbable (in 'tab' mode). It handles the logic for linear navigation, grid navigation (based on row/column attributes on items), and type-ahead matching.

LmnKeyboardNavigationItemDirective ([lmnKeyboardNavigationItem]): This directive is applied to each individual element that should be part of the keyboard-navigable group managed by a parent LmnKeyboardNavigationManagerDirective. It marks the element and provides metadata to the manager, such as its disabled state, row/column position (for grid navigation), and a label for type-ahead matching. It sets necessary ARIA-like data attributes on the host element (or a specified target element) to reflect its state (e.g., active, disabled) and its role within the navigation group. Each item is assigned a unique ID.

The system uses helper functions (e.g., parseCoordinates) and models (e.g., LmnKeyboardNavigationManagerDirection, LmnKeyboardNavigationManagerState) to define behavior and state. Keycodes are defined in keycodes.ts.

Key Features

  • Arrow Key Navigation: Supports navigation using Up, Down, Left, and Right arrow keys.
  • Configurable Direction: Navigation can be configured as 'vertical', 'horizontal', or 'both' (for grid-like structures).
  • Wrapping: Supports wrapping navigation from the last item to the first, and vice-versa.
  • Grid Navigation: Items can be assigned row and column attributes for 2D navigation.
  • Type-Ahead: Allows users to type characters to quickly jump to items whose typeAheadLabel matches the typed string (with a configurable debounce interval).
  • Navigation Modes:
    • 'arrows': Only the active item has tabindex="0", others have tabindex="-1". Arrow keys move focus.
    • 'tab': All items have tabindex="0", allowing standard tab navigation between them. Arrow key navigation might still be active depending on other settings.
  • Disabled Items: Navigation skips items marked as disabled.
  • Focus Management: Automatically manages tabindex and focuses the active item.
  • State Management: The manager can be initialized with a default active item/state.
  • Accessibility: Applies data attributes to items to signify their role and state (e.g., data-lmn-keyboard-navigation-item, data-lmn-keyboard-navigation-item-active).
  • Modifier Key Handling: By default, navigation is ignored if modifier keys (Ctrl, Alt, Meta, Shift) are pressed, but this can be configured with allowedModifierKeys.
  • Scope Control: Navigation can be scoped to a specific element if scopeElement is provided to the manager.
  • RTL Support: Arrow key direction for horizontal navigation adapts to the current page direction (LTR/RTL).

When to Use

Use the Keyboard Navigation Manager and Item directives when:

  • You have a list, menu, grid, or any collection of interactive elements that should be navigable using a keyboard in a roving tabindex pattern.
  • You need to implement accessible keyboard navigation for custom components like dropdowns, tabs, toolbars, or tree views.
  • You want to provide type-ahead functionality for quickly selecting items in a long list.
  • You need to manage focus within a group of elements to ensure a predictable and accessible user experience.

Implementation

LmnKeyboardNavigationManagerDirective

Apply this directive to the container element that groups the navigable items.

Selector: [lmnKeyboardNavigationManager]

Inputs:

  • lmnKeyboardNavigationManagerActive (alias active): Optional (boolean, default: true). Enables or disables the keyboard navigation functionality for the group.
  • lmnKeyboardNavigationManagerDirection (alias direction): Optional (LmnKeyboardNavigationManagerDirection, default: 'vertical'). Defines the primary navigation direction: 'vertical', 'horizontal', or 'both'.
  • lmnKeyboardNavigationManagerWrap (alias wrap): Optional (boolean, default: true). If true, navigation wraps around from the last item to the first and vice-versa.
  • lmnKeyboardNavigationManagerTypeAhead (alias typeAhead): Optional (boolean, default: true). Enables or disables type-ahead functionality.
  • lmnKeyboardNavigationManagerTypeAheadDebounceInterval (alias typeAheadDebounceInterval): Optional (number, default: 300ms). The debounce interval for type-ahead input.
  • lmnKeyboardNavigationManagerAllowedModifierKeys (alias allowedModifierKeys): Optional (LmnModifierKey[], default: []). An array of modifier keys (e.g., 'ctrlKey', 'shiftKey') that, if pressed along with a navigation key, will still allow navigation to occur.
  • lmnKeyboardNavigationManagerAllowCrossKeysNavigation (alias allowCrossKeysNavigation): Optional (boolean, default: true). If false and direction is 'vertical', left/right arrows won't navigate. If false and direction is 'horizontal', up/down arrows won't navigate.
  • lmnKeyboardNavigationManagerNavigationMode (alias navigationMode): Optional (LmnKeyboardNavigationManagerNavigationMode, default: 'arrows'). Defines the navigation interaction model:
    • 'arrows': Roving tabindex with arrow keys.
    • 'tab': All items are tabbable.
  • lmnKeyboardNavigationManagerDefaultState (alias defaultState): Optional (LmnKeyboardNavigationManagerState). Allows setting an initial active item by activeItemIndex (for linear) or activeItemRow and activeItemColumn (for grid).
  • lmnKeyboardNavigationManagerScopeElement (alias scopeElement): Optional (HTMLElement | (() => HTMLElement)). If provided, item querying is scoped to this element's direct descendants instead of the manager's host element.

Public Methods:

  • setActiveItem(index: number, focus: boolean = false): void: Programmatically sets the active item by its index within the collection of navigable items. Optionally focuses the item.

Host Attributes:

  • data-lmn-keyboard-navigation-manager: Set to 'true' or 'false' reflecting the active input.

LmnKeyboardNavigationItemDirective

Apply this directive to each individual interactive element within the group managed by LmnKeyboardNavigationManagerDirective.

Selector: [lmnKeyboardNavigationItem]

Inputs:

  • lmnKeyboardNavigationItemTarget (alias target): Optional (HTMLElement, default: the host element). The actual HTML element that should receive focus and ARIA-like attributes. Useful if the directive is on a wrapper.
  • lmnKeyboardNavigationItemActive (alias active): Optional (boolean, default: true). Indicates if the item is currently considered active (not to be confused with the manager's active item, more like an enabled state for participation).
  • lmnKeyboardNavigationItemDisabled (alias disabled): Optional (boolean, default: false). If true, this item will be skipped during navigation.
  • lmnKeyboardNavigationItemRow (alias row): Optional (number, default: 0). The row index for grid navigation.
  • lmnKeyboardNavigationItemColumn (alias column): Optional (number, default: 0). The column index for grid navigation.
  • lmnKeyboardNavigationItemTypeAheadLabel (alias typeAheadLabel): Optional (string, default: innerText of the host element). The string used for matching during type-ahead navigation.

Host Attributes (applied to the target element):

  • tabindex: Dynamically set to '-1' or '0' by the manager directive.
  • data-lmn-keyboard-navigation-item: Presence indicates the element is a navigable item.
  • data-lmn-keyboard-navigation-item-active: Reflects the active input ('true' or 'false').
  • data-lmn-keyboard-navigation-item-disabled: Reflects the disabled input ('true' or 'false').
  • data-lmn-keyboard-navigation-item-row: Reflects the row input.
  • data-lmn-keyboard-navigation-item-column: Reflects the column input.
  • data-lmn-keyboard-navigation-item-type-ahead-label: Reflects the typeAheadLabel input.
  • data-lmn-keyboard-navigation-item-id: A unique auto-generated ID for the item.
  • LmnKeyboardNavigationManagerDirection: 'horizontal' | 'vertical' | 'both'
  • LmnKeyboardNavigationManagerTarget: Describes the intended navigation movement (e.g., 'next-one-axis', 'previous-vertical').
  • LmnKeyboardNavigationManagerNavigationMode: 'arrows' | 'tab'
  • LmnKeyboardNavigationManagerState: { activeItemIndex?: number; activeItemRow?: number; activeItemColumn?: number; }
  • LmnModifierKey: 'altKey' | 'ctrlKey' | 'metaKey' | 'shiftKey'