Problem
web-haptics@0.0.6 no longer produces haptics on iOS 26.5+ for the current imperative trigger path.
There are already a few reports that iOS 26.5 stopped working, but I think the important implementation detail is this: the current fallback still depends on programmatically clicking a hidden <input type="checkbox" switch>/label. That appears to be the part Apple patched.
Why this matters
The library currently exposes a clean API like:
<button onClick={() => trigger("success")}>Tap me</button>
That still makes sense for Android via navigator.vibrate(), but on iOS 26.5+ the haptic seems to require the user to directly tap the native WebKit switch control itself. Calling .click() from JavaScript is no longer enough.
Related finding
m1ckc3s/project-fathom demonstrates a small working pattern on a physical iPhone running iOS 26.5:
- render a real
<input type="checkbox" switch>
- keep native appearance intact
- make it transparent with
opacity: 0, not display: none
- overlay it inside the visible button bounds
- clip the hit area to the visible button shape
- let the user’s actual tap toggle the native control
That is different from the current web-haptics fallback, which creates a hidden switch/label and triggers it through script.
Suggested direction
Consider adding an explicit iOS 26.5+ direct-tap integration instead of trying to keep the same imperative fallback working everywhere.
Possible API shape:
<HapticTarget type="success">
<button>Save</button>
</HapticTarget>
or:
const { getHapticTargetProps } = useWebHaptics();
<button {...getHapticTargetProps("success")}>
Save
</button>
The important part is that the iOS path may need to render/overlay a real native switch inside the user’s actual tap target, while Android can continue using navigator.vibrate().
Acceptance criteria
- iOS 26.5+ has a documented path that does not rely on programmatic
.click().
- The switch remains rendered/tappable on iOS, not
display: none.
- Native switch appearance is not stripped in a way that disables the haptic.
- The overlay is clipped to the visible target so rounded buttons do not have invisible rectangular tap areas.
- Existing imperative
trigger() behavior remains available for Android and existing integrations.
- Docs clearly explain that custom multi-pattern haptics are not possible on iOS 26.5+ through this direct-tap path; it is limited to the native system tick.
- Accessibility is handled deliberately, since an invisible checkbox/switch over a button can create confusing semantics if implemented naively.
Notes
This probably should not be treated as a simple bug fix to trigger(). If iOS now requires a real direct interaction with the native switch, the correct fix may need a new opt-in component/hook API for tap targets.
Problem
web-haptics@0.0.6no longer produces haptics on iOS 26.5+ for the current imperative trigger path.There are already a few reports that iOS 26.5 stopped working, but I think the important implementation detail is this: the current fallback still depends on programmatically clicking a hidden
<input type="checkbox" switch>/label. That appears to be the part Apple patched.Why this matters
The library currently exposes a clean API like:
That still makes sense for Android via
navigator.vibrate(), but on iOS 26.5+ the haptic seems to require the user to directly tap the native WebKit switch control itself. Calling.click()from JavaScript is no longer enough.Related finding
m1ckc3s/project-fathomdemonstrates a small working pattern on a physical iPhone running iOS 26.5:<input type="checkbox" switch>opacity: 0, notdisplay: noneThat is different from the current
web-hapticsfallback, which creates a hidden switch/label and triggers it through script.Suggested direction
Consider adding an explicit iOS 26.5+ direct-tap integration instead of trying to keep the same imperative fallback working everywhere.
Possible API shape:
or:
The important part is that the iOS path may need to render/overlay a real native switch inside the user’s actual tap target, while Android can continue using
navigator.vibrate().Acceptance criteria
.click().display: none.trigger()behavior remains available for Android and existing integrations.Notes
This probably should not be treated as a simple bug fix to
trigger(). If iOS now requires a real direct interaction with the native switch, the correct fix may need a new opt-in component/hook API for tap targets.