interactivelabsDesigned & engineered by Ayesha

Clean, fine-tuned, microinteractions

A hands-on collection of smooth, multi-step UI interactions.

Profile Dropdown Menu

Click the avatar to open a menu that slides and fades in. Hover between items to see the highlight glide, flip the theme toggle, or open the nested submenu.

'use client';

import { useEffect, useRef, useState } from 'react';
import { AnimatePresence, motion } from 'framer-motion';
import { ChevronDown, ChevronRight, CreditCard, LogOut, Moon, Settings, User } from 'lucide-react';

const menuItems = [
  { id: 'profile', label: 'Profile', icon: User },
  { id: 'billing', label: 'Billing', icon: CreditCard },
];

export default function ProfileDropdown() {
  const [open, setOpen] = useState(false);
  const [hovered, setHovered] = useState<string | null>(null);
  const [darkMode, setDarkMode] = useState(true);
  const [moreOpen, setMoreOpen] = useState(false);
  const ref = useRef<HTMLDivElement>(null);

  // Close on outside click or Escape
  useEffect(() => {
    if (!open) return;
    const handleClick = (e: MouseEvent) => {
      if (ref.current && !ref.current.contains(e.target as Node)) {
        setOpen(false);
        setMoreOpen(false);
      }
    };
    const handleKey = (e: KeyboardEvent) => {
      if (e.key === 'Escape') setOpen(false);
    };
    document.addEventListener('mousedown', handleClick);
    document.addEventListener('keydown', handleKey);
    return () => {
      document.removeEventListener('mousedown', handleClick);
      document.removeEventListener('keydown', handleKey);
    };
  }, [open]);

  return (
    <motion.div
      ref={ref}
      className="relative flex flex-col items-center gap-2"
    >
      <motion.button
        layout="position"
        onClick={() => setOpen((v) => !v)}
        transition={{ layout: { duration: 0.35, ease: [0.04, 0.62, 0.23, 0.98] } }}
        className="dropdown-trigger"
      >
        Account
        <motion.span animate={{ rotate: open ? 180 : 0 }} transition={{ duration: 0.2 }}>
          <ChevronDown className="size-4" />
        </motion.span>
      </motion.button>

      <AnimatePresence initial={false}>
        {open && (
          <motion.div
            initial={{ height: 0, opacity: 0 }}
            animate={{ height: 'auto', opacity: 1 }}
            exit={{ height: 0, opacity: 0 }}
            transition={{ duration: 0.35, ease: [0.04, 0.62, 0.23, 0.98] }}
            className="overflow-hidden"
          >
            <motion.div className="dropdown-panel">
            {menuItems.map((item) => (
              <button
                key={item.id}
                onMouseEnter={() => setHovered(item.id)}
                onMouseLeave={() => setHovered(null)}
                className="dropdown-item"
              >
                {/* Shared layoutId makes the highlight glide between items */}
                {hovered === item.id && (
                  <motion.span
                    layoutId="dropdown-highlight"
                    className="dropdown-highlight"
                    transition={{ type: 'spring', stiffness: 500, damping: 35 }}
                  />
                )}
                <item.icon className="size-4" />
                {item.label}
              </button>
            ))}

            {/* Toggle switch row */}
            <div className="dropdown-item dropdown-item--row">
              <span><Moon className="size-4" /> Dark mode</span>
              <button
                role="switch"
                aria-checked={darkMode}
                onClick={() => setDarkMode((v) => !v)}
                className={`switch ${darkMode ? 'switch--on' : ''}`}
              >
                <motion.span layout transition={{ type: 'spring', stiffness: 700, damping: 30 }} className="switch-knob" />
              </button>
            </div>

            {/* Expandable submenu */}
            <button onClick={() => setMoreOpen((v) => !v)} className="dropdown-item dropdown-item--row">
              <span><Settings className="size-4" /> More options</span>
              <motion.span animate={{ rotate: moreOpen ? 90 : 0 }} transition={{ duration: 0.2 }}>
                <ChevronRight className="size-4" />
              </motion.span>
            </button>
            <AnimatePresence initial={false}>
              {moreOpen && (
                <motion.div
                  initial={{ height: 0, opacity: 0 }}
                  animate={{ height: 'auto', opacity: 1 }}
                  exit={{ height: 0, opacity: 0 }}
                  transition={{ duration: 0.25, ease: [0.04, 0.62, 0.23, 0.98] }}
                  className="overflow-hidden"
                >
                  <div className="dropdown-submenu">
                    <button className="dropdown-item">Keyboard shortcuts</button>
                    <button className="dropdown-item">Integrations</button>
                    <button className="dropdown-item">Download data</button>
                  </div>
                </motion.div>
              )}
            </AnimatePresence>

            <button className="dropdown-item dropdown-item--danger">
              <LogOut className="size-4" /> Log out
            </button>
            </motion.div>
          </motion.div>
        )}
      </AnimatePresence>
    </motion.div>
  );
}

Settings Popover

A floating settings panel with a draggable slider, toggle switches, and a color picker that confirms your choice with an animated checkmark.

'use client';

import { useEffect, useRef, useState } from 'react';
import { AnimatePresence, motion } from 'framer-motion';
import { Bell, Check, Save, SlidersHorizontal, Volume2 } from 'lucide-react';

const colors = [
  { id: 'violet', value: '#8b5cf6' },
  { id: 'blue', value: '#3b82f6' },
  { id: 'emerald', value: '#10b981' },
  { id: 'rose', value: '#f43f5e' },
];

export default function SettingsPopover() {
  const [open, setOpen] = useState(false);
  const [volume, setVolume] = useState(60);
  const [notifications, setNotifications] = useState(true);
  const [autoSave, setAutoSave] = useState(false);
  const [color, setColor] = useState('violet');
  const ref = useRef<HTMLDivElement>(null);
  const trackRef = useRef<HTMLDivElement>(null);

  // Close on outside click / Escape
  useEffect(() => {
    if (!open) return;
    const handleClick = (e: MouseEvent) => {
      if (ref.current && !ref.current.contains(e.target as Node)) setOpen(false);
    };
    const handleKey = (e: KeyboardEvent) => e.key === 'Escape' && setOpen(false);
    document.addEventListener('mousedown', handleClick);
    document.addEventListener('keydown', handleKey);
    return () => {
      document.removeEventListener('mousedown', handleClick);
      document.removeEventListener('keydown', handleKey);
    };
  }, [open]);

  // Convert a pointer position into a 0-100 value along the track
  const updateFromPointer = (clientX: number) => {
    const track = trackRef.current;
    if (!track) return;
    const rect = track.getBoundingClientRect();
    const pct = ((clientX - rect.left) / rect.width) * 100;
    setVolume(Math.round(Math.min(100, Math.max(0, pct))));
  };

  return (
    <motion.div
      ref={ref}
      className="relative flex flex-col items-center gap-2"
    >
      <motion.button
        layout="position"
        onClick={() => setOpen((v) => !v)}
        transition={{ layout: { duration: 0.35, ease: [0.04, 0.62, 0.23, 0.98] } }}
        className="settings-trigger"
      >
        <SlidersHorizontal className="size-4 settings-trigger-icon" />
        <span className="settings-trigger-label">Customize</span>
      </motion.button>

      <AnimatePresence initial={false}>
        {open && (
          <motion.div
            initial={{ height: 0, opacity: 0 }}
            animate={{ height: 'auto', opacity: 1 }}
            exit={{ height: 0, opacity: 0 }}
            transition={{ duration: 0.35, ease: [0.04, 0.62, 0.23, 0.98] }}
            className="overflow-hidden"
          >
          <div className="settings-panel">
            {/* Draggable slider */}
            <div className="settings-row">
              <span><Volume2 className="size-4" /> Volume</span>
              <span>{volume}%</span>
            </div>
            <div
              ref={trackRef}
              onClick={(e) => updateFromPointer(e.clientX)}
              className="slider-track"
            >
              <div className="slider-fill" style={{ width: `${volume}%` }} />
              <motion.div
                onPan={(_e, info) => updateFromPointer(info.point.x)}
                whileTap={{ scale: 1.25 }}
                className="slider-knob"
                style={{ left: `${volume}%` }}
              />
            </div>

            {/* Toggle switches */}
            <ToggleRow icon={Bell} label="Notifications" checked={notifications} onChange={setNotifications} />
            <ToggleRow icon={Save} label="Auto-save" checked={autoSave} onChange={setAutoSave} />

            {/* Color picker with animated checkmark */}
            <div className="swatch-row">
              {colors.map((swatch) => (
                <button
                  key={swatch.id}
                  onClick={() => setColor(swatch.id)}
                  className="swatch"
                  style={{ backgroundColor: swatch.value }}
                >
                  {color === swatch.id && (
                    <motion.span
                      initial={{ scale: 0 }}
                      animate={{ scale: 1 }}
                      exit={{ scale: 0 }}
                      transition={{ type: 'spring', stiffness: 500, damping: 25 }}
                      className="swatch-check"
                    >
                      <Check className="size-3.5" />
                    </motion.span>
                  )}
                </button>
              ))}
            </div>
          </div>
          </motion.div>
        )}
      </AnimatePresence>
    </motion.div>
  );
}

function ToggleRow({ icon: Icon, label, checked, onChange }: any) {
  return (
    <div className="settings-row">
      <span><Icon className="size-4" /> {label}</span>
      <button
        role="switch"
        aria-checked={checked}
        onClick={() => onChange(!checked)}
        className={`switch ${checked ? 'switch--on' : ''}`}
      >
        <motion.span layout transition={{ type: 'spring', stiffness: 700, damping: 30 }} className="switch-knob" />
      </button>
    </div>
  );
}

Expandable Action Card

Click the card to expand it and reveal a row of action buttons that stagger into view, each with its own hover and tap feedback.

'use client';

import { useState } from 'react';
import { AnimatePresence, motion } from 'framer-motion';
import { Bookmark, ChevronDown, Heart, MessageCircle, Share2 } from 'lucide-react';

const listVariants = {
  hidden: {},
  visible: { transition: { staggerChildren: 0.06, delayChildren: 0.05 } },
};
const itemVariants = {
  hidden: { opacity: 0, y: 8, scale: 0.9 },
  visible: { opacity: 1, y: 0, scale: 1 },
};

export default function ExpandableActionCard() {
  const [expanded, setExpanded] = useState(false);
  const [liked, setLiked] = useState(false);
  const [saved, setSaved] = useState(false);
  const [burstKey, setBurstKey] = useState(0);

  const toggleLike = () => {
    setLiked((v) => {
      if (!v) setBurstKey((k) => k + 1);
      return !v;
    });
  };

  return (
    // "layout" animates the card's height smoothly as content is added/removed
    <motion.div
      layout
      onClick={() => setExpanded((v) => !v)}
      className="card"
      transition={{ layout: { duration: 0.35, ease: [0.04, 0.62, 0.23, 0.98] } }}
    >
      <motion.div layout="position" className="card-header">
        <div className="card-thumb" />
        <div>
          <h3>Designing with motion</h3>
          <p>Notes on building interfaces that feel alive.</p>
        </div>
        <motion.span animate={{ rotate: expanded ? 180 : 0 }} transition={{ duration: 0.2 }}>
          <ChevronDown className="size-4" />
        </motion.span>
      </motion.div>

      <AnimatePresence initial={false}>
        {expanded && (
          // staggerChildren makes each action button pop in one after another
          <motion.div variants={listVariants} initial="hidden" animate="visible" exit="hidden" className="card-actions">
            <motion.button
              variants={itemVariants}
              whileHover={{ scale: 1.08 }}
              whileTap={{ scale: 0.9 }}
              onClick={(e) => { e.stopPropagation(); toggleLike(); }}
              className={`action ${liked ? 'action--liked' : ''}`}
            >
              <Heart className="size-4" fill={liked ? 'currentColor' : 'none'} />
              Like
              {/* Particle burst radiating outward on like */}
              <AnimatePresence>
                {liked && Array.from({ length: 8 }).map((_, i) => {
                  const angle = (i / 8) * Math.PI * 2;
                  return (
                    <motion.span
                      key={`${burstKey}-${i}`}
                      initial={{ opacity: 1, scale: 0, x: 0, y: 0 }}
                      animate={{ opacity: 0, scale: 1, x: Math.cos(angle) * 28, y: Math.sin(angle) * 28 }}
                      transition={{ duration: 0.5, ease: 'easeOut' }}
                      className="particle"
                    />
                  );
                })}
              </AnimatePresence>
            </motion.button>

            <motion.button
              variants={itemVariants}
              whileHover={{ scale: 1.08 }}
              whileTap={{ scale: 0.9 }}
              onClick={(e) => { e.stopPropagation(); setSaved((v) => !v); }}
              className={`action ${saved ? 'action--saved' : ''}`}
            >
              <motion.span animate={saved ? { rotate: [0, -15, 15, 0] } : {}} transition={{ duration: 0.35 }}>
                <Bookmark className="size-4" fill={saved ? 'currentColor' : 'none'} />
              </motion.span>
              Save
            </motion.button>

            <motion.button variants={itemVariants} whileHover={{ scale: 1.08 }} whileTap={{ scale: 0.9 }} className="action">
              <Share2 className="size-4" /> Share
            </motion.button>

            <motion.span variants={itemVariants} className="card-meta">
              <MessageCircle className="size-4" /> 12
            </motion.span>
          </motion.div>
        )}
      </AnimatePresence>
    </motion.div>
  );
}

Two-Step Feedback Popover

Open the feedback popover, rate your experience and leave a note, then watch it morph into an animated success state.

'use client';

import { useEffect, useRef, useState } from 'react';
import { AnimatePresence, motion } from 'framer-motion';
import { MessageSquarePlus } from 'lucide-react';

const moods = [
  { id: 'bad', emoji: '😞', label: 'Bad' },
  { id: 'meh', emoji: '😐', label: 'Meh' },
  { id: 'good', emoji: '🙂', label: 'Good' },
  { id: 'great', emoji: '🤩', label: 'Great' },
];

export default function FeedbackPopover() {
  const [open, setOpen] = useState(false);
  const [submitted, setSubmitted] = useState(false);
  const [mood, setMood] = useState<string | null>(null);
  const [message, setMessage] = useState('');
  const ref = useRef<HTMLDivElement>(null);

  // Close on outside click / Escape
  useEffect(() => {
    if (!open) return;
    const handleClick = (e: MouseEvent) => {
      if (ref.current && !ref.current.contains(e.target as Node)) setOpen(false);
    };
    const handleKey = (e: KeyboardEvent) => e.key === 'Escape' && setOpen(false);
    document.addEventListener('mousedown', handleClick);
    document.addEventListener('keydown', handleKey);
    return () => {
      document.removeEventListener('mousedown', handleClick);
      document.removeEventListener('keydown', handleKey);
    };
  }, [open]);

  function handleSubmit(e: React.FormEvent) {
    e.preventDefault();
    if (!mood) return;
    setSubmitted(true);
    // Auto-close after the success state has been shown for a moment
    setTimeout(() => {
      setOpen(false);
      setTimeout(() => {
        setSubmitted(false);
        setMood(null);
        setMessage('');
      }, 200);
    }, 1600);
  }

  return (
    <motion.div
      ref={ref}
      className="relative flex flex-col-reverse items-center gap-2"
    >
      <motion.button
        layout="position"
        onClick={() => setOpen((v) => !v)}
        transition={{ layout: { duration: 0.35, ease: [0.04, 0.62, 0.23, 0.98] } }}
        className="feedback-trigger"
      >
        <MessageSquarePlus className="size-4 feedback-trigger-icon" />
        <span className="feedback-trigger-label">Feedback</span>
      </motion.button>

      <AnimatePresence initial={false}>
        {open && (
          <motion.div
            initial={{ height: 0, opacity: 0 }}
            animate={{ height: 'auto', opacity: 1 }}
            exit={{ height: 0, opacity: 0 }}
            transition={{ duration: 0.35, ease: [0.04, 0.62, 0.23, 0.98] }}
            className="overflow-hidden"
          >
          <div className="feedback-panel">
            {/* AnimatePresence cross-fades between the form and the success state */}
            <AnimatePresence mode="popLayout" initial={false}>
              {!submitted ? (
                <motion.form
                  key="form"
                  onSubmit={handleSubmit}
                  initial={{ opacity: 0, x: -8 }}
                  animate={{ opacity: 1, x: 0 }}
                  exit={{ opacity: 0, x: -8 }}
                  transition={{ duration: 0.15 }}
                  className="feedback-form"
                >
                  <p className="feedback-title">How's your experience?</p>
                  <div className="mood-row">
                    {moods.map((option) => (
                      <button
                        key={option.id}
                        type="button"
                        aria-pressed={mood === option.id}
                        onClick={() => setMood(option.id)}
                        className="mood-btn"
                      >
                        {mood === option.id && (
                          <motion.span
                            layoutId="mood-highlight"
                            transition={{ type: 'spring', stiffness: 500, damping: 30 }}
                            className="mood-highlight"
                          />
                        )}
                        <motion.span
                          animate={{ scale: mood === option.id ? 1.2 : 1 }}
                          transition={{ type: 'spring', stiffness: 500, damping: 15 }}
                          className="mood-emoji"
                        >
                          {option.emoji}
                        </motion.span>
                      </button>
                    ))}
                  </div>
                  <textarea
                    value={message}
                    onChange={(e) => setMessage(e.target.value)}
                    placeholder="Tell us more (optional)"
                    rows={2}
                    className="feedback-textarea"
                  />
                  <button type="submit" disabled={!mood} className="feedback-submit">
                    Send feedback
                  </button>
                </motion.form>
              ) : (
                <motion.div
                  key="success"
                  initial={{ opacity: 0, scale: 0.9 }}
                  animate={{ opacity: 1, scale: 1 }}
                  exit={{ opacity: 0, scale: 0.9 }}
                  transition={{ duration: 0.2 }}
                  className="feedback-success"
                >
                  <div className="feedback-check">
                    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
                      {/* pathLength animates from 0 to 1, drawing the checkmark stroke */}
                      <motion.path
                        d="M5 13l4 4L19 7"
                        initial={{ pathLength: 0 }}
                        animate={{ pathLength: 1 }}
                        transition={{ duration: 0.4, ease: 'easeOut' }}
                      />
                    </svg>
                  </div>
                  <div>
                    <p className="feedback-title">Thanks for your feedback!</p>
                    <p className="feedback-subtitle">We'll use this to improve.</p>
                  </div>
                </motion.div>
              )}
            </AnimatePresence>
          </div>
          </motion.div>
        )}
      </AnimatePresence>
    </motion.div>
  );
}

Command Menu

Open a command palette, search to filter the list in real time, navigate with arrow keys, and select with a satisfying checkmark.

'use client';

import { useEffect, useMemo, useRef, useState } from 'react';
import { AnimatePresence, motion } from 'framer-motion';
import {
  BarChart, Calendar, Check, CreditCard, FileText, Folder,
  LayoutGrid, Mail, Search, Settings, User,
} from 'lucide-react';

const items = [
  { id: 'new-file', label: 'New file', icon: FileText, group: 'Create' },
  { id: 'new-folder', label: 'New folder', icon: Folder, group: 'Create' },
  { id: 'dashboard', label: 'Go to dashboard', icon: LayoutGrid, group: 'Navigate' },
  { id: 'profile', label: 'Open profile', icon: User, group: 'Navigate' },
  { id: 'calendar', label: 'Open calendar', icon: Calendar, group: 'Navigate' },
  { id: 'analytics', label: 'View analytics', icon: BarChart, group: 'Navigate' },
  { id: 'billing', label: 'Manage billing', icon: CreditCard, group: 'Settings' },
  { id: 'settings', label: 'Open settings', icon: Settings, group: 'Settings' },
  { id: 'email', label: 'Compose email', icon: Mail, group: 'Create' },
];

export default function CommandMenu() {
  const [open, setOpen] = useState(false);
  const [query, setQuery] = useState('');
  const [activeIndex, setActiveIndex] = useState(0);
  const [selectedId, setSelectedId] = useState<string | null>(null);
  const inputRef = useRef<HTMLInputElement>(null);
  const ref = useRef<HTMLDivElement>(null);

  const filtered = useMemo(() => {
    const q = query.trim().toLowerCase();
    if (!q) return items;
    return items.filter((item) => item.label.toLowerCase().includes(q));
  }, [query]);

  function openMenu() {
    setOpen(true);
    setQuery('');
    setSelectedId(null);
    setActiveIndex(0);
    requestAnimationFrame(() => inputRef.current?.focus());
  }

  // Global Cmd+K / Ctrl+K shortcut to toggle the menu
  useEffect(() => {
    const handler = (e: KeyboardEvent) => {
      if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === 'k') {
        e.preventDefault();
        setOpen((v) => {
          if (v) return false;
          openMenu();
          return true;
        });
      }
    };
    document.addEventListener('keydown', handler);
    return () => document.removeEventListener('keydown', handler);
  }, []);

  // Close on outside click / Escape
  useEffect(() => {
    if (!open) return;
    const handleClick = (e: MouseEvent) => {
      if (ref.current && !ref.current.contains(e.target as Node)) setOpen(false);
    };
    const handleKey = (e: KeyboardEvent) => e.key === 'Escape' && setOpen(false);
    document.addEventListener('mousedown', handleClick);
    document.addEventListener('keydown', handleKey);
    return () => {
      document.removeEventListener('mousedown', handleClick);
      document.removeEventListener('keydown', handleKey);
    };
  }, [open]);

  function handleQueryChange(value: string) {
    setQuery(value);
    setActiveIndex(0);
  }

  function handleKeyDown(e: React.KeyboardEvent) {
    if (e.key === 'ArrowDown') {
      e.preventDefault();
      setActiveIndex((i) => Math.min(i + 1, filtered.length - 1));
    } else if (e.key === 'ArrowUp') {
      e.preventDefault();
      setActiveIndex((i) => Math.max(i - 1, 0));
    } else if (e.key === 'Enter') {
      e.preventDefault();
      const item = filtered[activeIndex];
      if (!item) return;
      setSelectedId(item.id);
      setTimeout(() => setOpen(false), 400);
    } else if (e.key === 'Escape') {
      setOpen(false);
    }
  }

  return (
    <motion.div ref={ref} className="relative flex flex-col items-center">
      {/* The shell only morphs its width and border radius — its height never
          changes, so this layout animation stays decoupled from the results
          panel growing in below it. The two pieces sit flush with matching
          radii so they read as one component. */}
      <motion.div
        layout
        transition={{ layout: { duration: 0.35, ease: [0.04, 0.62, 0.23, 0.98] } }}
        style={{
          borderTopLeftRadius: open ? 16 : 9999,
          borderTopRightRadius: open ? 16 : 9999,
          borderBottomLeftRadius: open ? 0 : 9999,
          borderBottomRightRadius: open ? 0 : 9999,
        }}
        className={`command-shell ${open ? 'command-shell--open' : 'command-shell--closed'}`}
      >
        {open ? (
          <>
            <Search className="size-4 shrink-0" />
            <input
              ref={inputRef}
              value={query}
              onChange={(e) => handleQueryChange(e.target.value)}
              onKeyDown={handleKeyDown}
              placeholder="Type a command or search..."
            />
            <kbd className="shrink-0">esc</kbd>
          </>
        ) : (
          <button type="button" onClick={openMenu} className="command-trigger">
            <Search className="size-4 shrink-0" />
            <span className="command-trigger-label">Search commands...</span>
            <kbd className="shrink-0">⌘K</kbd>
          </button>
        )}
      </motion.div>

      <AnimatePresence initial={false}>
        {open && (
          <motion.div
            initial={{ height: 0, opacity: 0 }}
            animate={{ height: 'auto', opacity: 1 }}
            exit={{ height: 0, opacity: 0 }}
            transition={{ duration: 0.35, ease: [0.04, 0.62, 0.23, 0.98] }}
            className="command-results-wrapper"
          >
            <div className="command-results">
              <div className="command-list">
                {filtered.length === 0 && <p className="command-empty">No results found.</p>}
                {filtered.map((item, index) => {
                  const Icon = item.icon;
                  const isActive = index === activeIndex;
                  const isSelected = selectedId === item.id;
                  return (
                    <button
                      key={item.id}
                      onMouseEnter={() => setActiveIndex(index)}
                      onClick={() => {
                        setSelectedId(item.id);
                        setTimeout(() => setOpen(false), 400);
                      }}
                      className="command-item"
                    >
                      {/* layoutId makes the highlight glide between items as activeIndex changes */}
                      {isActive && (
                        <motion.span
                          layoutId="command-highlight"
                          transition={{ type: 'spring', stiffness: 500, damping: 40 }}
                          className="command-highlight"
                        />
                      )}
                      <Icon className="relative size-4" />
                      <span className="relative flex-1">{item.label}</span>
                      <span className="relative command-group">{item.group}</span>
                      <AnimatePresence>
                        {isSelected && (
                          <motion.span
                            initial={{ scale: 0, opacity: 0 }}
                            animate={{ scale: 1, opacity: 1 }}
                            exit={{ scale: 0, opacity: 0 }}
                            transition={{ type: 'spring', stiffness: 500, damping: 25 }}
                            className="relative command-check"
                          >
                            <Check className="size-4" />
                          </motion.span>
                        )}
                      </AnimatePresence>
                    </button>
                  );
                })}
              </div>
            </div>
          </motion.div>
        )}
      </AnimatePresence>
    </motion.div>
  );
}