mobile
Long-Press Context Menu
Holding a surface lifts it into an iOS-style peek and reveals its actions; a small movement during the hold cancels it instead of firing mid-scroll.
- mobile
- long-press
- context-menu
- peek
Touch-first long-press with a peek scale and movement-cancel, distinct from Context Menu Surface's desktop right-click trigger.
Live preview
Quick usage
import { LongPressContextMenu } from "@pinky-ui/systems";
<LongPressContextMenu actions={actions}><PhotoTile /></LongPressContextMenu>Install
Add @pinky-ui/systems as a dependency, or use the CLI to copy this component's source directly into your project — no dependency to manage, fully editable.
npm install @pinky-ui/systemspnpm add @pinky-ui/systemsyarn add @pinky-ui/systemsnpx pinky-ui add long-press-context-menuPrefer to run the whole repository locally instead?
git clone https://github.com/florash/Pinky-UI.git
cd Pinky-UI
npm install
npm run devAccessibility
- Every gesture has a labelled button or keyboard path, and primary controls meet a 44px touch target.
- State remains readable without relying on colour or motion.
Performance
- Interaction state is local and bounded; gesture updates do not require a page-wide render loop.
- Timers and listeners are cleaned up on unmount.
Reduced motion
The same state change resolves immediately; travel, scale and spring motion are removed without removing the interaction.
When to use
- Touch-first product surfaces where the next action should stay close to the hand.
When not to use
- Desktop-only workflows or interactions whose gesture would hide the only available action.
Skill
Purpose
The iOS-style contextual menu: holding a surface lifts it into a small peek scale and reveals its actions beside it. Touch-first by construction — desktop gets an equivalent right-click trigger, not a translated gesture.
Use when
A grid or list item (a photo, a card, a message) has a small set of secondary actions that don't need their own visible affordance on every item — the hold itself is the discovery path, backed by a real button for anyone who can't or won't hold.
Avoid
- Primary actions — anything the user needs on every visit should have a visible control, not just a long-press
- Inside a horizontally-swipeable row (Swipe Actions) — two gesture systems competing for the same touch start is unresolvable; see [[mobile-gesture-conflicts]]
- Long lists where holding any item should feel safe — the movement-cancel threshold exists exactly so scrolling never accidentally opens a menu, but don't rely on it as the only path in
Interaction
A pointer-down starts a hold timer; moving more than ~10px before the timer fires cancels it, so scroll gestures never trigger the menu. On success, the surface scales up on Jelly's snappy spring, a scrim dims behind it, and the action list appears. Escape, a tap outside, or an action itself closes it.
Accessibility
A visually-hidden "More actions" button sits on every instance regardless of input method — long-press is a shortcut to the same menu, never the only way to reach it. role="menu"/role="menuitem" mark the surface; Escape and outside-press both dismiss.
Reduced motion
The peek scale and menu entrance resolve immediately; the scrim still appears so the menu reads as a distinct layer.
Composition and anti-patterns
Don't combine with Hold-to-Reveal Actions on the same element — pick one hold-triggered pattern per surface. Compose with a plain grid or list; it doesn't manage its own layout.