Skip to content

LongPress ​

Wraps any element and emits trigger after a press-and-hold — the web take on Haptic Touch. The wrapped element slowly scales down while held so the user sees something is happening. On macOS Safari with a Force Touch trackpad, a firm click triggers immediately.

Live Demo ​

Open a modal on hold ​

Context menu in a popover ​

A click opens the file; a long press opens its actions. Put the long press inside the popover's default slot and call toggle(true) on trigger.

User card with a peek preview ​

Click the card to open the profile; long press it to peek underneath first.

Quick action with a toast ​

source tells a hold from a Safari force click.

Hold to confirm ​

A longer delay and deeper scale for destructive actions. press-cancel explains what went wrong when the user lets go too early.

Reveal a hidden value ​

API key

Usage ​

vue
<template>
  <orio-long-press @trigger="openModal">
    <orio-button>Hold me</orio-button>
  </orio-long-press>

  <orio-modal v-bind="modalProps">
    <orio-view-text type="title">Hidden feature</orio-view-text>
    <orio-view-text>Opened by a long press.</orio-view-text>
    <orio-button @click="modalProps.show = false">Close</orio-button>
  </orio-modal>
</template>

<script setup>
const { modalProps, openModal } = useModal();
</script>

Context menu in a popover ​

vue
<template>
  <orio-popover position="bottom">
    <template #default="{ toggle }">
      <orio-long-press @trigger="toggle(true)">
        <orio-button variant="secondary" icon="file" @click="openFile">
          report.pdf
        </orio-button>
      </orio-long-press>
    </template>
    <template #content="{ toggle }">
      <div class="menu-panel">
        <orio-button
          v-for="action in fileActions"
          :key="action.label"
          variant="subdued"
          :icon="action.icon"
          @click="runFileAction(action, () => toggle(false))"
        >
          {{ action.label }}
        </orio-button>
      </div>
    </template>
  </orio-popover>
</template>

User card with a peek preview ​

vue
<template>
  <orio-popover position="bottom">
    <template #default="{ toggle }">
      <orio-long-press @trigger="toggle(true)">
        <button type="button" class="user-card" @click="openProfile">
          <img :src="user.avatar" :alt="user.name" draggable="false" />
          <orio-view-text type="subtitle">{{ user.name }}</orio-view-text>
        </button>
      </orio-long-press>
    </template>
    <template #content>
      <div class="user-peek">
        <orio-view-text type="title">{{ user.name }}</orio-view-text>
        <orio-view-text type="italics">{{ user.handle }}</orio-view-text>
        <orio-view-text>{{ user.bio }}</orio-view-text>
        <orio-view-text icon="map-pin">{{ user.location }}</orio-view-text>
      </div>
    </template>
  </orio-popover>
</template>

Hold to confirm ​

vue
<template>
  <orio-long-press
    :delay="1500"
    :scale="0.85"
    @trigger="deleteProject"
    @press-cancel="hintHoldLonger"
  >
    <orio-button icon="delete">Hold to delete project</orio-button>
  </orio-long-press>
</template>

Props ​

PropTypeDefaultDescription
delaynumber500Hold time in ms before trigger
scalenumber0.95Scale reached at the moment of trigger
forceTouchbooleantrueSafari force click triggers immediately and suppresses Look Up
disabledbooleanfalseNo feedback, no events; normal clicks still work
moveTolerancenumber10Pointer movement in px that cancels the press

Events ​

EventPayloadDescription
trigger(event: PointerEvent | MouseEvent, source: "hold" | "force")Hold reached delay, or force click
press-start(event: PointerEvent)Primary pointer went down
press-cancel—Press ended without triggering

trigger passes the original event, so @trigger="openModal" animates the modal from the pressed element.

Behavior ​

  • The click that follows a trigger is swallowed — the wrapped button does not also activate. A short press is a normal click.
  • Keyboard activation (Enter / Space) is never swallowed.
  • Only the left button / primary pointer counts. Ctrl+click (the macOS context-menu gesture) is ignored.
  • Moving past moveTolerance, leaving the element, or a touch scroll cancels.
  • While the finger is down — including after trigger, until release — text selection, the iOS callout and the native context menu are blocked. Text inside the wrapper stays selectable the rest of the time.
  • With prefers-reduced-motion: reduce the scale is skipped; trigger still fires.

Browser support ​

Long press works everywhere. Force click uses Safari's proprietary webkitmouseforce* events — Chrome, Firefox and iOS do not expose trackpad or screen pressure, so there it is a normal long press.

Accessibility ​

There is no keyboard equivalent of a long press. Make sure the action behind trigger is also reachable another way (a menu button, a shortcut).