Gestures

The @m3e/web/gestures module provides a gesture recognition subsystem supporting declarative and programmatic gesture detection. It uses a modular recognizer architecture with a priority-based disposition system that resolves competing claims on input.

Features include:

The @m3e/web/gestures module is organized around a base module that installs the gesture subsystem and provides shared infrastructure for all recognizers.

import "@m3e/web/gestures";

Each recognizer is published as its own entry point, so applications can import only the gestures they use. For example:

import "@m3e/web/gestures/tap";
import "@m3e/web/gestures/swipe";
Usage

This section outlines usage examples and configuration guidance for the components in this package.

Basic usage

Gestures represent semantic actions (e.g., tap, pan, long-press) derived from analyzing sequences of pointer events. Each gesture progresses through phases (start, update, end, cancel) with recognizers emitting semantic detail at each phase.

Gestures can be recognized declaratively or programmatically. Declarative recognition uses <m3e-*-gesture> elements bound to a target element using the for attribute. When the recognizer determines that a gesture has entered a phase, it emits a gesture event containing the phase and its associated detail. Programmatic recognition uses the detectGesture function, which binds one or more recognizers to an element and invokes callback handlers for each phase. These handlers receive the same gesture detail emitted by declarative usage, allowing both approaches to produce identical results.

Gesture recognition can be configured with a set of common options that apply to all recognizers.

All gestures emit a common set of detail fields that describe the semantic output of recognition.

Tap

Use the m3e-tap-gesture element or the tap function from the @m3e/web/gestures/tap module to recognize a short press and release with minimal pointer movement.

Tap the element below to see it in action.

<div id="item"></div>
<m3e-tap-gesture for="item"></m3e-tap-gesture>
import { detectGesture, phase } from "@m3e/web/gestures";
import { tap } from "@m3e/web/gestures/tap";

const item = document.getElementById("item");
detectGesture(item, tap(phase({
  onEnd: (detail) => {
    // Handle tap
  }
})));

In addition to common options, the tap recognizer provides settings that control how a tap is validated:

A tap gesture follows a simple lifecycle based on pointer events:

In addition to common detail, tap gesture events provide tap-specific fields that describe where the interaction began and how long it lasted:

Long-press

Use the m3e-long-press-gesture element or the longPress function from the @m3e/web/gestures/long-press module to recognize a pointer held in place for a minimum duration with minimal movement.

Press and hold the element below to see it in action.

<div id="item"></div>
<m3e-long-press-gesture for="item"></m3e-long-press-gesture>
import { detectGesture, phase } from "@m3e/web/gestures";
import { longPress } from "@m3e/web/gestures/long-press";

const item = document.getElementById("item");
detectGesture(item, longPress(phase({
  onEnd: (detail) => {
    // Handle long-press
  }
})));

The long-press recognizer shares the same options as tap, except for max-release-interval, and includes the following additional settings:

A long-press gesture progresses through start, end, and cancel phases. The exact lifecycle depends on whether the gesture is continuous (default) or discrete.

Long-press gesture events emit the same detail fields as tap, including the coordinates where the press began and the total duration of the interaction.

Swipe

Use the m3e-swipe-gesture element or the swipe function from the @m3e/web/gestures/swipe module to recognize high-velocity directional swipe gestures.

Swipe the element below to see it in action.

<div id="item"></div>
<m3e-swipe-gesture for="item"></m3e-swipe-gesture>
import { detectGesture, phase } from "@m3e/web/gestures";
import { swipe } from "@m3e/web/gestures/swipe";

const item = document.getElementById("item");
detectGesture(item, swipe(phase({
  onEnd: (detail) => {
    // Handle swipe
  }
})));

Swipe gestures can be configured using the following options, which control how movement, velocity, and direction are interpreted during recognition:

A swipe gesture progresses through all phases and is recognized on pointerup once its velocity, displacement, and direction requirements have been met.

Swipe gestures emit detail describing the resolved direction, movement, velocity, and initial input coordinates:

Pan

Use the m3e-pan-gesture element or the pan function from the @m3e/web/gestures/pan module to track continuous pointer movement across an element.

Pan the element below to see it in action.

<div id="item"></div>
<m3e-pan-gesture for="item"></m3e-pan-gesture>
import { detectGesture, phase } from "@m3e/web/gestures";
import { pan } from "@m3e/web/gestures/pan";

const item = document.getElementById("item");
detectGesture(item, pan(phase({
  onStart: (detail) => {
    // Begin tracking
  },
  onUpdate: (detail) => {
    // Continuous movement
  },
  onEnd: (detail) => {
    // Finalize pan
  },
  onCancel: (detail) => {
    // Abort pan
  }
})));

Pan gestures support options that control activation, movement thresholds, axis behavior, and multi-pointer coordination:

A pan gesture progresses through all phases and is recognized continuously as the pointer moves. It begins once the required pointers are pressed and the minimum displacement has been met, emits updates throughout movement, and completes on pointerup with the final translation and axis information.

The pan gesture detail describes the semantic output of continuous pointer movement. It reports both incremental and accumulated motion, along with positional, directional, and temporal information derived from the input stream.

Rotate

Use the m3e-rotate-gesture element or the rotate function from the @m3e/web/gestures/rotate module to detect rotational movement from multiple pointers.

Rotate the element below using two pointer devices (two fingers) to see the gesture in action.

<div id="item"></div>
<m3e-rotate-gesture for="item"></m3e-rotate-gesture>
import { detectGesture, phase } from "@m3e/web/gestures";
import { rotate } from "@m3e/web/gestures/rotate";

const item = document.getElementById("item");
detectGesture(item, rotate(phase({
  onStart: (detail) => {
    // Begin tracking
  },
  onUpdate: (detail) => {
    // Continuous movement
  },
  onEnd: (detail) => {
    // Finalize rotate
  },
  onCancel: (detail) => {
    // Abort rotate
  }
})));

Rotate gesture options control how multi-pointer rotation is interpreted. They define the number of pointers required, the activation mode, the minimum displacement needed to begin rotation, and the maximum allowed interval between pointer presses.

A rotate gesture progresses through all phases and is recognized once the required number of pointers have pressed within the allowed interval and rotation has begun. The recognizer computes pointer angles relative to the centroid and emits detail as rotation changes over time.

The rotate gesture detail describes the semantic output of multi-pointer rotation. It reports the initial angle, current angle, total rotation, incremental rotation, and angular velocity derived from pointer movement around the centroid.

Scale

Use the m3e-scale-gesture element or the scale function from the @m3e/web/gestures/scale module to detect multi-pointer scaling. A scale gesture measures how far active pointers move toward or away from their centroid, producing a scale factor that represents zoom in or out.

Pinch the element below using two pointer devices (two fingers) to see the gesture in action.

<div id="item"></div>
<m3e-scale-gesture for="item"></m3e-scale-gesture>
import { detectGesture, phase } from "@m3e/web/gestures";
import { scale } from "@m3e/web/gestures/scale";

const item = document.getElementById("item");
detectGesture(item, scale(phase({
  onStart: (detail) => {
    // Begin tracking
  },
  onUpdate: (detail) => {
    // Continuous movement
  },
  onEnd: (detail) => {
    // Finalize rotate
  },
  onCancel: (detail) => {
    // Abort rotate
  }
})));

Scale gesture options control how multi-pointer zooming is interpreted. They define the number of pointers required, the minimum displacement needed to begin scaling, and the maximum allowed interval between pointer presses.

A scale gesture progresses through all phases and is recognized once the required number of pointers have pressed within the allowed interval and the centroid has displaced enough to begin scaling. The recognizer computes distances from each pointer to the centroid and emits detail as the scale factor changes over time.

The scale gesture detail describes the semantic output of multi-pointer scaling. It reports the initial and current average distances from the centroid, the total scale factor, incremental scale changes, and instantaneous scale velocity.

Repeat

Use the m3e-repeat-gesture element or the repeat function from the @m3e/web/gestures/repeat module to detect repeated activations of a gesture over time. The element must contain a single nested gesture element that defines which gesture is repeated, and the repeat function requires the recognizer to repeat to be explicitly provided.

Tipple tap the element below to see repeat behavior in action.

<div id="item" class="target"></div>
<m3e-repeat-gesture for="item" count="3" max-interval="350">
  <m3e-tap-gesture></m3e-tap-gesture>
</m3e-repeat-gesture>
import { detectGesture, phase } from "@m3e/web/gestures";
import { tap } from "@m3e/web/gestures/tap";
import { repeat } from "@m3e/web/gestures/repeat";

const item = document.getElementById("item");
detectGesture(item, repeat(
  phase({
    onEnd: (detail) => {
      // Handle triple tap
    },
  }),
  {
    count: 3,
    maxInterval: 350
  },
  tap(),
));

Repeat gesture options control how consecutive activations of a nested gesture are interpreted. They define the maximum allowed interval between occurrences and the number of repetitions required before the repeat gesture is recognized.

A repeat gesture progresses through all phases and is recognized once its nested gesture has produced the required number of terminal occurrences within the allowed interval. Each occurrence contributes a terminal detail, and the repeat recognizer emits its own detail stream as repetitions accumulate.

The repeat gesture detail describes the semantic output of a repeated gesture. It aggregates the terminal details produced by each occurrence of the nested gesture in the order they were recognized.

Sequence

Use the m3e-sequence-gesture element or the sequence function from the @m3e/web/gestures/sequence module to detect ordered activations of multiple gestures over time. The element must contain the gesture elements in the order they must occur, and the sequence function requires the recognizers to be explicitly provided.

Press and hold the element below, then pan to see a sequential gesture in action.

<div id="item" class="target"></div>
<m3e-sequence-gesture for="item" max-interval="0">
  <m3e-long-press-gesture></m3e-long-press-gesture>
  <m3e-pan-gesture activation-mode="move"></m3e-pan-gesture>
</m3e-sequence-gesture>
import { detectGesture, phase } from "@m3e/web/gestures";
import { longPress } from "@m3e/web/gestures/long-press";
import { pan } from "@m3e/web/gestures/pan";
import { sequence } from "@m3e/web/gestures/sequence";

const item = document.getElementById("item");
detectGesture(
  item,
  sequence(
    { maxInterval: 0 },
    longPress(
      phase({
        onEnd: (detail) => {
          // Handle pick up
        },
      }),
    ),
    pan(
      {
        activationMode: "move",
      },
      phase({
        onUpdate: (detail) => {
          // Handle move
        },
      }),
    ),
  ),
);

Sequence gesture options control how ordered gesture activations are interpreted. They define the maximum allowed interval between each gesture in the sequence, ensuring that the sequence progresses within a consistent time window.

A sequence gesture progresses through all phases and is recognized once each gesture in the sequence has been activated in order within the allowed interval. The recognizer advances through its nested gestures one by one, emitting detail as each step completes.

The sequence gesture detail describes the semantic output of an ordered series of gestures. It aggregates the terminal details produced by each gesture in the sequence in the order they were recognized.

Accessibility

By default, gesture elements are not given an ARIA role, indicating that they are treated as neutral, non-semantic elements in the accessibility tree unless explicitly referenced or annotated.

Native module support

The @m3e/web package uses JavaScript Modules. To use it directly in a browser without a bundler, use a module script similar to the following.

<script type="module" src="/node_modules/@m3e/web/dist/gestures.js"></script>

To include specific recognizers:

<script type="module" src="/node_modules/@m3e/web/dist/gestures-long-press.js"></script>
<script type="module" src="/node_modules/@m3e/web/dist/gestures-pan.js"></script>
<script type="module" src="/node_modules/@m3e/web/dist/gestures-repeat.js"></script>
<script type="module" src="/node_modules/@m3e/web/dist/gestures-rotate.js"></script>
<script type="module" src="/node_modules/@m3e/web/dist/gestures-scale.js"></script>
<script type="module" src="/node_modules/@m3e/web/dist/gestures-sequence.js"></script>
<script type="module" src="/node_modules/@m3e/web/dist/gestures-swipe.js"></script>
<script type="module" src="/node_modules/@m3e/web/dist/gestures-tap.js"></script>
<script type="module" src="/node_modules/@m3e/web/dist/gestures-transform.js"></script>

In addition, you must use an import map to include dependencies.

<script type="importmap">
  {
    "imports": {
      "tslib": "https://cdn.jsdelivr.net/npm/tslib@2.8.1/+esm",
      "lit": "https://cdn.jsdelivr.net/npm/lit@3.3.0/+esm",
      "lit/": "https://cdn.jsdelivr.net/npm/lit@3.3.0/",
      "lit-html": "https://cdn.jsdelivr.net/npm/lit-html@3.3.0/+esm",
      "lit-html/directive.js": "https://cdn.jsdelivr.net/npm/lit-html@3.3.0/directive.js",
      "@lit/reactive-element": "https://cdn.jsdelivr.net/npm/@lit/reactive-element@2.0.4/+esm",
      "@lit/reactive-element/": "https://cdn.jsdelivr.net/npm/@lit/reactive-element@2.0.4/",
      "@m3e/web/core": "/node_modules/@m3e/web/dist/core.js"
      "@m3e/web/gestures": "/node_modules/@m3e/web/dist/gestures.js"
    }
  }
</script>

For production builds, use the minified files to ensure optimal load performance.

API

The @m3e/web package includes a Custom Elements Manifest (custom-elements.json), which documents the properties, attributes, slots, events and CSS custom properties of each component.

You can explore the API below, or integrate the manifest into your own tooling.

m3e-long-press-gesture m3e-pan-gesture m3e-repeat-gesture m3e-rotate-gesture m3e-scale-gesture m3e-sequence-gesture m3e-swipe-gesture m3e-tap-gesture
On this page Gestures