CDK Keyboard Navigation

Keyboard Navigation

Overview API Examples

The LmnKeyboardNavigationService is an Angular root-provided service designed to detect and manage the current mode of user interaction with the page, specifically distinguishing between keyboard-driven navigation and mouse-driven navigation. It automatically toggles a CSS class on the document.body to allow styles to adapt for better accessibility and user experience based on the input method. The service also provides methods for manual control over the navigation state and exposes the current state through both an Observable and a Signal.

Technical Overview

The LmnKeyboardNavigationService works by listening to global keyboard events (keydown, keyup, keypress) and mouse events (mousedown, mouseup, click) on the window.

  • When a keyboard event is detected, the service transitions into "keyboard navigation active" mode. In this mode, it adds the lmn-keyboard-navigation-active class to document.body and switches its listeners to primarily detect mouse activity (to switch out of keyboard mode).
  • When a significant mouse event is detected (e.g., a mousedown, or a click with mouse buttons pressed), the service transitions out of "keyboard navigation active" mode (or into "mouse navigation" mode). It removes the lmn-keyboard-navigation-active class from document.body and switches its listeners to primarily detect keyboard activity (to re-enter keyboard mode).
  • The service initializes by assuming mouse-based navigation and listens for keyboard events to activate the keyboard navigation mode.
  • It uses Angular's Renderer2 for managing global event listeners and ensures these listeners are cleaned up when the service is destroyed.
  • The core state is managed by an internal Angular Signal (#keyboardNavigationActive), which is exposed reactively.

Key Features

  • Automatic Mode Detection: Switches between keyboard and mouse navigation modes based on user input.
  • CSS Class Toggling: Adds/removes lmn-keyboard-navigation-active class on document.body to enable CSS-driven styling for focus indicators and other accessibility enhancements.
  • Manual Control: Provides public methods (activateKeyboardNavigation(), deactivateKeyboardNavigation()) to force a specific navigation mode.
  • Reactive State Exposure:
    • changes$: An Observable<boolean> that emits true when keyboard navigation becomes active, and false otherwise.
    • keyboardNavigationActive: A readonly Signal<boolean> providing the current state.
  • Efficient Event Listening: Dynamically adjusts which event types (keyboard or mouse) it's primarily listening for to optimize performance and avoid unnecessary checks.
  • Lifecycle Management: Properly removes event listeners when the service is destroyed.

When to Use

Use the LmnKeyboardNavigationService when:

  • You need to apply different styling for focus states depending on whether the user is navigating with a keyboard (e.g., always visible focus rings) versus a mouse (e.g., focus rings only on keyboard focus).
  • You want to implement accessibility features that adapt to the user's current input modality.
  • You need to programmatically know or control whether the application should behave as if it's in keyboard navigation mode.
  • You want a global, consistent way to manage the visual appearance of keyboard focus across your application.

Implementation

The LmnKeyboardNavigationService is typically injected into components or other services where knowledge of the navigation mode is required, or to manually control the mode.

Service Name: LmnKeyboardNavigationService (providedIn: 'root')

Public Properties & Observables:

  • keyboardNavigationActive: Signal<boolean> (readonly):
    • A Signal that emits true if keyboard navigation is currently considered active, false otherwise.
  • changes$: Observable<boolean>:
    • An Observable that emits true when keyboard navigation becomes active, and false when it becomes inactive.
  • isKeyboardNavigationActive: boolean (deprecated getter):
    • Returns the current boolean state. Prefer using the keyboardNavigationActive Signal.

Public Methods:

  • activateKeyboardNavigation(): void:

    • Manually forces the service into keyboard navigation active mode.
    • Adds lmn-keyboard-navigation-active to document.body.
    • Useful if you need to programmatically ensure keyboard-friendly styles are applied.
  • deactivateKeyboardNavigation(): void:

    • Manually forces the service out of keyboard navigation active mode (i.e., assumes mouse navigation).
    • Removes lmn-keyboard-navigation-active from document.body.

CSS Interaction:

The primary side effect of this service is the management of the lmn-keyboard-navigation-active class on the <body> tag. Your application's CSS should define rules based on the presence or absence of this class.