diff --git a/ui/components/shadcn/tabs/tabs.test.tsx b/ui/components/shadcn/tabs/tabs.test.tsx
new file mode 100644
index 0000000000..ceffefed55
--- /dev/null
+++ b/ui/components/shadcn/tabs/tabs.test.tsx
@@ -0,0 +1,35 @@
+import { render, screen } from "@testing-library/react";
+import userEvent from "@testing-library/user-event";
+import { describe, expect, it } from "vitest";
+
+import { Tabs, TabsContent, TabsList, TabsTrigger } from "./tabs";
+
+describe("Tabs", () => {
+ it("animates tab content when switching between tabs", async () => {
+ // Given - A tabs group with two content panels
+ const user = userEvent.setup();
+ render(
+
+
+ Overview
+ Events
+
+ Overview content
+ Events content
+ ,
+ );
+
+ // When - The user switches to another tab
+ await user.click(screen.getByRole("tab", { name: /events/i }));
+
+ const eventsPanel = screen.getByText("Events content");
+
+ // Then - The newly active content keeps the motion-ready content element
+ expect(eventsPanel).toHaveAttribute("data-slot", "tabs-content");
+ expect(eventsPanel).toHaveClass(
+ "will-change-transform",
+ "motion-reduce:transform-none",
+ );
+ expect(eventsPanel).toHaveAttribute("data-state", "active");
+ });
+});
diff --git a/ui/components/shadcn/tabs/tabs.tsx b/ui/components/shadcn/tabs/tabs.tsx
index 92ca5575c8..e838aa3f23 100644
--- a/ui/components/shadcn/tabs/tabs.tsx
+++ b/ui/components/shadcn/tabs/tabs.tsx
@@ -1,6 +1,7 @@
"use client";
import * as TabsPrimitive from "@radix-ui/react-tabs";
+import { motion, useReducedMotion } from "framer-motion";
import type { ComponentProps, ReactNode } from "react";
import {
@@ -30,7 +31,7 @@ const TRIGGER_STYLES = {
* Content component styles
*/
const CONTENT_STYLES =
- "mt-2 focus-visible:rounded-md focus-visible:outline-1 focus-visible:ring-[3px] focus-visible:border-ring focus-visible:outline-ring focus-visible:ring-ring/50" as const;
+ "mt-2 will-change-transform motion-reduce:transform-none focus-visible:rounded-md focus-visible:outline-1 focus-visible:ring-[3px] focus-visible:border-ring focus-visible:outline-ring focus-visible:ring-ring/50" as const;
/**
* Build trigger className by combining style parts
@@ -122,14 +123,27 @@ function TabsTrigger({
function TabsContent({
className,
+ children,
...props
}: ComponentProps) {
+ const shouldReduceMotion = useReducedMotion();
+
return (
-
+
+
+ {children}
+
+
);
}