Gallery's design card has no 'duplicate' button on hover — user wanting to fork a design has to open the per-card dropdown menu and click 'Duplicate', no one-click affordance for the most common action #277

Closed
opened 2026-10-04 03:42:02 +00:00 by Mark · 1 comment
Owner

Finding

src/components/gallery/DesignCard.tsx's grid view (per the snippet at line 281-289) has a per-card "Open" button and a DropdownMenu trigger. The DropdownMenu has the Duplicate option, but it requires two clicks (open menu, click Duplicate).

A user who wants to duplicate a design:

  • Hovers the card
  • Sees only the Open button + dropdown trigger
  • Has to open the dropdown for "Duplicate"

The polish move is to expose Duplicate (and maybe other common actions) as hover-revealed icon buttons on the card itself.

Where

  • DesignCard grid view: src/components/gallery/DesignCard.tsx:280-330
  • Buttons per row: just "Open" + DropdownMenu trigger
  • Duplicate is only in the dropdown

Suggested fix shape

Add hover-revealed icon buttons for the 3 most common actions:

<motion.div
  initial={{ opacity: 0 }}
  animate={{ opacity: isHovered ? 1 : 0 }}
  className="flex items-center gap-1"
>
  <button onClick={onDuplicate} title="Duplicate" data-testid="duplicate-card">
    <Copy size={14} />
  </button>
  <button onClick={onExportDesign} title="Export" data-testid="export-card">
    <Download size={14} />
  </button>
  <button onClick={onToggleFavorite} title="Favorite" data-testid="favorite-card">
    <Star size={14} fill={design.isFavorite ? '#E6A948' : 'none'} />
  </button>
</motion.div>

The buttons are revealed on hover (with framer-motion's initial/animate). The icons are the same ones used in the dropdown.

isHovered is the existing hover state (used by the dropdown trigger's background).

Severity

Low. The user can use the dropdown. Power users notice the friction.

Discovered by

Gap analysis (2026-10-02) of gallery card affordances.

  • The dropdown menu (per RightClickMenu.tsx) has 8+ actions; the hover-revealed buttons are the most-common 3.
  • Figma/Notion have hover-revealed actions on cards; this is the standard pattern.
  • The per-card favorites heart (line 501-505 of DesignCard) is already a hover-revealed button — the pattern is established.
## Finding `src/components/gallery/DesignCard.tsx`'s grid view (per the snippet at line 281-289) has a per-card "Open" button and a DropdownMenu trigger. The DropdownMenu has the Duplicate option, but it requires two clicks (open menu, click Duplicate). A user who wants to duplicate a design: - Hovers the card - Sees only the Open button + dropdown trigger - Has to open the dropdown for "Duplicate" The polish move is to expose Duplicate (and maybe other common actions) as hover-revealed icon buttons on the card itself. ## Where - DesignCard grid view: `src/components/gallery/DesignCard.tsx:280-330` - Buttons per row: just "Open" + DropdownMenu trigger - Duplicate is only in the dropdown ## Suggested fix shape Add hover-revealed icon buttons for the 3 most common actions: ```tsx <motion.div initial={{ opacity: 0 }} animate={{ opacity: isHovered ? 1 : 0 }} className="flex items-center gap-1" > <button onClick={onDuplicate} title="Duplicate" data-testid="duplicate-card"> <Copy size={14} /> </button> <button onClick={onExportDesign} title="Export" data-testid="export-card"> <Download size={14} /> </button> <button onClick={onToggleFavorite} title="Favorite" data-testid="favorite-card"> <Star size={14} fill={design.isFavorite ? '#E6A948' : 'none'} /> </button> </motion.div> ``` The buttons are revealed on hover (with framer-motion's `initial`/`animate`). The icons are the same ones used in the dropdown. `isHovered` is the existing hover state (used by the dropdown trigger's background). ## Severity Low. The user can use the dropdown. Power users notice the friction. ## Discovered by Gap analysis (2026-10-02) of gallery card affordances. ## Related - The dropdown menu (per `RightClickMenu.tsx`) has 8+ actions; the hover-revealed buttons are the most-common 3. - Figma/Notion have hover-revealed actions on cards; this is the standard pattern. - The per-card favorites heart (line 501-505 of DesignCard) is already a hover-revealed button — the pattern is established.
Author
Owner

Shipped fix(#277) at 6706ef3 on origin/main.

Grid-view cards now expose Duplicate, Export, and Favorite as hover-revealed icon buttons inside the card's info area — one-click access to the most common actions instead of the existing right-click → menu → action path (3 clicks). The row:

  • reveals on isHovered (or always-visible in selectable mode)
  • stops click propagation so the card's draggable body never sees the click
  • reuses the same lucide-react icons + amber accent as the existing thumbnail favorite star so the visual language stays consistent
  • honors the existing onExportDesign optional-prop contract — Export is silently a no-op if the parent hasn't wired it

The thumbnail favorite star (which the existing #112 aria tests pin to) gains a data-testid=favorite-card-thumbnail so #112 stays scoped to the original star while the new hover-row favorite is independently testable via data-testid=favorite-card. New regression tests in issue-277-hover-actions.test.tsx cover all four contracts: button rendering with accessible names, click handlers fire with correct args, click propagation is stopped, and Export works without onExportDesign.

Verified: 106/106 gallery tests pass, 1407 total vitest pass, 0 new TS errors, ESLint clean on all touched files.

Closes #277

Shipped fix(#277) at 6706ef3 on origin/main. Grid-view cards now expose Duplicate, Export, and Favorite as hover-revealed icon buttons inside the card's info area — one-click access to the most common actions instead of the existing right-click → menu → action path (3 clicks). The row: * reveals on `isHovered` (or always-visible in `selectable` mode) * stops click propagation so the card's draggable body never sees the click * reuses the same lucide-react icons + amber accent as the existing thumbnail favorite star so the visual language stays consistent * honors the existing `onExportDesign` optional-prop contract — Export is silently a no-op if the parent hasn't wired it The thumbnail favorite star (which the existing #112 aria tests pin to) gains a `data-testid=favorite-card-thumbnail` so #112 stays scoped to the original star while the new hover-row favorite is independently testable via `data-testid=favorite-card`. New regression tests in `issue-277-hover-actions.test.tsx` cover all four contracts: button rendering with accessible names, click handlers fire with correct args, click propagation is stopped, and Export works without `onExportDesign`. Verified: 106/106 gallery tests pass, 1407 total vitest pass, 0 new TS errors, ESLint clean on all touched files. Closes #277
Mark closed this issue 2026-10-04 14:08:47 +00:00
Sign in to join this conversation.
No description provided.