Haptic Feedback

Tactile feedback patterns that complement visual and audio cues. All Sitka components respect the haptic taxonomy for consistent physical feeling across platforms.

Categories

Haptics convey meaning through vibration patterns and intensity. Sitka provides a cross-platform mapping so a notification-success feels equally affirming on iOS, Android, and the web (where supported).

Impact3 tokens

Physical strikes and collisions; conveys weight, solidity, and confirmation of direct manipulation.

haptic.impact.light

Light tap, gentle confirmation, selection change

Picking from list, scroll end

haptic.impact.medium

Medium-impact collision, standard action feedback

Button press, toggle switch

haptic.impact.heavy

Heavy impact, strong physical feel

Destructive action confirmation, door knock

Platform API mapping

ios
UIImpactFeedbackGenerator(style: .medium)
android
Vibrator.vibrate(VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK))
web
navigator.vibrate(15)
reactNative
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium)

Notification3 tokens

Event-driven signals for attention, success, warning, or error states.

haptic.notification.success

Positive completion, task succeeded

Form submitted, upload complete

haptic.notification.warning

Non-critical issue needs attention

Validation warning, battery low

haptic.notification.error

Critical failure, action blocked

Network error, validation failed

Platform API mapping

ios
UINotificationFeedbackGenerator
android
Vibrator.vibrate(VibrationEffect.createPredefined(VibrationEffect.EFFECT_DOUBLE_CLICK))
web
navigator.vibrate([50, 50, 50])
reactNative
Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success)

Selection2 tokens

Continuous tactile feedback during manipulation tasks like picking or adjusting.

haptic.selection.click

Snap-to-grid or discrete selection tick

Picker wheel selection, slider increment

haptic.selection.pick

Item chosen from set

Dropdown selection, segmented control change

Platform API mapping

ios
UISelectionFeedbackGenerator
android
Vibrator.vibrate(VibrationEffect.createPredefined(VibrationEffect.EFFECT_TICK))
web
navigator.vibrate(8)
reactNative
Haptics.selectionAsync()

Best practices

Don't overuse

Every haptic should mean something. Avoid haptics for routine interactions; reserve for confirmation, error, or delight moments.

Respect accessibility

Users can disable haptics system-wide. Your app must work without them — never gate functionality behind haptic feedback.

Keep it brief

Haptics should last 10–50ms. Longer vibrations feel like buzzing and are annoying.

Sync with visual/audio

Haptics should align with the moment of action completion. Delay breaks the illusion of causality.

Test on device

Simulators cannot accurately reproduce haptics. Test on real hardware to verify intensity and duration.

Use platform defaults

Don't invent custom patterns unless your brand requires it. System patterns are already learned by users.

Implementation

Sitka exposes a unified haptic utility that maps to platform-native APIs. Use it in your interaction handlers to keep tactile feedback consistent.

hooks/useHaptic.ts
import * as Haptics from 'expo-haptics';
import { Platform } from 'react-native';

export type HapticType =
  | "impact.light" | "impact.medium" | "impact.heavy"
  | "notification.success" | "notification.warning" | "notification.error"
  | "selection.click" | "selection.pick";

export function useHaptic() {
  const trigger = (type: HapticType) => {
    if (Platform.OS === 'ios' || Platform.OS === 'android') {
      const [category, variant] = type.split('.') as [HapticCategory, string];
      // Maps to expo-haptics or react-native-haptic-feedback
      Haptics[category]?.[variant]?.();
    } else if (navigator.vibrate) {
      // Web fallback
      const patterns: Record<HapticType, number[]> = {
        "impact.light": [8],
        "impact.medium": [15],
        "impact.heavy": [25],
        "notification.success": [20, 50, 20],
        "notification.error": [30, 30, 30],
      };
      navigator.vibrate(patterns[type]);
    }
  };

  return { trigger };
}

Usage

const { trigger } = useHaptic();

// Button press
<Button
  onPress={() => {
    trigger("impact.medium");
    handleSubmit();
  }}
/>

// Error state
if (error) {
  trigger("notification.error");
}

// Selection in picker
trigger("selection.click");