From 7c0acf9ff954cbade9e438765add3d218583c9dc Mon Sep 17 00:00:00 2001 From: jayk293 Date: Sat, 7 Mar 2026 22:16:39 +0000 Subject: [PATCH 1/2] update isSupported check to expand detection of mobile devices create supportsVibrationAPI internal check for to replace isSupported to detect VibrationApi --- packages/web-haptics/README.md | 2 +- packages/web-haptics/src/lib/web-haptics/index.ts | 13 +++++++++---- site/src/surfaces/docs/index.tsx | 2 +- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/packages/web-haptics/README.md b/packages/web-haptics/README.md index 73c292a..9bdd74e 100644 --- a/packages/web-haptics/README.md +++ b/packages/web-haptics/README.md @@ -112,7 +112,7 @@ Show or hide the haptic feedback toggle switch. ### `WebHaptics.isSupported` -Static boolean — `true` if the device supports the Vibration API. +Static boolean — `true` if the device supports haptics (touch devices such as iOS and Android). ## License diff --git a/packages/web-haptics/src/lib/web-haptics/index.ts b/packages/web-haptics/src/lib/web-haptics/index.ts index e54ca9e..be44874 100644 --- a/packages/web-haptics/src/lib/web-haptics/index.ts +++ b/packages/web-haptics/src/lib/web-haptics/index.ts @@ -152,9 +152,14 @@ export class WebHaptics { this.showSwitch = options?.showSwitch ?? false; } - static readonly isSupported: boolean = + private static readonly supportsVibrationAPI: boolean = typeof navigator !== "undefined" && typeof navigator.vibrate === "function"; + static readonly isSupported: boolean = + typeof navigator !== "undefined" && + (typeof navigator.vibrate === "function" || + navigator.maxTouchPoints > 0); + async trigger( input: HapticInput = [{ duration: 25, intensity: 0.7 }], options?: TriggerOptions, @@ -186,11 +191,11 @@ export class WebHaptics { } } - if (WebHaptics.isSupported) { + if (WebHaptics.supportsVibrationAPI) { navigator.vibrate(toVibratePattern(vibrations, defaultIntensity)); } - if (!WebHaptics.isSupported || this.debug) { + if (!WebHaptics.supportsVibrationAPI || this.debug) { this.ensureDOM(); if (!this.hapticLabel) return; @@ -222,7 +227,7 @@ export class WebHaptics { cancel(): void { this.stopPattern(); - if (WebHaptics.isSupported) { + if (WebHaptics.supportsVibrationAPI) { navigator.vibrate(0); } } diff --git a/site/src/surfaces/docs/index.tsx b/site/src/surfaces/docs/index.tsx index 59f0731..051dfe5 100644 --- a/site/src/surfaces/docs/index.tsx +++ b/site/src/surfaces/docs/index.tsx @@ -61,7 +61,7 @@ const methods = [ { signature: "WebHaptics.isSupported: boolean", description: - "Static property. Returns true if the device supports the Vibration API.", + "Static property. Returns true if the device supports haptics (touch devices such as iOS and Android)", }, ]; From 89634444faf023dd9d321c1163b3a77592171e65 Mon Sep 17 00:00:00 2001 From: jayk293 Date: Sun, 15 Mar 2026 17:05:54 +0000 Subject: [PATCH 2/2] Updated isSupported to validate that device is actually a mobile device and not just a touch enabled device such as touch laptops or iPads --- packages/web-haptics/README.md | 2 +- .../web-haptics/src/lib/web-haptics/index.ts | 30 +++++++++++++++---- site/src/surfaces/docs/index.tsx | 2 +- 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/packages/web-haptics/README.md b/packages/web-haptics/README.md index 9bdd74e..c1ce6d9 100644 --- a/packages/web-haptics/README.md +++ b/packages/web-haptics/README.md @@ -112,7 +112,7 @@ Show or hide the haptic feedback toggle switch. ### `WebHaptics.isSupported` -Static boolean — `true` if the device supports haptics (touch devices such as iOS and Android). +Static boolean — `true` if the device supports haptics (iPhone and Android). Returns `false` on iPad/iPadOS, desktop, and other non-mobile platforms. ## License diff --git a/packages/web-haptics/src/lib/web-haptics/index.ts b/packages/web-haptics/src/lib/web-haptics/index.ts index be44874..5dae80b 100644 --- a/packages/web-haptics/src/lib/web-haptics/index.ts +++ b/packages/web-haptics/src/lib/web-haptics/index.ts @@ -155,15 +155,35 @@ export class WebHaptics { private static readonly supportsVibrationAPI: boolean = typeof navigator !== "undefined" && typeof navigator.vibrate === "function"; - static readonly isSupported: boolean = - typeof navigator !== "undefined" && - (typeof navigator.vibrate === "function" || - navigator.maxTouchPoints > 0); + private static readonly isMobileDevice: boolean = (() => { + if (typeof navigator === "undefined") return false; + if (navigator.maxTouchPoints <= 0) return false; + + if ("userAgentData" in navigator) { + return (navigator.userAgentData as { mobile: boolean }).mobile === true; + } + + // Safari & Firefox fallback + const userAgent = navigator.userAgent; + if (/Android|iPhone|iPod/.test(userAgent)) return true; + + return false; + })(); + + private static readonly needsTouchFallback = WebHaptics.isMobileDevice && !WebHaptics.supportsVibrationAPI; + + static readonly isSupported: boolean = WebHaptics.isMobileDevice; async trigger( input: HapticInput = [{ duration: 25, intensity: 0.7 }], options?: TriggerOptions, ): Promise { + + if (!WebHaptics.isSupported && !this.debug) { + console.warn(`[web-haptics] Haptics not supported on this device.`,); + return; + } + const normalized = normalizeInput(input); if (!normalized) return; @@ -195,7 +215,7 @@ export class WebHaptics { navigator.vibrate(toVibratePattern(vibrations, defaultIntensity)); } - if (!WebHaptics.supportsVibrationAPI || this.debug) { + if (WebHaptics.needsTouchFallback || this.debug) { this.ensureDOM(); if (!this.hapticLabel) return; diff --git a/site/src/surfaces/docs/index.tsx b/site/src/surfaces/docs/index.tsx index 051dfe5..26b92aa 100644 --- a/site/src/surfaces/docs/index.tsx +++ b/site/src/surfaces/docs/index.tsx @@ -61,7 +61,7 @@ const methods = [ { signature: "WebHaptics.isSupported: boolean", description: - "Static property. Returns true if the device supports haptics (touch devices such as iOS and Android)", + "Static property. Returns true if the device supports haptics (iPhone and Android). Returns false on iPad, desktops, and other non-mobile platforms.", }, ];