Back to Dictionary
Action Feedback
●●●Requires Setup
Advanced

Swipe Gesture Hint

A subtle, bouncing horizontal nudge on a list item indicating that it can be swiped to reveal hidden actions (like delete or archive).

Use

Mobile-first list views

Avoid

Desktop applications without touch support

Safer Alternative

No safer default listed.

Risk Level

Low risk

Live Demo

What It Is

A subtle, bouncing horizontal nudge on a list item indicating that it can be swiped to reveal hidden actions (like delete or archive).

When to Use

  • Mobile-first list views
  • Email or message inboxes
  • Task lists

When NOT to Use

  • Desktop applications without touch support
  • Items where a visible actions menu (e.g., "...") is present and sufficient

Configuration Tips

  • 01Play the hint animation only once upon first entering the view
  • 02Move the item slightly (e.g., 20px) to reveal the background action color momentarily

You've Seen It In

Apple MailGmailTodoist

AI Implementation Prompts

Move from a fast scaffold to production details, then tune timing and edge states.

Make the list item wiggle to the left briefly to show it can be swiped.

Reference Implementation

The same implementation approach used by the live preview, kept compact enough to inspect and adapt.

'use client';
import { motion, useAnimation } from 'framer-motion';
import { useEffect } from 'react';
import { Trash2 } from 'lucide-react';

export function SwipeGestureHintDemo({ isPlaying = false }: { isPlaying?: boolean }) {
    const controls = useAnimation();

    useEffect(() => {
        if (!isPlaying) {
            requestAnimationFrame(() => {
                try { controls.set({ x: 0 }); } catch (e) { }
            });
            return;
        }

        const playHint = async () => {
            await new Promise((r) => setTimeout(r, 600));
            "text-stone-400 italic">// Nudge to reveal destructive action
            await controls.start({
                x: -48,
                transition: { type: 'spring', stiffness: 400, damping: 25 }
            });
            await new Promise((r) => setTimeout(r, 300));
            "text-stone-400 italic">// Snap back into place
            await controls.start({
                x: 0,
                transition: { type: 'spring', stiffness: 300, damping: 20 }
            });
        };

        const interval = setInterval(playHint, 2500);
        playHint();

        return () => clearInterval(interval);
    }, [isPlaying, controls]);

    return (
        <div className="flex h-64 w-full items-center justify-center rounded-2xl bg-white p-6 shadow-sm ring-1 ring-stone-200">
            <div className="w-full max-w-[280px] overflow-hidden rounded-xl border border-stone-200 bg-stone-50">
                <div className="relative">

                    {"text-stone-400 italic">/* Background Action (Hidden Menu) */}
                    <div className="absolute inset-0 flex items-center justify-end bg-red-500 px-5">
                        <Trash2 className="h-5 w-5 text-white" />
                    </div>

                    {"text-stone-400 italic">/* Foreground List Item that slides */}
                    <motion.div
                        animate={controls}
                        className="relative flex items-center gap-4 bg-white p-4 shadow-sm"
                    >
                        <div className="h-10 w-10 shrink-0 rounded-full bg-stone-100" />
                        <div className="flex w-full flex-col gap-2">
                            <div className="h-3 w-3/4 rounded bg-stone-200" />
                            <div className="h-2 w-1/2 rounded bg-stone-100" />
                        </div>
                    </motion.div>

                </div>
            </div>
        </div>
    );
}