Skip to content

fix(kanban-card): jumping hover animation & drag overlay consistency#324

Merged
webdevcody merged 3 commits intoAutoMaker-Org:mainfrom
illia1f:fix/kanban-card-ui
Dec 30, 2025
Merged

fix(kanban-card): jumping hover animation & drag overlay consistency#324
webdevcody merged 3 commits intoAutoMaker-Org:mainfrom
illia1f:fix/kanban-card-ui

Conversation

@illia1f
Copy link
Contributor

@illia1f illia1f commented Dec 30, 2025

Summary

This PR fixes a visual regression in the Board view where Kanban cards would "jump" or jitter during hover animations and drag-and-drop interactions.

The Problem

The issue was caused by a conflict between dnd-kit's reordering logic and Tailwind CSS hover transforms. Using useSortable introduced automatic layout calculations and transitions
that competed with the card's hover lift effect (hover:-translate-y-0.5), leading to "snapping" and visual instability.

The Solution

  • Switched to useDraggable: Since card reordering within columns is not yet implemented, we refactored KanbanCard to use useDraggable instead of useSortable. This removes the
    complex reordering logic that was the primary source of the jitter.
  • Stabilized Source Card: Removed transform and transition properties from the source card's dndStyle. The source card now remains stationary with 50% opacity during a drag,
    completely eliminating transform conflicts.
  • Visual Fidelity with DragOverlay: Relied on DragOverlay for the moving visual representation. This ensures the user sees a smooth "lifted" card during the drag while the original
    layout stays perfectly stable.
  • Multi-layered structure:
    • Wrapper Div: Handles dnd-kit node registration and drag listeners.
    • Inner Card: Manages visual hover effects and transitions independently, ensuring they remain smooth and predictable.

Impact

  • Smooth, consistent hover animations in the Board view.
  • Elimination of "jumping" or jittering cards during and after drag-and-drop interactions.
  • A cleaner, more stable dragging experience that avoids unnecessary layout shifts.

Screenshots

Before

image

After

image

Testing

  • Verified hover animations are smooth and stable.
  • Verified drag-and-drop functionality correctly identifies drop targets (columns).
  • Confirmed DragOverlay correctly renders the "lifted" card visual.
  • TypeScript compile checks pass.

Summary by CodeRabbit

  • New Features / Improvements
    • Improved drag-and-drop for cards with more reliable dragging behavior and consistent cursor feedback.
    • Smooth lift animations and refined overlay visuals when moving cards across the board.
    • More consistent rendering of cards in both board and overlay contexts, preserving visual effects like opacity, glassmorphism and optional borders.
    • Enhanced border and cursor handling for clearer interactive affordances and better hover feedback.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 30, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

📝 Walkthrough

Walkthrough

The changes consolidate overlay rendering by replacing direct Card composition with the KanbanCard component for both normal and DragOverlay contexts, refactor the card's drag-and-drop from useSortable to useDraggable, and add an isOverlay prop plus lift, cursor, and border styling behaviors.

Changes

Cohort / File(s) Summary
KanbanCard drag-and-drop & overlay refactor
apps/ui/src/components/views/board-view/components/kanban-card/kanban-card.tsx
Swapped useSortableuseDraggable; added isOverlay?: boolean to KanbanCardProps; introduced lift behavior via useLayoutEffect and isLifted state; added helper funcs getCardBorderStyle and getCursorClass; separated wrapper/card classes and centralized drag styles (dndStyle); refactored render into renderCardContent and adjusted overlay visuals (cursor, background, lift animation).
KanbanBoard overlay & active feature rendering
apps/ui/src/components/views/board-view/kanban-board.tsx
Replaced inline Card composition in DragOverlay and column active feature rendering with KanbanCard instances wrapped in a width-fixing div; passed isOverlay and preserved contextual props; supplied stub handlers where needed.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant Board as KanbanBoard
  participant DnD as DnDProvider/useDraggable
  participant Card as KanbanCard
  Note over User,Board: User starts drag on a card
  User->>Card: pointerDown / initiate drag
  Card->>DnD: register drag via useDraggable (start)
  DnD-->>Board: emits dragging state / activeFeature
  Board->>Card: render DragOverlay with `isOverlay=true` KanbanCard (wrapped, fixed width)
  Note over Card: useLayoutEffect -> set isLifted for overlay visuals
  Card->>User: visual lift + drag cursor + dndStyle applied
  alt drop
    DnD->>Card: end drag / drop
    Card->>Board: update state (position/notify)
  else cancel
    DnD->>Card: cancel
    Card->>Board: revert visuals
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

Suggested labels

Bug

Poem

🐰 With nimble paws I hopped on through,

Swapped Sortable for Draggable too,
I lifted cards with gentle art,
Borders gleam and cursors start,
A joyful hop for UI crew! 🥕

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main fix: addressing jumping hover animations and drag overlay consistency issues in the Kanban card component, which aligns with the PR's core objective of resolving visual regressions.
✨ Finishing touches
  • 📝 Generate docstrings

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @illia1f, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request addresses a visual regression in the Kanban board view where cards exhibited "jumping" or jittering animations during hover and drag-and-drop interactions. The core issue stemmed from a conflict between dnd-kit's useSortable hook and Tailwind CSS hover transforms. The solution involves refactoring the KanbanCard component to use useDraggable for simpler drag functionality, stabilizing the source card's appearance during a drag, and relying on DragOverlay for a consistent visual experience, resulting in smoother and more predictable UI behavior.

Highlights

  • Refactored Drag-and-Drop Logic: Switched from useSortable to useDraggable in the KanbanCard component to resolve visual conflicts, as reordering within columns is not yet implemented.
  • Stabilized Card Hover and Drag: Eliminated "jumping" or jittering animations by removing conflicting transform and transition properties from the source card during drag operations, ensuring it remains stationary with 50% opacity.
  • Enhanced DragOverlay Usage: Leveraged DragOverlay for the visual representation of the dragged card, ensuring a smooth "lifted" effect while the original card stays perfectly stable.
  • Improved Component Structure: Implemented a multi-layered structure for KanbanCard with a wrapper div to handle DND node registration and listeners, and an inner card to manage independent visual hover effects and transitions.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

The pull request refactors the KanbanCard component to switch from @dnd-kit/sortable's useSortable to @dnd-kit/core's useDraggable, introducing new helper functions for card border styling and cursor management. It also adds an isOverlay prop to the KanbanCard and implements a useLayoutEffect to manage a isLifted state, intended for animating the drag overlay. The KanbanBoard component is updated to render the KanbanCard directly within its DragOverlay, reusing the component for the dragged item's visual representation. A review comment points out that the wrapperClasses for the KanbanCard's drag overlay animation are missing Tailwind transition classes, causing an instant 'lift' effect instead of a smooth one, and suggests adding transition-transform duration-200 ease-out to fix this.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
apps/ui/src/components/views/board-view/kanban-board.tsx (1)

192-195: Consider removing unused SortableContext.

Since cards now use useDraggable instead of useSortable, the SortableContext wrapper no longer provides functional value. The PR notes that in-column reordering is not implemented yet, so this could be cleaned up now or kept as scaffolding for future reordering support.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 57b7f92 and ab0cd95.

📒 Files selected for processing (2)
  • apps/ui/src/components/views/board-view/components/kanban-card/kanban-card.tsx
  • apps/ui/src/components/views/board-view/kanban-board.tsx
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Always import from shared packages (@automaker/*), never from old relative paths

Files:

  • apps/ui/src/components/views/board-view/components/kanban-card/kanban-card.tsx
  • apps/ui/src/components/views/board-view/kanban-board.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use resolveModelString() from @automaker/model-resolver to convert model aliases (haiku, sonnet, opus) to full model names

Files:

  • apps/ui/src/components/views/board-view/components/kanban-card/kanban-card.tsx
  • apps/ui/src/components/views/board-view/kanban-board.tsx
🧬 Code graph analysis (2)
apps/ui/src/components/views/board-view/components/kanban-card/kanban-card.tsx (2)
apps/ui/src/lib/utils.ts (1)
  • cn (5-7)
apps/ui/src/components/ui/card.tsx (1)
  • Card (84-84)
apps/ui/src/components/views/board-view/kanban-board.tsx (1)
apps/ui/src/components/views/board-view/components/kanban-card/kanban-card.tsx (1)
  • KanbanCard (59-222)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: e2e
🔇 Additional comments (5)
apps/ui/src/components/views/board-view/components/kanban-card/kanban-card.tsx (4)

1-2: LGTM!

The switch from useSortable to useDraggable aligns with the PR objective of removing reordering behavior that caused animation conflicts. The additional React hooks for managing the lift animation are appropriate.


12-29: LGTM!

Clean extraction of styling logic into helper functions. getCardBorderStyle handles border opacity mixing well, and getCursorClass provides clear cursor feedback based on component state.


89-95: LGTM!

Using useLayoutEffect with requestAnimationFrame is a valid pattern to defer the lift animation until after the overlay is painted, ensuring a smooth visual transition.


206-221: LGTM!

The multi-layered structure cleanly separates dnd-kit registration (outer wrapper with ref, attributes, listeners) from the inner card's visual styling and transitions. Conditionally spreading listeners only when draggable is a good safeguard.

apps/ui/src/components/views/board-view/kanban-board.tsx (1)

243-268: LGTM!

Good improvement to render KanbanCard with isOverlay prop in the DragOverlay instead of a separate Card-based composition. This ensures visual consistency between the dragged card and in-place cards. The fixed width wrapper maintains proper sizing, and empty stub handlers are appropriate since the overlay is non-interactive.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
apps/ui/src/components/views/board-view/components/kanban-card/kanban-card.tsx (1)

89-95: Add cleanup to prevent potential setState warning.

The requestAnimationFrame callback could fire after the component unmounts, causing a React warning about setting state on an unmounted component. While unlikely in practice (the DragOverlay typically stays mounted), adding cleanup improves defensive coding.

🔎 Proposed defensive cleanup
  useLayoutEffect(() => {
    if (isOverlay) {
-     requestAnimationFrame(() => {
+     const rafId = requestAnimationFrame(() => {
        setIsLifted(true);
      });
+     return () => cancelAnimationFrame(rafId);
    }
  }, [isOverlay]);
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ab0cd95 and 88bb5b9.

📒 Files selected for processing (1)
  • apps/ui/src/components/views/board-view/components/kanban-card/kanban-card.tsx
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Always import from shared packages (@automaker/*), never from old relative paths

Files:

  • apps/ui/src/components/views/board-view/components/kanban-card/kanban-card.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use resolveModelString() from @automaker/model-resolver to convert model aliases (haiku, sonnet, opus) to full model names

Files:

  • apps/ui/src/components/views/board-view/components/kanban-card/kanban-card.tsx
🧠 Learnings (1)
📚 Learning: 2025-12-30T01:02:07.114Z
Learnt from: illia1f
Repo: AutoMaker-Org/automaker PR: 324
File: apps/ui/src/components/views/board-view/components/kanban-card/kanban-card.tsx:122-131
Timestamp: 2025-12-30T01:02:07.114Z
Learning: Tailwind CSS v4 uses postfix syntax for the important modifier: append ! at the end of the utility class (e.g., backdrop-blur-[0px]! or hover:bg-red-500!). The older v3 style used a prefix (!) at the start (e.g., !backdrop-blur-[0px]); prefer the postfix form for consistency across TSX files.

Applied to files:

  • apps/ui/src/components/views/board-view/components/kanban-card/kanban-card.tsx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: e2e
🔇 Additional comments (6)
apps/ui/src/components/views/board-view/components/kanban-card/kanban-card.tsx (6)

12-29: LGTM! Clean helper functions.

Both getCardBorderStyle and getCursorClass are well-structured pure functions with clear logic and proper typing. The border style computation correctly handles the enabled/opacity cases, and the cursor states appropriately reflect the drag interaction model.


56-56: LGTM! Appropriate API extension.

The isOverlay optional prop correctly extends the component API to support overlay-specific behavior, aligning with the PR's drag-and-drop refactoring objectives.


107-109: LGTM! Correct dndStyle simplification.

Removing transform and transition from dndStyle and keeping only opacity correctly addresses the PR's core issue—eliminating the conflict between dnd-kit's reordering logic and Tailwind hover transforms that caused jitter.


113-117: LGTM! Complete transition classes present.

The wrapperClasses correctly includes transition-transform duration-200 ease-out, which will smoothly animate the scale and rotation when isLifted becomes true. This addresses the concern from the previous review about missing transition properties.


206-221: LGTM! Clean separation of concerns.

The wrapper structure correctly separates dnd-kit integration (outer div with ref, attributes, and conditional listeners) from the card's visual styling (inner content). The conditional listener spreading at line 211 adds a defensive layer ensuring overlay cards can't be directly dragged, complementing the disabled prop.


1-10: The imports in kanban-card.tsx comply with coding guidelines. The guideline specifies "never from old relative paths," which refers to imports like ../../../lib/utils. The current code uses @/ path aliases (mapped to ./src/* in tsconfig.json), which is the established pattern throughout the apps/ui codebase. No @automaker/* alternatives exist for these app-internal modules (lib/utils, components/ui/card, store/app-store), which are consistently imported via @/ across 15+ files in the application.

@Shironex Shironex added Enhancement Improvements to existing functionality or UI. Testers-Requested Request for others to test an enhancement or bug fix/etc. Do Not Merge Use this label if something should not be merged. labels Dec 30, 2025
@webdevcody webdevcody merged commit b798260 into AutoMaker-Org:main Dec 30, 2025
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Do Not Merge Use this label if something should not be merged. Enhancement Improvements to existing functionality or UI. Testers-Requested Request for others to test an enhancement or bug fix/etc.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants