diff --git a/ui/components/shadcn/collapsible.test.tsx b/ui/components/shadcn/collapsible.test.tsx
new file mode 100644
index 0000000000..827e2ce5dc
--- /dev/null
+++ b/ui/components/shadcn/collapsible.test.tsx
@@ -0,0 +1,59 @@
+import { render, screen } from "@testing-library/react";
+import { describe, expect, it } from "vitest";
+
+import {
+ Collapsible,
+ CollapsibleContent,
+ CollapsibleTrigger,
+} from "./collapsible";
+
+describe("Collapsible", () => {
+ it("uses an intentional open and close motion contract", () => {
+ // Given
+ render(
+
+ Toggle details
+ Expandable content
+ ,
+ );
+
+ // When
+ const content = screen.getByText("Expandable content");
+
+ // Then
+ expect(content).toHaveAttribute("data-slot", "collapsible-content");
+ expect(content).toHaveClass(
+ "overflow-hidden",
+ "duration-200",
+ "ease-out",
+ "data-[state=open]:animate-in",
+ "data-[state=open]:fade-in-0",
+ "data-[state=open]:slide-in-from-top-1",
+ "data-[state=closed]:animate-out",
+ "data-[state=closed]:fade-out-0",
+ "data-[state=closed]:slide-out-to-top-1",
+ "data-[state=closed]:duration-150",
+ "data-[state=closed]:ease-in",
+ );
+ });
+
+ it("removes transform-heavy motion for reduced-motion users", () => {
+ // Given
+ render(
+
+ Toggle details
+ Expandable content
+ ,
+ );
+
+ // When
+ const content = screen.getByText("Expandable content");
+
+ // Then
+ expect(content).toHaveClass(
+ "motion-reduce:animate-none",
+ "motion-reduce:transform-none",
+ "motion-reduce:transition-none",
+ );
+ });
+});
diff --git a/ui/components/shadcn/collapsible.tsx b/ui/components/shadcn/collapsible.tsx
index 58a2ac20a5..da4f1384ce 100644
--- a/ui/components/shadcn/collapsible.tsx
+++ b/ui/components/shadcn/collapsible.tsx
@@ -2,6 +2,8 @@
import * as CollapsiblePrimitive from "@radix-ui/react-collapsible";
+import { cn } from "@/lib/utils";
+
function Collapsible({
...props
}: React.ComponentProps) {
@@ -20,11 +22,20 @@ function CollapsibleTrigger({
}
function CollapsibleContent({
+ className,
...props
}: React.ComponentProps) {
return (
);
diff --git a/ui/components/shadcn/tree-view/tree-leaf.tsx b/ui/components/shadcn/tree-view/tree-leaf.tsx
index 1348bd9324..ba857bd784 100644
--- a/ui/components/shadcn/tree-view/tree-leaf.tsx
+++ b/ui/components/shadcn/tree-view/tree-leaf.tsx
@@ -58,7 +58,9 @@ export function TreeLeaf({
className={cn(
"flex items-center gap-2 rounded-md px-2 py-1.5",
"hover:bg-prowler-white/5 cursor-pointer",
+ "transition-[background-color,box-shadow,color] duration-150 ease-out motion-reduce:transition-none",
"focus-visible:ring-border-input-primary-press focus-visible:ring-2 focus-visible:outline-none",
+ isSelected && "bg-prowler-white/5",
item.disabled && "cursor-not-allowed opacity-50",
item.className,
)}
diff --git a/ui/components/shadcn/tree-view/tree-node.tsx b/ui/components/shadcn/tree-view/tree-node.tsx
index fbd0703df8..c50bddd090 100644
--- a/ui/components/shadcn/tree-view/tree-node.tsx
+++ b/ui/components/shadcn/tree-view/tree-node.tsx
@@ -96,7 +96,9 @@ export function TreeNode({
className={cn(
"flex items-center gap-2 rounded-md px-2 py-1.5",
"hover:bg-prowler-white/5 cursor-pointer",
+ "transition-[background-color,box-shadow,color] duration-150 ease-out motion-reduce:transition-none",
"focus-visible:ring-border-input-primary-press focus-visible:ring-2 focus-visible:outline-none",
+ isSelected && "bg-prowler-white/5",
item.disabled && "cursor-not-allowed opacity-50",
item.className,
)}
@@ -110,7 +112,7 @@ export function TreeNode({
onKeyDown={handleKeyDown}
>