Haptic Feedback Guide
The tactile layer of the semantic color system. Every interaction that changes system state has a corresponding haptic signal — selection, impact, and notification types mirror the agency/temporal/validation triad.
Haptic weight matches action weight. Selection = lightest (5ms pulse). Irreversible financial action = heaviest (25ms double-beat). A payment confirmation and a checkbox toggle must feel physically different — the body should know the stakes before the brain reads the screen.
Haptic Types
Subtle tick for toggle state changes, segment selection, checkbox toggle, picker scroll tick. The lightest haptic — acknowledges user choice without interrupting flow.
[5]Haptics.selectionAsync()--ds-motion-interaction (80ms)Small tap feedback for minor state changes. Used at swipe-action reveal threshold, pull-to-refresh peek, and list item press.
[10]Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light)--ds-motion-fast (150ms)Standard tap for primary actions and confirmations. Buttons, swipe-action execution, and agent task approval.
[15, 10, 15]Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium)--ds-motion-smooth (200ms)Strong confirmation for irreversible or high-value actions — payment submission, contract execution, agent authorization with no-undo path.
[25, 5, 25]Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Heavy)--ds-motion-deliberate (300ms)Double-tap pattern for completed operations: payment confirmed, agent task done, form submitted successfully.
[10, 50, 20]Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success)--ds-color-outcome-positiveTriple-tick for attention-required states: threshold breach, validation hold, agent uncertainty, review required.
[10, 30, 10, 30, 10]Haptics.notificationAsync(Haptics.NotificationFeedbackType.Warning)--ds-color-validationError buzz for failures, invalid inputs, payment declined, agent error state, authentication failure.
[30, 10, 30]Haptics.notificationAsync(Haptics.NotificationFeedbackType.Error)--ds-color-outcome-negativeWhich Component Triggers Which Haptic
Component → interaction → haptic category. Columns: component / what triggers it / why this weight.
React Native — expo-haptics
import * as Haptics from 'expo-haptics';
// DS Haptic helper — mirrors the category system
export const dsHaptic = {
// Selection — toggles, pickers, checkboxes
selection: () => Haptics.selectionAsync(),
// Impact — scaled by action weight
impact: {
light: () => Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light),
medium: () => Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium),
heavy: () => Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Heavy),
},
// Notification — outcome signals
notification: {
success: () => Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success),
warning: () => Haptics.notificationAsync(Haptics.NotificationFeedbackType.Warning),
error: () => Haptics.notificationAsync(Haptics.NotificationFeedbackType.Error),
},
} as const;
// Usage in components:
function ApproveButton({ onApprove }) {
const handlePress = async () => {
await dsHaptic.impact.medium(); // medium = approval action
await onApprove();
await dsHaptic.notification.success(); // success on completion
};
return <Pressable onPress={handlePress}>Approve</Pressable>;
}
function PaymentConfirmButton({ onConfirm }) {
const handlePress = async () => {
await dsHaptic.impact.heavy(); // heavy = irreversible financial action
await onConfirm();
await dsHaptic.notification.success();
};
return <Pressable onPress={handlePress}>Confirm Payment</Pressable>;
}Web / PWA — navigator.vibrate()
// Web haptic patterns — navigator.vibrate(pattern)
// pattern: [vibrate, pause, vibrate, ...] in ms
// Single value = one vibration of that duration
export const webHaptic = {
selection: () => navigator.vibrate?.(5),
impactLight: () => navigator.vibrate?.(10),
impactMedium: () => navigator.vibrate?.([15, 10, 15]),
impactHeavy: () => navigator.vibrate?.([25, 5, 25]),
notificationSuccess: () => navigator.vibrate?.([10, 50, 20]),
notificationWarning: () => navigator.vibrate?.([10, 30, 10, 30, 10]),
notificationError: () => navigator.vibrate?.([30, 10, 30]),
} as const;
// Feature detection — graceful degradation
function triggerHaptic(fn: () => void) {
if (typeof navigator !== 'undefined' && 'vibrate' in navigator) fn();
// Silently skip on unsupported platforms (desktop, iOS Safari)
}
// Note: iOS Safari does NOT support navigator.vibrate().
// Use expo-haptics for native iOS apps.
// For iOS PWAs, haptics are unavailable — rely on visual feedback alone.