DEVELOPER DOCUMENTATION

Core concepts

Learn what sparsh observes, how the policies decide, and how to tune the guard without breaking expected input.

INTERACTION MODEL

Protect the intent behind an activation

A user begins an interaction against the screen they can perceive. Before the browser commits the click or key activation, an interface update may move the target, replace its identity, or change what it means. sparsh records intent and a target snapshot, then compares the snapshot at activation time. A suspicious activation can be reported or stopped according to mode.

  1. Resolve: map the raw event target to the nearest interactive element.
  2. Snapshot: record its perceivable age, position, identity, and semantic fingerprint.
  3. Evaluate: run applicable policies and produce one decision.
  4. Respond: allow the activation or cancel it, then emit callbacks for application feedback.

MODES

Enforce by default; use report to tune

The default mode is enforce. In report mode, policies still evaluate and callbacks still fire, but the activation is allowed through. You can set one global mode or override modes by policy.

import { ActivationGuardProvider } from '@sparshlabs/react'

<ActivationGuardProvider
  mode={{ age: 'report' }}
  policies={['age', 'continuity', 'semantics', 'doubleFire']}
>
  <Application />
</ActivationGuardProvider>

When a mode map is supplied, any omitted policy remains enforce. The first applicable policy that returns a suspicious verdict determines the decision; a report-mode verdict lets that activation through and ends evaluation for it. Consider policy order and overlap when mixing modes.

Decision fieldMeaning
allowedThe first suspicious policy verdict; false means a policy flagged the activation.
enforcedTrue only when that verdict was in enforce mode and sparsh stopped the activation.
policyThe policy that produced the verdict; absent when all applicable policies allow.
reasonHuman-readable explanation for a suspicious verdict, when provided.
eventInput kind and activation phase.
targetIdentity summary, perceivable age, and optional stable guard key.

POLICY REFERENCE

Four independent signals

PolicyWhat it checksInputsDefault
ageTarget has been perceivable for at least cooldownMs.Pointer, keyboard, virtualEnforce; threshold 500 ms
continuityTarget identity and rectangle stayed stable from pointer down to pointer up.PointerEnforce; tolerance 4 px
semanticsMeaningful target properties did not change during the interaction.All kinds; needs a stored intent snapshotEnforce; text comparison off
doubleFireA rapid residual press did not land on a new control at overlapping coordinates.PointerEnforce; 300 ms window

Age

Age measures time since an element became perceivable, not simply when its DOM node was created. A target younger than cooldownMs is flagged. Unknown or pre-existing targets are treated as old (infinite age), which fails open. Age also applies to keyboard and virtual activations; if the threshold catches legitimate fast keyboard use, configure Age in report mode or tune the cooldown.

Continuity

Continuity compares identity and position across the pointer press. It does not run for keyboard or virtual activations because they have no pointer-down snapshot.

Semantics

The default fingerprint checks disabled state, accessible label, role, link target or control value, and data-guard-key. Text comparison is opt-in because counters, timestamps, and live labels can change during an otherwise valid interaction. The current DOM host records semantic intent on pointer down; without a stored intent snapshot, Semantics allows the activation. Age still evaluates the target’s current age for keyboard and virtual input.

DoubleFire

This pointer-only policy looks for a different, newly visible target at overlapping coordinates shortly after an allowed pointer activation. A quick repeat on the same stable control is allowed; this policy is not a general double-click debounce.

DYNAMIC LISTS

Give reused rows a stable identity

A framework key helps the renderer reconcile nodes, but it is not automatically available to the DOM host. Add data-guard-key when one physical row can represent different entities after a reorder or update.

<ul>
  {invoices.map((invoice) => (
    <li key={invoice.id} data-guard-key={invoice.id}>
      <button onClick={() => openInvoice(invoice.id)}>
        Open {invoice.name}
      </button>
    </li>
  ))}
</ul>

sparsh reads the nearest ancestor’s key. Without it, a recycled DOM element whose content changes in place may look like the same target, especially while text comparison is disabled.

INPUT AND ACCESSIBILITY

Keyboard and assistive technology remain part of the model

The DOM host observes Pointer Events and activation keys Enter and Space. It does not intercept Escape, Tab, focus changes, scrolling, or unrelated keys. Continuity and DoubleFire are pointer-only. Age evaluates keyboard and virtual activations; Semantics only compares meaning when it has a stored intent snapshot. Report mode is available if Age needs tuning for fast keyboard users.

A block should be explained. Use onDecision or a per-element hook to show a status message, preserve focus, and invite the user to try again. onBlocked is optional visual feedback, not a replacement for an accessible explanation.

KNOWN LIMITS

Signals are intentionally conservative

  • Opacity-only reveals on an element that already exists are not detected by the current age tracking.
  • An overlay can cover an unmoved target without changing its resolved identity; occlusion is not generally detected.
  • Age treats unknown/pre-existing targets as old and allows them, favoring fewer false positives.
  • In-place entity changes need data-guard-key for reliable identity comparison.
  • Exceptions in policy evaluation fail open. Visual callback exceptions are ignored and do not affect cancellation.

For binding-specific APIs, see the React, Vue, and DOM references.