InputModality

Reactively track the user's current input method (keyboard, mouse, or touch).

Loading demo...

Usage

angular-ts
import { Component, computed } from '@angular/core';
import { inputModality } from '@signality/core';

@Component({
  template: `
    <div [class]="'input-mode-' + modality()">
      <p>Current input: {{ modality() ?? 'none' }}</p>
      <p>Using keyboard: {{ isKeyboard() }}</p>
    </div>
  `,
})
export class InputDemo {
  readonly modality = inputModality(); 
  readonly isKeyboard = computed(() => this.modality() === 'keyboard');
}

Use InjectionToken for Singleton

For global state tracking like inputModality, consider using the provided INPUT_MODALITY token instead of calling the function directly. This provides a singleton instance that can be shared across your entire application, reducing memory usage and event listener overhead:

typescript
import { inject } from '@angular/core';
import { INPUT_MODALITY } from '@signality/core';

const modality = inputModality(); 
const modality = inject(INPUT_MODALITY); 

Learn more about Token-based utilities.

Parameters

ParameterTypeDescription
optionsWithInjectorOptional configuration (see Options below)

Options

The WithInjector interface provides:

OptionTypeDescription
injectorInjectorOptional injector for DI context

Return Value

The inputModality() function returns a Signal<InputModality>:

  • 'keyboard' — User is using keyboard input
  • 'mouse' — User is using mouse input
  • 'touch' — User is using touch input
  • null — No input detected yet

Examples

Touch-optimized UI

angular-ts
import { Component, computed } from '@angular/core';
import { inputModality } from '@signality/core';

@Component({
  template: `
    <nav [class.touch-mode]="isTouch()">
      <button [style.padding]="buttonPadding()">Menu</button>
      <button [style.padding]="buttonPadding()">Settings</button>
    </nav>
  `,
})
export class AdaptiveNav {
  readonly modality = inputModality();
  readonly isTouch = computed(() => this.modality() === 'touch');
  
  // Larger touch targets for touch input
  readonly buttonPadding = computed(() => 
    this.isTouch() ? '1rem 2rem' : '0.5rem 1rem'
  );
}

Analytics tracking

angular-ts
import { Component, effect, inject } from '@angular/core';
import { inputModality } from '@signality/core';
import { AnalyticsService } from './analytics.service';

@Component({ /* ... */ })
export class AppComponent {
  readonly analytics = inject(AnalyticsService);
  readonly modality = inputModality();
  
  constructor() {
    effect(() => {
      const current = this.modality();
      if (current) {
        this.analytics.setUserProperty('input_modality', current);
      }
    });
  }
}

Conditional tooltips

angular-ts
import { Component, computed } from '@angular/core';
import { inputModality } from '@signality/core';

@Component({
  template: `
    <button 
      [attr.title]="showTooltip() ? 'Click to save' : null"
    >
      💾
    </button>
  `,
})
export class SaveButton {
  readonly modality = inputModality();
  
  // Only show native tooltips for mouse users
  readonly showTooltip = computed(() => this.modality() === 'mouse');
}

SSR Compatibility

On the server, the signal initializes with null.

Type Definitions

typescript
type InputModality = 'keyboard' | 'mouse' | 'touch' | null;

function inputModality(options?: WithInjector): Signal<InputModality>;

export const INPUT_MODALITY: InjectionToken<Signal<InputModality>>;
Edit this page on GitHub Last updated: Mar 19, 2026, 23:28:23